mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
XIN-121 Reward hook (#57)
* v2 Hook Reward, need test * test reward * fix RewardHook due to modifying params config directly (#56) * more test * finish test Co-authored-by: Jerome <wjrjerome@gmail.com>
This commit is contained in:
parent
9b47146120
commit
89acbdd742
8 changed files with 352 additions and 24 deletions
|
|
@ -53,7 +53,7 @@ type XDPoS_v2 struct {
|
||||||
highestTimeoutCert *utils.TimeoutCert
|
highestTimeoutCert *utils.TimeoutCert
|
||||||
highestCommitBlock *utils.BlockInfo
|
highestCommitBlock *utils.BlockInfo
|
||||||
|
|
||||||
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
|
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error)
|
||||||
HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error)
|
HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,13 +235,14 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
|
||||||
// rewards given, and returns the final block.
|
// rewards given, and returns the final block.
|
||||||
func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (x *XDPoS_v2) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, parentState *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||||
// set block reward
|
// set block reward
|
||||||
number := header.Number.Uint64()
|
|
||||||
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
|
|
||||||
|
|
||||||
// _ = c.CacheData(header, txs, receipts)
|
isEpochSwitch, _, err := x.IsEpochSwitch(header)
|
||||||
|
if err != nil {
|
||||||
if x.HookReward != nil && number%rCheckpoint == 0 {
|
log.Error("[Finalize] IsEpochSwitch bug!", "err", err)
|
||||||
err, rewards := x.HookReward(chain, state, parentState, header)
|
return nil, err
|
||||||
|
}
|
||||||
|
if x.HookReward != nil && isEpochSwitch {
|
||||||
|
rewards, err := x.HookReward(chain, state, parentState, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1385,20 +1386,30 @@ func (x *XDPoS_v2) GetMasternodesByHash(chain consensus.ChainReader, hash common
|
||||||
return epochSwitchInfo.Masternodes
|
return epochSwitchInfo.Masternodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given hash, get master node from the epoch switch block of the previous `limit` epoch
|
// get epoch switch of the previous `limit` epoch
|
||||||
func (x *XDPoS_v2) GetPreviousPenaltyByHash(chain consensus.ChainReader, hash common.Hash, limit int) []common.Address {
|
func (x *XDPoS_v2) getPreviousEpochSwitchInfoByHash(chain consensus.ChainReader, hash common.Hash, limit int) (*utils.EpochSwitchInfo, error) {
|
||||||
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash)
|
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[GetMasternodes] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
||||||
return []common.Address{}
|
return nil, err
|
||||||
}
|
}
|
||||||
for i := 0; i < limit; i++ {
|
for i := 0; i < limit; i++ {
|
||||||
epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, epochSwitchInfo.EpochSwitchParentBlockInfo.Hash)
|
epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, epochSwitchInfo.EpochSwitchParentBlockInfo.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[GetMasternodes] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
|
||||||
return []common.Address{}
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return epochSwitchInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given hash, get master node from the epoch switch block of the previous `limit` epoch
|
||||||
|
func (x *XDPoS_v2) GetPreviousPenaltyByHash(chain consensus.ChainReader, hash common.Hash, limit int) []common.Address {
|
||||||
|
epochSwitchInfo, err := x.getPreviousEpochSwitchInfoByHash(chain, hash, limit)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[GetPreviousPenaltyByHash] Adaptor v2 getPreviousEpochSwitchInfoByHash has error, potentially bug", "err", err)
|
||||||
|
return []common.Address{}
|
||||||
|
}
|
||||||
header := chain.GetHeaderByHash(epochSwitchInfo.EpochSwitchBlockInfo.Hash)
|
header := chain.GetHeaderByHash(epochSwitchInfo.EpochSwitchBlockInfo.Hash)
|
||||||
return common.ExtractAddressFromBytes(header.Penalties)
|
return common.ExtractAddressFromBytes(header.Penalties)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func TestHookPenaltyV2Comeback(t *testing.T) {
|
||||||
assert.Equal(t, 4, len(penalty))
|
assert.Equal(t, 4, len(penalty))
|
||||||
header6285 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*7 - common.MergeSignRange)
|
header6285 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*7 - common.MergeSignRange)
|
||||||
// forcely insert signing tx into cache, to cancel comeback. since no comeback, penalty is 3
|
// forcely insert signing tx into cache, to cancel comeback. since no comeback, penalty is 3
|
||||||
tx, err := signingTx(header6285, 0, signer, signFn)
|
tx, err := signingTxWithSignerFn(header6285, 0, signer, signFn)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
adaptor.CacheSigningTxs(header6285.Hash(), []*types.Transaction{tx})
|
adaptor.CacheSigningTxs(header6285.Hash(), []*types.Transaction{tx})
|
||||||
penalty, err = adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
|
penalty, err = adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
|
||||||
|
|
|
||||||
163
consensus/tests/reward_test.go
Normal file
163
consensus/tests/reward_test.go
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/hooks"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHookRewardV2(t *testing.T) {
|
||||||
|
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
configString := string(b)
|
||||||
|
|
||||||
|
var config params.ChainConfig
|
||||||
|
err = json.Unmarshal([]byte(configString), &config)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs
|
||||||
|
config.XDPoS.V2.SwitchBlock.SetUint64(1800)
|
||||||
|
|
||||||
|
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*5, &config, 0)
|
||||||
|
|
||||||
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
hooks.AttachConsensusV2Hooks(adaptor, blockchain, &config)
|
||||||
|
assert.NotNil(t, adaptor.EngineV2.HookReward)
|
||||||
|
// forcely insert signing tx into cache, to give rewards.
|
||||||
|
header915 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 15)
|
||||||
|
header916 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 16)
|
||||||
|
header1799 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 - 1)
|
||||||
|
header1801 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 + 1)
|
||||||
|
tx, err := signingTxWithSignerFn(header915, 0, signer, signFn)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
adaptor.CacheSigningTxs(header916.Hash(), []*types.Transaction{tx})
|
||||||
|
statedb, err := blockchain.StateAt(header1799.Root)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
parentState := statedb.Copy()
|
||||||
|
reward, err := adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header1801)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Zero(t, len(reward))
|
||||||
|
header2699 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*3 - 1)
|
||||||
|
header2700 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 3)
|
||||||
|
statedb, err = blockchain.StateAt(header2699.Root)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
parentState = statedb.Copy()
|
||||||
|
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header2700)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
owner := state.GetCandidateOwner(parentState, signer)
|
||||||
|
result := reward["rewards"].(map[common.Address]interface{})
|
||||||
|
assert.Equal(t, 1, len(result))
|
||||||
|
for _, x := range result {
|
||||||
|
r := x.(map[common.Address]*big.Int)
|
||||||
|
a, _ := big.NewInt(0).SetString("225000000000000000000", 10)
|
||||||
|
assert.Zero(t, a.Cmp(r[owner]))
|
||||||
|
b, _ := big.NewInt(0).SetString("25000000000000000000", 10)
|
||||||
|
assert.Zero(t, b.Cmp(r[common.HexToAddress("0x0000000000000000000000000000000000000068")]))
|
||||||
|
}
|
||||||
|
header2685 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 + 885)
|
||||||
|
header2716 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*3 + 16)
|
||||||
|
header3599 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*4 - 1)
|
||||||
|
header3600 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 4)
|
||||||
|
tx, err = signingTxWithSignerFn(header2685, 0, signer, signFn)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// signed block hash and block contains tx are in different epoch, we should get same rewards
|
||||||
|
adaptor.CacheSigningTxs(header2716.Hash(), []*types.Transaction{tx})
|
||||||
|
statedb, err = blockchain.StateAt(header3599.Root)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
parentState = statedb.Copy()
|
||||||
|
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header3600)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
result = reward["rewards"].(map[common.Address]interface{})
|
||||||
|
assert.Equal(t, 1, len(result))
|
||||||
|
for _, x := range result {
|
||||||
|
r := x.(map[common.Address]*big.Int)
|
||||||
|
a, _ := big.NewInt(0).SetString("225000000000000000000", 10)
|
||||||
|
assert.Zero(t, a.Cmp(r[owner]))
|
||||||
|
b, _ := big.NewInt(0).SetString("25000000000000000000", 10)
|
||||||
|
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]))
|
||||||
|
}
|
||||||
|
// if no signing tx, then reward will be 0
|
||||||
|
header4499 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*5 - 1)
|
||||||
|
header4500 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 5)
|
||||||
|
statedb, err = blockchain.StateAt(header4499.Root)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
parentState = statedb.Copy()
|
||||||
|
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header4500)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
result = reward["rewards"].(map[common.Address]interface{})
|
||||||
|
assert.Equal(t, 0, len(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookRewardV2SplitReward(t *testing.T) {
|
||||||
|
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
configString := string(b)
|
||||||
|
|
||||||
|
var config params.ChainConfig
|
||||||
|
err = json.Unmarshal([]byte(configString), &config)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs
|
||||||
|
config.XDPoS.V2.SwitchBlock.SetUint64(1800)
|
||||||
|
|
||||||
|
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*3, &config, 0)
|
||||||
|
|
||||||
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
hooks.AttachConsensusV2Hooks(adaptor, blockchain, &config)
|
||||||
|
assert.NotNil(t, adaptor.EngineV2.HookReward)
|
||||||
|
// forcely insert signing tx into cache, to give rewards.
|
||||||
|
header915 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 15)
|
||||||
|
header916 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 16)
|
||||||
|
// header917 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 17)
|
||||||
|
header1785 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 - 15)
|
||||||
|
header1799 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 - 1)
|
||||||
|
header1801 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 + 1)
|
||||||
|
tx, err := signingTxWithSignerFn(header915, 0, signer, signFn)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
adaptor.CacheSigningTxs(header916.Hash(), []*types.Transaction{tx})
|
||||||
|
tx2, err := signingTxWithKey(header915, 0, acc1Key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
tx3, err := signingTxWithKey(header1785, 0, acc1Key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
adaptor.CacheSigningTxs(header1799.Hash(), []*types.Transaction{tx2, tx3})
|
||||||
|
|
||||||
|
statedb, err := blockchain.StateAt(header1799.Root)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
parentState := statedb.Copy()
|
||||||
|
reward, err := adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header1801)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Zero(t, len(reward))
|
||||||
|
header2699 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*3 - 1)
|
||||||
|
header2700 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 3)
|
||||||
|
statedb, err = blockchain.StateAt(header2699.Root)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
parentState = statedb.Copy()
|
||||||
|
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header2700)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
result := reward["rewards"].(map[common.Address]interface{})
|
||||||
|
assert.Equal(t, 2, len(result))
|
||||||
|
// two signing account, 3 txs, reward is split by 1:2 (total reward is 250...000)
|
||||||
|
for addr, x := range result {
|
||||||
|
if addr == acc1Addr {
|
||||||
|
r := x.(map[common.Address]*big.Int)
|
||||||
|
owner := state.GetCandidateOwner(parentState, acc1Addr)
|
||||||
|
a, _ := big.NewInt(0).SetString("149999999999999999999", 10)
|
||||||
|
assert.Zero(t, a.Cmp(r[owner]))
|
||||||
|
b, _ := big.NewInt(0).SetString("16666666666666666666", 10)
|
||||||
|
assert.Zero(t, b.Cmp(r[common.HexToAddress("0x0000000000000000000000000000000000000068")]))
|
||||||
|
} else if addr == signer {
|
||||||
|
r := x.(map[common.Address]*big.Int)
|
||||||
|
owner := state.GetCandidateOwner(parentState, signer)
|
||||||
|
a, _ := big.NewInt(0).SetString("74999999999999999999", 10)
|
||||||
|
assert.Zero(t, a.Cmp(r[owner]))
|
||||||
|
b, _ := big.NewInt(0).SetString("8333333333333333333", 10)
|
||||||
|
assert.Zero(t, b.Cmp(r[common.HexToAddress("0x0000000000000000000000000000000000000068")]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -230,7 +230,7 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
|
||||||
return signedTX, nil
|
return signedTX, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func signingTx(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
|
func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
|
||||||
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
||||||
s := types.NewEIP155Signer(big.NewInt(chainID))
|
s := types.NewEIP155Signer(big.NewInt(chainID))
|
||||||
h := s.Hash(tx)
|
h := s.Hash(tx)
|
||||||
|
|
@ -245,6 +245,21 @@ func signingTx(header *types.Header, nonce uint64, signer common.Address, signFn
|
||||||
return signedTx, nil
|
return signedTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
|
||||||
|
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
|
||||||
|
s := types.NewEIP155Signer(big.NewInt(chainID))
|
||||||
|
h := s.Hash(tx)
|
||||||
|
sig, err := crypto.Sign(h[:], privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signedTx, err := tx.WithSignature(s, sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return signedTx, nil
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateSigner(bc *BlockChain) error {
|
func UpdateSigner(bc *BlockChain) error {
|
||||||
err := bc.UpdateM1()
|
err := bc.UpdateM1()
|
||||||
return err
|
return err
|
||||||
|
|
@ -534,8 +549,8 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
|
||||||
Coinbase: common.HexToAddress(blockCoinBase),
|
Coinbase: common.HexToAddress(blockCoinBase),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject the hardcoded master node list for the last v1 epoch block
|
// Inject the hardcoded master node list for the last v1 epoch block and all v1 epoch switch blocks (excluding genesis)
|
||||||
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 0 {
|
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 {
|
||||||
// reset extra
|
// reset extra
|
||||||
header.Extra = []byte{}
|
header.Extra = []byte{}
|
||||||
if len(header.Extra) < utils.ExtraVanity {
|
if len(header.Extra) < utils.ExtraVanity {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te
|
||||||
blockInfo := &utils.BlockInfo{
|
blockInfo := &utils.BlockInfo{
|
||||||
Hash: currentBlock.Hash(),
|
Hash: currentBlock.Hash(),
|
||||||
Round: utils.Round(1),
|
Round: utils.Round(1),
|
||||||
Number: big.NewInt(11),
|
Number: big.NewInt(901),
|
||||||
}
|
}
|
||||||
voteSigningHash := utils.VoteSigHash(blockInfo)
|
voteSigningHash := utils.VoteSigHash(blockInfo)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ const (
|
||||||
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||||
)
|
)
|
||||||
|
|
||||||
type rewardLog struct {
|
type RewardLog struct {
|
||||||
Sign uint64 `json:"sign"`
|
Sign uint64 `json:"sign"`
|
||||||
Reward *big.Int `json:"reward"`
|
Reward *big.Int `json:"reward"`
|
||||||
}
|
}
|
||||||
|
|
@ -319,13 +319,13 @@ func DecryptRandomizeFromSecretsAndOpening(secrets [][32]byte, opening [32]byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate reward for reward checkpoint.
|
// Calculate reward for reward checkpoint.
|
||||||
func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, rCheckpoint uint64, totalSigner *uint64) (map[common.Address]*rewardLog, error) {
|
func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, rCheckpoint uint64, totalSigner *uint64) (map[common.Address]*RewardLog, error) {
|
||||||
// Not reward for singer of genesis block and only calculate reward at checkpoint block.
|
// Not reward for singer of genesis block and only calculate reward at checkpoint block.
|
||||||
number := header.Number.Uint64()
|
number := header.Number.Uint64()
|
||||||
prevCheckpoint := number - (rCheckpoint * 2)
|
prevCheckpoint := number - (rCheckpoint * 2)
|
||||||
startBlockNumber := prevCheckpoint + 1
|
startBlockNumber := prevCheckpoint + 1
|
||||||
endBlockNumber := startBlockNumber + rCheckpoint - 1
|
endBlockNumber := startBlockNumber + rCheckpoint - 1
|
||||||
signers := make(map[common.Address]*rewardLog)
|
signers := make(map[common.Address]*RewardLog)
|
||||||
mapBlkHash := map[uint64]common.Hash{}
|
mapBlkHash := map[uint64]common.Hash{}
|
||||||
|
|
||||||
data := make(map[common.Hash][]common.Address)
|
data := make(map[common.Hash][]common.Address)
|
||||||
|
|
@ -376,7 +376,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
|
||||||
if exist {
|
if exist {
|
||||||
signers[addr].Sign++
|
signers[addr].Sign++
|
||||||
} else {
|
} else {
|
||||||
signers[addr] = &rewardLog{1, new(big.Int)}
|
signers[addr] = &RewardLog{1, new(big.Int)}
|
||||||
}
|
}
|
||||||
*totalSigner++
|
*totalSigner++
|
||||||
}
|
}
|
||||||
|
|
@ -390,7 +390,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate reward for signers.
|
// Calculate reward for signers.
|
||||||
func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]*rewardLog, totalSigner uint64) (map[common.Address]*big.Int, error) {
|
func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]*RewardLog, totalSigner uint64) (map[common.Address]*big.Int, error) {
|
||||||
resultSigners := make(map[common.Address]*big.Int)
|
resultSigners := make(map[common.Address]*big.Int)
|
||||||
// Add reward for signers.
|
// Add reward for signers.
|
||||||
if totalSigner > 0 {
|
if totalSigner > 0 {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
package hooks
|
package hooks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/contracts"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/util"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
@ -135,4 +139,139 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
|
||||||
return penalties, nil
|
return penalties, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hook calculates reward for masternodes
|
||||||
|
adaptor.EngineV2.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) {
|
||||||
|
number := header.Number.Uint64()
|
||||||
|
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr
|
||||||
|
if foundationWalletAddr == (common.Address{}) {
|
||||||
|
log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr)
|
||||||
|
return nil, errors.New("foundation wallet address is empty")
|
||||||
|
}
|
||||||
|
rewards := make(map[string]interface{})
|
||||||
|
// skip hook reward if this is the first v2
|
||||||
|
if number == chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 {
|
||||||
|
return rewards, nil
|
||||||
|
}
|
||||||
|
start := time.Now()
|
||||||
|
// Get reward inflation.
|
||||||
|
chainReward := new(big.Int).Mul(new(big.Int).SetUint64(chain.Config().XDPoS.Reward), new(big.Int).SetUint64(params.Ether))
|
||||||
|
chainReward = util.RewardInflation(chain, chainReward, number, common.BlocksPerYear)
|
||||||
|
|
||||||
|
// Get signers/signing tx count
|
||||||
|
totalSigner := new(uint64)
|
||||||
|
signers, err := GetSigningTxCount(adaptor, chain, header, totalSigner)
|
||||||
|
|
||||||
|
log.Debug("Time Get Signers", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[HookReward] Fail to get signers count for reward checkpoint", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rewards["signers"] = signers
|
||||||
|
rewardSigners, err := contracts.CalculateRewardForSigner(chainReward, signers, *totalSigner)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[HookReward] Fail to calculate reward for signers", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Add reward for coin holders.
|
||||||
|
voterResults := make(map[common.Address]interface{})
|
||||||
|
if len(signers) > 0 {
|
||||||
|
for signer, calcReward := range rewardSigners {
|
||||||
|
err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("[HookReward] Fail to calculate reward for holders.", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rewards) > 0 {
|
||||||
|
for holder, reward := range rewards {
|
||||||
|
stateBlock.AddBalance(holder, reward)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
voterResults[signer] = rewards
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rewards["rewards"] = voterResults
|
||||||
|
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
|
||||||
|
return rewards, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get signing transaction sender count
|
||||||
|
func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, totalSigner *uint64) (map[common.Address]*contracts.RewardLog, error) {
|
||||||
|
// header should be a new epoch switch block
|
||||||
|
number := header.Number.Uint64()
|
||||||
|
rewardEpochCount := 2
|
||||||
|
signEpochCount := 1
|
||||||
|
signers := make(map[common.Address]*contracts.RewardLog)
|
||||||
|
mapBlkHash := map[uint64]common.Hash{}
|
||||||
|
|
||||||
|
data := make(map[common.Hash][]common.Address)
|
||||||
|
epochCount := 0
|
||||||
|
var masternodes []common.Address
|
||||||
|
var startBlockNumber, endBlockNumber uint64
|
||||||
|
for i := number - 1; ; i-- {
|
||||||
|
header = chain.GetHeader(header.ParentHash, i)
|
||||||
|
isEpochSwitch, _, err := c.IsEpochSwitch(header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isEpochSwitch && i != chain.Config().XDPoS.V2.SwitchBlock.Uint64()+1 {
|
||||||
|
epochCount += 1
|
||||||
|
if epochCount == signEpochCount {
|
||||||
|
endBlockNumber = header.Number.Uint64() - 1
|
||||||
|
}
|
||||||
|
if epochCount == rewardEpochCount {
|
||||||
|
startBlockNumber = header.Number.Uint64() + 1
|
||||||
|
masternodes = c.GetMasternodesFromCheckpointHeader(header)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapBlkHash[i] = header.Hash()
|
||||||
|
signData, ok := c.GetCachedSigningTxs(header.Hash())
|
||||||
|
if !ok {
|
||||||
|
log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i)
|
||||||
|
block := chain.GetBlock(header.Hash(), i)
|
||||||
|
txs := block.Transactions()
|
||||||
|
signData = c.CacheSigningTxs(header.Hash(), txs)
|
||||||
|
}
|
||||||
|
txs := signData.([]*types.Transaction)
|
||||||
|
for _, tx := range txs {
|
||||||
|
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
||||||
|
from := *tx.From()
|
||||||
|
data[blkHash] = append(data[blkHash], from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := startBlockNumber; i <= endBlockNumber; i++ {
|
||||||
|
if i%common.MergeSignRange == 0 {
|
||||||
|
addrs := data[mapBlkHash[i]]
|
||||||
|
// Filter duplicate address.
|
||||||
|
if len(addrs) > 0 {
|
||||||
|
addrSigners := make(map[common.Address]bool)
|
||||||
|
for _, masternode := range masternodes {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if addr == masternode {
|
||||||
|
if _, ok := addrSigners[addr]; !ok {
|
||||||
|
addrSigners[addr] = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr := range addrSigners {
|
||||||
|
_, exist := signers[addr]
|
||||||
|
if exist {
|
||||||
|
signers[addr].Sign++
|
||||||
|
} else {
|
||||||
|
signers[addr] = &contracts.RewardLog{Sign: 1, Reward: new(big.Int)}
|
||||||
|
}
|
||||||
|
*totalSigner++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Calculate reward at checkpoint", "startBlock", startBlockNumber, "endBlock", endBlockNumber)
|
||||||
|
|
||||||
|
return signers, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ var (
|
||||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil}
|
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil}
|
||||||
|
|
||||||
// XDPoS config with v2 engine after block 901
|
// XDPoS config with v2 engine after block 901
|
||||||
TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, V2: TestXDPoSV2Config}}
|
TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, V2: TestXDPoSV2Config, FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), Reward: 250}}
|
||||||
|
|
||||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
||||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue