mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-04 12:11:17 +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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"crypto/aes"
|
||||||
"crypto/ecdsa"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
M2ByteLength = 4
|
||||||
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
|
||||||
acc3Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||||
acc4Key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee04aefe388d1e14474d32c45c72ce7b7a")
|
|
||||||
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
|
|
||||||
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
|
|
||||||
acc3Addr = crypto.PubkeyToAddress(acc3Key.PublicKey)
|
|
||||||
acc4Addr = crypto.PubkeyToAddress(acc4Key.PublicKey)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getCommonBackend() *backends.SimulatedBackend {
|
type rewardLog struct {
|
||||||
genesis := core.GenesisAlloc{acc1Addr: {Balance: big.NewInt(1000000000000)}}
|
Sign uint64 `json:"sign"`
|
||||||
backend := backends.NewSimulatedBackend(genesis)
|
Reward *big.Int `json:"reward"`
|
||||||
backend.Commit()
|
|
||||||
|
|
||||||
return backend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendTxSign(t *testing.T) {
|
// Send tx sign for block number to smart contract blockSigner.
|
||||||
accounts := []common.Address{acc2Addr, acc3Addr, acc4Addr}
|
func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database) error {
|
||||||
keys := []*ecdsa.PrivateKey{acc2Key, acc3Key, acc4Key}
|
if chainConfig.XDPoS != nil {
|
||||||
backend := getCommonBackend()
|
// Find active account.
|
||||||
signer := types.HomesteadSigner{}
|
account := accounts.Account{}
|
||||||
ctx := context.Background()
|
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)
|
// Create and send tx to smart contract for sign validate block.
|
||||||
blockSignerAddr, blockSigner, err := blocksigner.DeployBlockSigner(transactOpts, backend, big.NewInt(99))
|
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 {
|
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()
|
opts := new(bind.CallOpts)
|
||||||
|
addrs, err := blockSigner.GetSigners(opts, blockHash)
|
||||||
nonces := make(map[*ecdsa.PrivateKey]int)
|
if err != nil {
|
||||||
oldBlocks := make(map[common.Hash]common.Address)
|
log.Error("Fail get block signers", "error", err)
|
||||||
|
return nil, err
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tx sign for signer.
|
return addrs, nil
|
||||||
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++
|
|
||||||
|
|
||||||
// Tx sign for validators.
|
// Get random from randomize contract.
|
||||||
for _, key := range keys {
|
func GetRandomizeFromContract(client bind.ContractBackend, addrMasternode common.Address) (int64, error) {
|
||||||
if key != accKey {
|
randomize, err := randomizeContract.NewXDCRandomize(common.HexToAddress(common.RandomizeSMC), client)
|
||||||
signTx(ctx, backend, signer, nonces, key, new(big.Int).SetInt64(i), blockHash)
|
if err != nil {
|
||||||
signCount++
|
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 {
|
return random, nil
|
||||||
signers, err := blockSigner.GetSigners(blockHash)
|
}
|
||||||
|
|
||||||
|
// 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 {
|
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
|
||||||
}
|
}
|
||||||
|
// Filter duplicate address.
|
||||||
if signers[0].String() != oldBlocks[blockHash].String() {
|
if len(addrs) > 0 {
|
||||||
t.Errorf("Tx sign for block signer not match %v - %v", signers[0].String(), oldBlocks[blockHash].String())
|
addrSigners := make(map[common.Address]bool)
|
||||||
}
|
for _, addr := range addrs {
|
||||||
|
if _, ok := addrSigners[addr]; !ok {
|
||||||
if len(signers) != len(keys) {
|
addrSigners[addr] = true
|
||||||
t.Error("Tx sign for block validators not match")
|
}
|
||||||
|
}
|
||||||
|
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.
|
// Generate random string.
|
||||||
func randomHash() common.Hash {
|
func RandStringByte(n int) []byte {
|
||||||
letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
|
letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"
|
||||||
var b common.Hash
|
b := make([]byte, n)
|
||||||
for i := range b {
|
for i := range b {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||||
|
|
@ -110,86 +534,45 @@ func randomHash() common.Hash {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unit test for get random position of masternodes.
|
// Helper function check string is numeric.
|
||||||
func TestRandomMasterNode(t *testing.T) {
|
func isInt(strNumber string) bool {
|
||||||
oldSlice := NewSlice(0, 10, 1)
|
if _, err := strconv.Atoi(strNumber); err == nil {
|
||||||
newSlice := Shuffle(oldSlice)
|
return true
|
||||||
for _, newNumber := range newSlice {
|
} else {
|
||||||
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) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i, vs := range a {
|
}
|
||||||
for j, v := range vs {
|
|
||||||
if v != b[i][j] {
|
// Get masternodes address from checkpoint Header.
|
||||||
return false
|
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
|
return m2List, nil
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue