Add XDC APIs, contracts, and header fields

- Add XDPoS header fields (Validators, Validator, Penalties) to block.go
- Add XDC constants and contract addresses to common/types.go
- Add consensus/XDPoS/utils package with types and utilities
- Add contracts/randomize contract binding
- Add contracts/utils.go for contract utilities
- Add core/state/statedb_utils.go for XDPoS state access
- Add core/state/state_reader.go for slot reading utilities
- Add core/types/consensus_v2.go for XDPoS 2.0 types
- Add internal/ethapi/api_xdc.go with XDC-specific RPC methods
  - GetBlockSignersByHash/Number
  - GetBlockFinalityByHash/Number
  - GetMasternodes
  - GetCandidateStatus
  - GetCandidates
- Add BlockChain() method to ethapi.Backend interface
This commit is contained in:
anilchinchawale 2026-01-28 21:31:18 +01:00
parent 0dc883e299
commit 64defd91ef
14 changed files with 1780 additions and 1 deletions

View file

@ -41,6 +41,44 @@ const (
AddressLength = 20
)
// XDC-specific constants
const (
BlockSigners = "xdc0000000000000000000000000000000000000089"
MasternodeVotingSMC = "xdc0000000000000000000000000000000000000088"
RandomizeSMC = "xdc0000000000000000000000000000000000000090"
FoudationAddr = "xdc0000000000000000000000000000000000000068"
TeamAddr = "xdc0000000000000000000000000000000000000099"
XDCXAddr = "xdc0000000000000000000000000000000000000091"
TradingStateAddr = "xdc0000000000000000000000000000000000000092"
XDCXLendingAddress = "xdc0000000000000000000000000000000000000093"
XDCXLendingFinalizedTradeAddress = "xdc0000000000000000000000000000000000000094"
XDCNativeAddress = "xdc0000000000000000000000000000000000000001"
LendingLockAddress = "xdc0000000000000000000000000000000000000011"
// Method signatures for XDPoS contracts
VoteMethod = "0x6dd7d8ea"
UnvoteMethod = "0x02aa9be2"
ProposeMethod = "0x01267951"
ResignMethod = "0xae6e43f5"
SignMethod = "0xe341eaa4"
XDCXApplyMethod = "0xc6b32f34"
XDCZApplyMethod = "0xc6b32f34"
HexSignMethod = "e341eaa4"
HexSetSecret = "34d38600"
HexSetOpening = "e11f5ba2"
// Reward percentages
RewardMasterPercent = 40
RewardVoterPercent = 50
RewardFoundationPercent = 10
// Epoch block constants
MergeSignRange = 15
EpocBlockSecret = 800
EpocBlockOpening = 850
EpocBlockRandomize = 900
)
var (
hashT = reflect.TypeFor[Hash]()
addressT = reflect.TypeFor[Address]()
@ -50,11 +88,34 @@ var (
// MaxHash represents the maximum possible hash value.
MaxHash = HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
// XDC contract addresses (binary form)
BlockSignersBinary = HexToAddress("0x0000000000000000000000000000000000000089")
MasternodeVotingSMCBinary = HexToAddress("0x0000000000000000000000000000000000000088")
RandomizeSMCBinary = HexToAddress("0x0000000000000000000000000000000000000090")
FoudationAddrBinary = HexToAddress("0x0000000000000000000000000000000000000068")
TeamAddrBinary = HexToAddress("0x0000000000000000000000000000000000000099")
XDCXAddrBinary = HexToAddress("0x0000000000000000000000000000000000000091")
TradingStateAddrBinary = HexToAddress("0x0000000000000000000000000000000000000092")
XDCXLendingAddressBinary = HexToAddress("0x0000000000000000000000000000000000000093")
XDCXLendingFinalizedTradeAddressBinary = HexToAddress("0x0000000000000000000000000000000000000094")
XDCNativeAddressBinary = HexToAddress("0x0000000000000000000000000000000000000001")
LendingLockAddressBinary = HexToAddress("0x0000000000000000000000000000000000000011")
MintedRecordAddressBinary = HexToAddress("0x000000000000000000000000000000000000009a")
// TIP2019 block number for XDPoS reward updates
TIP2019Block = big.NewInt(0)
)
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
type Hash [HashLength]byte
// Vote represents a masternode vote from a voter
type Vote struct {
Masternode Address
Voter Address
}
// BytesToHash sets b to hash.
// If b is larger than len(h), b will be cropped from the left.
func BytesToHash(b []byte) Hash {

View file

@ -0,0 +1,35 @@
// Copyright (c) 2018 XDPoSChain
// XDPoS delegated-proof-of-stake protocol constants.
package utils
import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)
var (
EpochLength = uint64(900) // 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
NonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
NonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.
UncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
InmemoryEpochs = 5 * EpochLength // Number of mapping from block to epoch switch infos to keep in memory
InmemoryRound2Epochs = 65536 // Number of mapping of epoch switch blocks for quickly locating epoch switch block.
)
const (
InmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
BlockSignersCacheLimit = 9000
M2ByteLength = 4
)
const (
PeriodicJobPeriod = 60
PoolHygieneRound = 10
)

View file

@ -0,0 +1,49 @@
// Copyright (c) 2018 XDPoSChain
// XDPoS types and interfaces
package utils
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/types"
)
type Masternode struct {
Address common.Address
Stake *big.Int
}
type PublicApiSnapshot struct {
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
}
type MissedRoundInfo struct {
Round types.Round
Miner common.Address
CurrentBlockHash common.Hash
CurrentBlockNum *big.Int
ParentBlockHash common.Hash
ParentBlockNum *big.Int
}
type PublicApiMissedRoundsMetadata struct {
EpochRound types.Round
EpochBlockNumber *big.Int
MissedRounds []MissedRoundInfo
}
// Given an epoch number, this struct records the epoch switch block (first block in epoch) infos such as block number
type EpochNumInfo struct {
EpochBlockHash common.Hash `json:"hash"`
EpochRound types.Round `json:"round"`
EpochFirstBlockNumber *big.Int `json:"firstBlock"`
EpochLastBlockNumber *big.Int `json:"lastBlock"`
}
type SigLRU = lru.Cache[common.Hash, common.Address]

View file

@ -0,0 +1,104 @@
// Copyright (c) 2018 XDPoSChain
// XDPoS utility functions
package utils
import (
"bytes"
"errors"
"fmt"
"reflect"
"sort"
"strconv"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
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
}
// compare 2 signers lists
// return true if they are same elements, otherwise return false
func CompareSignersLists(list1 []common.Address, list2 []common.Address) bool {
l1 := make([]common.Address, len(list1))
l2 := make([]common.Address, len(list2))
copy(l1, list1)
copy(l2, list2)
if len(l1) == 0 && len(l2) == 0 {
return true
}
if len(l1) != len(l2) {
return false
}
sort.Slice(l1, func(i, j int) bool {
return bytes.Compare(l1[i][:], l1[j][:]) == -1
})
sort.Slice(l2, func(i, j int) bool {
return bytes.Compare(l2[i][:], l2[j][:]) == -1
})
return reflect.DeepEqual(l1, l2)
}
// 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 errors.New("extra field is 0 length")
}
switch b[0] {
case 2:
return rlp.DecodeBytes(b[1:], val)
default:
return fmt.Errorf("consensus version %d is not defined, or this block is v1 block", b[0])
}
}
func RlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewLegacyKeccak256()
err := rlp.Encode(hw, x)
if err != nil {
log.Error("[rlpHash] Fail to hash item", "Error", err)
}
hw.Sum(h[:0])
return h
}

