mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
Added TxDependency Metadata to ExtraData in Block Header in Bor for Block-STM (#8037)
This PR adds support to store the transaction dependency (generated by the block producer) in the block header for bor. This transaction dependency will then be used by the parallel processor ([Block-STM](https://github.com/ledgerwatch/erigon/pull/7812/)). I have created another [PR](https://github.com/ledgerwatch/erigon-lib/pull/1064) in the erigon-lib repo which adds the `IsParallelUniverse()` function.
This commit is contained in:
parent
28fff1b35e
commit
59909a7efe
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user