Skip to content

Commit

Permalink
Dev support decimal (#7)
Browse files Browse the repository at this point in the history
* support decimal for transfer

* more detail log
  • Loading branch information
wangdayong228 authored Dec 8, 2020
1 parent 233e3e0 commit 878996e
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 52 deletions.
3 changes: 2 additions & 1 deletion transfer/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package transfer

import (
"github.com/Conflux-Chain/go-conflux-sdk/types"
"github.com/shopspring/decimal"
)

// Receiver represents transfer receiver infomation
type Receiver struct {
Address types.Address
Weight uint
Weight decimal.Decimal
}
137 changes: 86 additions & 51 deletions transfer/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
clientRpc "github.com/Conflux-Chain/go-conflux-sdk/rpc"
"github.com/Conflux-Chain/go-conflux-sdk/types"

"github.com/shopspring/decimal"

"github.com/spf13/cobra"
)

Expand All @@ -33,7 +35,7 @@ var (

// command flags
receiverListFile string
receiveNumber uint
receiveNumber decimal.Decimal
// from string
perBatchNum uint
)
Expand All @@ -46,10 +48,18 @@ func init() {
rootCmd.PersistentFlags().StringVar(&receiverListFile, "receivers", "", "receiver list file path")
rootCmd.MarkPersistentFlagRequired("receivers")

rootCmd.PersistentFlags().UintVar(&receiveNumber, "number", 1, "send value in CFX")
receiveNumberInStr := ""
rootCmd.PersistentFlags().StringVar(&receiveNumberInStr, "number", "1", "send value in CFX")
rootCmd.MarkPersistentFlagRequired("number")

rootCmd.PersistentFlags().UintVar(&perBatchNum, "batch", 100, "send tx number per batch")
rootCmd.PersistentFlags().UintVar(&perBatchNum, "batch", 1000, "send tx number per batch")
formatReceiverNumber(receiveNumberInStr)
}

func formatReceiverNumber(receiveNumberInStr string) {
var err error
receiveNumber, err = decimal.NewFromString(receiveNumberInStr)
util.OsExitIfErr(err, "receiveNumber %v is not a number", receiveNumberInStr)
}

// SetParent sets parent command
Expand All @@ -66,50 +76,41 @@ func doTransfers(cmd *cobra.Command, args []string) {
}()

receiverInfos := mustParseInput()
client, am, from, lastPoint, nonce := initialEnviorment()
checkBalance(client, from, receiverInfos)
client, am, lastPoint, from, nonce, chainID, epochHeight := initialEnviorment()

fmt.Println("===== Check if balance enough =====")
checkBalance(client, warnFs, from, receiverInfos)

sendCount := uint(0)
failCount := uint(0)
rpcBatchElems := []clientRpc.BatchElem{}
tx := &types.UnsignedTransaction{}
var e error

fmt.Println("===== Start batch transfer =====")
for i, v := range receiverInfos {
// composite tx
if i == 0 {
tx, e = client.CreateUnsignedTransaction(from, v.Address, types.NewBigInt(0), nil)
util.OsExitIfErr(e, "create unsigned tx error")
}
if i <= lastPoint {
continue
}

if types.NormalAddress != v.Address.GetAddressType() {
if failCount == 0 {
warnFs.WriteString("=======invalid addresses==========\n")
}
msg := fmt.Sprintf("invalid address: %v\n", v.Address)
msg := fmt.Sprintf("%v. *****Invalid address: %v", i+1, v.Address)
fmt.Println(msg)
_, err := warnFs.WriteString(msg)
if err != nil {
fmt.Printf("write warn msg \" %v \" fail", msg)
fmt.Printf("Fail to write warn msg \" %v \"\n", msg)
}
failCount++
continue
}

tx.To = &v.Address
rawValue := big.NewInt(1).Mul(big.NewInt(int64(receiveNumber*v.Weight)), big.NewInt(1e18))
tx.Value = types.NewBigIntByRaw(rawValue)
tx.Nonce = types.NewBigIntByRaw(nonce)
tx.GasPrice = types.NewBigIntByRaw(account.MustParsePrice())
tx.Gas = defaultGasLimit
if i <= lastPoint {
continue
}

tx := createTx(from, v, nonce, chainID, epochHeight)

// sign
encoded, e := am.SignTransaction(*tx)
util.OsExitIfErr(e, "Failed to sign transaction")
fmt.Printf("sign to %v with value %v CFX done\n", tx.To, receiveNumber*v.Weight)
util.OsExitIfErr(e, "Failed to sign transaction %+v", tx)
fmt.Printf("%v. Sign to %v with value %v done\n", i+1, tx.To, util.DisplayValueWithUnit(tx.Value.ToInt()))

// push to batch item array
batchElemResult := types.Hash("")
Expand All @@ -123,17 +124,17 @@ func doTransfers(cmd *cobra.Command, args []string) {
if sendCount == perBatchNum || i == len(receiverInfos)-1 {
// batch send
e := client.BatchCallRPC(rpcBatchElems)
util.OsExitIfErr(e, "batch send error")
util.OsExitIfErr(e, "Batch send error")

// save record
ioutil.WriteFile(resultPath, []byte(strconv.Itoa(i)), 0777)

// wait last packed
lastHash := rpcBatchElems[len(rpcBatchElems)-1].Result.(*types.Hash)

fmt.Printf("batch send %v tx, total send %v done, failed %v, wait last be executed: %v\n", len(rpcBatchElems), uint(i)+1-failCount, failCount, lastHash)
fmt.Printf("Batch send %v tx, total send %v done, failed %v, wait last be executed: %v\n", len(rpcBatchElems), uint(i)+1-failCount, failCount, lastHash)
_, e = client.WaitForTransationReceipt(*lastHash, time.Second)
util.OsExitIfErr(e, "failed to get result of tx %+v", tx)
util.OsExitIfErr(e, "Fail to get result of tx %+v", tx)

// reset count and batch elem result
rpcBatchElems = []clientRpc.BatchElem{}
Expand All @@ -143,16 +144,38 @@ func doTransfers(cmd *cobra.Command, args []string) {
nonce = nonce.Add(nonce, big.NewInt(1))
}

e = os.Remove(resultPath)
util.OsExitIfErr(e, "remove result file error.")
e := os.Remove(resultPath)
util.OsExitIfErr(e, "Remove result file error.")

if failCount == 0 {
fmt.Printf("fail count: %v\n", failCount)
e = os.Remove(warnPath)
util.OsExitIfErr(e, "remove result file error.")
util.OsExitIfErr(e, "Remove result file error.")
}

fmt.Printf("transfer done\n")
fmt.Printf("===== Transfer done! =====\n")
}

func createTx(from types.Address, receiver Receiver, nonce *big.Int, chainID uint, epochHeight uint64) *types.UnsignedTransaction {
tx := &types.UnsignedTransaction{}

tx.From = &from
tx.Gas = defaultGasLimit
tx.GasPrice = types.NewBigIntByRaw(account.MustParsePrice())
tx.StorageLimit = types.NewUint64(0)
tx.ChainID = types.NewUint(chainID)
tx.EpochHeight = types.NewUint64(epochHeight)

tx.To = &receiver.Address
tx.Nonce = types.NewBigIntByRaw(nonce)

valueInBigInt := calcValue(receiveNumber, receiver.Weight)
tx.Value = types.NewBigIntByRaw(valueInBigInt)

return tx
}

func calcValue(numberPerTime decimal.Decimal, weigh decimal.Decimal) *big.Int {
return receiveNumber.Mul(weigh).Mul(decimal.NewFromInt(1e18)).BigInt()
}

func mustParseInput() []Receiver {
Expand All @@ -163,7 +186,7 @@ func mustParseInput() []Receiver {
// parse to struct
lines := strings.Split(string(content), "\n")
receiverInfos := []Receiver{}
for _, v := range lines {
for i, v := range lines {
v = strings.Replace(v, "\t", " ", -1)
v = strings.Replace(v, ",", " ", -1)
items := strings.Fields(v)
Expand All @@ -172,23 +195,23 @@ func mustParseInput() []Receiver {
}

if len(items) != 2 {
util.OsExit("elems length of %#v is %v not equal to 2\n", v, len(items))
util.OsExit("Line %v: %#v column number is %v, which shoule be 2\n", i, v, len(items))
}

weight, err := strconv.Atoi(items[1])
util.OsExitIfErr(err, "parse %v to int error", weight)
weight, err := decimal.NewFromString(items[1])
util.OsExitIfErr(err, "Parse %v to int error", weight)

info := Receiver{
Address: types.Address(items[0]),
Weight: uint(weight),
Weight: weight,
}
receiverInfos = append(receiverInfos, info)
}
fmt.Printf("receiver list count :%+v\n", len(receiverInfos))
fmt.Printf("Receiver list count :%+v\n", len(receiverInfos))
return receiverInfos
}

func initialEnviorment() (client *sdk.Client, am *sdk.AccountManager, from types.Address, lastPoint int, nonce *big.Int) {
func initialEnviorment() (client *sdk.Client, am *sdk.AccountManager, lastPoint int, from types.Address, nonce *big.Int, chainID uint, epochHeight uint64) {

am = account.DefaultAccountManager
client = rpc.MustCreateClientWithRetry(100)
Expand All @@ -198,13 +221,22 @@ func initialEnviorment() (client *sdk.Client, am *sdk.AccountManager, from types

password := account.MustInputPassword("Enter password: ")
am.Unlock(from, password)
fmt.Println()

// get inital Nonce
nonce, e := client.GetNextNonce(from)
util.OsExitIfErr(e, "get nonce of from %v", from)
util.OsExitIfErr(e, "Fail to get nonce of from %v", from)

status, err := client.GetStatus()
util.OsExitIfErr(err, "Fail to get status")
chainID = uint(*status.ChainID)

epoch, err := client.GetEpochNumber(types.EpochLatestState)
util.OsExitIfErr(err, "Fail to get epoch")
epochHeight = epoch.Uint64()

lastPointStr, e := ioutil.ReadFile(resultPath)
util.OsExitIfErr(e, "read result content error")
util.OsExitIfErr(e, "Fail to read result content")

if len(lastPointStr) > 0 {
lastPoint, e = strconv.Atoi(string(lastPointStr))
Expand All @@ -216,35 +248,38 @@ func initialEnviorment() (client *sdk.Client, am *sdk.AccountManager, from types

func creatRecordFiles() (resultFs, warnFs *os.File) {
resultFs, e := os.OpenFile(resultPath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0777)
util.OsExitIfErr(e, "failed to create file")
util.OsExitIfErr(e, "Failed to create file")
// defer resultFs.Close()

warnFs, e = os.OpenFile(warnPath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0777)
util.OsExitIfErr(e, "failed to create file")
util.OsExitIfErr(e, "Failed to create file")
// defer warnFs.Close()
return
}

func checkBalance(client *sdk.Client, from types.Address, receivers []Receiver) {
func checkBalance(client *sdk.Client, warnFs *os.File, from types.Address, receivers []Receiver) {
balance, err := client.GetBalance(from)
util.OsExitIfErr(err, "failed to get balance")
util.OsExitIfErr(err, "Failed to get balance")

need := big.NewInt(0)
for _, v := range receivers {
receiverNeed := big.NewInt(1).Mul(big.NewInt(int64(v.Weight*receiveNumber)), big.NewInt(1e18))
// receiverNeed := big.NewInt(1).Mul(big.NewInt(int64(v.Weight*receiveNumber)), big.NewInt(1e18))
receiverNeed := calcValue(receiveNumber, v.Weight)
gasFee := big.NewInt(1).Mul(defaultGasLimit.ToInt(), account.MustParsePrice())
need = need.Add(need, receiverNeed)
need = need.Add(need, gasFee)
}

if balance.Cmp(need) < 0 {
lastPointStr, e := ioutil.ReadFile(resultPath)
util.OsExitIfErr(e, "read result content error")
util.OsExitIfErr(e, "Read result content error")

if len(lastPointStr) == 0 {
os.Remove(resultPath)
}
util.OsExit("out of balance, need %v, has %v", util.DisplayValueWithUnit(need), util.DisplayValueWithUnit(balance))
msg := fmt.Sprintf("Out of balance, need %v, has %v", util.DisplayValueWithUnit(need), util.DisplayValueWithUnit(balance))
warnFs.WriteString(msg)
util.OsExit(msg)
}
fmt.Printf("balance is enough, need %v, has %v\n", util.DisplayValueWithUnit(need), util.DisplayValueWithUnit(balance))
fmt.Printf("Balance is enough, need %v, has %v\n", util.DisplayValueWithUnit(need), util.DisplayValueWithUnit(balance))
}

0 comments on commit 878996e

Please sign in to comment.