View file

@ -0,0 +1,433 @@
// Code generated - DO NOT EDIT.
// This file is a generated binding and any manual changes will be lost.
package contract
import (
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
// SafeMathABI is the input ABI used to generate the binding from.
const SafeMathABI = "[]"
// SafeMathBin is the compiled bytecode used for deploying new contracts.
const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146060604052600080fd00a165627a7a72305820b9407d48ebc7efee5c9f08b3b3a957df2939281f5913225e8c1291f069b900490029`
// DeploySafeMath deploys a new Ethereum contract, binding an instance of SafeMath to it.
func DeploySafeMath(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SafeMath, error) {
parsed, err := abi.JSON(strings.NewReader(SafeMathABI))
if err != nil {
return common.Address{}, nil, nil, err
}
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(SafeMathBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &SafeMath{SafeMathCaller: SafeMathCaller{contract: contract}, SafeMathTransactor: SafeMathTransactor{contract: contract}, SafeMathFilterer: SafeMathFilterer{contract: contract}}, nil
}
// SafeMath is an auto generated Go binding around an Ethereum contract.
type SafeMath struct {
SafeMathCaller // Read-only binding to the contract
SafeMathTransactor // Write-only binding to the contract
SafeMathFilterer // Log filterer for contract events
}
// SafeMathCaller is an auto generated read-only Go binding around an Ethereum contract.
type SafeMathCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SafeMathTransactor is an auto generated write-only Go binding around an Ethereum contract.
type SafeMathTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SafeMathFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type SafeMathFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// SafeMathSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type SafeMathSession struct {
Contract *SafeMath // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// SafeMathCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type SafeMathCallerSession struct {
Contract *SafeMathCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// SafeMathTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type SafeMathTransactorSession struct {
Contract *SafeMathTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// SafeMathRaw is an auto generated low-level Go binding around an Ethereum contract.
type SafeMathRaw struct {
Contract *SafeMath // Generic contract binding to access the raw methods on
}
// SafeMathCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type SafeMathCallerRaw struct {
Contract *SafeMathCaller // Generic read-only contract binding to access the raw methods on
}
// SafeMathTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type SafeMathTransactorRaw struct {
Contract *SafeMathTransactor // Generic write-only contract binding to access the raw methods on
}
// NewSafeMath creates a new instance of SafeMath, bound to a specific deployed contract.
func NewSafeMath(address common.Address, backend bind.ContractBackend) (*SafeMath, error) {
contract, err := bindSafeMath(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &SafeMath{SafeMathCaller: SafeMathCaller{contract: contract}, SafeMathTransactor: SafeMathTransactor{contract: contract}, SafeMathFilterer: SafeMathFilterer{contract: contract}}, nil
}
// NewSafeMathCaller creates a new read-only instance of SafeMath, bound to a specific deployed contract.
func NewSafeMathCaller(address common.Address, caller bind.ContractCaller) (*SafeMathCaller, error) {
contract, err := bindSafeMath(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &SafeMathCaller{contract: contract}, nil
}
// NewSafeMathTransactor creates a new write-only instance of SafeMath, bound to a specific deployed contract.
func NewSafeMathTransactor(address common.Address, transactor bind.ContractTransactor) (*SafeMathTransactor, error) {
contract, err := bindSafeMath(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &SafeMathTransactor{contract: contract}, nil
}
// NewSafeMathFilterer creates a new log filterer instance of SafeMath, bound to a specific deployed contract.
func NewSafeMathFilterer(address common.Address, filterer bind.ContractFilterer) (*SafeMathFilterer, error) {
contract, err := bindSafeMath(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &SafeMathFilterer{contract: contract}, nil
}
// bindSafeMath binds a generic wrapper to an already deployed contract.
func bindSafeMath(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(SafeMathABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_SafeMath *SafeMathRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _SafeMath.Contract.SafeMathCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_SafeMath *SafeMathRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _SafeMath.Contract.SafeMathTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_SafeMath *SafeMathRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _SafeMath.Contract.SafeMathTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_SafeMath *SafeMathCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _SafeMath.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_SafeMath *SafeMathTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _SafeMath.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_SafeMath *SafeMathTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _SafeMath.Contract.contract.Transact(opts, method, params...)
}
// XDCRandomizeABI is the input ABI used to generate the binding from.
const XDCRandomizeABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getSecret\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_secret\",\"type\":\"bytes32[]\"}],\"name\":\"setSecret\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getOpening\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_opening\",\"type\":\"bytes32\"}],\"name\":\"setOpening\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
// XDCRandomizeBin is the compiled bytecode used for deploying new contracts.
const XDCRandomizeBin = `0x6060604052341561000f57600080fd5b6103368061001e6000396000f3006060604052600436106100615763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663284180fc811461006657806334d38600146100d8578063d442d6cc14610129578063e11f5ba21461015a575b600080fd5b341561007157600080fd5b610085600160a060020a0360043516610170565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156100c45780820151838201526020016100ac565b505050509050019250505060405180910390f35b34156100e357600080fd5b61012760046024813581810190830135806020818102016040519081016040528093929190818152602001838360200280828437509496506101f395505050505050565b005b341561013457600080fd5b610148600160a060020a0360043516610243565b60405190815260200160405180910390f35b341561016557600080fd5b61012760043561025e565b61017861028e565b60008083600160a060020a0316600160a060020a031681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156101e757602002820191906000526020600020905b815481526001909101906020018083116101d2575b50505050509050919050565b610384430661032081101561020757600080fd5b610352811061021557600080fd5b600160a060020a033316600090815260208190526040902082805161023e9291602001906102a0565b505050565b600160a060020a031660009081526001602052604090205490565b610384430661035281101561027257600080fd5b50600160a060020a033316600090815260016020526040902055565b60206040519081016040526000815290565b8280548282559060005260206000209081019282156102dd579160200282015b828111156102dd57825182556020909201916001909101906102c0565b506102e99291506102ed565b5090565b61030791905b808211156102e957600081556001016102f3565b905600a165627a7a7230582034991c8dc4001fc254f3ba2811c05d2e7d29bee3908946ca56d1545b2c852de20029`
// DeployXDCRandomize deploys a new Ethereum contract, binding an instance of XDCRandomize to it.
func DeployXDCRandomize(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *XDCRandomize, error) {
parsed, err := abi.JSON(strings.NewReader(XDCRandomizeABI))
if err != nil {
return common.Address{}, nil, nil, err
}
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(XDCRandomizeBin), backend)
if err != nil {
return common.Address{}, nil, nil, err
}
return address, tx, &XDCRandomize{XDCRandomizeCaller: XDCRandomizeCaller{contract: contract}, XDCRandomizeTransactor: XDCRandomizeTransactor{contract: contract}, XDCRandomizeFilterer: XDCRandomizeFilterer{contract: contract}}, nil
}
// XDCRandomize is an auto generated Go binding around an Ethereum contract.
type XDCRandomize struct {
XDCRandomizeCaller // Read-only binding to the contract
XDCRandomizeTransactor // Write-only binding to the contract
XDCRandomizeFilterer // Log filterer for contract events
}
// XDCRandomizeCaller is an auto generated read-only Go binding around an Ethereum contract.
type XDCRandomizeCaller struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// XDCRandomizeTransactor is an auto generated write-only Go binding around an Ethereum contract.
type XDCRandomizeTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// XDCRandomizeFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type XDCRandomizeFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// XDCRandomizeSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options.
type XDCRandomizeSession struct {
Contract *XDCRandomize // Generic contract binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// XDCRandomizeCallerSession is an auto generated read-only Go binding around an Ethereum contract,
// with pre-set call options.
type XDCRandomizeCallerSession struct {
Contract *XDCRandomizeCaller // Generic contract caller binding to set the session for
CallOpts bind.CallOpts // Call options to use throughout this session
}
// XDCRandomizeTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
// with pre-set transact options.
type XDCRandomizeTransactorSession struct {
Contract *XDCRandomizeTransactor // Generic contract transactor binding to set the session for
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
}
// XDCRandomizeRaw is an auto generated low-level Go binding around an Ethereum contract.
type XDCRandomizeRaw struct {
Contract *XDCRandomize // Generic contract binding to access the raw methods on
}
// XDCRandomizeCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
type XDCRandomizeCallerRaw struct {
Contract *XDCRandomizeCaller // Generic read-only contract binding to access the raw methods on
}
// XDCRandomizeTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
type XDCRandomizeTransactorRaw struct {
Contract *XDCRandomizeTransactor // Generic write-only contract binding to access the raw methods on
}
// NewXDCRandomize creates a new instance of XDCRandomize, bound to a specific deployed contract.
func NewXDCRandomize(address common.Address, backend bind.ContractBackend) (*XDCRandomize, error) {
contract, err := bindXDCRandomize(address, backend, backend, backend)
if err != nil {
return nil, err
}
return &XDCRandomize{XDCRandomizeCaller: XDCRandomizeCaller{contract: contract}, XDCRandomizeTransactor: XDCRandomizeTransactor{contract: contract}, XDCRandomizeFilterer: XDCRandomizeFilterer{contract: contract}}, nil
}
// NewXDCRandomizeCaller creates a new read-only instance of XDCRandomize, bound to a specific deployed contract.
func NewXDCRandomizeCaller(address common.Address, caller bind.ContractCaller) (*XDCRandomizeCaller, error) {
contract, err := bindXDCRandomize(address, caller, nil, nil)
if err != nil {
return nil, err
}
return &XDCRandomizeCaller{contract: contract}, nil
}
// NewXDCRandomizeTransactor creates a new write-only instance of XDCRandomize, bound to a specific deployed contract.
func NewXDCRandomizeTransactor(address common.Address, transactor bind.ContractTransactor) (*XDCRandomizeTransactor, error) {
contract, err := bindXDCRandomize(address, nil, transactor, nil)
if err != nil {
return nil, err
}
return &XDCRandomizeTransactor{contract: contract}, nil
}
// NewXDCRandomizeFilterer creates a new log filterer instance of XDCRandomize, bound to a specific deployed contract.
func NewXDCRandomizeFilterer(address common.Address, filterer bind.ContractFilterer) (*XDCRandomizeFilterer, error) {
contract, err := bindXDCRandomize(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &XDCRandomizeFilterer{contract: contract}, nil
}
// bindXDCRandomize binds a generic wrapper to an already deployed contract.
func bindXDCRandomize(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(XDCRandomizeABI))
if err != nil {
return nil, err
}
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_XDCRandomize *XDCRandomizeRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _XDCRandomize.Contract.XDCRandomizeCaller.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_XDCRandomize *XDCRandomizeRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _XDCRandomize.Contract.XDCRandomizeTransactor.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_XDCRandomize *XDCRandomizeRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _XDCRandomize.Contract.XDCRandomizeTransactor.contract.Transact(opts, method, params...)
}
// Call invokes the (constant) contract method with params as input values and
// sets the output to result. The result type might be a single field for simple
// returns, a slice of interfaces for anonymous returns and a struct for named
// returns.
func (_XDCRandomize *XDCRandomizeCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
return _XDCRandomize.Contract.contract.Call(opts, result, method, params...)
}
// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_XDCRandomize *XDCRandomizeTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
return _XDCRandomize.Contract.contract.Transfer(opts)
}
// Transact invokes the (paid) contract method with params as input values.
func (_XDCRandomize *XDCRandomizeTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
return _XDCRandomize.Contract.contract.Transact(opts, method, params...)
}
// GetOpening is a free data retrieval call binding the contract method 0xd442d6cc.
//
// Solidity: function getOpening(_validator address) constant returns(bytes32)
func (_XDCRandomize *XDCRandomizeCaller) GetOpening(opts *bind.CallOpts, _validator common.Address) ([32]byte, error) {
var (
ret0 = new([32]byte)
)
out := &[]interface{}{
ret0,
}
err := _XDCRandomize.contract.Call(opts, out, "getOpening", _validator)
return *ret0, err
}
// GetOpening is a free data retrieval call binding the contract method 0xd442d6cc.
//
// Solidity: function getOpening(_validator address) constant returns(bytes32)
func (_XDCRandomize *XDCRandomizeSession) GetOpening(_validator common.Address) ([32]byte, error) {
return _XDCRandomize.Contract.GetOpening(&_XDCRandomize.CallOpts, _validator)
}
// GetOpening is a free data retrieval call binding the contract method 0xd442d6cc.
//
// Solidity: function getOpening(_validator address) constant returns(bytes32)
func (_XDCRandomize *XDCRandomizeCallerSession) GetOpening(_validator common.Address) ([32]byte, error) {
return _XDCRandomize.Contract.GetOpening(&_XDCRandomize.CallOpts, _validator)
}
// GetSecret is a free data retrieval call binding the contract method 0x284180fc.
//
// Solidity: function getSecret(_validator address) constant returns(bytes32[])
func (_XDCRandomize *XDCRandomizeCaller) GetSecret(opts *bind.CallOpts, _validator common.Address) ([][32]byte, error) {
var (
ret0 = new([][32]byte)
)
out := &[]interface{}{
ret0,
}
err := _XDCRandomize.contract.Call(opts, out, "getSecret", _validator)
return *ret0, err
}
// GetSecret is a free data retrieval call binding the contract method 0x284180fc.
//
// Solidity: function getSecret(_validator address) constant returns(bytes32[])
func (_XDCRandomize *XDCRandomizeSession) GetSecret(_validator common.Address) ([][32]byte, error) {
return _XDCRandomize.Contract.GetSecret(&_XDCRandomize.CallOpts, _validator)
}
// GetSecret is a free data retrieval call binding the contract method 0x284180fc.
//
// Solidity: function getSecret(_validator address) constant returns(bytes32[])
func (_XDCRandomize *XDCRandomizeCallerSession) GetSecret(_validator common.Address) ([][32]byte, error) {
return _XDCRandomize.Contract.GetSecret(&_XDCRandomize.CallOpts, _validator)
}
// SetOpening is a paid mutator transaction binding the contract method 0xe11f5ba2.
//
// Solidity: function setOpening(_opening bytes32) returns()
func (_XDCRandomize *XDCRandomizeTransactor) SetOpening(opts *bind.TransactOpts, _opening [32]byte) (*types.Transaction, error) {
return _XDCRandomize.contract.Transact(opts, "setOpening", _opening)
}
// SetOpening is a paid mutator transaction binding the contract method 0xe11f5ba2.
//
// Solidity: function setOpening(_opening bytes32) returns()
func (_XDCRandomize *XDCRandomizeSession) SetOpening(_opening [32]byte) (*types.Transaction, error) {
return _XDCRandomize.Contract.SetOpening(&_XDCRandomize.TransactOpts, _opening)
}
// SetOpening is a paid mutator transaction binding the contract method 0xe11f5ba2.
//
// Solidity: function setOpening(_opening bytes32) returns()
func (_XDCRandomize *XDCRandomizeTransactorSession) SetOpening(_opening [32]byte) (*types.Transaction, error) {
return _XDCRandomize.Contract.SetOpening(&_XDCRandomize.TransactOpts, _opening)
}
// SetSecret is a paid mutator transaction binding the contract method 0x34d38600.
//
// Solidity: function setSecret(_secret bytes32[]) returns()
func (_XDCRandomize *XDCRandomizeTransactor) SetSecret(opts *bind.TransactOpts, _secret [][32]byte) (*types.Transaction, error) {
return _XDCRandomize.contract.Transact(opts, "setSecret", _secret)
}
// SetSecret is a paid mutator transaction binding the contract method 0x34d38600.
//
// Solidity: function setSecret(_secret bytes32[]) returns()
func (_XDCRandomize *XDCRandomizeSession) SetSecret(_secret [][32]byte) (*types.Transaction, error) {
return _XDCRandomize.Contract.SetSecret(&_XDCRandomize.TransactOpts, _secret)
}
// SetSecret is a paid mutator transaction binding the contract method 0x34d38600.
//
// Solidity: function setSecret(_secret bytes32[]) returns()
func (_XDCRandomize *XDCRandomizeTransactorSession) SetSecret(_secret [][32]byte) (*types.Transaction, error) {
return _XDCRandomize.Contract.SetSecret(&_XDCRandomize.TransactOpts, _secret)
}

View file

@ -0,0 +1,56 @@
// Copyright (c) 2018 XDPoSChain
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package randomize
import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/contracts/randomize/contract"
)
type Randomize struct {
*contract.XDCRandomizeSession
contractBackend bind.ContractBackend
}
func NewRandomize(transactOpts *bind.TransactOpts, contractAddr common.Address, contractBackend bind.ContractBackend) (*Randomize, error) {
randomize, err := contract.NewXDCRandomize(contractAddr, contractBackend)
if err != nil {
return nil, err
}
return &Randomize{
&contract.XDCRandomizeSession{
Contract: randomize,
TransactOpts: *transactOpts,
},
contractBackend,
}, nil
}
func DeployRandomize(transactOpts *bind.TransactOpts, contractBackend bind.ContractBackend) (common.Address, *Randomize, error) {
randomizeAddr, _, _, err := contract.DeployXDCRandomize(transactOpts, contractBackend)
if err != nil {
return randomizeAddr, nil, err
}
randomize, err := NewRandomize(transactOpts, randomizeAddr, contractBackend)
if err != nil {
return randomizeAddr, nil, err
}
return randomizeAddr, randomize, nil
}

279
contracts/utils.go Normal file
View file

@ -0,0 +1,279 @@
// Copyright (c) 2018 XDPoSChain
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package contracts
import (
"bytes"
"crypto/aes"
"crypto/cipher"
cryptoRand "crypto/rand"
"encoding/base64"
"fmt"
"io"
"math/big"
"math/rand"
"strconv"
"time"
"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/XDPoS/utils"
randomizeContract "github.com/ethereum/go-ethereum/contracts/randomize/contract"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
const (
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
)
type RewardLog struct {
Sign uint64 `json:"sign"`
Reward *big.Int `json:"reward"`
}
// Create tx sign.
func CreateTxSign(blockNumber *big.Int, blockHash common.Hash, nonce uint64, blockSigner common.Address) *types.Transaction {
data := common.Hex2Bytes(common.HexSignMethod)
inputData := append(data, common.LeftPadBytes(blockNumber.Bytes(), 32)...)
inputData = append(inputData, common.LeftPadBytes(blockHash.Bytes(), 32)...)
tx := types.NewTransaction(nonce, blockSigner, big.NewInt(0), 200000, big.NewInt(0), inputData)
return tx
}
// Send secret key into randomize smartcontract.
func BuildTxSecretRandomize(nonce uint64, randomizeAddr common.Address, epocNumber uint64, randomizeKey []byte) (*types.Transaction, error) {
data := common.Hex2Bytes(common.HexSetSecret)
rand.Seed(time.Now().UnixNano())
secretNumb := rand.Intn(int(epocNumber))
// Append randomize suffix in -1, 0, 1.
secrets := []int64{int64(secretNumb)}
sizeOfArray := int64(32)
// Build extra data for tx with first position is size of array byte and second position are length of array byte.
arrSizeOfSecrets := common.LeftPadBytes(new(big.Int).SetInt64(sizeOfArray).Bytes(), 32)
arrLengthOfSecrets := common.LeftPadBytes(new(big.Int).SetInt64(int64(len(secrets))).Bytes(), 32)
inputData := append(data, arrSizeOfSecrets...)
inputData = append(inputData, arrLengthOfSecrets...)
for _, secret := range secrets {
encryptSecret := Encrypt(randomizeKey, new(big.Int).SetInt64(secret).String())
inputData = append(inputData, common.LeftPadBytes([]byte(encryptSecret), int(sizeOfArray))...)
}
tx := types.NewTransaction(nonce, randomizeAddr, big.NewInt(0), 200000, big.NewInt(0), inputData)
return tx, nil
}
// Send opening to randomize SMC.
func BuildTxOpeningRandomize(nonce uint64, randomizeAddr common.Address, randomizeKey []byte) (*types.Transaction, error) {
data := common.Hex2Bytes(common.HexSetOpening)
inputData := append(data, randomizeKey...)
tx := types.NewTransaction(nonce, randomizeAddr, big.NewInt(0), 200000, big.NewInt(0), inputData)
return tx, nil
}
// Get random from randomize contract.
func GetRandomizeFromContract(client bind.ContractBackend, addrMasternode common.Address) (int64, error) {
randomize, err := randomizeContract.NewXDCRandomize(common.RandomizeSMCBinary, client)
if err != nil {
log.Error("Fail to get instance of randomize", "error", err)
}
opts := new(bind.CallOpts)
secrets, err := randomize.GetSecret(opts, addrMasternode)
if err != nil {
log.Error("Fail get secrets from randomize", "error", err)
}
opening, err := randomize.GetOpening(opts, addrMasternode)
if err != nil {
log.Error("Fail get opening from randomize", "error", err)
}
return DecryptRandomizeFromSecretsAndOpening(secrets, opening)
}
// Generate m2 listing from randomize array.
func GenM2FromRandomize(randomizes []int64, lenSigners int64) ([]int64, error) {
blockValidator := NewSlice(int64(0), lenSigners, 1)
randIndexs := make([]int64, lenSigners)
total := int64(0)
var temp int64 = 0
for _, j := range randomizes {
total += j
}
rand.Seed(total)
for i := len(blockValidator) - 1; i >= 0; i-- {
blockLength := len(blockValidator) - 1
if blockLength <= 1 {
blockLength = 1
}
randomIndex := int64(rand.Intn(blockLength))
temp = blockValidator[randomIndex]
blockValidator[randomIndex] = blockValidator[i]
blockValidator[i] = temp
blockValidator = append(blockValidator[:i], blockValidator[i+1:]...)
randIndexs[i] = temp
}
return randIndexs, nil
}
// Get validators from m2 array integer.
func BuildValidatorFromM2(listM2 []int64) []byte {
var validatorBytes []byte
for _, numberM2 := range listM2 {
// Convert number to byte.
m2Byte := common.LeftPadBytes([]byte(fmt.Sprintf("%d", numberM2)), utils.M2ByteLength)
validatorBytes = append(validatorBytes, m2Byte...)
}
return validatorBytes
}
// Decode validator hex string.
func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
validatorsByte, err := hexutil.Decode(validatorsStr)
if err != nil {
return nil, err
}
return utils.ExtractValidatorsFromBytes(validatorsByte), nil
}
// Decrypt randomize from secrets and opening.
func DecryptRandomizeFromSecretsAndOpening(secrets [][32]byte, opening [32]byte) (int64, error) {
var random int64
if len(secrets) > 0 {
for _, secret := range secrets {
trimSecret := bytes.TrimLeft(secret[:], "\x00")
decryptSecret := Decrypt(opening[:], string(trimSecret))
if isInt(decryptSecret) {
intNumber, err := strconv.Atoi(decryptSecret)
if err != nil {
log.Error("Can not convert string to integer", "error", err)
return -1, err
}
random = int64(intNumber)
}
}
}
return random, nil
}
// Dynamic generate array sequence of numbers.
func NewSlice(start int64, end int64, step int64) []int64 {
s := make([]int64, end-start)
for i := range s {
s[i] = start
start += step
}
return s
}
// Shuffle array.
func Shuffle(slice []int64) []int64 {
newSlice := make([]int64, len(slice))
copy(newSlice, slice)
for i := 0; i < len(slice)-1; i++ {
rand.Seed(time.Now().UnixNano())
randIndex := rand.Intn(len(newSlice))
x := newSlice[i]
newSlice[i] = newSlice[randIndex]
newSlice[randIndex] = x
}
return newSlice
}
// encrypt string to base64 crypto using AES
func Encrypt(key []byte, text string) string {
plaintext := []byte(text)
block, err := aes.NewCipher(key)
if err != nil {
log.Error("Fail to encrypt", "err", err)
return ""
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(cryptoRand.Reader, iv); err != nil {
log.Error("Fail to encrypt iv", "err", err)
return ""
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
// convert to base64
return base64.URLEncoding.EncodeToString(ciphertext)
}
// decrypt from base64 to decrypted string
func Decrypt(key []byte, cryptoText string) string {
ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)
block, err := aes.NewCipher(key)
if err != nil {
log.Error("Fail to decrypt", "err", err)
return ""
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < aes.BlockSize {
log.Error("ciphertext too short")
return ""
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(ciphertext, ciphertext)
return string(ciphertext[:])
}
// Generate random string.
func RandStringByte(n int) []byte {
letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
b := make([]byte, n)
for i := range b {
rand.Seed(time.Now().UnixNano())
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return b
}
// Helper function check string is numeric.
func isInt(strNumber string) bool {
if _, err := strconv.Atoi(strNumber); err == nil {
return true
} else {
return false
}
}

View file

@ -0,0 +1,41 @@
// Copyright (c) 2018 XDPoSChain
// This file provides utilities for reading smart contract state slots.
package state
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
func GetLocSimpleVariable(slot uint64) common.Hash {
slotHash := common.BigToHash(new(big.Int).SetUint64(slot))
return slotHash
}
func GetLocMappingAtKey(key common.Hash, slot uint64) *big.Int {
slotHash := common.BigToHash(new(big.Int).SetUint64(slot))
retByte := crypto.Keccak256(key.Bytes(), slotHash.Bytes())
ret := new(big.Int)
ret.SetBytes(retByte)
return ret
}
func GetLocDynamicArrAtElement(slotHash common.Hash, index uint64, elementSize uint64) common.Hash {
slotKecBig := crypto.Keccak256Hash(slotHash.Bytes()).Big()
//arrBig = slotKecBig + index * elementSize
arrBig := slotKecBig.Add(slotKecBig, new(big.Int).SetUint64(index*elementSize))
return common.BigToHash(arrBig)
}
func GetLocFixedArrAtElement(slot uint64, index uint64, elementSize uint64) common.Hash {
slotBig := new(big.Int).SetUint64(slot)
arrBig := slotBig.Add(slotBig, new(big.Int).SetUint64(index*elementSize))
return common.BigToHash(arrBig)
}
func GetLocOfStructElement(locOfStruct *big.Int, elementSlotInstruct *big.Int) common.Hash {
return common.BigToHash(new(big.Int).Add(locOfStruct, elementSlotInstruct))
}

184
core/state/statedb_utils.go Normal file
View file

@ -0,0 +1,184 @@
// Copyright (c) 2018 XDPoSChain
// This file provides utilities for accessing XDPoS state data.
package state
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
var (
slotBlockSignerMapping = map[string]uint64{
"blockSigners": 0,
"blocks": 1,
}
)
func GetSigners(statedb *StateDB, block *types.Block) []common.Address {
slot := slotBlockSignerMapping["blockSigners"]
keys := []common.Hash{}
keyArrSlot := GetLocMappingAtKey(block.Hash(), slot)
arrSlot := statedb.GetState(common.BlockSignersBinary, common.BigToHash(keyArrSlot))
arrLength := arrSlot.Big().Uint64()
for i := uint64(0); i < arrLength; i++ {
key := GetLocDynamicArrAtElement(common.BigToHash(keyArrSlot), i, 1)
keys = append(keys, key)
}
rets := []common.Address{}
for _, key := range keys {
ret := statedb.GetState(common.BlockSignersBinary, key)
rets = append(rets, common.HexToAddress(ret.Hex()))
}
return rets
}
var (
slotRandomizeMapping = map[string]uint64{
"randomSecret": 0,
"randomOpening": 1,
}
)
func GetSecret(statedb *StateDB, address common.Address) [][32]byte {
slot := slotRandomizeMapping["randomSecret"]
locSecret := GetLocMappingAtKey(common.BytesToHash(address.Bytes()), slot)
arrLength := statedb.GetState(common.RandomizeSMCBinary, common.BigToHash(locSecret))
keys := []common.Hash{}
for i := uint64(0); i < arrLength.Big().Uint64(); i++ {
key := GetLocDynamicArrAtElement(common.BigToHash(locSecret), i, 1)
keys = append(keys, key)
}
rets := [][32]byte{}
for _, key := range keys {
ret := statedb.GetState(common.RandomizeSMCBinary, key)
rets = append(rets, ret)
}
return rets
}
func GetOpening(statedb *StateDB, address common.Address) [32]byte {
slot := slotRandomizeMapping["randomOpening"]
locOpening := GetLocMappingAtKey(common.BytesToHash(address.Bytes()), slot)
ret := statedb.GetState(common.RandomizeSMCBinary, common.BigToHash(locOpening))
return ret
}
// The smart contract and the compiled byte code (in corresponding *.go file) is at commit "KYC Layer added." 7f856ffe672162dfa9c4006c89afb45a24fb7f9f
// Notice that if smart contract and the compiled byte code (in corresponding *.go file) changes, below also changes
var (
slotValidatorMapping = map[string]uint64{
"withdrawsState": 0,
"validatorsState": 1,
"voters": 2,
"KYCString": 3,
"invalidKYCCount": 4,
"hasVotedInvalid": 5,
"ownerToCandidate": 6,
"owners": 7,
"candidates": 8,
"candidateCount": 9,
"ownerCount": 10,
"minCandidateCap": 11,
"minVoterCap": 12,
"maxValidatorNumber": 13,
"candidateWithdrawDelay": 14,
"voterWithdrawDelay": 15,
}
)
func GetCandidates(statedb *StateDB) []common.Address {
slot := slotValidatorMapping["candidates"]
slotHash := common.BigToHash(new(big.Int).SetUint64(slot))
arrLength := statedb.GetState(common.MasternodeVotingSMCBinary, slotHash)
count := arrLength.Big().Uint64()
rets := make([]common.Address, 0, count)
emptyHash := common.Hash{}
for i := uint64(0); i < count; i++ {
key := GetLocDynamicArrAtElement(slotHash, i, 1)
ret := statedb.GetState(common.MasternodeVotingSMCBinary, key)
if ret != emptyHash {
rets = append(rets, common.HexToAddress(ret.Hex()))
}
}
return rets
}
func GetCandidateOwner(statedb *StateDB, candidate common.Address) common.Address {
slot := slotValidatorMapping["validatorsState"]
// validatorsState[_candidate].owner;
locValidatorsState := GetLocMappingAtKey(common.BytesToHash(candidate.Bytes()), slot)
locCandidateOwner := locValidatorsState.Add(locValidatorsState, new(big.Int).SetUint64(uint64(0)))
ret := statedb.GetState(common.MasternodeVotingSMCBinary, common.BigToHash(locCandidateOwner))
return common.HexToAddress(ret.Hex())
}
func GetCandidateCap(statedb *StateDB, candidate common.Address) *big.Int {
slot := slotValidatorMapping["validatorsState"]
// validatorsState[_candidate].cap;
locValidatorsState := GetLocMappingAtKey(common.BytesToHash(candidate.Bytes()), slot)
locCandidateCap := locValidatorsState.Add(locValidatorsState, new(big.Int).SetUint64(uint64(1)))
ret := statedb.GetState(common.MasternodeVotingSMCBinary, common.BigToHash(locCandidateCap))
return ret.Big()
}
func GetVoters(statedb *StateDB, candidate common.Address) []common.Address {
//mapping(address => address[]) voters;
slot := slotValidatorMapping["voters"]
locVoters := GetLocMappingAtKey(common.BytesToHash(candidate.Bytes()), slot)
arrLength := statedb.GetState(common.MasternodeVotingSMCBinary, common.BigToHash(locVoters))
keys := []common.Hash{}
for i := uint64(0); i < arrLength.Big().Uint64(); i++ {
key := GetLocDynamicArrAtElement(common.BigToHash(locVoters), i, 1)
keys = append(keys, key)
}
rets := []common.Address{}
for _, key := range keys {
ret := statedb.GetState(common.MasternodeVotingSMCBinary, key)
rets = append(rets, common.HexToAddress(ret.Hex()))
}
return rets
}
func GetVoterCap(statedb *StateDB, candidate, voter common.Address) *big.Int {
slot := slotValidatorMapping["validatorsState"]
locValidatorsState := GetLocMappingAtKey(common.BytesToHash(candidate.Bytes()), slot)
locCandidateVoters := locValidatorsState.Add(locValidatorsState, new(big.Int).SetUint64(uint64(2)))
retByte := crypto.Keccak256(common.BytesToHash(voter.Bytes()).Bytes(), common.BigToHash(locCandidateVoters).Bytes())
ret := statedb.GetState(common.MasternodeVotingSMCBinary, common.BytesToHash(retByte))
return ret.Big()
}
var (
slotMintedRecordTotalMinted uint64 = 0
slotMintedRecordLastEpochNum uint64 = 1
)
func GetTotalMinted(statedb *StateDB) common.Hash {
hash := GetLocSimpleVariable(slotMintedRecordTotalMinted)
totalMinted := statedb.GetState(common.MintedRecordAddressBinary, hash)
return totalMinted
}
func PutTotalMinted(statedb *StateDB, value common.Hash) {
hash := GetLocSimpleVariable(slotMintedRecordTotalMinted)
statedb.SetState(common.MintedRecordAddressBinary, hash, value)
}
func GetLastEpochNum(statedb *StateDB) common.Hash {
hash := GetLocSimpleVariable(slotMintedRecordLastEpochNum)
totalMinted := statedb.GetState(common.MintedRecordAddressBinary, hash)
return totalMinted
}
func PutLastEpochNum(statedb *StateDB, value common.Hash) {
hash := GetLocSimpleVariable(slotMintedRecordLastEpochNum)
statedb.SetState(common.MintedRecordAddressBinary, hash, value)
}

View file

@ -81,6 +81,11 @@ type Header struct {
MixDigest common.Hash `json:"mixHash"`
Nonce BlockNonce `json:"nonce"`
// XDPoS fields for validator consensus
Validators []byte `json:"validators" rlp:"optional"`
Validator []byte `json:"validator" rlp:"optional"`
Penalties []byte `json:"penalties" rlp:"optional"`
// BaseFee was added by EIP-1559 and is ignored in legacy headers.
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
@ -112,6 +117,10 @@ type headerMarshaling struct {
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
BlobGasUsed *hexutil.Uint64
ExcessBlobGas *hexutil.Uint64
// XDPoS fields
Validators hexutil.Bytes
Validator hexutil.Bytes
Penalties hexutil.Bytes
}
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
@ -129,7 +138,7 @@ func (h *Header) Size() common.StorageSize {
if h.BaseFee != nil {
baseFeeBits = h.BaseFee.BitLen()
}
return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+baseFeeBits)/8)
return headerSize + common.StorageSize(len(h.Extra)+len(h.Validators)+len(h.Validator)+len(h.Penalties)+(h.Difficulty.BitLen()+h.Number.BitLen()+baseFeeBits)/8)
}
// SanityCheck checks a few basic things -- these checks are way beyond what

141
core/types/consensus_v2.go Normal file
View file

@ -0,0 +1,141 @@
// Copyright (c) 2018 XDPoSChain
// XDPoS 2.0 consensus types
package types
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
// Round number type in XDPoS 2.0
type Round uint64
type Signature []byte
// Block Info struct in XDPoS 2.0, used for vote message, etc.
type BlockInfo struct {
Hash common.Hash `json:"hash"`
Round Round `json:"round"`
Number *big.Int `json:"number"`
}
// Vote message in XDPoS 2.0
type VoteXDPoS struct {
signer common.Address //field not exported
ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"`
Signature Signature `json:"signature"`
GapNumber uint64 `json:"gapNumber"`
}
func (v *VoteXDPoS) Hash() common.Hash {
return rlpHash(v)
}
func (v *VoteXDPoS) PoolKey() string {
// return the voted block hash
return fmt.Sprint(v.ProposedBlockInfo.Round, ":", v.GapNumber, ":", v.ProposedBlockInfo.Number, ":", v.ProposedBlockInfo.Hash.Hex())
}
func (v *VoteXDPoS) GetSigner() common.Address {
return v.signer
}
func (v *VoteXDPoS) SetSigner(signer common.Address) {
v.signer = signer
}
// Timeout message in XDPoS 2.0
type Timeout struct {
signer common.Address
Round Round
Signature Signature
GapNumber uint64
}
func (t *Timeout) Hash() common.Hash {
return rlpHash(t)
}
func (t *Timeout) PoolKey() string {
// timeout pool key is round:gapNumber
return fmt.Sprint(t.Round, ":", t.GapNumber)
}
func (t *Timeout) GetSigner() common.Address {
return t.signer
}
func (t *Timeout) SetSigner(signer common.Address) {
t.signer = signer
}
// BFT Sync Info message in XDPoS 2.0
type SyncInfo struct {
HighestQuorumCert *QuorumCert
HighestTimeoutCert *TimeoutCert
}
func (s *SyncInfo) Hash() common.Hash {
return rlpHash(s)
}
// Quorum Certificate struct in XDPoS 2.0
type QuorumCert struct {
ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"`
Signatures []Signature `json:"signatures"`
GapNumber uint64 `json:"gapNumber"`
}
// Timeout Certificate struct in XDPoS 2.0
type TimeoutCert struct {
Round Round
Signatures []Signature
GapNumber uint64
}
// The parsed extra fields in block header in XDPoS 2.0 (excluding the version byte)
// The version byte (consensus version) is the first byte in header's extra and it's only valid with value >= 2
type ExtraFields_v2 struct {
Round Round
QuorumCert *QuorumCert
}
// Encode XDPoS 2.0 extra fields into bytes
func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
bytes, err := rlp.EncodeToBytes(e)
if err != nil {
return nil, err
}
versionByte := []byte{2}
return append(versionByte, bytes...), nil
}
type EpochSwitchInfo struct {
Penalties []common.Address
Standbynodes []common.Address
Masternodes []common.Address
MasternodesLen int
EpochSwitchBlockInfo *BlockInfo
EpochSwitchParentBlockInfo *BlockInfo
}
type VoteForSign struct {
ProposedBlockInfo *BlockInfo
GapNumber uint64
}
func VoteSigHash(m *VoteForSign) common.Hash {
return rlpHash(m)
}
type TimeoutForSign struct {
Round Round
GapNumber uint64
}
func TimeoutSigHash(m *TimeoutForSign) common.Hash {
return rlpHash(m)
}

