mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
move DV to posv package
This commit is contained in:
parent
996f8580df
commit
7d0e91fd0d
3 changed files with 111 additions and 142 deletions
|
|
@ -19,12 +19,13 @@ package posv
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
|
@ -47,6 +48,7 @@ const (
|
|||
inmemorySnapshots = 128 // Number of recent vote snapshots 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
|
||||
M2ByteLength = 4
|
||||
)
|
||||
|
||||
type Masternode struct {
|
||||
|
|
@ -132,6 +134,8 @@ var (
|
|||
// errUnauthorized is returned if a header is signed by a non-authorized entity.
|
||||
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
|
||||
// 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.
|
||||
|
|
@ -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
|
||||
// headers that aren't yet part of the local blockchain to generate the snapshots
|
||||
// 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 {
|
||||
// Verifying the genesis block is not supported
|
||||
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
|
||||
signer, err := ecrecover(header, c.signatures)
|
||||
creator, err := ecrecover(header, c.signatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -604,22 +610,22 @@ func (c *Posv) verifySeal(chain consensus.ChainReader, header *types.Header, par
|
|||
for _, n := range snap.GetSigners() {
|
||||
nstring = append(nstring, n.String())
|
||||
}
|
||||
if _, ok := snap.Signers[signer]; !ok {
|
||||
if _, ok := snap.Signers[creator]; !ok {
|
||||
valid := false
|
||||
for _, m := range masternodes {
|
||||
if m == signer {
|
||||
if m == creator {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
if len(masternodes) > 1 {
|
||||
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
|
||||
// There is only case that we don't allow signer to create two continuous blocks.
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
// header for running the transactions on top.
|
||||
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
|
||||
}
|
||||
|
||||
// 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/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/posv"
|
||||
"github.com/ethereum/go-ethereum/contracts/blocksigner/contract"
|
||||
randomizeContract "github.com/ethereum/go-ethereum/contracts/randomize/contract"
|
||||
contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract"
|
||||
|
|
@ -37,16 +44,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
M2ByteLength = 4
|
||||
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
|
||||
)
|
||||
|
|
@ -256,30 +256,13 @@ func BuildValidatorFromM2(listM2 []int64) []byte {
|
|||
var validatorBytes []byte
|
||||
for _, numberM2 := range listM2 {
|
||||
// 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...)
|
||||
}
|
||||
|
||||
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.
|
||||
func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
|
||||
validatorsByte, err := hexutil.Decode(validatorsStr)
|
||||
|
|
@ -287,7 +270,7 @@ func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ExtractValidatorsFromBytes(validatorsByte), nil
|
||||
return posv.ExtractValidatorsFromBytes(validatorsByte), nil
|
||||
}
|
||||
|
||||
// Decrypt randomize from secrets and opening.
|
||||
|
|
@ -558,33 +541,3 @@ func isInt(strNumber string) bool {
|
|||
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"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"bytes"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
|
|
@ -189,60 +188,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
if eth.chainConfig.Posv != nil {
|
||||
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 {
|
||||
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)
|
||||
|
|
@ -250,7 +195,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
eth.protocolManager.fetcher.SetDoubleValidateHook(doubleValidateHook)
|
||||
eth.protocolManager.fetcher.SetSignHook(signHook)
|
||||
|
||||
// Hook prepares validators M2 for the current epoch
|
||||
|
|
@ -372,28 +316,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
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 {
|
||||
if len(extra) == 0 {
|
||||
// create default extradata
|
||||
|
|
|
|||
Loading…
Reference in a new issue