mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
Fixed duplicate nonce value for randomize and sign txs
This commit is contained in:
parent
deb79f4a23
commit
fa02ac70cb
1 changed files with 530 additions and 147 deletions
|
|
@ -2,107 +2,531 @@ package contracts
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
cryptoRand "crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/contracts/blocksigner"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/contracts/blocksigner/contract"
|
||||
randomizeContract "github.com/ethereum/go-ethereum/contracts/randomize/contract"
|
||||
contractValidator "github.com/ethereum/go-ethereum/contracts/validator/contract"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
acc3Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
acc4Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee04aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
|
||||
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
|
||||
acc3Addr = crypto.PubkeyToAddress(acc3Key.PublicKey)
|
||||
acc4Addr = crypto.PubkeyToAddress(acc4Key.PublicKey)
|
||||
const (
|
||||
M2ByteLength = 4
|
||||
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
|
||||
)
|
||||
|
||||
func getCommonBackend() *backends.SimulatedBackend {
|
||||
genesis := core.GenesisAlloc{acc1Addr: {Balance: big.NewInt(1000000000000)}}
|
||||
backend := backends.NewSimulatedBackend(genesis)
|
||||
backend.Commit()
|
||||
|
||||
return backend
|
||||
type rewardLog struct {
|
||||
Sign uint64 `json:"sign"`
|
||||
Reward *big.Int `json:"reward"`
|
||||
}
|
||||
|
||||
func TestSendTxSign(t *testing.T) {
|
||||
accounts := []common.Address{acc2Addr, acc3Addr, acc4Addr}
|
||||
keys := []*ecdsa.PrivateKey{acc2Key, acc3Key, acc4Key}
|
||||
backend := getCommonBackend()
|
||||
signer := types.HomesteadSigner{}
|
||||
ctx := context.Background()
|
||||
// Send tx sign for block number to smart contract blockSigner.
|
||||
func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database) error {
|
||||
if chainConfig.XDPoS != 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]
|
||||
}
|
||||
}
|
||||
|
||||
transactOpts := bind.NewKeyedTransactor(acc1Key)
|
||||
blockSignerAddr, blockSigner, err := blocksigner.DeployBlockSigner(transactOpts, backend, big.NewInt(99))
|
||||
// Create and send tx to smart contract for sign validate block.
|
||||
nonce := pool.State().GetNonce(account.Address)
|
||||
tx := CreateTxSign(block.Number(), block.Hash(), 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.
|
||||
err = pool.AddLocal(txSigned)
|
||||
if err != nil {
|
||||
log.Error("Fail to add tx sign to local pool.", "error", err)
|
||||
}
|
||||
|
||||
// Create secret tx.
|
||||
blockNumber := block.Number().Uint64()
|
||||
checkNumber := blockNumber % chainConfig.XDPoS.Epoch
|
||||
// Generate random private key and save into chaindb.
|
||||
randomizeKeyName := []byte("randomizeKey")
|
||||
exist, _ := chainDb.Has(randomizeKeyName)
|
||||
|
||||
// Set secret for randomize.
|
||||
if !exist && checkNumber > 0 && common.EpocBlockSecret <= checkNumber && common.EpocBlockOpening > checkNumber {
|
||||
// Only process when private key empty in state db.
|
||||
// Save randomize key into state db.
|
||||
randomizeKeyValue := RandStringByte(32)
|
||||
tx, err := BuildTxSecretRandomize(nonce+1, common.HexToAddress(common.RandomizeSMC), chainConfig.XDPoS.Epoch, randomizeKeyValue)
|
||||
if err != nil {
|
||||
log.Error("Fail to get tx opening for randomize", "error", err)
|
||||
return err
|
||||
}
|
||||
txSigned, err := wallet.SignTx(account, tx, chainConfig.ChainId)
|
||||
if err != nil {
|
||||
log.Error("Fail to create tx secret", "error", err)
|
||||
return err
|
||||
}
|
||||
// Add tx signed to local tx pool.
|
||||
err = pool.AddLocal(txSigned)
|
||||
if err != nil {
|
||||
log.Error("Fail to add tx secret to local pool.", "error", err)
|
||||
}
|
||||
|
||||
// Put randomize key into chainDb.
|
||||
chainDb.Put(randomizeKeyName, randomizeKeyValue)
|
||||
}
|
||||
|
||||
// Set opening for randomize.
|
||||
if exist && checkNumber > 0 && common.EpocBlockOpening <= checkNumber && common.EpocBlockRandomize >= checkNumber {
|
||||
randomizeKeyValue, err := chainDb.Get(randomizeKeyName)
|
||||
if err != nil {
|
||||
log.Error("Fail to get randomize key from state db.", "error", err)
|
||||
}
|
||||
|
||||
tx, err := BuildTxOpeningRandomize(nonce+1, common.HexToAddress(common.RandomizeSMC), randomizeKeyValue)
|
||||
if err != nil {
|
||||
log.Error("Fail to get tx opening for randomize", "error", err)
|
||||
return err
|
||||
}
|
||||
txSigned, err := wallet.SignTx(account, tx, chainConfig.ChainId)
|
||||
if err != nil {
|
||||
log.Error("Fail to create tx opening", "error", err)
|
||||
return err
|
||||
}
|
||||
// Add tx to pool.
|
||||
err = pool.AddLocal(txSigned)
|
||||
if err != nil {
|
||||
log.Error("Fail to add tx opening to local pool.", "error", err)
|
||||
}
|
||||
|
||||
// Clear randomize key in state db.
|
||||
chainDb.Delete(randomizeKeyName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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), 4200000, 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), 4200000, big.NewInt(0), inputData)
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// Get signers signed for blockNumber from blockSigner contract.
|
||||
func GetSignersFromContract(addrBlockSigner common.Address, client bind.ContractBackend, blockHash common.Hash) ([]common.Address, error) {
|
||||
blockSigner, err := contract.NewBlockSigner(addrBlockSigner, client)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't get block signer: %v", err)
|
||||
log.Error("Fail get instance of blockSigner", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
nonces := make(map[*ecdsa.PrivateKey]int)
|
||||
oldBlocks := make(map[common.Hash]common.Address)
|
||||
|
||||
signTx := func(ctx context.Context, backend *backends.SimulatedBackend, signer types.HomesteadSigner, nonces map[*ecdsa.PrivateKey]int, accKey *ecdsa.PrivateKey, blockNumber *big.Int, blockHash common.Hash) *types.Transaction {
|
||||
tx, _ := types.SignTx(CreateTxSign(blockNumber, blockHash, uint64(nonces[accKey]), blockSignerAddr), signer, accKey)
|
||||
backend.SendTransaction(ctx, tx)
|
||||
backend.Commit()
|
||||
nonces[accKey]++
|
||||
|
||||
return tx
|
||||
opts := new(bind.CallOpts)
|
||||
addrs, err := blockSigner.GetSigners(opts, blockHash)
|
||||
if err != nil {
|
||||
log.Error("Fail get block signers", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Tx sign for signer.
|
||||
signCount := int64(0)
|
||||
blockHashes := make([]common.Hash, 10)
|
||||
for i := int64(0); i < 10; i++ {
|
||||
blockHash := randomHash()
|
||||
blockHashes[i] = blockHash
|
||||
randIndex := rand.Intn(len(keys))
|
||||
accKey := keys[randIndex]
|
||||
signTx(ctx, backend, signer, nonces, accKey, new(big.Int).SetInt64(i), blockHash)
|
||||
oldBlocks[blockHash] = accounts[randIndex]
|
||||
signCount++
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// Tx sign for validators.
|
||||
for _, key := range keys {
|
||||
if key != accKey {
|
||||
signTx(ctx, backend, signer, nonces, key, new(big.Int).SetInt64(i), blockHash)
|
||||
signCount++
|
||||
// Get random from randomize contract.
|
||||
func GetRandomizeFromContract(client bind.ContractBackend, addrMasternode common.Address) (int64, error) {
|
||||
randomize, err := randomizeContract.NewXDCRandomize(common.HexToAddress(common.RandomizeSMC), 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)), M2ByteLength)
|
||||
validatorBytes = append(validatorBytes, m2Byte...)
|
||||
}
|
||||
|
||||
return validatorBytes
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
validators = append(validators, int64(intNumber))
|
||||
}
|
||||
|
||||
return validators
|
||||
}
|
||||
|
||||
// Decode validator hex string.
|
||||
func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
|
||||
validatorsByte, err := hexutil.Decode(validatorsStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return 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)
|
||||
}
|
||||
random = int64(intNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, blockHash := range blockHashes {
|
||||
signers, err := blockSigner.GetSigners(blockHash)
|
||||
return random, nil
|
||||
}
|
||||
|
||||
// Calculate reward for reward checkpoint.
|
||||
func GetRewardForCheckpoint(chain consensus.ChainReader, 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++ {
|
||||
block := chain.GetHeaderByNumber(i)
|
||||
addrs, err := GetSignersFromContract(blockSignerAddr, client, block.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("Can't get signers: %v", err)
|
||||
log.Error("Fail to get signers from smartcontract.", "error", err, "blockNumber", i)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if signers[0].String() != oldBlocks[blockHash].String() {
|
||||
t.Errorf("Tx sign for block signer not match %v - %v", signers[0].String(), oldBlocks[blockHash].String())
|
||||
}
|
||||
|
||||
if len(signers) != len(keys) {
|
||||
t.Error("Tx sign for block validators not match")
|
||||
// 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++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Calculate reward at checkpoint", "startBlock", startBlockNumber, "endBlock", endBlockNumber)
|
||||
|
||||
return signers, nil
|
||||
}
|
||||
|
||||
// Calculate reward for signers.
|
||||
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)
|
||||
// Add reward for signers.
|
||||
if totalSigner > 0 {
|
||||
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 GetCandidatesOwnerBySigner(validator *contractValidator.XDCValidator, signerAddr common.Address) common.Address {
|
||||
owner := signerAddr
|
||||
opts := new(bind.CallOpts)
|
||||
owner, err := validator.GetCandidateOwner(opts, signerAddr)
|
||||
if err != nil {
|
||||
log.Error("Fail get candidate owner", "error", err)
|
||||
return owner
|
||||
}
|
||||
|
||||
return owner
|
||||
}
|
||||
|
||||
// Calculate reward for holders.
|
||||
func CalculateRewardForHolders(foudationWalletAddr common.Address, validator *contractValidator.XDCValidator, state *state.StateDB, signer common.Address, calcReward *big.Int) error {
|
||||
rewards, err := GetRewardBalancesRate(foudationWalletAddr, signer, calcReward, validator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rewards) > 0 {
|
||||
for holder, reward := range rewards {
|
||||
state.AddBalance(holder, reward)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get reward balance rates for master node, founder and holders.
|
||||
func GetRewardBalancesRate(foudationWalletAddr common.Address, masterAddr common.Address, totalReward *big.Int, validator *contractValidator.XDCValidator) (map[common.Address]*big.Int, error) {
|
||||
owner := GetCandidatesOwnerBySigner(validator, masterAddr)
|
||||
balances := make(map[common.Address]*big.Int)
|
||||
rewardMaster := new(big.Int).Mul(totalReward, new(big.Int).SetInt64(common.RewardMasterPercent))
|
||||
rewardMaster = new(big.Int).Div(rewardMaster, new(big.Int).SetInt64(100))
|
||||
balances[owner] = rewardMaster
|
||||
// Get voters for masternode.
|
||||
opts := new(bind.CallOpts)
|
||||
voters, err := validator.GetVoters(opts, masterAddr)
|
||||
if err != nil {
|
||||
log.Error("Fail to get voters", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(voters) > 0 {
|
||||
totalVoterReward := new(big.Int).Mul(totalReward, new(big.Int).SetUint64(common.RewardVoterPercent))
|
||||
totalVoterReward = new(big.Int).Div(totalVoterReward, new(big.Int).SetUint64(100))
|
||||
totalCap := new(big.Int)
|
||||
// Get voters capacities.
|
||||
voterCaps := make(map[common.Address]*big.Int)
|
||||
for _, voteAddr := range voters {
|
||||
voterCap, err := validator.GetVoterCap(opts, masterAddr, voteAddr)
|
||||
if err != nil {
|
||||
log.Error("Fail to get vote capacity", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalCap.Add(totalCap, voterCap)
|
||||
voterCaps[voteAddr] = voterCap
|
||||
}
|
||||
if totalCap.Cmp(new(big.Int).SetInt64(0)) > 0 {
|
||||
for addr, voteCap := range voterCaps {
|
||||
// Only valid voter has cap > 0.
|
||||
if voteCap.Cmp(new(big.Int).SetInt64(0)) > 0 {
|
||||
rcap := new(big.Int).Mul(totalVoterReward, voteCap)
|
||||
rcap = new(big.Int).Div(rcap, totalCap)
|
||||
if balances[addr] != nil {
|
||||
balances[addr].Add(balances[addr], rcap)
|
||||
} else {
|
||||
balances[addr] = rcap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foudationReward := new(big.Int).Mul(totalReward, new(big.Int).SetInt64(common.RewardFoundationPercent))
|
||||
foudationReward = new(big.Int).Div(foudationReward, new(big.Int).SetInt64(100))
|
||||
balances[foudationWalletAddr] = foudationReward
|
||||
|
||||
jsonHolders, err := json.Marshal(balances)
|
||||
if err != nil {
|
||||
log.Error("Fail to parse json holders", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Holders reward", "holders", string(jsonHolders), "master node", masterAddr.String())
|
||||
|
||||
return balances, 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 {
|
||||
// key := []byte(keyText)
|
||||
plaintext := []byte(text)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
panic("ciphertext too short")
|
||||
}
|
||||
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 fmt.Sprintf("%s", ciphertext)
|
||||
}
|
||||
|
||||
// Generate random string.
|
||||
func randomHash() common.Hash {
|
||||
func RandStringByte(n int) []byte {
|
||||
letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
|
||||
var b common.Hash
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||
|
|
@ -110,86 +534,45 @@ func randomHash() common.Hash {
|
|||
return b
|
||||
}
|
||||
|
||||
// Unit test for get random position of masternodes.
|
||||
func TestRandomMasterNode(t *testing.T) {
|
||||
oldSlice := NewSlice(0, 10, 1)
|
||||
newSlice := Shuffle(oldSlice)
|
||||
for _, newNumber := range newSlice {
|
||||
for i, oldNumber := range oldSlice {
|
||||
if oldNumber == newNumber {
|
||||
// Delete find element.
|
||||
oldSlice = append(oldSlice[:i], oldSlice[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(oldSlice) != 0 {
|
||||
t.Errorf("Test generate random masternode fail %v - %v", oldSlice, newSlice)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
//byteInteger := common.LeftPadBytes([]byte(new(big.Int).SetInt64(4).String()), 32)
|
||||
randomByte := RandStringByte(32)
|
||||
encrypt := Encrypt(randomByte, new(big.Int).SetInt64(4).String())
|
||||
decrypt := Decrypt(randomByte, encrypt)
|
||||
t.Log("Encrypt", encrypt, "Test", string(randomByte), "Decrypt", decrypt, "trim", string(bytes.TrimLeft([]byte(decrypt), "\x00")))
|
||||
}
|
||||
|
||||
func isArrayEqual(a [][]int64, b [][]int64) bool {
|
||||
if len(a) != len(b) {
|
||||
// Helper function check string is numeric.
|
||||
func isInt(strNumber string) bool {
|
||||
if _, err := strconv.Atoi(strNumber); err == nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
for i, vs := range a {
|
||||
for j, v := range vs {
|
||||
if v != b[i][j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Get masternodes address from checkpoint Header.
|
||||
func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address {
|
||||
masternodes := make([]common.Address, (len(checkpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength)
|
||||
for i := 0; i < len(masternodes); i++ {
|
||||
copy(masternodes[i][:], checkpointHeader.Extra[extraVanity+i*common.AddressLength:])
|
||||
}
|
||||
return masternodes
|
||||
}
|
||||
|
||||
// Get m2 list from checkpoint block.
|
||||
func GetM2FromCheckpointBlock(checkpointBlock types.Block) ([]common.Address, error) {
|
||||
if checkpointBlock.Number().Int64()%common.EpocBlockRandomize != 0 {
|
||||
return nil, errors.New("This block is not checkpoint block epoc.")
|
||||
}
|
||||
|
||||
// Get singers from this block.
|
||||
masternodes := GetMasternodesFromCheckpointHeader(checkpointBlock.Header())
|
||||
validators := ExtractValidatorsFromBytes(checkpointBlock.Header().Validators)
|
||||
|
||||
var m2List []common.Address
|
||||
lenMasternodes := len(masternodes)
|
||||
var valAddr common.Address
|
||||
for validatorIndex := range validators {
|
||||
if validatorIndex < lenMasternodes {
|
||||
valAddr = masternodes[validatorIndex]
|
||||
} else {
|
||||
valAddr = masternodes[validatorIndex-lenMasternodes]
|
||||
}
|
||||
m2List = append(m2List, valAddr)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Unit test for
|
||||
func TestGenM2FromRandomize(t *testing.T) {
|
||||
var a []int64
|
||||
for i := 0; i <= 10; i++ {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
a = append(a, int64(rand.Intn(9999)))
|
||||
}
|
||||
b, err := GenM2FromRandomize(a, common.MaxMasternodes)
|
||||
t.Log("randomize", b, "len", len(b))
|
||||
if err != nil {
|
||||
t.Error("Fail to test gen m2 for randomize.", err)
|
||||
}
|
||||
// Test Permutation Without Fixed-point.
|
||||
M1List := NewSlice(int64(0), common.MaxMasternodes, 1)
|
||||
for i, m1 := range M1List {
|
||||
if m1 == b[i] {
|
||||
t.Errorf("Error check Permutation Without Fixed-point %v - %v - %v", i, b[i], a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unit test for validator m2.
|
||||
func TestBuildValidatorFromM2(t *testing.T) {
|
||||
a := []int64{84, 58, 27, 96, 127, 60, 136, 20, 121, 31, 87, 85, 40, 120, 149, 109, 141, 145, 11, 110, 147, 35, 76, 46, 34, 108, 72, 103, 102, 12, 23, 47, 70, 86, 125, 112, 128, 13, 130, 98, 126, 62, 132, 111, 134, 6, 106, 67, 24, 91, 101, 50, 94, 43, 77, 73, 129, 71, 51, 10, 92, 29, 80, 95, 33, 100, 124, 75, 38, 133, 79, 83, 61, 36, 122, 99, 16, 28, 18, 116, 140, 97, 119, 82, 148, 48, 56, 32, 93, 107, 69, 68, 123, 81, 22, 137, 25, 115, 44, 8, 42, 131, 143, 17, 55, 89, 9, 15, 19, 59, 146, 54, 5, 30, 41, 144, 117, 1, 104, 49, 105, 45, 88, 78, 74, 135, 0, 21, 57, 3, 66, 52, 63, 138, 4, 114, 37, 118, 14, 2, 26, 7, 65, 139, 39, 64, 90, 142, 53, 113}
|
||||
b := BuildValidatorFromM2(a)
|
||||
c := ExtractValidatorsFromBytes(b)
|
||||
if !isArrayEqual([][]int64{a}, [][]int64{c}) {
|
||||
t.Errorf("Fail to get m2 result %v", b)
|
||||
}
|
||||
}
|
||||
|
||||
// Unit test for decode validator string data.
|
||||
func TestDecodeValidatorsHexData(t *testing.T) {
|
||||
a := "0x000000310000003000000032000000310000003000000032000000310000003000000032000000310000003000000031000000320000003000000031000000320000003000000031000000320000003000000030000000310000003200000030000000310000003200000030000000310000003200000030000000300000003100000032000000300000003100000032000000300000003100000032000000300000003200000030000000310000003200000030000000310000003200000030000000310000003000000030"
|
||||
b, err := DecodeValidatorsHexData(a)
|
||||
if err != nil {
|
||||
t.Error("Fail to decode validator from hex string", err)
|
||||
}
|
||||
c := []int64{1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 2, 0, 1, 2, 0, 1, 2, 0, 1, 0, 0}
|
||||
if !isArrayEqual([][]int64{b}, [][]int64{c}) {
|
||||
t.Errorf("Fail to get m2 result %v", b)
|
||||
}
|
||||
t.Log("b", b)
|
||||
return m2List, nil
|
||||
}
|
||||
Loading…
Reference in a new issue