mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-20 21:54:30 +00:00
* New struct in consensus/XDPoS/utils/types.go, util functions, and test. (#14) * define vote, timeout, sync info, qc, tc, extra fields in types.go, add test in types_test.go * add json tag in types.go, refine encoder decoder of extra fields * refactor types.go utils.go * re-write types, comments * add Hash SigHash for types, and tests * define Round type * remove unnecessary logs * add v2 engine functions placeholder * typo fix on the consensus v2 function placeholders * add countdown timer * make initilised private to countdown * add v2 specific config struct * rename some config variables * Implement BFT Message receiver (#13) * fix or skip tests due to PR-136 changes * add bft receiver functions * add bft receiver functions * rename tc to TimeoutCert * implement more functions * New struct in consensus/XDPoS/utils/types.go, util functions, and test. (#14) * define vote, timeout, sync info, qc, tc, extra fields in types.go, add test in types_test.go * add json tag in types.go, refine encoder decoder of extra fields * refactor types.go utils.go * re-write types, comments * add Hash SigHash for types, and tests * define Round type * remove unnecessary logs * add temp functions * add v2 engine functions placeholder * typo fix on the consensus v2 function placeholders * add countdown timer * make initilised private to countdown * push verify function * add test on receiving vote * revert type change * add async on broadcast function * add quit initial * fix test Co-authored-by: Jianrong <wjrjerome@gmail.com> Co-authored-by: wgr523 <wgr523@gmail.com> * generate and verify timeout message * Consensus V2 variable, timeout pool (#19) * fill in XDPoS_v2 variables and processQC/TC * add timeout pool, refine engine variables * refactor type functions * solve a small pointer bug * create general pool and its test, refine engine * refine pool, add xdpos v2 config cert threshold * refine config * vote and timeout handlers * fix pool test * bft miner preparation * review comment improvement * update * relocate tests * add and remove comment * fix the syntax error * update network layer and add handler functions (#23) * update network layer and add handler functions * fix test syntax error * add ProcessQC implementation * add ProcessQC tests * add snapshot test * add wait qc process * remove testing files * add route snapshot * fix merge issue * add default v2 behaviour (#24) * add v2 ecrecover functions and refactor test * fix all the tests * put minimun lock variable * debugging prepare and seal v2 blocks * Trigger proposeBlockHandler after v2 block received and verified in fetcher * skip snapshot apply related tests * update test check * rename bfter to bft handler and ignore normal behviour * fix bugs during local 4 node run * fix test * fix sync info test * fix bugs during local 4 node run * rebase and fix bug * remove hook validators function" Co-authored-by: wgr523 <wgr523@gmail.com> Co-authored-by: Jianrong <wjrjerome@gmail.com>
242 lines
7 KiB
Go
242 lines
7 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/XinFinOrg/XDPoSChain/common"
|
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
|
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
|
"github.com/XinFinOrg/XDPoSChain/log"
|
|
"github.com/XinFinOrg/XDPoSChain/params"
|
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
|
lru "github.com/hashicorp/golang-lru"
|
|
)
|
|
|
|
func Position(list []common.Address, x common.Address) int {
|
|
for i, item := range list {
|
|
if item == x {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func Hop(len, pre, cur int) int {
|
|
switch {
|
|
case pre < cur:
|
|
return cur - (pre + 1)
|
|
case pre > cur:
|
|
return (len - pre) + (cur - 1)
|
|
default:
|
|
return len - 1
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) {
|
|
if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 {
|
|
return nil, errors.New("This block is not checkpoint block epoc.")
|
|
}
|
|
// Get signers from this block.
|
|
masternodes := GetMasternodesFromCheckpointHeader(checkpointHeader)
|
|
validators := ExtractValidatorsFromBytes(checkpointHeader.Validators)
|
|
m1m2, _, err := GetM1M2(masternodes, validators, currentHeader, config)
|
|
if err != nil {
|
|
return map[common.Address]common.Address{}, err
|
|
}
|
|
return m1m2, nil
|
|
}
|
|
|
|
func GetM1M2(masternodes []common.Address, validators []int64, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, uint64, error) {
|
|
m1m2 := map[common.Address]common.Address{}
|
|
maxMNs := len(masternodes)
|
|
moveM2 := uint64(0)
|
|
if len(validators) < maxMNs {
|
|
return nil, moveM2, errors.New("len(m2) is less than len(m1)")
|
|
}
|
|
if maxMNs > 0 {
|
|
isForked := config.IsTIPRandomize(currentHeader.Number)
|
|
if isForked {
|
|
moveM2 = ((currentHeader.Number.Uint64() % config.XDPoS.Epoch) / uint64(maxMNs)) % uint64(maxMNs)
|
|
}
|
|
for i, m1 := range masternodes {
|
|
m2Index := uint64(validators[i] % int64(maxMNs))
|
|
m2Index = (m2Index + moveM2) % uint64(maxMNs)
|
|
m1m2[m1] = masternodes[m2Index]
|
|
}
|
|
}
|
|
return m1m2, moveM2, nil
|
|
}
|
|
|
|
// compare 2 signers lists
|
|
// return true if they are same elements, otherwise return false
|
|
func CompareSignersLists(list1 []common.Address, list2 []common.Address) bool {
|
|
if len(list1) == 0 && len(list2) == 0 {
|
|
return true
|
|
}
|
|
sort.Slice(list1, func(i, j int) bool {
|
|
return list1[i].String() <= list1[j].String()
|
|
})
|
|
sort.Slice(list2, func(i, j int) bool {
|
|
return list2[i].String() <= list2[j].String()
|
|
})
|
|
return reflect.DeepEqual(list1, list2)
|
|
}
|
|
|
|
// SignerFn is a signer callback function to request a hash to be signed by a
|
|
// backing account.
|
|
//type SignerFn func(accounts.Account, []byte) ([]byte, error)
|
|
|
|
// sigHash returns the hash which is used as input for the delegated-proof-of-stake
|
|
// signing. It is the hash of the entire header apart from the 65 byte signature
|
|
// contained at the end of the extra data.
|
|
//
|
|
// Note, the method requires the extra data to be at least 65 bytes, otherwise it
|
|
// panics. This is done to avoid accidentally using both forms (signature present
|
|
// or not), which could be abused to produce different hashes for the same header.
|
|
func SigHash(header *types.Header) (hash common.Hash) {
|
|
hasher := sha3.NewKeccak256()
|
|
|
|
err := rlp.Encode(hasher, []interface{}{
|
|
header.ParentHash,
|
|
header.UncleHash,
|
|
header.Coinbase,
|
|
header.Root,
|
|
header.TxHash,
|
|
header.ReceiptHash,
|
|
header.Bloom,
|
|
header.Difficulty,
|
|
header.Number,
|
|
header.GasLimit,
|
|
header.GasUsed,
|
|
header.Time,
|
|
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
|
header.MixDigest,
|
|
header.Nonce,
|
|
})
|
|
if err != nil {
|
|
log.Debug("Fail to encode", err)
|
|
}
|
|
hasher.Sum(hash[:0])
|
|
return hash
|
|
}
|
|
|
|
func SigHashV2(header *types.Header) (hash common.Hash) {
|
|
hasher := sha3.NewKeccak256()
|
|
|
|
err := rlp.Encode(hasher, []interface{}{
|
|
header.ParentHash,
|
|
header.UncleHash,
|
|
header.Coinbase,
|
|
header.Root,
|
|
header.TxHash,
|
|
header.ReceiptHash,
|
|
header.Bloom,
|
|
header.Difficulty,
|
|
header.Number,
|
|
header.GasLimit,
|
|
header.GasUsed,
|
|
header.Time,
|
|
header.Extra,
|
|
header.MixDigest,
|
|
header.Nonce,
|
|
header.Validators,
|
|
header.Penalties,
|
|
})
|
|
if err != nil {
|
|
log.Debug("Fail to encode", err)
|
|
}
|
|
hasher.Sum(hash[:0])
|
|
return hash
|
|
}
|
|
|
|
// Decode extra fields for consensus version >= 2 (XDPoS 2.0 and future versions)
|
|
func DecodeBytesExtraFields(b []byte, val interface{}) error {
|
|
if len(b) == 0 {
|
|
return fmt.Errorf("extra field is 0 length")
|
|
}
|
|
switch b[0] {
|
|
case 1:
|
|
return fmt.Errorf("consensus version 1 is not applicable for decoding extra fields")
|
|
case 2:
|
|
return rlp.DecodeBytes(b[1:], val)
|
|
default:
|
|
return fmt.Errorf("consensus version %d is not defined", b[0])
|
|
}
|
|
}
|
|
|
|
// ecrecover extracts the Ethereum account address from a signed header.
|
|
func Ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
|
// If the signature's already cached, return that
|
|
hash := header.Hash()
|
|
if address, known := sigcache.Get(hash); known {
|
|
return address.(common.Address), nil
|
|
}
|
|
// Retrieve the signature from the header extra-data
|
|
if len(header.Extra) < ExtraSeal {
|
|
return common.Address{}, ErrMissingSignature
|
|
}
|
|
signature := header.Extra[len(header.Extra)-ExtraSeal:]
|
|
|
|
// Recover the public key and the Ethereum address
|
|
pubkey, err := crypto.Ecrecover(SigHash(header).Bytes(), signature)
|
|
if err != nil {
|
|
return common.Address{}, err
|
|
}
|
|
var signer common.Address
|
|
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
|
|
|
sigcache.Add(hash, signer)
|
|
return signer, nil
|
|
}
|
|
|
|
func EcrecoverV2(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
|
// If the signature's already cached, return that
|
|
hash := header.Hash()
|
|
if address, known := sigcache.Get(hash); known {
|
|
return address.(common.Address), nil
|
|
}
|
|
|
|
// Recover the public key and the Ethereum address
|
|
pubkey, err := crypto.Ecrecover(SigHashV2(header).Bytes(), header.Validator)
|
|
if err != nil {
|
|
return common.Address{}, err
|
|
}
|
|
var signer common.Address
|
|
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
|
|
|
sigcache.Add(hash, signer)
|
|
return signer, nil
|
|
}
|