mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
582 lines
19 KiB
Go
582 lines
19 KiB
Go
// Copyright (c) 2018 XDPoSChain
|
|
//
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
package contracts
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
cryptoRand "crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"math/rand"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/XinFinOrg/XDPoSChain/accounts"
|
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
|
"github.com/XinFinOrg/XDPoSChain/common"
|
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
|
"github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract"
|
|
randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract"
|
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
|
"github.com/XinFinOrg/XDPoSChain/log"
|
|
"github.com/XinFinOrg/XDPoSChain/params"
|
|
)
|
|
|
|
const (
|
|
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 {
|
|
Sign uint64 `json:"sign"`
|
|
Reward *big.Int `json:"reward"`
|
|
}
|
|
|
|
var TxSignMu sync.RWMutex
|
|
|
|
// Send tx sign for block number to smart contract blockSigner.
|
|
func CreateTransactionSign(chainConfig *params.ChainConfig, pool *txpool.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database, eb common.Address) error {
|
|
TxSignMu.Lock()
|
|
defer TxSignMu.Unlock()
|
|
if chainConfig.XDPoS != nil {
|
|
// Find active account.
|
|
account := accounts.Account{}
|
|
var wallet accounts.Wallet
|
|
etherbaseAccount := accounts.Account{
|
|
Address: eb,
|
|
URL: accounts.URL{},
|
|
}
|
|
if wallets := manager.Wallets(); len(wallets) > 0 {
|
|
if w, err := manager.Find(etherbaseAccount); err == nil && w != nil {
|
|
wallet = w
|
|
account = etherbaseAccount
|
|
} else {
|
|
wallet = wallets[0]
|
|
if accts := wallets[0].Accounts(); len(accts) > 0 {
|
|
account = accts[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create and send tx to smart contract for sign validate block.
|
|
nonce := pool.Nonce(account.Address)
|
|
tx := CreateTxSign(block.Number(), block.Hash(), nonce, common.BlockSignersBinary)
|
|
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, true)
|
|
if err != nil {
|
|
log.Error("Fail to add tx sign to local pool.", "error", err, "number", block.NumberU64(), "hash", block.Hash().Hex(), "from", account.Address, "nonce", nonce)
|
|
return err
|
|
}
|
|
|
|
// Create secret tx.
|
|
blockNumber := block.Number().Uint64()
|
|
checkNumber := blockNumber % chainConfig.XDPoS.Epoch
|
|
// Generate random private key and save into chaindb.
|
|
exist := rawdb.HasRandomize(chainDb)
|
|
|
|
// 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.RandomizeSMCBinary, 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, true)
|
|
if err != nil {
|
|
log.Error("Fail to add tx secret to local pool.", "error", err, "number", block.NumberU64(), "hash", block.Hash().Hex(), "from", account.Address, "nonce", nonce)
|
|
return err
|
|
}
|
|
|
|
// Put randomize key into chainDb.
|
|
rawdb.WriteRandomize(chainDb, randomizeKeyValue)
|
|
}
|
|
|
|
// Set opening for randomize.
|
|
if exist && checkNumber > 0 && common.EpocBlockOpening <= checkNumber && common.EpocBlockRandomize >= checkNumber {
|
|
randomizeKeyValue, err := rawdb.ReadRandomize(chainDb)
|
|
if err != nil {
|
|
log.Error("Fail to get randomize key from state db.", "error", err)
|
|
return err
|
|
}
|
|
|
|
tx, err := BuildTxOpeningRandomize(nonce+1, common.RandomizeSMCBinary, 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, true)
|
|
if err != nil {
|
|
log.Error("Fail to add tx opening to local pool.", "error", err, "number", block.NumberU64(), "hash", block.Hash().Hex(), "from", account.Address, "nonce", nonce)
|
|
return err
|
|
}
|
|
|
|
// Clear randomize key in state db.
|
|
rawdb.DeleteRandomize(chainDb)
|
|
}
|
|
}
|
|
|
|
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), 200000, 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), 200000, big.NewInt(0), inputData)
|
|
|
|
return tx, nil
|
|
}
|
|
|
|
// Get signers signed for blockNumber from blockSigner contract.
|
|
func GetSignersFromContract(statedb *state.StateDB, block *types.Block) ([]common.Address, error) {
|
|
return statedb.GetSigners(block), nil
|
|
}
|
|
|
|
// Get signers signed for blockNumber from blockSigner contract.
|
|
func GetSignersByExecutingEVM(addrBlockSigner common.Address, client bind.ContractBackend, blockHash common.Hash) ([]common.Address, error) {
|
|
blockSigner, err := contract.NewBlockSigner(addrBlockSigner, client)
|
|
if err != nil {
|
|
log.Error("Fail get instance of blockSigner", "error", err)
|
|
return nil, err
|
|
}
|
|
opts := new(bind.CallOpts)
|
|
addrs, err := blockSigner.GetSigners(opts, blockHash)
|
|
if err != nil {
|
|
log.Error("Fail get block signers", "error", err)
|
|
return nil, err
|
|
}
|
|
return addrs, nil
|
|
}
|
|
|
|
// Get random from randomize contract.
|
|
func GetRandomizeFromContract(client bind.ContractBackend, addrMasternode common.Address) (int64, error) {
|
|
randomize, err := randomizeContract.NewXDCRandomize(common.RandomizeSMCBinary, 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)
|
|
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 {
|
|
validatorBytes := make([]byte, 0, len(listM2)*utils.M2ByteLength)
|
|
for _, numberM2 := range listM2 {
|
|
// Convert number to byte.
|
|
m2Byte := common.LeftPadBytes(fmt.Appendf(nil, "%d", numberM2), utils.M2ByteLength)
|
|
validatorBytes = append(validatorBytes, m2Byte...)
|
|
}
|
|
|
|
return validatorBytes
|
|
}
|
|
|
|
// Decode validator hex string.
|
|
func DecodeValidatorsHexData(validatorsStr string) ([]int64, error) {
|
|
validatorsByte, err := hexutil.Decode(validatorsStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return utils.ExtractValidatorsFromBytes(validatorsByte)
|
|
}
|
|
|
|
// 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)
|
|
return -1, err
|
|
}
|
|
random = int64(intNumber)
|
|
}
|
|
}
|
|
}
|
|
|
|
return random, nil
|
|
}
|
|
|
|
// Calculate reward for reward checkpoint.
|
|
func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header *types.Header, rCheckpoint uint64, totalSigner *uint64) (map[common.Address]*RewardLog, error) {
|
|
// Not reward for singer of genesis block and only calculate reward at checkpoint block.
|
|
number := header.Number.Uint64()
|
|
prevCheckpoint := number - (rCheckpoint * 2)
|
|
startBlockNumber := prevCheckpoint + 1
|
|
endBlockNumber := startBlockNumber + rCheckpoint - 1
|
|
signers := make(map[common.Address]*RewardLog)
|
|
mapBlkHash := map[uint64]common.Hash{}
|
|
|
|
data := make(map[common.Hash][]common.Address)
|
|
for i := prevCheckpoint + (rCheckpoint * 2) - 1; i >= startBlockNumber; i-- {
|
|
header = chain.GetHeader(header.ParentHash, i)
|
|
mapBlkHash[i] = header.Hash()
|
|
signingTxs, ok := c.GetCachedSigningTxs(header.Hash())
|
|
if !ok {
|
|
log.Debug("Failed get from cached", "hash", header.Hash(), "number", i)
|
|
block := chain.GetBlock(header.Hash(), i)
|
|
txs := block.Transactions()
|
|
if !chain.Config().IsTIPSigning(header.Number) {
|
|
receipts := rawdb.ReadRawReceipts(c.GetDb(), header.Hash(), i)
|
|
signingTxs = c.CacheNoneTIPSigningTxs(header, txs, receipts)
|
|
} else {
|
|
signingTxs = c.CacheSigningTxs(header.Hash(), txs)
|
|
}
|
|
}
|
|
for _, tx := range signingTxs {
|
|
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
|
from := *tx.From()
|
|
data[blkHash] = append(data[blkHash], from)
|
|
}
|
|
}
|
|
header = chain.GetHeader(header.ParentHash, prevCheckpoint)
|
|
masternodes := c.GetMasternodesFromCheckpointHeader(header)
|
|
|
|
for i := startBlockNumber; i <= endBlockNumber; i++ {
|
|
if i%common.MergeSignRange == 0 || !chain.Config().IsTIP2019(big.NewInt(int64(i))) {
|
|
addrs := data[mapBlkHash[i]]
|
|
// Filter duplicate address.
|
|
if len(addrs) > 0 {
|
|
addrSigners := make(map[common.Address]bool)
|
|
for _, masternode := range masternodes {
|
|
for _, addr := range addrs {
|
|
if addr == masternode {
|
|
if _, ok := addrSigners[addr]; !ok {
|
|
addrSigners[addr] = true
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
log.Info("Signers data", "totalSigner", totalSigner, "totalReward", chainReward)
|
|
for addr, signer := range signers {
|
|
log.Debug("Signer reward", "signer", addr, "sign", signer.Sign, "reward", signer.Reward)
|
|
}
|
|
|
|
return resultSigners, nil
|
|
}
|
|
|
|
// Get candidate owner by address.
|
|
func GetCandidatesOwnerBySigner(statedb *state.StateDB, signerAddr common.Address) common.Address {
|
|
return statedb.GetCandidateOwner(signerAddr)
|
|
}
|
|
|
|
func CalculateRewardForHolders(foundationWalletAddr common.Address, state *state.StateDB, signer common.Address, calcReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) {
|
|
rewards, err := GetRewardBalancesRate(foundationWalletAddr, state, signer, calcReward, blockNumber)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return rewards, nil
|
|
}
|
|
|
|
func GetRewardBalancesRate(foundationWalletAddr common.Address, statedb *state.StateDB, masterAddr common.Address, totalReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) {
|
|
owner := GetCandidatesOwnerBySigner(statedb, 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.
|
|
voters := statedb.GetVoters(masterAddr)
|
|
|
|
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 {
|
|
if _, ok := voterCaps[voteAddr]; ok && common.TIP2019Block.Uint64() <= blockNumber {
|
|
continue
|
|
}
|
|
voterCap := statedb.GetVoterCap(masterAddr, voteAddr)
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foundationReward := new(big.Int).Mul(totalReward, new(big.Int).SetInt64(common.RewardFoundationPercent))
|
|
foundationReward = new(big.Int).Div(foundationReward, new(big.Int).SetInt64(100))
|
|
|
|
if blockNumber >= common.TIPUpgradeReward.Uint64() && balances[foundationWalletAddr] != nil {
|
|
balances[foundationWalletAddr].Add(balances[foundationWalletAddr], foundationReward)
|
|
} else {
|
|
balances[foundationWalletAddr] = foundationReward
|
|
}
|
|
|
|
jsonHolders, err := json.Marshal(balances)
|
|
if err != nil {
|
|
log.Error("Fail to parse json holders", "error", err)
|
|
return nil, err
|
|
}
|
|
log.Trace("Holders reward", "holders", string(jsonHolders), "masternode", masterAddr)
|
|
|
|
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 {
|
|
log.Error("Fail to encrypt", "err", err)
|
|
return ""
|
|
}
|
|
|
|
// 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 {
|
|
log.Error("Fail to encrypt iv", "err", err)
|
|
return ""
|
|
}
|
|
|
|
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 {
|
|
log.Error("Fail to decrypt", "err", err)
|
|
return ""
|
|
}
|
|
|
|
// 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 {
|
|
log.Error("ciphertext too short")
|
|
return ""
|
|
}
|
|
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 string(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
|
|
}
|
|
}
|