Upgrade reward 2.0 (#865)

* refactor: reward hook gets prepared for upgrade

* feat: new reward hook, config change, unit tests

* add missing code

* feat: filter penalty in reward. add unit test

* update constant and config

---------

Co-authored-by: liam.lai <liam.lai@us>
This commit is contained in:
wgr523 2025-03-02 18:44:34 +08:00 committed by GitHub
parent 8bd1c9cee9
commit c36e4d54c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 578 additions and 63 deletions

View file

@ -61,6 +61,7 @@ type constant struct {
TIPV2SwitchBlock *big.Int
tipXDCXMinerDisable *big.Int
tipXDCXReceiverDisable *big.Int
tipUpgradeReward *big.Int
eip1559Block *big.Int
cancunBlock *big.Int
@ -102,6 +103,7 @@ var (
BlockNumberGas50x = MaintnetConstant.blockNumberGas50x
TIPXDCXMinerDisable = MaintnetConstant.tipXDCXMinerDisable
TIPXDCXReceiverDisable = MaintnetConstant.tipXDCXReceiverDisable
TIPUpgradeReward = MaintnetConstant.tipUpgradeReward
Eip1559Block = MaintnetConstant.eip1559Block
CancunBlock = MaintnetConstant.cancunBlock
@ -167,6 +169,7 @@ func CopyConstans(chainID uint64) {
BlockNumberGas50x = c.blockNumberGas50x
TIPXDCXMinerDisable = c.tipXDCXMinerDisable
TIPXDCXReceiverDisable = c.tipXDCXReceiverDisable
TIPUpgradeReward = c.tipUpgradeReward
Eip1559Block = c.eip1559Block
CancunBlock = c.cancunBlock

View file

@ -28,6 +28,7 @@ var DevnetConstant = constant{
TIPV2SwitchBlock: big.NewInt(1800),
tipXDCXMinerDisable: big.NewInt(0),
tipXDCXReceiverDisable: big.NewInt(0),
tipUpgradeReward: big.NewInt(1773000),
eip1559Block: big.NewInt(0),
cancunBlock: big.NewInt(1702800),

View file

@ -28,6 +28,7 @@ var MaintnetConstant = constant{
TIPV2SwitchBlock: big.NewInt(80370000), // Target 2nd Oct 2024
tipXDCXMinerDisable: big.NewInt(80370000), // Target 2nd Oct 2024
tipXDCXReceiverDisable: big.NewInt(80370900), // Target 2nd Oct 2024, safer to release after disable miner
tipUpgradeReward: big.NewInt(9999999999),
eip1559Block: big.NewInt(9999999999),
cancunBlock: big.NewInt(9999999999),

View file

@ -28,6 +28,7 @@ var TestnetConstant = constant{
shanghaiBlock: big.NewInt(61290000), // Target 31st March 2024
tipXDCXMinerDisable: big.NewInt(61290000), // Target 31st March 2024
tipXDCXReceiverDisable: big.NewInt(66825000), // Target 26 Aug 2024
tipUpgradeReward: big.NewInt(9999999999),
eip1559Block: big.NewInt(71550000), // Target 14th Feb 2025
cancunBlock: big.NewInt(9999999999),

View file

@ -49,6 +49,15 @@ var (
acc3Addr = crypto.PubkeyToAddress(acc3Key.PublicKey) //xdc71562b71999873DB5b286dF957af199Ec94617F7
voterAddr = crypto.PubkeyToAddress(voterKey.PublicKey) //xdc5F74529C0338546f82389402a01c31fB52c6f434
chainID = int64(1337)
protector1Key, _ = crypto.HexToECDSA("071c71a67e127fad4e901695e1b4b9ee04ae0e301d1e14474d32c45c72ce7b70")
protector1Addr = crypto.PubkeyToAddress(protector1Key.PublicKey)
protector2Key, _ = crypto.HexToECDSA("1d1e144127fad4e9016a977b97b0c89921839df052d7adc2f789034678902378")
protector2Addr = crypto.PubkeyToAddress(protector2Key.PublicKey)
observer1Key, _ = crypto.HexToECDSA("71a67e127fad4e9016a977b97b0c89921839df052d7adc2f7890346789023789")
observer1Addr = crypto.PubkeyToAddress(observer1Key.PublicKey)
observer2Key, _ = crypto.HexToECDSA("789034678902378971a67e127fad4e9016a977b97b0c89921839df052d7adc2f")
observer2Addr = crypto.PubkeyToAddress(observer2Key.PublicKey)
)
func SignHashByPK(pk *ecdsa.PrivateKey, itemToSign []byte) []byte {
@ -279,6 +288,91 @@ func getMultiCandidatesBackend(t *testing.T, chainConfig *params.ChainConfig, n
return contractBackend2
}
func getProtectorObserverBackend(t *testing.T, chainConfig *params.ChainConfig) *backends.SimulatedBackend {
// initial helper backend
contractBackendForSC := backends.NewXDCSimulatedBackend(types.GenesisAlloc{
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
}, 10000000, chainConfig)
transactOpts := bind.NewKeyedTransactor(voterKey)
var candidates []common.Address
var caps []*big.Int
defalutCap := new(big.Int)
defalutCap.SetString("1000000000", 10)
for i := 1; i <= 15; i++ {
addr := fmt.Sprintf("%02d", i)
candidates = append(candidates, common.StringToAddress(addr)) // StringToAddress does not exist
caps = append(caps, defalutCap)
}
candidates = append(candidates, protector1Addr, protector2Addr, observer1Addr, observer2Addr)
caps = append(caps, defalutCap, defalutCap, big.NewInt(999999), big.NewInt(999999)) // 99..9 is a small cap
acc1Cap, acc2Cap, acc3Cap, voterCap := new(big.Int), new(big.Int), new(big.Int), new(big.Int)
acc1Cap.SetString("10000001", 10)
acc2Cap.SetString("10000002", 10)
acc3Cap.SetString("10000003", 10)
voterCap.SetString("1000000000", 10)
caps = append(caps, voterCap, acc1Cap, acc2Cap, acc3Cap)
candidates = append(candidates, voterAddr, acc1Addr, acc2Addr, acc3Addr)
// create validator smart contract
validatorSCAddr, _, _, err := contractValidator.DeployXDCValidator(
transactOpts,
contractBackendForSC,
candidates,
caps,
voterAddr, // first owner, not used
big.NewInt(50000),
big.NewInt(1),
big.NewInt(99),
big.NewInt(100),
big.NewInt(100),
)
if err != nil {
t.Fatalf("can't deploy root registry: %v", err)
}
contractBackendForSC.Commit() // Write into database(state)
// Prepare Code and Storage
d := time.Now().Add(1000 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
defer cancel()
code, _ := contractBackendForSC.CodeAt(ctx, validatorSCAddr, nil)
storage := make(map[common.Hash]common.Hash)
f := func(key, val common.Hash) bool {
decode := []byte{}
trim := bytes.TrimLeft(val.Bytes(), "\x00")
err := rlp.DecodeBytes(trim, &decode)
if err != nil {
t.Fatalf("Failed while decode byte")
}
storage[key] = common.BytesToHash(decode)
log.Info("DecodeBytes", "value", val.String(), "decode", storage[key].String())
return true
}
err = contractBackendForSC.ForEachStorageAt(ctx, validatorSCAddr, nil, f)
if err != nil {
t.Fatalf("Failed while trying to read all keys from SC")
}
// create test backend with smart contract in it
contractBackend2 := backends.NewXDCSimulatedBackend(types.GenesisAlloc{
acc1Addr: {Balance: new(big.Int).SetUint64(10000000000)},
acc2Addr: {Balance: new(big.Int).SetUint64(10000000000)},
acc3Addr: {Balance: new(big.Int).SetUint64(10000000000)},
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
common.MasternodeVotingSMCBinary: {Balance: new(big.Int).SetUint64(1), Code: code, Storage: storage}, // Binding the MasternodeVotingSMC with newly created 'code' for SC execution
}, 10000000, chainConfig)
return contractBackend2
}
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.BlockSignersBinary)
s := types.LatestSignerForChainID(big.NewInt(chainID))
@ -483,7 +577,11 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in
}
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()
// use signer itself as penalty
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:], nil, "")
penalty := signer[:]
if roundNumber%int64(chainConfig.XDPoS.Epoch) != 0 {
penalty = nil
}
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, penalty, nil, "")
err = blockchain.InsertBlock(block)
if err != nil {
@ -564,6 +662,78 @@ func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, ch
return blockchain, backend, currentBlock, signer, signFn
}
// V2 concensus engine
func PrepareXDCTestBlockChainWithProtectorObserver(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
// Preparation
var err error
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
if err != nil {
panic(fmt.Errorf("error while creating simulated wallet for generating singer address and signer fn: %v", err))
}
backend := getProtectorObserverBackend(t, chainConfig)
blockchain := backend.BlockChain()
blockchain.Client = backend
engine := blockchain.Engine().(*XDPoS.XDPoS)
// Authorise
engine.Authorize(signer, signFn)
currentBlock := blockchain.Genesis()
go func() {
for range core.CheckpointCh {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V2] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
}
}()
// Insert initial blocks
for i := 1; i <= numOfBlocks; i++ {
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
// for v2 blocks, fill in correct coinbase
if int64(i) > chainConfig.XDPoS.V2.SwitchBlock.Int64() {
blockCoinBase = signer.Hex()
}
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()
// use observer2 as penalty and put in checkpoint block
penalty := observer2Addr[:]
if i != 900 {
penalty = nil
}
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, penalty, nil, "f11ec19df702aa6bd9b3b2186edbc66d6b50b06334455a4a2ae8d166f28a14ff")
if i == 900 {
fmt.Println(block.Penalties())
}
err = blockchain.InsertBlock(block)
if err != nil {
t.Fatal(err)
}
// First v2 block
if (int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()) == 1 {
lastv1BlockNumber := block.Header().Number.Uint64() - 1
checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch
checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber)
err := engine.EngineV2.Initial(blockchain, checkpointHeader)
if err != nil {
panic(err)
}
}
currentBlock = block
}
// Update Signer as there is no previous signer assigned
err = UpdateSigner(blockchain)
if err != nil {
t.Fatal(err)
}
return blockchain, backend, currentBlock, signer, signFn
}
func CreateBlock(blockchain *core.BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte, signersKey []*ecdsa.PrivateKey, merkleRoot string) *types.Block {
currentBlock := startingBlock
if len(merkleRoot) == 0 {
@ -596,9 +766,6 @@ func CreateBlock(blockchain *core.BlockChain, chainConfig *params.ChainConfig, s
for _, v := range masternodesFromV1LastEpoch {
header.Validators = append(header.Validators, v[:]...)
}
if penalties != nil {
header.Penalties = penalties
}
}
} else {
// V1 block
@ -634,6 +801,9 @@ func CreateBlock(blockchain *core.BlockChain, chainConfig *params.ChainConfig, s
copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash)
}
}
if penalties != nil {
header.Penalties = penalties
}
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig)
if err != nil {
panic(fmt.Errorf("fail to create block in test helper, %v", err))

View file

@ -59,7 +59,7 @@ func TestHookRewardV2(t *testing.T) {
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")]))
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]))
}
header2685 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*2 + 885)
header2716 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*3 + 16)
@ -146,18 +146,154 @@ func TestHookRewardV2SplitReward(t *testing.T) {
for addr, x := range result {
if addr == acc1Addr {
r := x.(map[common.Address]*big.Int)
owner := state.GetCandidateOwner(parentState, acc1Addr)
owner := state.GetCandidateOwner(parentState, addr)
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")]))
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]))
} else if addr == signer {
r := x.(map[common.Address]*big.Int)
owner := state.GetCandidateOwner(parentState, signer)
owner := state.GetCandidateOwner(parentState, addr)
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")]))
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]))
} else {
assert.Fail(t, "wrong reward")
}
}
}
func TestHookRewardAfterUpgrade(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)
// set upgrade number to 0
backup := common.TIPUpgradeReward
common.TIPUpgradeReward = big.NewInt(0)
blockchain, _, _, signer, signFn := PrepareXDCTestBlockChainWithProtectorObserver(t, int(config.XDPoS.Epoch)*3+10, &config)
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)
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)
tx4, err := signingTxWithKey(header1785, 0, protector1Key)
assert.Nil(t, err)
tx5, err := signingTxWithKey(header1785, 0, observer1Key)
assert.Nil(t, err)
tx6, err := signingTxWithKey(header915, 0, protector2Key)
assert.Nil(t, err)
tx7, err := signingTxWithKey(header1785, 0, protector2Key)
assert.Nil(t, err)
tx8, err := signingTxWithKey(header1785, 0, observer2Key)
assert.Nil(t, err)
adaptor.CacheSigningTxs(header1799.Hash(), []*types.Transaction{tx2, tx3, tx4, tx5, tx6, tx7, tx8})
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, addr)
a, _ := big.NewInt(0).SetString("299999999999999999998", 10)
assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner])
b, _ := big.NewInt(0).SetString("33333333333333333333", 10)
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr])
} else if addr == signer {
r := x.(map[common.Address]*big.Int)
owner := state.GetCandidateOwner(parentState, addr)
a, _ := big.NewInt(0).SetString("149999999999999999999", 10)
assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner])
b, _ := big.NewInt(0).SetString("16666666666666666666", 10)
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr])
} else {
assert.Fail(t, "wrong reward")
}
}
// 5 master nodes inside header are:
//xdc703c4b2bD70c169f5717101CaeE543299Fc946C7
//xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
//xdc71562b71999873DB5b286dF957af199Ec94617F7
//xdc5F74529C0338546f82389402a01c31fB52c6f434
//signer
// 20 master nodes candidate inside XDCValidator contract are:
//xdc703c4b2bD70c169f5717101CaeE543299Fc946C7
//xdc0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e
//xdc71562b71999873DB5b286dF957af199Ec94617F7
//xdc5F74529C0338546f82389402a01c31fB52c6f434
// and xdc00...01, xdc00...02, ..., protector1 protector2 observer1 observer2
// so xdc00...01, xdc00...02, ..., protector1 protector2 are protectors
// only protector1 and 2 has signingtx.
resultProtector := reward["rewardsProtector"].(map[common.Address]interface{})
// 2 protector and split by 1:2
assert.Equal(t, 2, len(resultProtector))
for addr, x := range resultProtector {
if addr == protector1Addr {
r := x.(map[common.Address]*big.Int)
owner := state.GetCandidateOwner(parentState, addr)
a, _ := big.NewInt(0).SetString("119999999999999999999", 10)
assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner])
b, _ := big.NewInt(0).SetString("13333333333333333333", 10)
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr])
} else if addr == protector2Addr {
r := x.(map[common.Address]*big.Int)
owner := state.GetCandidateOwner(parentState, addr)
a, _ := big.NewInt(0).SetString("239999999999999999999", 10)
assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner])
b, _ := big.NewInt(0).SetString("26666666666666666666", 10)
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr])
} else {
assert.Fail(t, "wrong reward")
}
}
resultObserver := reward["rewardsObserver"].(map[common.Address]interface{})
// observer1 and it signs one tx, observer2 is inside penalty so no reward
assert.Equal(t, 1, len(resultObserver))
for addr, x := range resultObserver {
assert.Equal(t, addr, observer1Addr)
r := x.(map[common.Address]*big.Int)
owner := state.GetCandidateOwner(parentState, addr)
a, _ := big.NewInt(0).SetString("270000000000000000000", 10)
assert.Zero(t, a.Cmp(r[owner]), "real reward is", r[owner])
b, _ := big.NewInt(0).SetString("30000000000000000000", 10)
assert.Zero(t, b.Cmp(r[config.XDPoS.FoudationWalletAddr]), "real reward is", r[config.XDPoS.FoudationWalletAddr])
}
common.TIPUpgradeReward = backup
}

View file

@ -6,8 +6,10 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/sort"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/contracts"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
@ -17,6 +19,21 @@ import (
"github.com/XinFinOrg/XDPoSChain/params"
)
// Declaring an enum type Beneficiary of reward
type Beneficiary int
// Enumerating reward beneficiary
const (
MasterNodeBeneficiary Beneficiary = iota
ProtectorNodeBeneficiary
ObserverNodeBeneficiary
)
type RewardLog struct {
Sign uint64 `json:"sign"`
Reward *big.Int `json:"reward"`
}
func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConfig *params.ChainConfig) {
// Hook scans for bad masternodes and decide to penalty them
adaptor.EngineV2.HookPenalty = func(chain consensus.ChainReader, number *big.Int, currentHash common.Hash, candidates []common.Address) ([]common.Address, error) {
@ -165,61 +182,138 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr)
return nil, errors.New("foundation wallet address is empty")
}
rewards := make(map[string]interface{})
rewardsMap := 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
return rewardsMap, 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)
round, err := adaptor.EngineV2.GetRoundNumber(header)
if err != nil {
log.Error("[HookReward] Fail to get round", "error", err)
return nil, err
}
currentConfig := chain.Config().XDPoS.V2.Config(uint64(round))
// Get signers/signing tx count
totalSigner := new(uint64)
signers, err := GetSigningTxCount(adaptor, chain, header, totalSigner)
signers, err := GetSigningTxCount(adaptor, chain, header, parentState, currentConfig)
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
rewardsMap["signers"] = signers[MasterNodeBeneficiary]
rewardSigners := make(map[common.Address]*big.Int)
rewardSignersProtector := make(map[common.Address]*big.Int)
rewardSignersObserver := make(map[common.Address]*big.Int)
if !chain.Config().IsTIPUpgradeReward(header.Number) {
// 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)
rewardSigners, err = CalculateRewardForSigner(chainReward, signers[MasterNodeBeneficiary])
if err != nil {
log.Error("[HookReward] Fail to calculate reward for masternode", "error", err)
return nil, err
}
} else {
rewardsMap["signersProtector"] = signers[ProtectorNodeBeneficiary]
rewardsMap["signersObserver"] = signers[ObserverNodeBeneficiary]
// Masternode rewards
chainReward := new(big.Int).Mul(new(big.Int).SetUint64(currentConfig.MasternodeReward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, number, common.BlocksPerYear)
rewardSigners, err = CalculateRewardForSigner(chainReward, signers[MasterNodeBeneficiary])
if err != nil {
log.Error("[HookReward] Fail to calculate reward for masternode", "error", err)
return nil, err
}
// Protector rewards
chainReward = new(big.Int).Mul(new(big.Int).SetUint64(currentConfig.ProtectorReward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, number, common.BlocksPerYear)
rewardSignersProtector, err = CalculateRewardForSigner(chainReward, signers[ProtectorNodeBeneficiary])
if err != nil {
log.Error("[HookReward] Fail to calculate reward for protector", "error", err)
return nil, err
}
// Observer rewards
chainReward = new(big.Int).Mul(new(big.Int).SetUint64(currentConfig.ObserverReward), new(big.Int).SetUint64(params.Ether))
chainReward = util.RewardInflation(chain, chainReward, number, common.BlocksPerYear)
rewardSignersObserver, err = CalculateRewardForSigner(chainReward, signers[ObserverNodeBeneficiary])
if err != nil {
log.Error("[HookReward] Fail to calculate reward for observer", "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 {
rewards, err := 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
for signer, calcReward := range rewardSigners {
rewards, err := 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
}
rewardsMap["rewards"] = voterResults
voterResultsProtector := make(map[common.Address]interface{})
for signer, calcReward := range rewardSignersProtector {
rewards, err := 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)
}
}
voterResultsProtector[signer] = rewards
}
if len(voterResultsProtector) > 0 {
rewardsMap["rewardsProtector"] = voterResultsProtector
}
voterResultsObserver := make(map[common.Address]interface{})
for signer, calcReward := range rewardSignersObserver {
rewards, err := 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)
}
}
voterResultsObserver[signer] = rewards
}
if len(voterResultsObserver) > 0 {
rewardsMap["rewardsObserver"] = voterResultsObserver
}
rewards["rewards"] = voterResults
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
return rewards, nil
return rewardsMap, 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) {
func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, parentState *state.StateDB, currentConfig *params.V2Config) (map[Beneficiary]map[common.Address]*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)
signers := make(map[Beneficiary]map[common.Address]*RewardLog)
signers[MasterNodeBeneficiary] = make(map[common.Address]*RewardLog)
signers[ProtectorNodeBeneficiary] = make(map[common.Address]*RewardLog)
signers[ObserverNodeBeneficiary] = make(map[common.Address]*RewardLog)
mapBlkHash := map[uint64]common.Hash{}
// prevent overflow
@ -229,32 +323,75 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type
data := make(map[common.Hash][]common.Address)
epochCount := 0
var masternodes []common.Address
var startBlockNumber, endBlockNumber uint64
nodesToKeep := make(map[Beneficiary][]common.Address)
h := header
for i := number - 1; ; i-- {
header = chain.GetHeader(header.ParentHash, i)
isEpochSwitch, _, err := c.IsEpochSwitch(header)
h = chain.GetHeader(h.ParentHash, i)
isEpochSwitch, _, err := c.IsEpochSwitch(h)
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
endBlockNumber = h.Number.Uint64() - 1
}
if epochCount == rewardEpochCount {
startBlockNumber = header.Number.Uint64() + 1
masternodes = c.GetMasternodesFromCheckpointHeader(header)
startBlockNumber = h.Number.Uint64() + 1
nodesToKeep[MasterNodeBeneficiary] = c.GetMasternodesFromCheckpointHeader(h)
// in reward upgrade, add protector and observer nodes
if chain.Config().IsTIPUpgradeReward(header.Number) {
candidates := state.GetCandidates(parentState)
var ms []utils.Masternode
for _, candidate := range candidates {
// ignore "0x0000000000000000000000000000000000000000"
if !candidate.IsZero() {
v := state.GetCandidateCap(parentState, candidate)
ms = append(ms, utils.Masternode{Address: candidate, Stake: v})
}
}
sort.Slice(ms, func(i, j int) bool {
return ms[i].Stake.Cmp(ms[j].Stake) >= 0
})
// find penalty and filter them out
penalties := common.ExtractAddressFromBytes(h.Penalties)
filterMap := make(map[common.Address]struct{})
for _, addr := range penalties {
filterMap[addr] = struct{}{}
}
for _, addr := range nodesToKeep[MasterNodeBeneficiary] {
filterMap[addr] = struct{}{}
}
// find top candidates
// maxMNP := currentConfig.MaxMasternodes + currentConfig.MaxProtectorNodes
protector := []common.Address{}
observer := []common.Address{}
for _, node := range ms {
if _, ok := filterMap[node.Address]; ok {
continue
}
if len(protector) < currentConfig.MaxProtectorNodes {
protector = append(protector, node.Address)
} else {
observer = append(observer, node.Address)
}
}
nodesToKeep[ProtectorNodeBeneficiary] = protector
nodesToKeep[ObserverNodeBeneficiary] = observer
}
break
}
}
mapBlkHash[i] = header.Hash()
signingTxs, ok := c.GetCachedSigningTxs(header.Hash())
mapBlkHash[i] = h.Hash()
signingTxs, ok := c.GetCachedSigningTxs(h.Hash())
if !ok {
log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i)
block := chain.GetBlock(header.Hash(), i)
log.Debug("Failed get from cached", "hash", h.Hash().String(), "number", i)
block := chain.GetBlock(h.Hash(), i)
txs := block.Transactions()
signingTxs = c.CacheSigningTxs(header.Hash(), txs)
signingTxs = c.CacheSigningTxs(h.Hash(), txs)
}
for _, tx := range signingTxs {
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
@ -272,26 +409,35 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type
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
addrSigners := make(map[Beneficiary]map[common.Address]bool)
addrSigners[MasterNodeBeneficiary] = make(map[common.Address]bool)
addrSigners[ProtectorNodeBeneficiary] = make(map[common.Address]bool)
addrSigners[ObserverNodeBeneficiary] = make(map[common.Address]bool)
for _, addr := range addrs {
for _, beneficiary := range []Beneficiary{MasterNodeBeneficiary, ProtectorNodeBeneficiary, ObserverNodeBeneficiary} {
if _, ok := nodesToKeep[beneficiary]; ok {
for _, protector := range nodesToKeep[beneficiary] {
if addr == protector {
if _, ok := addrSigners[beneficiary][addr]; !ok {
addrSigners[beneficiary][addr] = true
}
break
}
}
break
}
}
}
for addr := range addrSigners {
_, exist := signers[addr]
if exist {
signers[addr].Sign++
} else {
signers[addr] = &contracts.RewardLog{Sign: 1, Reward: new(big.Int)}
for _, beneficiary := range []Beneficiary{MasterNodeBeneficiary, ProtectorNodeBeneficiary, ObserverNodeBeneficiary} {
for addr := range addrSigners[beneficiary] {
_, exist := signers[beneficiary][addr]
if exist {
signers[beneficiary][addr].Sign++
} else {
signers[beneficiary][addr] = &RewardLog{Sign: 1, Reward: new(big.Int)}
}
}
*totalSigner++
}
}
}
@ -301,3 +447,31 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type
return signers, nil
}
// Calculate reward for signers.
func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]*RewardLog) (map[common.Address]*big.Int, error) {
totalSignerCount := uint64(0)
for _, rLog := range signers {
totalSignerCount += rLog.Sign
}
resultSigners := make(map[common.Address]*big.Int)
// Add reward for signers.
if totalSignerCount > 0 {
for signer, rLog := range signers {
// Add reward for signer.
calcReward := new(big.Int)
calcReward.Div(chainReward, new(big.Int).SetUint64(totalSignerCount))
calcReward.Mul(calcReward, new(big.Int).SetUint64(rLog.Sign))
rLog.Reward = calcReward
resultSigners[signer] = calcReward
}
}
log.Info("Signers data", "totalSigner", totalSignerCount, "totalReward", chainReward)
for addr, signer := range signers {
log.Debug("Signer reward", "signer", addr, "sign", signer.Sign, "reward", signer.Reward)
}
return resultSigners, nil
}