View file

@ -57,6 +57,11 @@ func (b *EthAPIBackend) ChainConfig() *params.ChainConfig {
return b.eth.blockchain.Config()
}
// BlockChain returns the underlying blockchain for XDPoS operations.
func (b *EthAPIBackend) BlockChain() *core.BlockChain {
return b.eth.blockchain
}
func (b *EthAPIBackend) CurrentBlock() *types.Header {
return b.eth.blockchain.CurrentBlock()
}

379
internal/ethapi/api_xdc.go Normal file
View file

@ -0,0 +1,379 @@
// Copyright (c) 2018 XDPoSChain
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ethapi
import (
"context"
"errors"
"math/big"
"sort"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/XDPoS"
"github.com/ethereum/go-ethereum/consensus/XDPoS/utils"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)
// XDC status field names
const (
fieldStatus = "status"
fieldCapacity = "capacity"
fieldSuccess = "success"
fieldEpoch = "epoch"
statusMasternode = "MASTERNODE"
statusSlashed = "SLASHED"
statusProposed = "PROPOSED"
statusPenalty = "PENALTY"
)
var (
errEmptyHeader = errors.New("empty header")
)
// GetBlockSignersByHash returns the signers of a block by hash
func (s *BlockChainAPI) GetBlockSignersByHash(ctx context.Context, blockHash common.Hash) ([]common.Address, error) {
block, err := s.b.BlockByHash(ctx, blockHash)
if err != nil || block == nil {
return []common.Address{}, err
}
masternodes, err := s.GetMasternodes(ctx, block)
if err != nil || len(masternodes) == 0 {
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
return []common.Address{}, err
}
return s.rpcOutputBlockSigners(block, ctx, masternodes)
}
// GetBlockSignersByNumber returns the signers of a block by number
func (s *BlockChainAPI) GetBlockSignersByNumber(ctx context.Context, blockNumber rpc.BlockNumber) ([]common.Address, error) {
block, err := s.b.BlockByNumber(ctx, blockNumber)
if err != nil || block == nil {
return []common.Address{}, err
}
masternodes, err := s.GetMasternodes(ctx, block)
if err != nil || len(masternodes) == 0 {
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
return []common.Address{}, err
}
return s.rpcOutputBlockSigners(block, ctx, masternodes)
}
// GetBlockFinalityByHash returns the finality of a block by hash
func (s *BlockChainAPI) GetBlockFinalityByHash(ctx context.Context, blockHash common.Hash) (uint, error) {
block, err := s.b.BlockByHash(ctx, blockHash)
if err != nil || block == nil {
return uint(0), err
}
masternodes, err := s.GetMasternodes(ctx, block)
if err != nil || len(masternodes) == 0 {
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
return uint(0), err
}
return s.findFinalityOfBlock(ctx, block, masternodes)
}
// GetBlockFinalityByNumber returns the finality of a block by number
func (s *BlockChainAPI) GetBlockFinalityByNumber(ctx context.Context, blockNumber rpc.BlockNumber) (uint, error) {
block, err := s.b.BlockByNumber(ctx, blockNumber)
if err != nil || block == nil {
return uint(0), err
}
masternodes, err := s.GetMasternodes(ctx, block)
if err != nil || len(masternodes) == 0 {
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes))
return uint(0), err
}
return s.findFinalityOfBlock(ctx, block, masternodes)
}
// GetMasternodes returns masternodes set at the starting block of epoch of the given block
func (s *BlockChainAPI) GetMasternodes(ctx context.Context, b *types.Block) ([]common.Address, error) {
var masternodes []common.Address
if b.Number().Int64() >= 0 {
if engine, ok := s.b.Engine().(*XDPoS.XDPoS); ok {
// Get block epoch masternodes
chain := s.b.BlockChain()
if chain != nil {
return engine.GetMasternodes(chain, b.Header()), nil
}
} else {
log.Error("Undefined XDPoS consensus engine")
}
}
return masternodes, nil
}
// GetCandidateStatus returns status of the given candidate at a specified epochNumber
func (s *BlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAddress common.Address, epoch rpc.BlockNumber) (map[string]interface{}, error) {
result := map[string]interface{}{
fieldStatus: "",
fieldCapacity: 0,
fieldSuccess: true,
}
config := s.b.ChainConfig()
if config.XDPoS == nil {
return result, errors.New("XDPoS config not found")
}
epochConfig := config.XDPoS.Epoch
// Calculate checkpoint block number
blockNum := uint64(epoch)
if epoch < 0 {
blockNum = s.b.CurrentBlock().Number.Uint64()
}
checkpointNumber := (blockNum / epochConfig) * epochConfig
if checkpointNumber == 0 {
checkpointNumber = epochConfig
}
result[fieldEpoch] = checkpointNumber / epochConfig
block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(checkpointNumber))
if err != nil || block == nil {
result[fieldSuccess] = false
return result, err
}
header := block.Header()
if header == nil {
log.Error("Empty header at checkpoint", "num", checkpointNumber)
return result, errEmptyHeader
}
// Get candidates from state
statedb, _, err := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(checkpointNumber))
if err != nil {
result[fieldSuccess] = false
return result, err
}
if statedb == nil {
result[fieldSuccess] = false
return result, errors.New("nil statedb in GetCandidateStatus")
}
candidatesAddresses := state.GetCandidates(statedb)
candidates := make([]utils.Masternode, 0, len(candidatesAddresses))
for _, address := range candidatesAddresses {
v := state.GetCandidateCap(statedb, address)
candidates = append(candidates, utils.Masternode{Address: address, Stake: v})
}
if len(candidates) == 0 {
log.Debug("Candidates list cannot be found", "len(candidates)", len(candidates), "err", err)
result[fieldSuccess] = false
return result, err
}
// Check if the address is a candidate
isCandidate := false
for i := 0; i < len(candidates); i++ {
if coinbaseAddress == candidates[i].Address {
isCandidate = true
result[fieldStatus] = statusProposed
result[fieldCapacity] = candidates[i].Stake
break
}
}
// Get masternode list
var masternodes []common.Address
if engine, ok := s.b.Engine().(*XDPoS.XDPoS); ok {
masternodes = engine.GetMasternodesFromCheckpointHeader(header, header.Number.Uint64(), epochConfig)
if len(masternodes) == 0 {
log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes), "blockNum", header.Number.Uint64())
result[fieldSuccess] = false
return result, err
}
} else {
log.Error("Undefined XDPoS consensus engine")
}
// Set to statusMasternode if it is masternode
for _, masternode := range masternodes {
if coinbaseAddress == masternode {
result[fieldStatus] = statusMasternode
if !isCandidate {
result[fieldCapacity] = -1
log.Warn("Find non-candidate masternode", "masternode", masternode.String(), "checkpointNumber", checkpointNumber, "epoch", epoch)
}
return result, nil
}
}
return result, nil
}
// GetCandidates returns status of all candidates at a specified epochNumber
func (s *BlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.BlockNumber) (map[string]interface{}, error) {
result := map[string]interface{}{
"candidates": []map[string]interface{}{},
"success": true,
}
config := s.b.ChainConfig()
if config.XDPoS == nil {
result["success"] = false
return result, errors.New("XDPoS config not found")
}
epochConfig := config.XDPoS.Epoch
// Calculate checkpoint block number
blockNum := uint64(epoch)
if epoch < 0 {
blockNum = s.b.CurrentBlock().Number.Uint64()
}
checkpointNumber := (blockNum / epochConfig) * epochConfig
if checkpointNumber == 0 {
checkpointNumber = epochConfig
}
result["epoch"] = checkpointNumber / epochConfig
// Get candidates from state
statedb, _, err := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(checkpointNumber))
if err != nil {
result["success"] = false
return result, err
}
if statedb == nil {
result["success"] = false
return result, errors.New("nil statedb in GetCandidates")
}
candidatesAddresses := state.GetCandidates(statedb)
candidates := make([]map[string]interface{}, 0, len(candidatesAddresses))
for _, address := range candidatesAddresses {
cap := state.GetCandidateCap(statedb, address)
owner := state.GetCandidateOwner(statedb, address)
candidates = append(candidates, map[string]interface{}{
"address": address,
"capacity": cap,
"owner": owner,
})
}
// Sort by capacity descending
sort.Slice(candidates, func(i, j int) bool {
capI := candidates[i]["capacity"].(*big.Int)
capJ := candidates[j]["capacity"].(*big.Int)
return capI.Cmp(capJ) > 0
})
result["candidates"] = candidates
return result, nil
}
// rpcOutputBlockSigners returns block signers for RPC output
func (s *BlockChainAPI) rpcOutputBlockSigners(block *types.Block, ctx context.Context, masternodes []common.Address) ([]common.Address, error) {
if block == nil {
return nil, errors.New("block not found")
}
header := block.Header()
if header == nil {
return nil, errors.New("header not found")
}
// Extract signers from header's Validator field
signers := make([]common.Address, 0)
if len(header.Validator) > 0 {
// Decode validator bytes to addresses
for i := 0; i+common.AddressLength <= len(header.Validator); i += common.AddressLength {
var addr common.Address
copy(addr[:], header.Validator[i:i+common.AddressLength])
signers = append(signers, addr)
}
}
// If no signers found in Validator field, return the block miner
if len(signers) == 0 {
signers = append(signers, header.Coinbase)
}
return signers, nil
}
// findFinalityOfBlock calculates the finality percentage of a block
func (s *BlockChainAPI) findFinalityOfBlock(ctx context.Context, block *types.Block, masternodes []common.Address) (uint, error) {
if block == nil || len(masternodes) == 0 {
return 0, nil
}
signers, err := s.rpcOutputBlockSigners(block, ctx, masternodes)
if err != nil {
return 0, err
}
// Calculate finality percentage
signerCount := len(signers)
masternodeCount := len(masternodes)
if masternodeCount == 0 {
return 0, nil
}
finality := uint((signerCount * 100) / masternodeCount)
return finality, nil
}
// RPCMarshalHeaderXDC converts a header to the RPC output with XDC fields
func RPCMarshalHeaderXDC(header *types.Header) map[string]interface{} {
result := map[string]interface{}{
"number": (*hexutil.Big)(header.Number),
"hash": header.Hash(),
"parentHash": header.ParentHash,
"nonce": header.Nonce,
"mixHash": header.MixDigest,
"sha3Uncles": header.UncleHash,
"logsBloom": header.Bloom,
"stateRoot": header.Root,
"miner": header.Coinbase,
"difficulty": (*hexutil.Big)(header.Difficulty),
"extraData": hexutil.Bytes(header.Extra),
"size": hexutil.Uint64(header.Size()),
"gasLimit": hexutil.Uint64(header.GasLimit),
"gasUsed": hexutil.Uint64(header.GasUsed),
"timestamp": hexutil.Uint64(header.Time),
"transactionsRoot": header.TxHash,
"receiptsRoot": header.ReceiptHash,
}
// Add XDPoS-specific fields
if header.Validators != nil {
result["validators"] = hexutil.Bytes(header.Validators)
}
if header.Validator != nil {
result["validator"] = hexutil.Bytes(header.Validator)
}
if header.Penalties != nil {
result["penalties"] = hexutil.Bytes(header.Penalties)
}
if header.BaseFee != nil {
result["baseFeePerGas"] = (*hexutil.Big)(header.BaseFee)
}
if header.WithdrawalsHash != nil {
result["withdrawalsRoot"] = header.WithdrawalsHash
}
return result
}

View file

@ -91,6 +91,9 @@ type Backend interface {
Engine() consensus.Engine
HistoryPruningCutoff() uint64
// XDPoS-specific method
BlockChain() *core.BlockChain
// This is copied from filters.Backend
// eth/filters needs to be initialized from this backend type, so methods needed by
// it must also be included here.