diff --git a/common/types.go b/common/types.go
index a96d6c7c83..d02721e78f 100644
--- a/common/types.go
+++ b/common/types.go
@@ -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 {
diff --git a/consensus/XDPoS/utils/constants.go b/consensus/XDPoS/utils/constants.go
new file mode 100644
index 0000000000..5a9589c82c
--- /dev/null
+++ b/consensus/XDPoS/utils/constants.go
@@ -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
+)
diff --git a/consensus/XDPoS/utils/types.go b/consensus/XDPoS/utils/types.go
new file mode 100644
index 0000000000..b89001a0f1
--- /dev/null
+++ b/consensus/XDPoS/utils/types.go
@@ -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]
diff --git a/consensus/XDPoS/utils/utils.go b/consensus/XDPoS/utils/utils.go
new file mode 100644
index 0000000000..d62d654b92
--- /dev/null
+++ b/consensus/XDPoS/utils/utils.go
@@ -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
+}
diff --git a/contracts/randomize/contract/randomize.go b/contracts/randomize/contract/randomize.go
new file mode 100644
index 0000000000..801c9f38bb
--- /dev/null
+++ b/contracts/randomize/contract/randomize.go
@@ -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)
+}
diff --git a/contracts/randomize/randomize.go b/contracts/randomize/randomize.go
new file mode 100644
index 0000000000..ca4a0cb154
--- /dev/null
+++ b/contracts/randomize/randomize.go
@@ -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 .
+
+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
+}
diff --git a/contracts/utils.go b/contracts/utils.go
new file mode 100644
index 0000000000..23c5bcd212
--- /dev/null
+++ b/contracts/utils.go
@@ -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 .
+
+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
+ }
+}
diff --git a/core/state/state_reader.go b/core/state/state_reader.go
new file mode 100644
index 0000000000..c0735425bd
--- /dev/null
+++ b/core/state/state_reader.go
@@ -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))
+}
diff --git a/core/state/statedb_utils.go b/core/state/statedb_utils.go
new file mode 100644
index 0000000000..72a03c292a
--- /dev/null
+++ b/core/state/statedb_utils.go
@@ -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)
+}
diff --git a/core/types/block.go b/core/types/block.go
index c52c05a4c7..ba4682000a 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -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
diff --git a/core/types/consensus_v2.go b/core/types/consensus_v2.go
new file mode 100644
index 0000000000..1b8cc67841
--- /dev/null
+++ b/core/types/consensus_v2.go
@@ -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)
+}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 3f826b7861..5d44c6f3fa 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -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()
}
diff --git a/internal/ethapi/api_xdc.go b/internal/ethapi/api_xdc.go
new file mode 100644
index 0000000000..d84bb4c876
--- /dev/null
+++ b/internal/ethapi/api_xdc.go
@@ -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 .
+
+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
+}
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index af3d592b82..e6ce63580b 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -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.