diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 507119845c3..fe913ed779e 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -68,9 +68,6 @@ var ( "0": 64, } // Default number of blocks after which to checkpoint and reset the pending votes - extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity - extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal - uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. // diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures @@ -154,11 +151,11 @@ func Ecrecover(header *types.Header, sigcache *lru.ARCCache[libcommon.Hash, libc return address, nil } // Retrieve the signature from the header extra-data - if len(header.Extra) < extraSeal { + if len(header.Extra) < types.ExtraSealLength { return libcommon.Address{}, errMissingSignature } - signature := header.Extra[len(header.Extra)-extraSeal:] + signature := header.Extra[len(header.Extra)-types.ExtraSealLength:] // Recover the public key and the Ethereum address pubkey, err := crypto.Ecrecover(SealHash(header, c).Bytes(), signature) @@ -542,7 +539,7 @@ func (c *Bor) verifyHeader(chain consensus.ChainHeaderReader, header *types.Head isSprintEnd := isSprintStart(number+1, c.config.CalculateSprint(number)) // Ensure that the extra-data contains a signer list on checkpoint, but none otherwise - signersBytes := len(header.Extra) - extraVanity - extraSeal + signersBytes := len(GetValidatorBytes(header, c.config)) if !isSprintEnd && signersBytes != 0 { return errExtraValidators } @@ -584,11 +581,11 @@ func (c *Bor) verifyHeader(chain consensus.ChainHeaderReader, header *types.Head // ValidateHeaderExtraField validates that the extra-data contains both the vanity and signature. // header.Extra = header.Vanity + header.ProducerBytes (optional) + header.Seal func ValidateHeaderExtraField(extraBytes []byte) error { - if len(extraBytes) < extraVanity { + if len(extraBytes) < types.ExtraVanityLength { return errMissingVanity } - if len(extraBytes) < extraVanity+extraSeal { + if len(extraBytes) < types.ExtraVanityLength+types.ExtraSealLength { return errMissingSignature } @@ -917,11 +914,11 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header, s header.Difficulty = new(big.Int).SetUint64(snap.Difficulty(c.authorizedSigner.Load().signer)) // Ensure the extra data has all it's components - if len(header.Extra) < extraVanity { - header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) + if len(header.Extra) < types.ExtraVanityLength { + header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, types.ExtraVanityLength-len(header.Extra))...) } - header.Extra = header.Extra[:extraVanity] + header.Extra = header.Extra[:types.ExtraVanityLength] // get validator set if number // Note: headers.Extra has producer set and not validator set. The bor @@ -941,13 +938,47 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header, s // sort validator by address sort.Sort(valset.ValidatorsByAddress(newValidators)) - for _, validator := range newValidators { - header.Extra = append(header.Extra, validator.HeaderBytes()...) + if c.config.IsParallelUniverse(header.Number.Uint64()) { + var tempValidatorBytes []byte + + for _, validator := range newValidators { + tempValidatorBytes = append(tempValidatorBytes, validator.HeaderBytes()...) + } + + blockExtraData := &BlockExtraData{ + ValidatorBytes: tempValidatorBytes, + TxDependency: nil, + } + + blockExtraDataBytes, err := rlp.EncodeToBytes(blockExtraData) + if err != nil { + log.Error("error while encoding block extra data: %v", err) + return fmt.Errorf("error while encoding block extra data: %v", err) + } + + header.Extra = append(header.Extra, blockExtraDataBytes...) + } else { + for _, validator := range newValidators { + header.Extra = append(header.Extra, validator.HeaderBytes()...) + } + } + } else if c.config.IsParallelUniverse(header.Number.Uint64()) { + blockExtraData := &BlockExtraData{ + ValidatorBytes: nil, + TxDependency: nil, + } + + blockExtraDataBytes, err := rlp.EncodeToBytes(blockExtraData) + if err != nil { + log.Error("error while encoding block extra data: %v", err) + return fmt.Errorf("error while encoding block extra data: %v", err) } + + header.Extra = append(header.Extra, blockExtraDataBytes...) } // add extra seal space - header.Extra = append(header.Extra, make([]byte, extraSeal)...) + header.Extra = append(header.Extra, make([]byte, types.ExtraSealLength)...) // Mix digest is reserved for now, set to empty header.MixDigest = libcommon.Hash{} @@ -1161,7 +1192,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result if err != nil { return err } - copy(header.Extra[len(header.Extra)-extraSeal:], sighash) + copy(header.Extra[len(header.Extra)-types.ExtraSealLength:], sighash) go func() { // Wait until sealing is terminated or delay timeout. @@ -1576,3 +1607,54 @@ func getUpdatedValidatorSet(oldValidatorSet *valset.ValidatorSet, newVals []*val func isSprintStart(number, sprint uint64) bool { return number%sprint == 0 } + +// In bor, RLP encoding of BlockExtraData will be stored in the Extra field in the header +type BlockExtraData struct { + // Validator bytes of bor + ValidatorBytes []byte + + // length of TxDependency -> n (n = number of transactions in the block) + // length of TxDependency[i] -> k (k = a whole number) + // k elements in TxDependency[i] -> transaction indexes on which transaction i is dependent on + TxDependency [][]uint64 +} + +// Returns the Block-STM Transaction Dependency from the block header +func GetTxDependency(b *types.Block) [][]uint64 { + tempExtra := b.Extra() + + if len(tempExtra) < types.ExtraVanityLength+types.ExtraSealLength { + log.Error("length of extra less is than vanity and seal") + return nil + } + + var blockExtraData BlockExtraData + + if err := rlp.DecodeBytes(tempExtra[types.ExtraVanityLength:len(tempExtra)-types.ExtraSealLength], &blockExtraData); err != nil { + log.Error("error while decoding block extra data", "err", err) + return nil + } + + return blockExtraData.TxDependency +} + +func GetValidatorBytes(h *types.Header, config *chain.BorConfig) []byte { + tempExtra := h.Extra + + if !config.IsParallelUniverse(h.Number.Uint64()) { + return tempExtra[types.ExtraVanityLength : len(tempExtra)-types.ExtraSealLength] + } + + if len(tempExtra) < types.ExtraVanityLength+types.ExtraSealLength { + log.Error("length of extra less is than vanity and seal") + return nil + } + + var blockExtraData BlockExtraData + if err := rlp.DecodeBytes(tempExtra[types.ExtraVanityLength:len(tempExtra)-types.ExtraSealLength], &blockExtraData); err != nil { + log.Error("error while decoding block extra data", "err", err) + return nil + } + + return blockExtraData.ValidatorBytes +} diff --git a/consensus/bor/snapshot.go b/consensus/bor/snapshot.go index d28d76c7cfb..836acf36343 100644 --- a/consensus/bor/snapshot.go +++ b/consensus/bor/snapshot.go @@ -181,7 +181,7 @@ func (s *Snapshot) Apply(parent *types.Header, headers []*types.Header, logger l if err := ValidateHeaderExtraField(header.Extra); err != nil { return snap, err } - validatorBytes := header.Extra[extraVanity : len(header.Extra)-extraSeal] + validatorBytes := GetValidatorBytes(header, s.config) // get validators from headers and use that for new validator set newVals, _ := valset.ParseValidators(validatorBytes) diff --git a/core/types/block.go b/core/types/block.go index af025683318..1a434098bbf 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -22,12 +22,13 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/ledgerwatch/erigon-lib/common/hexutil" "io" "math/big" "reflect" "sync/atomic" + "github.com/ledgerwatch/erigon-lib/common/hexutil" + "github.com/gballet/go-verkle" libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/common/hexutility" @@ -40,6 +41,9 @@ import ( var ( EmptyRootHash = libcommon.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") EmptyUncleHash = rlpHash([]*Header(nil)) + + ExtraVanityLength = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity + ExtraSealLength = 65 // Fixed number of extra-data suffix bytes reserved for signer seal ) // A BlockNonce is a 64-bit hash which proves (combined with the diff --git a/core/types/block_test.go b/core/types/block_test.go index 3b62f66b18f..4e2a8303d3f 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -28,6 +28,7 @@ import ( libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/common/hexutility" types2 "github.com/ledgerwatch/erigon-lib/types" + "github.com/ledgerwatch/log/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -39,6 +40,86 @@ import ( "github.com/ledgerwatch/erigon/rlp" ) +// the following 2 functions are replica for the test +// This is a replica of `bor.GetValidatorBytes` function +// This was needed because currently, `IsParallelUniverse` will always return false. +func GetValidatorBytesTest(h *Header) []byte { + if len(h.Extra) < ExtraVanityLength+ExtraSealLength { + log.Error("length of extra is less than vanity and seal") + return nil + } + + var blockExtraData BlockExtraDataTest + if err := rlp.DecodeBytes(h.Extra[ExtraVanityLength:len(h.Extra)-ExtraSealLength], &blockExtraData); err != nil { + log.Error("error while decoding block extra data", "err", err) + return nil + } + + return blockExtraData.ValidatorBytes +} + +func GetTxDependencyTest(b *Block) [][]uint64 { + if len(b.header.Extra) < ExtraVanityLength+ExtraSealLength { + log.Error("length of extra less is than vanity and seal") + return nil + } + + var blockExtraData BlockExtraDataTest + if err := rlp.DecodeBytes(b.header.Extra[ExtraVanityLength:len(b.header.Extra)-ExtraSealLength], &blockExtraData); err != nil { + log.Error("error while decoding block extra data", "err", err) + return nil + } + + return blockExtraData.TxDependency +} + +type BlockExtraDataTest struct { + // Validator bytes of bor + ValidatorBytes []byte + + // length of TxDependency -> n (n = number of transactions in the block) + // length of TxDependency[i] -> k (k = a whole number) + // k elements in TxDependency[i] -> transaction indexes on which transaction i is dependent on + TxDependency [][]uint64 +} + +func TestTxDependencyBlockDecoding(t *testing.T) { + t.Parallel() + + blockEnc := common.FromHex("f90270f9026ba00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8825208845506eb07b8710000000000000000000000000000000000000000000000000000000000000000cf8776616c20736574c6c20201c201800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498880000000000000000c0c0") + + var block Block + + if err := rlp.DecodeBytes(blockEnc, &block); err != nil { + t.Fatal("decode error: ", err) + } + check := func(f string, got, want interface{}) { + if !reflect.DeepEqual(got, want) { + t.Errorf("%s mismatch: got %v, want %v", f, got, want) + } + } + + check("Coinbase", block.Coinbase(), libcommon.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1")) + check("MixDigest", block.MixDigest(), libcommon.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498")) + check("Root", block.Root(), libcommon.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) + check("Time", block.Time(), uint64(1426516743)) + + validatorBytes := GetValidatorBytesTest(block.header) + txDependency := GetTxDependencyTest(&block) + + check("validatorBytes", validatorBytes, []byte("val set")) + check("txDependency", txDependency, [][]uint64{{2, 1}, {1, 0}}) + + ourBlockEnc, err := rlp.EncodeToBytes(&block) + + if err != nil { + t.Fatal("encode error: ", err) + } + if !bytes.Equal(ourBlockEnc, blockEnc) { + t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc) + } +} + // from bcValidBlockTest.json, "SimpleTx" func TestBlockEncoding(t *testing.T) { t.Parallel() diff --git a/erigon-lib/chain/chain_config.go b/erigon-lib/chain/chain_config.go index 78d8da8e6cd..6e93c59ff90 100644 --- a/erigon-lib/chain/chain_config.go +++ b/erigon-lib/chain/chain_config.go @@ -470,6 +470,8 @@ type BorConfig struct { AgraBlock *big.Int `json:"agraBlock"` // Agra switch block (nil = no fork, 0 = already in agra) StateSyncConfirmationDelay map[string]uint64 `json:"stateSyncConfirmationDelay"` // StateSync Confirmation Delay, in seconds, to calculate `to` + ParallelUniverseBlock *big.Int `json:"parallelUniverseBlock"` // TODO: update all occurrence, change name and finalize number (hardfork for block-stm related changes) + sprints sprints } @@ -561,6 +563,17 @@ func (c *BorConfig) IsIndore(number uint64) bool { return isForked(c.IndoreBlock, number) } +// TODO: modify this function once the block number is finalized +func (c *BorConfig) IsParallelUniverse(number uint64) bool { + if c.ParallelUniverseBlock != nil { + if c.ParallelUniverseBlock.Cmp(big.NewInt(0)) == 0 { + return false + } + } + + return isForked(c.ParallelUniverseBlock, number) +} + func (c *BorConfig) CalculateStateSyncDelay(number uint64) uint64 { return borKeyValueConfigHelper(c.StateSyncConfirmationDelay, number) } diff --git a/eth/stagedsync/stage_bor_heimdall.go b/eth/stagedsync/stage_bor_heimdall.go index 46b7507d3e3..d990fc952de 100644 --- a/eth/stagedsync/stage_bor_heimdall.go +++ b/eth/stagedsync/stage_bor_heimdall.go @@ -294,7 +294,7 @@ func BorHeimdallForward( if !mine && header != nil { sprintLength := cfg.chainConfig.Bor.CalculateSprint(blockNum) if blockNum > zerothSpanEnd && ((blockNum+1)%sprintLength == 0) { - if err = checkHeaderExtraData(u, ctx, chain, blockNum, header); err != nil { + if err = checkHeaderExtraData(u, ctx, chain, blockNum, header, cfg.chainConfig.Bor); err != nil { return err } } @@ -322,6 +322,7 @@ func checkHeaderExtraData( chain consensus.ChainHeaderReader, blockNum uint64, header *types.Header, + config *chain.BorConfig, ) error { var spanID uint64 if blockNum+1 > zerothSpanEnd { @@ -339,7 +340,7 @@ func checkHeaderExtraData( sort.Sort(valset.ValidatorsByAddress(producerSet)) - headerVals, err := valset.ParseValidators(header.Extra[extraVanity : len(header.Extra)-extraSeal]) + headerVals, err := valset.ParseValidators(bor.GetValidatorBytes(header, config)) if err != nil { return err }