View file

@ -118,6 +118,22 @@ var (
TimeoutPeriod: 5,
MinePeriod: 2,
ExpTimeoutConfig: ExpTimeoutConfig{Base: 2.0, MaxExponent: 5},
MasternodeReward: 5000,
ProtectorReward: 4000,
ObserverReward: 1000,
},
9999999999: {
MaxMasternodes: 15,
MaxProtectorNodes: 2,
SwitchRound: 9999999999,
CertThreshold: 0.667,
TimeoutSyncThreshold: 3,
TimeoutPeriod: 5,
MinePeriod: 2,
ExpTimeoutConfig: ExpTimeoutConfig{Base: 2.0, MaxExponent: 5},
MasternodeReward: 5000,
ProtectorReward: 4000,
ObserverReward: 1000,
},
}
@ -142,12 +158,16 @@ var (
},
900: {
MaxMasternodes: 20,
MaxProtectorNodes: 17,
SwitchRound: 900,
CertThreshold: 0.667,
TimeoutSyncThreshold: 4,
TimeoutPeriod: 5,
MinePeriod: 2,
ExpTimeoutConfig: ExpTimeoutConfig{Base: 1.0, MaxExponent: 0},
MasternodeReward: 500, // double as Reward
ProtectorReward: 400,
ObserverReward: 300,
},
}
@ -421,12 +441,17 @@ type V2 struct {
type V2Config struct {
MaxMasternodes int `json:"maxMasternodes"` // v2 max masternodes
MaxProtectorNodes int `json:"maxProtectorNodes"` // v2 max ProtectorNodes
SwitchRound uint64 `json:"switchRound"` // v1 to v2 switch block number
MinePeriod int `json:"minePeriod"` // Miner mine period to mine a block
TimeoutSyncThreshold int `json:"timeoutSyncThreshold"` // send syncInfo after number of timeout
TimeoutPeriod int `json:"timeoutPeriod"` // Duration in ms
CertThreshold float64 `json:"certificateThreshold"` // Necessary number of messages from master nodes to form a certificate
MasternodeReward uint64 `json:"masternodeReward"` // Block reward for master nodes (core validators) - unit Ether
ProtectorReward uint64 `json:"protectorReward"` // Block reward for protectors - unit Ether
ObserverReward uint64 `json:"observerReward"` // Block reward for observer - unit Ether
ExpTimeoutConfig ExpTimeoutConfig `json:"expTimeoutConfig"`
}
@ -723,6 +748,10 @@ func (c *ChainConfig) IsTIPXDCXCancellationFee(num *big.Int) bool {
return isForked(common.TIPXDCXCancellationFee, num)
}
func (c *ChainConfig) IsTIPUpgradeReward(num *big.Int) bool {
return isForked(common.TIPUpgradeReward, num)
}
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
//
// The returned GasTable's fields shouldn't, under any circumstances, be changed.