mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
Merge pull request #234 from ngtuna/double-validation-refactor
move DV to posv package
This commit is contained in:
commit
0ae81138f5
3 changed files with 111 additions and 142 deletions
|
|
@ -19,12 +19,13 @@ package posv
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -47,6 +48,7 @@ const (
|
||||||
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
|
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
|
||||||
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
|
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
|
||||||
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
|
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
|
||||||
|
M2ByteLength = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
type Masternode struct {
|
type Masternode struct {
|
||||||
|
|
@ -132,6 +134,8 @@ var (
|
||||||
// errUnauthorized is returned if a header is signed by a non-authorized entity.
|
// errUnauthorized is returned if a header is signed by a non-authorized entity.
|
||||||
errUnauthorized = errors.New("unauthorized")
|
errUnauthorized = errors.New("unauthorized")
|
||||||
|
|
||||||
|
errFailedDoubleValidation = errors.New("wrong pair of creator-validator in double validation")
|
||||||
|
|
||||||
// errWaitTransactions is returned if an empty block is attempted to be sealed
|
// errWaitTransactions is returned if an empty block is attempted to be sealed
|
||||||
// on an instant chain (0 second period). It's important to refuse these as the
|
// on an instant chain (0 second period). It's important to refuse these as the
|
||||||
// block reward is zero, so an empty block just bloats the chain... fast.
|
// block reward is zero, so an empty block just bloats the chain... fast.
|
||||||
|
|
@ -578,6 +582,8 @@ func (c *Posv) VerifySeal(chain consensus.ChainReader, header *types.Header) err
|
||||||
// consensus protocol requirements. The method accepts an optional list of parent
|
// consensus protocol requirements. The method accepts an optional list of parent
|
||||||
// headers that aren't yet part of the local blockchain to generate the snapshots
|
// headers that aren't yet part of the local blockchain to generate the snapshots
|
||||||
// from.
|
// from.
|
||||||
|
// verifySeal also checks the pair of creator-validator set in the header satisfies
|
||||||
|
// the double validation.
|
||||||
func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
|
func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
|
||||||
// Verifying the genesis block is not supported
|
// Verifying the genesis block is not supported
|
||||||
number := header.Number.Uint64()
|
number := header.Number.Uint64()
|
||||||
|
|
@ -591,7 +597,7 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the authorization key and check against signers
|
// Resolve the authorization key and check against signers
|
||||||
signer, err := ecrecover(header, c.signatures)
|
creator, err := ecrecover(header, c.signatures)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -604,22 +610,22 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par
|
||||||
for _, n := range snap.GetSigners() {
|
for _, n := range snap.GetSigners() {
|
||||||
nstring = append(nstring, n.String())
|
nstring = append(nstring, n.String())
|
||||||
}
|
}
|
||||||
if _, ok := snap.Signers[signer]; !ok {
|
if _, ok := snap.Signers[creator]; !ok {
|
||||||
valid := false
|
valid := false
|
||||||
for _, m := range masternodes {
|
for _, m := range masternodes {
|
||||||
if m == signer {
|
if m == creator {
|
||||||
valid = true
|
valid = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !valid {
|
if !valid {
|
||||||
log.Debug("Unauthorized signer found", "block number", number, "signer", signer.String(), "masternodes", mstring, "snapshot from parent block", nstring)
|
log.Debug("Unauthorized creator found", "block number", number, "creator", creator.String(), "masternodes", mstring, "snapshot from parent block", nstring)
|
||||||
return errUnauthorized
|
return errUnauthorized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(masternodes) > 1 {
|
if len(masternodes) > 1 {
|
||||||
for seen, recent := range snap.Recents {
|
for seen, recent := range snap.Recents {
|
||||||
if recent == signer {
|
if recent == creator {
|
||||||
// Signer is among recents, only fail if the current block doesn't shift it out
|
// Signer is among recents, only fail if the current block doesn't shift it out
|
||||||
// There is only case that we don't allow signer to create two continuous blocks.
|
// There is only case that we don't allow signer to create two continuous blocks.
|
||||||
if limit := uint64(2); seen > number-limit {
|
if limit := uint64(2); seen > number-limit {
|
||||||
|
|
@ -631,9 +637,50 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// header must contain validator info following double validation design
|
||||||
|
validator, err := RecoverValidator(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify validator
|
||||||
|
assignedValidator, err := c.getValidator(creator, snap, chain, header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if validator != assignedValidator {
|
||||||
|
log.Debug("Bad block detected. Header contains wrong pair of creator-validator", "creator", creator, "assigned validator", assignedValidator, "wrong validator", validator)
|
||||||
|
return errFailedDoubleValidation
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Posv) getValidator(creator common.Address, snap *Snapshot, chain consensus.ChainReader, header *types.Header) (common.Address, error) {
|
||||||
|
epoch := c.config.Epoch
|
||||||
|
no := header.Number.Uint64()
|
||||||
|
cpNo := no
|
||||||
|
if no%epoch != 0 {
|
||||||
|
cpNo = no - (no % epoch)
|
||||||
|
}
|
||||||
|
if cpNo == 0 {
|
||||||
|
return common.Address{}, nil
|
||||||
|
}
|
||||||
|
cpHeader := chain.GetHeaderByNumber(cpNo)
|
||||||
|
if cpHeader == nil {
|
||||||
|
if no%epoch == 0 {
|
||||||
|
cpHeader = header
|
||||||
|
} else {
|
||||||
|
return common.Address{}, fmt.Errorf("couldn't find checkpoint header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m, err := GetM1M2FromCheckpointHeader(cpHeader)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
return m[creator], nil
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
||||||
// header for running the transactions on top.
|
// header for running the transactions on top.
|
||||||
func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
||||||
|
|
@ -926,3 +973,50 @@ func RemovePenaltiesFromBlock(chain consensus.ChainReader, signers []common.Addr
|
||||||
}
|
}
|
||||||
return signers
|
return signers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get masternodes address from checkpoint Header.
|
||||||
|
func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address {
|
||||||
|
masternodes := make([]common.Address, (len(checkpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength)
|
||||||
|
for i := 0; i < len(masternodes); i++ {
|
||||||
|
copy(masternodes[i][:], checkpointHeader.Extra[extraVanity+i*common.AddressLength:])
|
||||||
|
}
|
||||||
|
return masternodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get m2 list from checkpoint block.
|
||||||
|
func GetM1M2FromCheckpointHeader(checkpointHeader *types.Header) (map[common.Address]common.Address, error) {
|
||||||
|
if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 {
|
||||||
|
return nil, errors.New("This block is not checkpoint block epoc.")
|
||||||
|
}
|
||||||
|
m1m2 := map[common.Address]common.Address{}
|
||||||
|
// Get signers from this block.
|
||||||
|
masternodes := GetMasternodesFromCheckpointHeader(checkpointHeader)
|
||||||
|
validators := ExtractValidatorsFromBytes(checkpointHeader.Validators)
|
||||||
|
|
||||||
|
if len(validators) < len(masternodes) {
|
||||||
|
return nil, errors.New("len(m2) is less than len(m1)")
|
||||||
|
}
|
||||||
|
if len(masternodes) > 0 {
|
||||||
|
for i, m1 := range masternodes {
|
||||||
|
m1m2[m1] = masternodes[validators[i]%int64(len(masternodes))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m1m2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract validators from byte array.
|
||||||
|
func ExtractValidatorsFromBytes(byteValidators []byte) []int64 {
|
||||||
|
lenValidator := len(byteValidators) / M2ByteLength
|
||||||
|
var validators []int64
|
||||||
|
for i := 0; i < lenValidator; i++ {
|
||||||
|
trimByte := bytes.Trim(byteValidators[i*M2ByteLength:(i+1)*M2ByteLength], "\x00")
|
||||||
|
intNumber, err := strconv.Atoi(string(trimByte))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can not convert string to integer", "error", err)
|
||||||
|
return []int64{}
|
||||||
|
}
|
||||||
|
validators = append(validators, int64(intNumber))
|
||||||
|
}
|
||||||
|
|
||||||
|
return validators
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,18 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/posv"
|
||||||
"github.com/ethereum/go-ethereum/contracts/blocksigner/contract"
|
"github.com/ethereum/go-ethereum/contracts/blocksigner/contract"
|
||||||
randomizeContract "github.com/ethereum/go-ethereum/contracts/randomize/contract"
|
randomizeContract "github.com/ethereum/go-ethereum/contracts/randomize/contract"
|
||||||
contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract"
|
contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract"
|
||||||
|
|
@ -37,18 +44,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
"math/rand"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
M2ByteLength = 4
|
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
|
||||||
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
|
||||||
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type rewardLog struct {
|
type rewardLog struct {
|
||||||
|
|
@ -256,30 +256,13 @@ func BuildValidatorFromM2(listM2 []int64) []byte {
|
||||||
var validatorBytes []byte
|
var validatorBytes []byte
|
||||||
for _, numberM2 := range listM2 {
|
for _, numberM2 := range listM2 {
|
||||||
// Convert number to byte.
|
// Convert number to byte.
|
||||||
m2Byte := common.LeftPadBytes([]byte(fmt.Sprintf("%d", numberM2)), M2ByteLength)
|
m2Byte := common.LeftPadBytes([]byte(fmt.Sprintf("%d", numberM2)), posv.M2ByteLength)
|
||||||
validatorBytes = append(validatorBytes, m2Byte...)
|
validatorBytes = append(validatorBytes, m2Byte...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return validatorBytes
|
return validatorBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract validators from byte array.
|
|
||||||
func ExtractValidatorsFromBytes(byteValidators []byte) []int64 {
|
|
||||||
lenValidator := len(byteValidators) / M2ByteLength
|
|
||||||
var validators []int64
|
|
||||||
for i := 0; i < lenValidator; i++ {
|
|
||||||
trimByte := bytes.Trim(byteValidators[i*M2ByteLength:(i+1)*M2ByteLength], "\x00")
|
|
||||||
intNumber, err := strconv.Atoi(string(trimByte))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Can not convert string to integer", "error", err)
|
|
||||||
return []int64{}
|
|
||||||
}
|
|
||||||
validators = append(validators, int64(intNumber))
|
|
||||||
}
|
|
||||||
|
|
||||||
return validators
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode validator hex string.
|
// Decode validator hex string.
|
||||||
func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
|
func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
|
||||||
validatorsByte, err := hexutil.Decode(validatorsStr)
|
validatorsByte, err := hexutil.Decode(validatorsStr)
|
||||||
|
|
@ -287,7 +270,7 @@ func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExtractValidatorsFromBytes(validatorsByte), nil
|
return posv.ExtractValidatorsFromBytes(validatorsByte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt randomize from secrets and opening.
|
// Decrypt randomize from secrets and opening.
|
||||||
|
|
@ -558,33 +541,3 @@ func isInt(strNumber string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get masternodes address from checkpoint Header.
|
|
||||||
func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address {
|
|
||||||
masternodes := make([]common.Address, (len(checkpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength)
|
|
||||||
for i := 0; i < len(masternodes); i++ {
|
|
||||||
copy(masternodes[i][:], checkpointHeader.Extra[extraVanity+i*common.AddressLength:])
|
|
||||||
}
|
|
||||||
return masternodes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get m2 list from checkpoint block.
|
|
||||||
func GetM1M2FromCheckpointBlock(checkpointBlock *types.Block) (map[common.Address]common.Address, error) {
|
|
||||||
if checkpointBlock.Number().Int64()%common.EpocBlockRandomize != 0 {
|
|
||||||
return nil, errors.New("This block is not checkpoint block epoc.")
|
|
||||||
}
|
|
||||||
m1m2 := map[common.Address]common.Address{}
|
|
||||||
// Get signers from this block.
|
|
||||||
masternodes := GetMasternodesFromCheckpointHeader(checkpointBlock.Header())
|
|
||||||
validators := ExtractValidatorsFromBytes(checkpointBlock.Header().Validators)
|
|
||||||
|
|
||||||
if len(validators) < len(masternodes) {
|
|
||||||
return nil, errors.New("len(m2) is less than len(m1)")
|
|
||||||
}
|
|
||||||
if len(masternodes) > 0 {
|
|
||||||
for i, m1 := range masternodes {
|
|
||||||
m1m2[m1] = masternodes[validators[i]%int64(len(masternodes))]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m1m2, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
|
@ -189,60 +188,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
if eth.chainConfig.Posv != nil {
|
if eth.chainConfig.Posv != nil {
|
||||||
c := eth.engine.(*posv.Posv)
|
c := eth.engine.(*posv.Posv)
|
||||||
|
|
||||||
// Hook double validation
|
|
||||||
doubleValidateHook := func(block *types.Block) error {
|
|
||||||
parentBlk := eth.blockchain.GetBlockByHash(block.ParentHash())
|
|
||||||
if parentBlk == nil {
|
|
||||||
return fmt.Errorf("Fail to get parent block for hash: %v", block.ParentHash())
|
|
||||||
}
|
|
||||||
snap, err := c.GetSnapshot(eth.blockchain, parentBlk.Header())
|
|
||||||
if err != nil {
|
|
||||||
if err == consensus.ErrUnknownAncestor {
|
|
||||||
log.Warn("Block chain forked.", "error", err)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("Fail to get snapshot for sign tx validator: %v", err)
|
|
||||||
}
|
|
||||||
if _, authorized := snap.Signers[eth.etherbase]; authorized {
|
|
||||||
m2, err := getM2(snap, eth, block)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Fail to validate M2 condition for importing block: %v", err)
|
|
||||||
}
|
|
||||||
if eth.etherbase != m2 {
|
|
||||||
txCh := make(chan core.TxPreEvent, txChanSize)
|
|
||||||
subEvent := eth.txPool.SubscribeSpecialTxPreEvent(txCh)
|
|
||||||
defer subEvent.Unsubscribe()
|
|
||||||
// firstly, look into pending txPool
|
|
||||||
pendingMap, err := eth.txPool.Pending()
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Fail to get txPool pending", "err", err, "Continue with empty txPool pending.")
|
|
||||||
//reset pendingMap
|
|
||||||
pendingMap = map[common.Address]types.Transactions{}
|
|
||||||
}
|
|
||||||
txsSentFromM2 := pendingMap[m2]
|
|
||||||
if len(txsSentFromM2) > 0 {
|
|
||||||
for _, tx := range txsSentFromM2 {
|
|
||||||
if tx.To().String() == common.BlockSigners {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//then wait until signTx from m2 comes into txPool
|
|
||||||
select {
|
|
||||||
case event := <-txCh:
|
|
||||||
from, err := eth.txPool.GetSender(event.Tx)
|
|
||||||
if (err == nil) && (event.Tx.To().String() == common.BlockSigners) && (from == m2) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
//timeout 10s
|
|
||||||
case <-time.After(time.Duration(10) * time.Second):
|
|
||||||
return fmt.Errorf("Time out waiting for confirmation from m2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("This address is not authorized to validate block")
|
|
||||||
}
|
|
||||||
|
|
||||||
signHook := func(block *types.Block) error {
|
signHook := func(block *types.Block) error {
|
||||||
if err := contracts.CreateTransactionSign(chainConfig, eth.txPool, eth.accountManager, block, chainDb); err != nil {
|
if err := contracts.CreateTransactionSign(chainConfig, eth.txPool, eth.accountManager, block, chainDb); err != nil {
|
||||||
return fmt.Errorf("Fail to create tx sign for importing block: %v", err)
|
return fmt.Errorf("Fail to create tx sign for importing block: %v", err)
|
||||||
|
|
@ -250,7 +195,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
eth.protocolManager.fetcher.SetDoubleValidateHook(doubleValidateHook)
|
|
||||||
eth.protocolManager.fetcher.SetSignHook(signHook)
|
eth.protocolManager.fetcher.SetSignHook(signHook)
|
||||||
|
|
||||||
// Hook prepares validators M2 for the current epoch
|
// Hook prepares validators M2 for the current epoch
|
||||||
|
|
@ -372,28 +316,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
return eth, nil
|
return eth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getM2(snap *posv.Snapshot, eth *Ethereum, block *types.Block) (common.Address, error) {
|
|
||||||
epoch := eth.chainConfig.Posv.Epoch
|
|
||||||
no := block.NumberU64()
|
|
||||||
cpNo := no
|
|
||||||
if no%epoch != 0 {
|
|
||||||
cpNo = no - (no % epoch)
|
|
||||||
}
|
|
||||||
if cpNo == 0 {
|
|
||||||
return eth.etherbase, nil
|
|
||||||
}
|
|
||||||
cpBlk := eth.blockchain.GetBlockByNumber(cpNo)
|
|
||||||
m, err := contracts.GetM1M2FromCheckpointBlock(cpBlk)
|
|
||||||
if err != nil {
|
|
||||||
return common.Address{}, err
|
|
||||||
}
|
|
||||||
m1, err := posv.WhoIsCreator(snap, block.Header())
|
|
||||||
if err != nil {
|
|
||||||
return common.Address{}, err
|
|
||||||
}
|
|
||||||
return m[m1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeExtraData(extra []byte) []byte {
|
func makeExtraData(extra []byte) []byte {
|
||||||
if len(extra) == 0 {
|
if len(extra) == 0 {
|
||||||
// create default extradata
|
// create default extradata
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue