diff --git a/contracts/randomize/contract/randomize.go b/contracts/randomize/contract/randomize.go index 970aecf674..eed947a190 100644 --- a/contracts/randomize/contract/randomize.go +++ b/contracts/randomize/contract/randomize.go @@ -17,7 +17,7 @@ import ( const SafeMathABI = "[]" // SafeMathBin is the compiled bytecode used for deploying new contracts. -const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146060604052600080fd00a165627a7a72305820b9407d48ebc7efee5c9f08b3b3a957df2939281f5913225e8c1291f069b900490029` +const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146080604052600080fd00a165627a7a72305820a3f63b465e1cf25f306b1eb1efefc8dac3c38993a7340f69d8b470c3bf599ff30029` // DeploySafeMath deploys a new Ethereum contract, binding an instance of SafeMath to it. func DeploySafeMath(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SafeMath, error) { @@ -178,7 +178,7 @@ func (_SafeMath *SafeMathTransactorRaw) Transact(opts *bind.TransactOpts, method const XDCRandomizeABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getSecret\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_secret\",\"type\":\"bytes32[]\"}],\"name\":\"setSecret\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"randomNumber\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_validator\",\"type\":\"address\"}],\"name\":\"getOpening\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_opening\",\"type\":\"bytes32\"}],\"name\":\"setOpening\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_randomNumber\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]" // XDCRandomizeBin is the compiled bytecode used for deploying new contracts. -const XDCRandomizeBin = `0x6060604052341561000f57600080fd5b604051602080610359833981016040528080516000555050610323806100366000396000f30060606040526004361061006c5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663284180fc811461007157806334d38600146100e3578063ccbac9f514610134578063d442d6cc14610159578063e11f5ba214610178575b600080fd5b341561007c57600080fd5b610090600160a060020a036004351661018e565b60405160208082528190810183818151815260200191508051906020019060200280838360005b838110156100cf5780820151838201526020016100b7565b505050509050019250505060405180910390f35b34156100ee57600080fd5b610132600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061021295505050505050565b005b341561013f57600080fd5b61014761023f565b60405190815260200160405180910390f35b341561016457600080fd5b610147600160a060020a0360043516610245565b341561018357600080fd5b610132600435610260565b61019661027b565b6001600083600160a060020a0316600160a060020a0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561020657602002820191906000526020600020905b815481526001909101906020018083116101f1575b50505050509050919050565b600160a060020a033316600090815260016020526040902081805161023b92916020019061028d565b5050565b60005481565b600160a060020a031660009081526002602052604090205490565b600160a060020a033316600090815260026020526040902055565b60206040519081016040526000815290565b8280548282559060005260206000209081019282156102ca579160200282015b828111156102ca57825182556020909201916001909101906102ad565b506102d69291506102da565b5090565b6102f491905b808211156102d657600081556001016102e0565b905600a165627a7a7230582043a345787b9942e3361515ce1a0a4dbaab0ed6ce42c6ca0344119134351d9ffe0029` +const XDCRandomizeBin = `0x608060405234801561001057600080fd5b5060405160208061035b8339810160405251600055610327806100346000396000f30060806040526004361061006c5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663284180fc811461007157806334d38600146100ef578063ccbac9f514610146578063d442d6cc1461016d578063e11f5ba21461019b575b600080fd5b34801561007d57600080fd5b5061009f73ffffffffffffffffffffffffffffffffffffffff600435166101b3565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156100db5781810151838201526020016100c3565b505050509050019250505060405180910390f35b3480156100fb57600080fd5b50604080516020600480358082013583810280860185019096528085526101449536959394602494938501929182918501908490808284375094975061022d9650505050505050565b005b34801561015257600080fd5b5061015b610251565b60408051918252519081900360200190f35b34801561017957600080fd5b5061015b73ffffffffffffffffffffffffffffffffffffffff60043516610257565b3480156101a757600080fd5b5061014460043561027f565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001602090815260409182902080548351818402810184019094528084526060939283018282801561022157602002820191906000526020600020905b8154815260019091019060200180831161020c575b50505050509050919050565b336000908152600160209081526040909120825161024d92840190610291565b5050565b60005481565b73ffffffffffffffffffffffffffffffffffffffff1660009081526002602052604090205490565b33600090815260026020526040902055565b8280548282559060005260206000209081019282156102ce579160200282015b828111156102ce57825182556020909201916001909101906102b1565b506102da9291506102de565b5090565b6102f891905b808211156102da57600081556001016102e4565b905600a165627a7a7230582027c1c755e5d546967a2057a4338eb36cf7ec5972758d7f7a8427abdfc890bd590029` // DeployXDCRandomize deploys a new Ethereum contract, binding an instance of XDCRandomize to it. func DeployXDCRandomize(auth *bind.TransactOpts, backend bind.ContractBackend, _randomNumber *big.Int) (common.Address, *types.Transaction, *XDCRandomize, error) { diff --git a/contracts/randomize/randomize_test.go b/contracts/randomize/randomize_test.go index 319b8eb90d..871a26f872 100644 --- a/contracts/randomize/randomize_test.go +++ b/contracts/randomize/randomize_test.go @@ -11,17 +11,23 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/contracts" ) var ( - key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - addr = crypto.PubkeyToAddress(key.PublicKey) - byte0 = make([][32]byte, 2) + epocNumber = int64(12) + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key.PublicKey) + byte0 = make([][32]byte, epocNumber) + acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) ) func TestRandomize(t *testing.T) { - contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}) + contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(100000000000000)}}) transactOpts := bind.NewKeyedTransactor(key) + transactOpts.GasLimit = 1000000 randomizeAddress, randomize, err := DeployRandomize(transactOpts, contractBackend, big.NewInt(2)) t.Log("contract address", randomizeAddress.String()) @@ -40,11 +46,88 @@ func TestRandomize(t *testing.T) { return true } contractBackend.ForEachStorageAt(ctx, randomizeAddress, nil, f) - s, err := randomize.SetSecret(byte0) if err != nil { - t.Fatalf("can't get secret: %v", err) + t.Fatalf("can't set secret: %v", err) } t.Log("tx data", s) contractBackend.Commit() +} + +func TestSendTxRandomizeSecretAndOpening(t *testing.T) { + genesis := core.GenesisAlloc{acc1Addr: {Balance: big.NewInt(1000000000000)}} + backend := backends.NewSimulatedBackend(genesis) + backend.Commit() + signer := types.HomesteadSigner{} + ctx := context.Background() + + transactOpts := bind.NewKeyedTransactor(acc1Key) + transactOpts.GasLimit = 4200000 + epocNumber := uint64(99) + randomizeAddr, randomizeContract, err := DeployRandomize(transactOpts, backend, new(big.Int).SetInt64(0)) + if err != nil { + t.Fatalf("Can't deploy randomize SC: %v", err) + } + backend.Commit() + + nonce := uint64(1) + randomizeKeyValue := contracts.RandStringByte(32) + tx, err := contracts.BuildTxSecretRandomize(nonce, randomizeAddr, epocNumber, randomizeKeyValue) + if err != nil { + t.Fatalf("Can't create tx randomize secret: %v", err) + } + tx, err = types.SignTx(tx, signer, acc1Key) + if err != nil { + t.Fatalf("Can't sign tx randomize secret: %v", err) + } + + err = backend.SendTransaction(ctx, tx) + if err != nil { + t.Fatalf("Can't send tx for create randomize secret: %v", err) + } + backend.Commit() + // Increment nonce. + nonce++ + // Set opening. + tx, err = contracts.BuildTxOpeningRandomize(nonce, randomizeAddr, randomizeKeyValue) + if err != nil { + t.Fatalf("Can't create tx randomize opening: %v", err) + } + tx, err = types.SignTx(tx, signer, acc1Key) + if err != nil { + t.Fatalf("Can't sign tx randomize opening: %v", err) + } + + err = backend.SendTransaction(ctx, tx) + if err != nil { + t.Fatalf("Can't send tx for create randomize opening: %v", err) + } + backend.Commit() + + // Get randomize secret from SC. + secretsArr, err := randomizeContract.GetSecret(acc1Addr) + if err != nil { + t.Fatalf("Can't get secret from SC: %v", err) + } + if len(secretsArr) <= 0 { + t.Error("Empty get secrets from SC", err) + } + + // Decrypt randomize from SC. + secrets, err := randomizeContract.GetSecret(acc1Addr) + if err != nil { + t.Error("Fail get secrets from randomize", err) + } + opening, err := randomizeContract.GetOpening(acc1Addr) + if err != nil { + t.Fatalf("Can't get secret from SC: %v", err) + } + randomizes, err := contracts.DecryptRandomizeFromSecretsAndOpening(secrets, opening) + t.Log("randomizes", randomizes) + if err != nil { + t.Error("Can't decrypt secret and opening", err) + } + if len(randomizes) != 991 { + t.Error("Randomize length not match") + } } \ No newline at end of file diff --git a/contracts/utils.go b/contracts/utils.go index c93788d897..8cdbfc7d78 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -1,19 +1,34 @@ package contracts import ( + "bytes" + "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/common" "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/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/pkg/errors" + "io" + "math" "math/big" + "math/rand" + "strconv" + "time" + "github.com/ethereum/go-ethereum/common/hexutil" ) const ( @@ -21,7 +36,14 @@ const ( RewardMasterPercent = 40 RewardVoterPercent = 50 RewardFoundationPercent = 10 - FoudationWalletAddr = "0x0000000000000000000000000000000000000068" + HexSetSecret = "34d38600" + HexSetOpening = "e11f5ba2" + EpocBlockSecret = 950 + EpocBlockOpening = 980 + EpocBlockRandomize = 900 + 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 ) type rewardLog struct { @@ -30,7 +52,7 @@ type rewardLog struct { } // 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 { +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{} @@ -50,9 +72,69 @@ func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, m log.Error("Fail to create tx sign", "error", err) return err } - // Add tx signed to local tx pool. - pool.AddLocal(txSigned) + 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 && EpocBlockSecret <= checkNumber && EpocBlockOpening > checkNumber { + // Only process when private key empty in state db. + // Save randomize key into state db. + randomizeKeyValue := RandStringByte(32) + chainDb.Put(randomizeKeyName, randomizeKeyValue) + + tx, err := BuildTxSecretRandomize(nonce, 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) + } + } + + // Set opening for randomize. + if exist && checkNumber > 0 && EpocBlockOpening <= checkNumber && 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, 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 @@ -68,6 +150,42 @@ func CreateTxSign(blockNumber *big.Int, blockHash common.Hash, nonce uint64, blo 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(HexSetSecret) + secretMax := math.Round(float64(epocNumber / 10)) + secrets := Shuffle(NewSlice(0, int64(secretMax), 1)) + + // Append randomize suffix in -1, 0, 1. + arrSuffix := []int64{-1, 1} + rand.Seed(time.Now().UnixNano()) + randIndex := rand.Intn(len(arrSuffix)) + secrets = append(secrets, arrSuffix[randIndex]) + + 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(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) @@ -85,6 +203,122 @@ func GetSignersFromContract(addrBlockSigner common.Address, client bind.Contract return addrs, nil } +// 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) ([]int64, error) { + // Separate array. + arrRandomizes := TransposeMatrix(randomizes) + lenRandomize := len(arrRandomizes) + arrayA := arrRandomizes[:lenRandomize-2] + arrayB := arrRandomizes[lenRandomize-1:] + + matrixResult, err := DotMatrix(arrayA, arrayB) + if err != nil { + log.Error("Fail to dot matrix", "error", err) + + return nil, err + } + lenMasternode := len(arrayB[0]) + result := make([]int64, lenRandomize) + for i, v := range matrixResult { + result[i] = int64(math.Abs(float64(v))) % int64(lenMasternode) + } + + return result, 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 + var completedRandomize []int64 + random = make([]int64, len(secrets)) + if len(secrets) > 0 { + for i, 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[i] = int64(int64(intNumber)) + } + } + } + + // Generate full randomize. + randomMax := len(random) - 1 + if randomMax > 0 { + for i, randNumber := range random { + if i < randomMax { + for j := 0; j < 10; j++ { + completedRandomize = append(completedRandomize, randNumber*10+int64(j)) + } + } + } + completedRandomize = append(completedRandomize, random[randomMax]) + } + log.Error("random", "completedRandomize", completedRandomize, "randomMax", randomMax) + + return completedRandomize, 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. @@ -235,4 +469,155 @@ func GetRewardBalancesRate(foudationWalletAddr common.Address, masterAddr common 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 RandStringByte(n int) []byte { + letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789" + b := make([]byte, n) + for i := range b { + rand.Seed(time.Now().UnixNano()) + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return b +} + +// Helper function check string is numeric. +func isInt(strNumber string) bool { + if _, err := strconv.Atoi(strNumber); err == nil { + return true + } else { + return false + } +} + +// Helper function for transpose matrix. +func TransposeMatrix(x [][]int64) [][]int64 { + out := make([][]int64, len(x[0])) + for i := 0; i < len(x); i += 1 { + for j := 0; j < len(x[0]); j += 1 { + out[j] = append(out[j], x[i][j]) + } + } + return out +} + +// Helper function for multiplication matrix. +func DotMatrix(x, y [][]int64) ([]int64, error) { + if len(x[0]) != len(y[0]) { + return nil, errors.New("Can't do matrix multiplication.") + } + + out := make([]int64, len(x)) + for i := 0; i < len(x); i += 1 { + for j := 0; j < len(y[0]); j += 1 { + out[i] += x[i][j] * y[0][j] + } + } + + return out, nil +} + +// 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()%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 + for validatorIndex := range validators { + m2List = append(m2List, masternodes[validatorIndex]) + } + + return m2List, nil } \ No newline at end of file diff --git a/contracts/utils_test.go b/contracts/utils_test.go index cfb473c173..bb6a8b242c 100644 --- a/contracts/utils_test.go +++ b/contracts/utils_test.go @@ -1,21 +1,7 @@ -// Copyright (c) 2018 Xinfin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program. If not, see . - package contracts import ( + "bytes" "context" "crypto/ecdsa" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -31,22 +17,30 @@ import ( "time" ) -func TestSendTxSign(t *testing.T) { - 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) - accounts := []common.Address{acc2Addr, acc3Addr, acc4Addr} - keys := []*ecdsa.PrivateKey{acc2Key, acc3Key, acc4Key} +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) +) - signer := types.HomesteadSigner{} - genesis := core.GenesisAlloc{acc1Addr: {Balance: big.NewInt(1000000000)}} +func getCommonBackend() *backends.SimulatedBackend { + genesis := core.GenesisAlloc{acc1Addr: {Balance: big.NewInt(1000000000000)}} backend := backends.NewSimulatedBackend(genesis) backend.Commit() + + return backend +} + +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() transactOpts := bind.NewKeyedTransactor(acc1Key) @@ -114,4 +108,119 @@ func randomHash() common.Hash { b[i] = letterBytes[rand.Intn(len(letterBytes))] } 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 TestTransposeMatrix(t *testing.T) { + a := [][]int64{ + {0, 1, 2, 3, 4}, + {4, 5, 6, 7, 8}, + } + b := [][]int64{ + {0, 4}, + {1, 5}, + {2, 6}, + {3, 7}, + {4, 8}, + } + if !isArrayEqual(b, TransposeMatrix(a)) { + t.Errorf("Fail to transpose matrix %v - %v", a, TransposeMatrix(a)) + } +} + +func TestMultiMatrix(t *testing.T) { + a := make([][]int64, 6) + b := [][]int64{ + {1, -1, -1, 1, 1, -1}, + } + for i := 0; i < len(b[0]); i++ { + a[i] = Shuffle(NewSlice(0, 6, 1)) + } + c, err := DotMatrix(a, b) + if err != nil { + t.Error("Fail to test dot matrix", err) + } + if len(a[0]) != len(c) { + t.Errorf("Fail to test dot matrix result %v - %v - %v", a, b, c) + } +} + +func isArrayEqual(a [][]int64, b [][]int64) bool { + if len(a) != len(b) { + return false + } + for i, vs := range a { + for j, v := range vs { + if v != b[i][j] { + return false + } + } + } + return true +} + +// Unit test for +func TestGenM2FromRandomize(t *testing.T) { + a := [][]int64{ + {37, 23, 17, 45, 38, 8, 21, 28, 15, 41, 1, 25, 4, 30, 31, 0, 9, 16, 46, 13, 36, 7, 19, 27, 47, 32, 22, 3, 20, 33, 2, 35, 49, 6, 42, 34, 44, 10, 29, 26, 12, 43, 48, 24, 40, 14, 18, 39, 5, 11, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {28, 16, 13, 31, 32, 36, 44, 14, 37, 33, 3, 23, 17, 46, 35, 30, 45, 27, 9, 41, 7, 19, 10, 24, 5, 34, 29, 18, 21, 15, 0, 2, 25, 39, 11, 4, 22, 6, 48, 42, 12, 26, 1, 47, 43, 20, 40, 38, 8, 49, -1}, + } + b, err := GenM2FromRandomize(a) + t.Log("randomize", b) + if err != nil { + t.Error("Fail to test gen m2 for randomize.", err) + } + c := []int64{0, 1, 1, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 1, 1, 0, 0, 2, 1, 1, 2, 0, 0, 0, 0, 2, 1, 0, 1, 0, 2, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 2, 2, 2, 0, 0, 1, 1, 0, 0, 0} + if !isArrayEqual([][]int64{b}, [][]int64{c}) { + t.Errorf("Fail to get m2 result %v", b) + } +} + +// Unit test for validator m2. +func TestBuildValidatorFromM2(t *testing.T) { + a := []int64{0, 1, 1, 2, 0, 1, 2, 128, 150, 2, 2, 2, 1, 1, 1, 0, 0, 2, 1, 1, 2, 0, 0, 0, 0, 2, 1, 0, 1, 0, 2, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 2, 2, 2, 0, 0, 1, 1, 0, 0, 0} + 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) } \ No newline at end of file diff --git a/core/tx_list.go b/core/tx_list.go index 55fc42617d..bbcc113d91 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -251,15 +251,19 @@ func (l *txList) Overlaps(tx *types.Transaction) bool { func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { // If there's an older better transaction, abort old := l.txs.Get(tx.Nonce()) - if old != nil { - threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100)) - // Have to ensure that the new gas price is higher than the old gas - // price as well as checking the percentage threshold to ensure that - // this is accurate for low (Wei-level) gas price replacements - if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { - return false, nil + + if (tx.To() != nil && tx.To().String() != common.RandomizeSMC) || tx.To() == nil { + if old != nil { + threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100)) + // Have to ensure that the new gas price is higher than the old gas + // price as well as checking the percentage threshold to ensure that + // this is accurate for low (Wei-level) gas price replacements + if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { + return false, nil + } } } + // Otherwise overwrite the old transaction with the current one l.txs.Put(tx) if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { @@ -505,4 +509,4 @@ func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions heap.Push(l.items, tx) } return drop -} +} \ No newline at end of file diff --git a/core/tx_pool.go b/core/tx_pool.go index 67da91d0be..e3f878fa70 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -589,7 +589,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInsufficientFunds } - if tx.To() == nil || (tx.To() != nil && tx.To().String() != common.BlockSigners) { + if tx.To() != nil && tx.To().String() != common.BlockSigners && tx.To().String() != common.RandomizeSMC { intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead) if err != nil { return err @@ -793,8 +793,6 @@ func (pool *TxPool) AddRemotes(txs []*types.Transaction) []error { // addTx enqueues a single transaction into the pool if it is valid. func (pool *TxPool) addTx(tx *types.Transaction, local bool) error { - tx.CacheHash() - types.CacheSigner(pool.signer, tx) pool.mu.Lock() defer pool.mu.Unlock() @@ -813,9 +811,6 @@ func (pool *TxPool) addTx(tx *types.Transaction, local bool) error { // addTxs attempts to queue a batch of transactions if they are valid. func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error { - for _, tx := range txs { - types.CacheSigner(pool.signer, tx) - } pool.mu.Lock() defer pool.mu.Unlock() diff --git a/core/types/block.go b/core/types/block.go index 92b868d9da..f1aa43649f 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -84,6 +84,7 @@ type Header struct { Extra []byte `json:"extraData" gencodec:"required"` MixDigest common.Hash `json:"mixHash" gencodec:"required"` Nonce BlockNonce `json:"nonce" gencodec:"required"` + Validators []byte `json:"validators" gencodec:"required"` } // field type overrides for gencodec diff --git a/eth/backend.go b/eth/backend.go index e8fb2bc6fd..fb3e29bfd5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -201,7 +201,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { return } if _, authorized := snap.Signers[eth.etherbase]; authorized { - if err := contracts.CreateTransactionSign(chainConfig, eth.txPool, eth.accountManager, block); err != nil { + if err := contracts.CreateTransactionSign(chainConfig, eth.txPool, eth.accountManager, block, chainDb); err != nil { log.Error("Fail to create tx sign for imported block", "error", err) return } @@ -251,6 +251,42 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } } } + + // Check m2 exists on chaindb. + // Get secrets and opening at epoc block checkpoint. + if number > 0 && number%contracts.EpocBlockRandomize == 0 { + var candidates [][]int64 + // Get signers from snapshot. + snap, err := c.GetSnapshot(eth.blockchain, chain.CurrentHeader()) + if err != nil { + log.Error("Fail to get snapshot for get secret and opening.", "error", err) + return err + } + signers := snap.Signers + + if len(signers) > 0 { + for addr := range signers { + random, err := contracts.GetRandomizeFromContract(client, addr) + if err != nil { + log.Error("Fail to get random m2 from contract.", "error", err) + } + if random != nil { + candidates = append(candidates, random) + } + } + } + + // Get randomize m2 list. + m2, err := contracts.GenM2FromRandomize(candidates) + if err != nil { + log.Error("Can not get m2 from randomize SC", "error", err) + } + if len(m2) > 0 { + header.Validators = contracts.BuildValidatorFromM2(m2) + } + log.Debug("list m2", "M2", m2) + } + return nil } } @@ -434,16 +470,6 @@ func (s *Ethereum) ValidateStaker() (bool, error) { return true, nil } -// Store new set of masternodes into local db -func (s *Ethereum) UpdateMasternodes(ms []XDPoS.Masternode) error { - // get snapshot from local db - if s.chainConfig.XDPoS == nil { - return errors.New("not XDPoS") - } - c := s.engine.(*XDPoS.XDPoS) - return c.UpdateMasternodes(s.blockchain, s.blockchain.CurrentHeader(), ms) -} - func (s *Ethereum) StartStaking(local bool) error { eb, err := s.Etherbase() if err != nil { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e06116baf2..2fc378b02f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -816,6 +816,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "timestamp": (*hexutil.Big)(head.Time), "transactionsRoot": head.TxHash, "receiptsRoot": head.ReceiptHash, + "validators": hexutil.Bytes(head.Validators), } if inclTx { diff --git a/miner/worker.go b/miner/worker.go index dc375b524d..4597323fdb 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -364,7 +364,7 @@ func (self *worker) wait() { } } // Send tx sign to smart contract blockSigners. - if err := contracts.CreateTransactionSign(self.config, self.eth.TxPool(), self.eth.AccountManager(), block); err != nil { + if err := contracts.CreateTransactionSign(self.config, self.eth.TxPool(), self.eth.AccountManager(), block, self.chainDb); err != nil { log.Error("Fail to create tx sign for signer", "error", "err") } }