Skip to content

Commit 89acbdd

Browse files
wgr523wjrjerome
andauthored
XIN-121 Reward hook (ethereum#57)
* v2 Hook Reward, need test * test reward * fix RewardHook due to modifying params config directly (ethereum#56) * more test * finish test Co-authored-by: Jerome <[email protected]>
1 parent 9b47146 commit 89acbdd

File tree

8 files changed

+352
-24
lines changed

8 files changed

+352
-24
lines changed

consensus/XDPoS/engines/engine_v2/engine.go

+24-13
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type XDPoS_v2 struct {
5353
highestTimeoutCert *utils.TimeoutCert
5454
highestCommitBlock *utils.BlockInfo
5555

56-
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
56+
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error)
5757
HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error)
5858
}
5959

@@ -235,13 +235,14 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
235235
// rewards given, and returns the final block.
236236
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) {
237237
// set block reward
238-
number := header.Number.Uint64()
239-
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
240-
241-
// _ = c.CacheData(header, txs, receipts)
242238

243-
if x.HookReward != nil && number%rCheckpoint == 0 {
244-
err, rewards := x.HookReward(chain, state, parentState, header)
239+
isEpochSwitch, _, err := x.IsEpochSwitch(header)
240+
if err != nil {
241+
log.Error("[Finalize] IsEpochSwitch bug!", "err", err)
242+
return nil, err
243+
}
244+
if x.HookReward != nil && isEpochSwitch {
245+
rewards, err := x.HookReward(chain, state, parentState, header)
245246
if err != nil {
246247
return nil, err
247248
}
@@ -1385,20 +1386,30 @@ func (x *XDPoS_v2) GetMasternodesByHash(chain consensus.ChainReader, hash common
13851386
return epochSwitchInfo.Masternodes
13861387
}
13871388

1388-
// Given hash, get master node from the epoch switch block of the previous `limit` epoch
1389-
func (x *XDPoS_v2) GetPreviousPenaltyByHash(chain consensus.ChainReader, hash common.Hash, limit int) []common.Address {
1389+
// get epoch switch of the previous `limit` epoch
1390+
func (x *XDPoS_v2) getPreviousEpochSwitchInfoByHash(chain consensus.ChainReader, hash common.Hash, limit int) (*utils.EpochSwitchInfo, error) {
13901391
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash)
13911392
if err != nil {
1392-
log.Error("[GetMasternodes] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
1393-
return []common.Address{}
1393+
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
1394+
return nil, err
13941395
}
13951396
for i := 0; i < limit; i++ {
13961397
epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, epochSwitchInfo.EpochSwitchParentBlockInfo.Hash)
13971398
if err != nil {
1398-
log.Error("[GetMasternodes] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
1399-
return []common.Address{}
1399+
log.Error("[getPreviousEpochSwitchInfoByHash] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
1400+
return nil, err
14001401
}
14011402
}
1403+
return epochSwitchInfo, nil
1404+
}
1405+
1406+
// Given hash, get master node from the epoch switch block of the previous `limit` epoch
1407+
func (x *XDPoS_v2) GetPreviousPenaltyByHash(chain consensus.ChainReader, hash common.Hash, limit int) []common.Address {
1408+
epochSwitchInfo, err := x.getPreviousEpochSwitchInfoByHash(chain, hash, limit)
1409+
if err != nil {
1410+
log.Error("[GetPreviousPenaltyByHash] Adaptor v2 getPreviousEpochSwitchInfoByHash has error, potentially bug", "err", err)
1411+
return []common.Address{}
1412+
}
14021413
header := chain.GetHeaderByHash(epochSwitchInfo.EpochSwitchBlockInfo.Hash)
14031414
return common.ExtractAddressFromBytes(header.Penalties)
14041415
}

consensus/tests/penalty_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func TestHookPenaltyV2Comeback(t *testing.T) {
7171
assert.Equal(t, 4, len(penalty))
7272
header6285 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*7 - common.MergeSignRange)
7373
// forcely insert signing tx into cache, to cancel comeback. since no comeback, penalty is 3
74-
tx, err := signingTx(header6285, 0, signer, signFn)
74+
tx, err := signingTxWithSignerFn(header6285, 0, signer, signFn)
7575
assert.Nil(t, err)
7676
adaptor.CacheSigningTxs(header6285.Hash(), []*types.Transaction{tx})
7777
penalty, err = adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)

consensus/tests/reward_test.go

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package tests
2+
3+
import (
4+
"encoding/json"
5+
"math/big"
6+
"testing"
7+
8+
"github.com/XinFinOrg/XDPoSChain/common"
9+
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
10+
"github.com/XinFinOrg/XDPoSChain/core/state"
11+
"github.com/XinFinOrg/XDPoSChain/core/types"
12+
"github.com/XinFinOrg/XDPoSChain/eth/hooks"
13+
"github.com/XinFinOrg/XDPoSChain/params"
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestHookRewardV2(t *testing.T) {
18+
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
19+
assert.Nil(t, err)
20+
configString := string(b)
21+
22+
var config params.ChainConfig
23+
err = json.Unmarshal([]byte(configString), &config)
24+
assert.Nil(t, err)
25+
// set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs
26+
config.XDPoS.V2.SwitchBlock.SetUint64(1800)
27+
28+
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*5, &config, 0)
29+
30+
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
31+
hooks.AttachConsensusV2Hooks(adaptor, blockchain, &config)
32+
assert.NotNil(t, adaptor.EngineV2.HookReward)
33+
// forcely insert signing tx into cache, to give rewards.
34+
header915 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 15)
35+
header916 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 16)
36+
header1799 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 - 1)
37+
header1801 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 + 1)
38+
tx, err := signingTxWithSignerFn(header915, 0, signer, signFn)
39+
assert.Nil(t, err)
40+
adaptor.CacheSigningTxs(header916.Hash(), []*types.Transaction{tx})
41+
statedb, err := blockchain.StateAt(header1799.Root)
42+
assert.Nil(t, err)
43+
parentState := statedb.Copy()
44+
reward, err := adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header1801)
45+
assert.Nil(t, err)
46+
assert.Zero(t, len(reward))
47+
header2699 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*3 - 1)
48+
header2700 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 3)
49+
statedb, err = blockchain.StateAt(header2699.Root)
50+
assert.Nil(t, err)
51+
parentState = statedb.Copy()
52+
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header2700)
53+
assert.Nil(t, err)
54+
owner := state.GetCandidateOwner(parentState, signer)
55+
result := reward["rewards"].(map[common.Address]interface{})
56+
assert.Equal(t, 1, len(result))
57+
for _, x := range result {
58+
r := x.(map[common.Address]*big.Int)
59+
a, _ := big.NewInt(0).SetString("225000000000000000000", 10)
60+
assert.Zero(t, a.Cmp(r[owner]))
61+
b, _ := big.NewInt(0).SetString("25000000000000000000", 10)
62+
assert.Zero(t, b.Cmp(r[common.HexToAddress("0x0000000000000000000000000000000000000068")]))
63+
}
64+
header2685 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 + 885)
65+
header2716 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*3 + 16)
66+
header3599 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*4 - 1)
67+
header3600 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 4)
68+
tx, err = signingTxWithSignerFn(header2685, 0, signer, signFn)
69+
assert.Nil(t, err)
70+
// signed block hash and block contains tx are in different epoch, we should get same rewards
71+
adaptor.CacheSigningTxs(header2716.Hash(), []*types.Transaction{tx})
72+
statedb, err = blockchain.StateAt(header3599.Root)
73+
assert.Nil(t, err)
74+
parentState = statedb.Copy()
75+
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header3600)
76+
assert.Nil(t, err)
77+
result = reward["rewards"].(map[common.Address]interface{})
78+
assert.Equal(t, 1, len(result))
79+
for _, x := range result {
80+
r := x.(map[common.Address]*big.Int)
81+
a, _ := big.NewInt(0).SetString("225000000000000000000", 10)
82+
assert.Zero(t, a.Cmp(r[owner]))
83+
b, _ := big.NewInt(0).SetString("25000000000000000000", 10)
84+
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]))
85+
}
86+
// if no signing tx, then reward will be 0
87+
header4499 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*5 - 1)
88+
header4500 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 5)
89+
statedb, err = blockchain.StateAt(header4499.Root)
90+
assert.Nil(t, err)
91+
parentState = statedb.Copy()
92+
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header4500)
93+
assert.Nil(t, err)
94+
result = reward["rewards"].(map[common.Address]interface{})
95+
assert.Equal(t, 0, len(result))
96+
}
97+
98+
func TestHookRewardV2SplitReward(t *testing.T) {
99+
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
100+
assert.Nil(t, err)
101+
configString := string(b)
102+
103+
var config params.ChainConfig
104+
err = json.Unmarshal([]byte(configString), &config)
105+
assert.Nil(t, err)
106+
// set switch to 1800, so that it covers 901-1799, 1800-2700 two epochs
107+
config.XDPoS.V2.SwitchBlock.SetUint64(1800)
108+
109+
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*3, &config, 0)
110+
111+
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
112+
hooks.AttachConsensusV2Hooks(adaptor, blockchain, &config)
113+
assert.NotNil(t, adaptor.EngineV2.HookReward)
114+
// forcely insert signing tx into cache, to give rewards.
115+
header915 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 15)
116+
header916 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 16)
117+
// header917 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 17)
118+
header1785 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 - 15)
119+
header1799 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 - 1)
120+
header1801 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 + 1)
121+
tx, err := signingTxWithSignerFn(header915, 0, signer, signFn)
122+
assert.Nil(t, err)
123+
adaptor.CacheSigningTxs(header916.Hash(), []*types.Transaction{tx})
124+
tx2, err := signingTxWithKey(header915, 0, acc1Key)
125+
assert.Nil(t, err)
126+
tx3, err := signingTxWithKey(header1785, 0, acc1Key)
127+
assert.Nil(t, err)
128+
adaptor.CacheSigningTxs(header1799.Hash(), []*types.Transaction{tx2, tx3})
129+
130+
statedb, err := blockchain.StateAt(header1799.Root)
131+
assert.Nil(t, err)
132+
parentState := statedb.Copy()
133+
reward, err := adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header1801)
134+
assert.Nil(t, err)
135+
assert.Zero(t, len(reward))
136+
header2699 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*3 - 1)
137+
header2700 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 3)
138+
statedb, err = blockchain.StateAt(header2699.Root)
139+
assert.Nil(t, err)
140+
parentState = statedb.Copy()
141+
reward, err = adaptor.EngineV2.HookReward(blockchain, statedb, parentState, header2700)
142+
assert.Nil(t, err)
143+
result := reward["rewards"].(map[common.Address]interface{})
144+
assert.Equal(t, 2, len(result))
145+
// two signing account, 3 txs, reward is split by 1:2 (total reward is 250...000)
146+
for addr, x := range result {
147+
if addr == acc1Addr {
148+
r := x.(map[common.Address]*big.Int)
149+
owner := state.GetCandidateOwner(parentState, acc1Addr)
150+
a, _ := big.NewInt(0).SetString("149999999999999999999", 10)
151+
assert.Zero(t, a.Cmp(r[owner]))
152+
b, _ := big.NewInt(0).SetString("16666666666666666666", 10)
153+
assert.Zero(t, b.Cmp(r[common.HexToAddress("0x0000000000000000000000000000000000000068")]))
154+
} else if addr == signer {
155+
r := x.(map[common.Address]*big.Int)
156+
owner := state.GetCandidateOwner(parentState, signer)
157+
a, _ := big.NewInt(0).SetString("74999999999999999999", 10)
158+
assert.Zero(t, a.Cmp(r[owner]))
159+
b, _ := big.NewInt(0).SetString("8333333333333333333", 10)
160+
assert.Zero(t, b.Cmp(r[common.HexToAddress("0x0000000000000000000000000000000000000068")]))
161+
}
162+
}
163+
}

consensus/tests/test_helper.go

+18-3
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
230230
return signedTX, nil
231231
}
232232

233-
func signingTx(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
233+
func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
234234
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
235235
s := types.NewEIP155Signer(big.NewInt(chainID))
236236
h := s.Hash(tx)
@@ -245,6 +245,21 @@ func signingTx(header *types.Header, nonce uint64, signer common.Address, signFn
245245
return signedTx, nil
246246
}
247247

248+
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
249+
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
250+
s := types.NewEIP155Signer(big.NewInt(chainID))
251+
h := s.Hash(tx)
252+
sig, err := crypto.Sign(h[:], privateKey)
253+
if err != nil {
254+
return nil, err
255+
}
256+
signedTx, err := tx.WithSignature(s, sig)
257+
if err != nil {
258+
return nil, err
259+
}
260+
return signedTx, nil
261+
}
262+
248263
func UpdateSigner(bc *BlockChain) error {
249264
err := bc.UpdateM1()
250265
return err
@@ -534,8 +549,8 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
534549
Coinbase: common.HexToAddress(blockCoinBase),
535550
}
536551

537-
// Inject the hardcoded master node list for the last v1 epoch block
538-
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 0 {
552+
// Inject the hardcoded master node list for the last v1 epoch block and all v1 epoch switch blocks (excluding genesis)
553+
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 0 || blockNumber%int(chainConfig.XDPoS.Epoch) == 0 {
539554
// reset extra
540555
header.Extra = []byte{}
541556
if len(header.Extra) < utils.ExtraVanity {

consensus/tests/vote_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te
2222
blockInfo := &utils.BlockInfo{
2323
Hash: currentBlock.Hash(),
2424
Round: utils.Round(1),
25-
Number: big.NewInt(11),
25+
Number: big.NewInt(901),
2626
}
2727
voteSigningHash := utils.VoteSigHash(blockInfo)
2828

contracts/utils.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const (
5454
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
5555
)
5656

57-
type rewardLog struct {
57+
type RewardLog struct {
5858
Sign uint64 `json:"sign"`
5959
Reward *big.Int `json:"reward"`
6060
}
@@ -319,13 +319,13 @@ func DecryptRandomizeFromSecretsAndOpening(secrets [][32]byte, opening [32]byte)
319319
}
320320

321321
// Calculate reward for reward checkpoint.
322-
func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, rCheckpoint uint64, totalSigner *uint64) (map[common.Address]*rewardLog, error) {
322+
func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, rCheckpoint uint64, totalSigner *uint64) (map[common.Address]*RewardLog, error) {
323323
// Not reward for singer of genesis block and only calculate reward at checkpoint block.
324324
number := header.Number.Uint64()
325325
prevCheckpoint := number - (rCheckpoint * 2)
326326
startBlockNumber := prevCheckpoint + 1
327327
endBlockNumber := startBlockNumber + rCheckpoint - 1
328-
signers := make(map[common.Address]*rewardLog)
328+
signers := make(map[common.Address]*RewardLog)
329329
mapBlkHash := map[uint64]common.Hash{}
330330

331331
data := make(map[common.Hash][]common.Address)
@@ -376,7 +376,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
376376
if exist {
377377
signers[addr].Sign++
378378
} else {
379-
signers[addr] = &rewardLog{1, new(big.Int)}
379+
signers[addr] = &RewardLog{1, new(big.Int)}
380380
}
381381
*totalSigner++
382382
}
@@ -390,7 +390,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
390390
}
391391

392392
// Calculate reward for signers.
393-
func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]*rewardLog, totalSigner uint64) (map[common.Address]*big.Int, error) {
393+
func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]*RewardLog, totalSigner uint64) (map[common.Address]*big.Int, error) {
394394
resultSigners := make(map[common.Address]*big.Int)
395395
// Add reward for signers.
396396
if totalSigner > 0 {

0 commit comments

Comments
 (0)