package contracts import ( "encoding/json" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/blocksigner/contract" contract2 "github.com/ethereum/go-ethereum/contracts/validator/contract" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "math/big" ) const ( HexSignMethod = "2fb1b25f" ) type rewardLog struct { Sign uint64 `json:"sign"` Reward *big.Int `json:"reward"` } // Send tx sign for block number to smart contract blockSigner. func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, manager *accounts.Manager, block *types.Block) error { if chainConfig.Clique != nil { // Find active account. account := accounts.Account{} var wallet accounts.Wallet if wallets := manager.Wallets(); len(wallets) > 0 { wallet = wallets[0] if accts := wallets[0].Accounts(); len(accts) > 0 { account = accts[0] } } // Create and send tx to smart contract for sign validate block. nonce := pool.State().GetNonce(account.Address) tx := CreateTxSign(block.Number(), nonce, common.HexToAddress(common.BlockSigners)) txSigned, err := wallet.SignTx(account, tx, chainConfig.ChainId) if err != nil { log.Error("Fail to create tx sign", "error", err) return err } // Add tx signed to local tx pool. pool.AddLocal(txSigned) } return nil } // Create tx sign. func CreateTxSign(blockNumber *big.Int, nonce uint64, blockSigner common.Address) *types.Transaction { blockHex := common.LeftPadBytes(blockNumber.Bytes(), 32) data := common.Hex2Bytes(HexSignMethod) inputData := append(data, blockHex...) tx := types.NewTransaction(nonce, blockSigner, big.NewInt(0), 100000, big.NewInt(0), inputData) return tx } // Get signers signed for blockNumber from blockSigner contract. func GetSignersFromContract(addrBlockSigner common.Address, client bind.ContractBackend, blockNumber uint64) ([]common.Address, error) { blockSigner, err := contract.NewBlockSigner(addrBlockSigner, client) if err != nil { log.Error("Fail get instance of blockSigner", "error", err) return nil, err } opts := new(bind.CallOpts) addrs, err := blockSigner.GetSigners(opts, new(big.Int).SetUint64(blockNumber)) if err != nil { log.Error("Fail get block signers", "error", err) return nil, err } return addrs, nil } // Calculate reward for reward checkpoint. func GetRewardForCheckpoint(blockSignerAddr common.Address, number uint64, rCheckpoint uint64, client bind.ContractBackend, totalSigner *uint64) (map[common.Address]*rewardLog, error) { // Not reward for singer of genesis block and only calculate reward at checkpoint block. startBlockNumber := number - (rCheckpoint * 2) + 1 endBlockNumber := startBlockNumber + rCheckpoint - 1 signers := make(map[common.Address]*rewardLog) for i := startBlockNumber; i <= endBlockNumber; i++ { addrs, err := GetSignersFromContract(blockSignerAddr, client, i) if err != nil { log.Error("Fail to get signers from smartcontract.", "error", err, "blockNumber", i) return nil, err } // Filter duplicate address. if len(addrs) > 0 { addrSigners := make(map[common.Address]bool) for _, addr := range addrs { if _, ok := addrSigners[addr]; !ok { addrSigners[addr] = true } } for addr := range addrSigners { _, exist := signers[addr] if exist { signers[addr].Sign++ } else { signers[addr] = &rewardLog{1, new(big.Int)} } *totalSigner++ } } } // Get Owner for signers. owners := make(map[common.Address]*rewardLog) if len(signers) > 0 { for addr, log := range signers { owner, err := GetCandidatesOwnerByAddress(client, addr) if err != nil { owner = addr } if _, ok := owners[owner]; ok { owners[owner].Sign = owners[owner].Sign + log.Sign } else { owners[owner] = log } } } log.Info("Calculate reward at checkpoint", "startBlock", startBlockNumber, "endBlock", endBlockNumber) return owners, nil } // Calculate reward for signers. func CalculateReward(chainReward *big.Int, signers map[common.Address]*rewardLog, totalSigner uint64) (map[common.Address]*big.Int, error) { resultSigners := make(map[common.Address]*big.Int) // Add reward for signers. for signer, rLog := range signers { // Add reward for signer. calcReward := new(big.Int) calcReward.Div(chainReward, new(big.Int).SetUint64(totalSigner)) calcReward.Mul(calcReward, new(big.Int).SetUint64(rLog.Sign)) rLog.Reward = calcReward resultSigners[signer] = calcReward } jsonSigners, err := json.Marshal(signers) if err != nil { log.Error("Fail to parse json signers", "error", err) return nil, err } log.Info("Signers data", "signers", string(jsonSigners), "totalSigner", totalSigner, "totalReward", chainReward) return resultSigners, nil } // Get candidate owner by address. func GetCandidatesOwnerByAddress(client bind.ContractBackend, addr common.Address) (common.Address, error) { owner := common.Address{} validator, err := contract2.NewXDCValidator(common.HexToAddress(common.XDCValidator), client) if err != nil { log.Error("Fail get instance of IValidator", "error", err) return owner, err } opts := new(bind.CallOpts) owner, err = validator.GetCandidateOwner(opts, addr) if err != nil { log.Error("Fail get candidate owner", "error", err) return owner, err } return owner, nil }