main.go
主要是构造一个交易
package main
import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"os"
"strconv"
"time"
"github.com/Luxurioust/excelize"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/multivactech/MultiVAC/rlp"
)
//if isTest is true,if will send transaction to respropsten
const isTest = true
//Set your private key
var myPrikey string = ""
//Set your address
var myAccount string = "0x6Ed230dB6803551917F268a55dB4708c0AbEd7F3"
//Set constarct Address
var constractAddr = "0x85437e56772a863d5a8661938156b6dce5a01652"
var gasLimit uint64 = 300000
var gasPrice int64 = 50000000000
var apikey = "DM692A9ZMX7WRFRNREY1ES9J8WSIXRA4AR"
const MAXBIT = 64
const accountsPath = "./test.xlsx"
const txsPath = "./txFilePath.txt"
var funcName = "a9059cbb"
func main() {
count := queryTransCount(myAccount, isTest)
//var count uint64 = 1489835
var chainId int64
allAccount := getAllAddr(accountsPath)
//var count uint64 = 0
fileName := txsPath
dstFile, err := os.Create(fileName)
if err != nil {
fmt.Println(err.Error())
return
}
defer dstFile.Close()
for i, account := range allAccount {
fmt.Printf("Start transform token to the %d's account.\n", i+1)
if !isValidAddr(account.account) {
continue
}
trans := newTrans(count, constractAddr, 0, gasLimit, gasPrice, getInputData(account.account, int64(account.value)))
privateKey := generatePrikey(myPrikey)
if isTest {
chainId = 3
} else {
chainId = 1
}
signType := setSignType(chainId)
signData := signTx(trans, signType, privateKey)
if signData == nil {
continue
}
rlpData, err := rlp.EncodeToBytes(signData)
if err != nil {
fmt.Printf("encode data err:%s\n", err)
}
rawTrans := hex.EncodeToString(rlpData)
//fmt.Println(rawTrans)
time.Sleep(time.Second * 10)
if i != 0 && i%10 == 0 {
time.Sleep(time.Second * 30)
}
txHash, err := sendRawTrans(i, rawTrans, isTest)
if err == nil {
dstFile.WriteString(strconv.Itoa(i) + ":" + txHash + "\n")
} else {
writeErrorToFile(i, account.account, txHash)
}
count++
}
}
func isValidAddr(addr string) bool {
if _, err := hex.DecodeString(addr); err != nil {
fmt.Println("hex decode ", err)
return false
} else if len(addr) != 40 {
fmt.Println("Length error!")
fmt.Println(len(addr))
return false
}
return true
}
//这里我的账户和对应转账金额是保存在excel中的,你可以根据你的需要来更改
func getAllAddr(path string) []accountAndValue {
var allAccount = make([]accountAndValue, 0)
xlsx, err := excelize.OpenFile(path)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Get all the rows in a sheet.
rows, _ := xlsx.GetRows("sheet1")
for _, row := range rows {
var res [2]string
for i, colCell := range row {
if colCell == "" {
break
}
res[i] = colCell
}
if res[0] == "" {
return allAccount
}
account := res[0][2:]
value, err := strconv.Atoi(res[1])
if err != nil {
fmt.Println("value to int err:", err)
continue
}
av := accountAndValue{
account: account,
value: value,
}
allAccount = append(allAccount, av)
}
return allAccount
}
//这里是因为我的原始数据中的金额是gwei单位,所以还需要加18个零
//另外原始数据是10进制的,需要转换为16进制
func getInputData(to string, value int64) string {
strvalue := strconv.Itoa(int(value)) + "000000000000000000"
bigValue, _ := new(big.Int).SetString(strvalue, 10)
str := fmt.Sprintf("%x", bigValue)
inputData := funcName + to64Bit(to) + to64Bit(str)
return inputData
}
//构造一个交易
func newTrans(nonce uint64, to string, amount int64, gasLimit uint64, gasPrice int64, input string) *types.Transaction {
return types.NewTransaction(nonce, common.HexToAddress(to), big.NewInt(amount), gasLimit, big.NewInt(gasPrice), common.Hex2Bytes(input))
}
func generatePrikey(pri string) *ecdsa.PrivateKey {
pr, _ := crypto.HexToECDSA(pri)
return pr
}
func setSignType(index int64) types.Signer {
return types.NewEIP155Signer(big.NewInt(index))
}
func signTx(trans *types.Transaction, signType types.Signer, privateKey *ecdsa.PrivateKey) *types.Transaction {
signData, err := types.SignTx(trans, signType, privateKey)
if err != nil {
fmt.Println("sign transaction err:", err)
return nil
} else {
return signData
}
}
func to64Bit(data string) string {
length := len(data)
for i := length; i < MAXBIT; i++ {
data = "0" + data
}
return data
}
//发送过程中,如果出现错误会将错误信息记录在这里
func writeErrorToFile(index int, address string, errMsg string) error {
dstFile, err := os.OpenFile(failedTransPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Printf("open %s error:%s\n", failedTransPath, err)
return err
}
defer dstFile.Close()
dstFile.WriteString(strconv.Itoa(index) + " account : " + address + "\n" + errMsg + "\n\n")
return nil
}
主要负责发送交易到接口
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strconv"
)
type ethQueryCount struct {
Jsonrpc string `json:"jsonrpc"`
Result string `json:"result"`
Id string `json:"id"`
}
type ethQueryStatus struct {
Status string `json:"status"`
Message string `json:"message"`
TxResult ethTxResult `json:"result"`
}
type ethTxResult struct {
IsError string `json:"iserror"`
ErrDescription string `json:"errdescription"`
}
type accountAndValue struct {
account string
value int
}
type ethTxHashRes struct {
TxHash string `json:"result"`
Error ethError `json:"error"`
}
type ethError struct {
Message string `json:"message"`
Code int `json:"code:"`
}
//const sendRawTransUrl = "https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=%s&apikey=%s"
const TestQueryTxStatusUrl = "https://api-ropsten.etherscan.io/api?module=transaction&action=getstatus&txhash=%s&apikey=%s"
const TestSendRawTransUrl = "https://api-ropsten.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=%s&apikey=%s"
const TestQueryTransCountUrl = "https://api-ropsten.etherscan.io/api?module=proxy&action=eth_getTransactionCount&address=%s&tag=latest&apikey=%s"
const QueryTxStatusUrl = "https://api.etherscan.io/api?module=transaction&action=getstatus&txhash=%s&apikey=%s"
const SendRawTransUrl = "https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=%s&apikey=%s"
const QueryTransCountUrl = "https://api.etherscan.io/api?module=proxy&action=eth_getTransactionCount&address=%s&tag=latest&apikey=%s"
const failedTransPath = "./errorTrans.txt"
//查询账户的count
func queryTransCount(account string, isTest bool) uint64 {
var ethqueryRes ethQueryCount
var count int64
var err error
var url string
if isTest {
url = fmt.Sprintf(TestQueryTransCountUrl, account, apikey)
} else {
url = fmt.Sprintf(QueryTransCountUrl, account, apikey)
}
fmt.Println("queryTransCount Url:", url)
request, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("queryTransCount err:", err)
return 0
}
if resp, err := http.DefaultClient.Do(request); err == nil {
respBody, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(respBody, ðqueryRes)
fmt.Println("queryTransCount result: ", string(respBody))
count, err = strconv.ParseInt(ethqueryRes.Result[2:], 16, 64)
if err != nil {
fmt.Println("string to int64 error:", err)
} else {
fmt.Printf("count of the account %s ,count is :%d\n", account, count)
}
}
return uint64(count)
}
//发送交易
func sendRawTrans(index int, rawTrans string, isTest bool) (string, error) {
var resError ethError
txhashRes := ethTxHashRes{
Error: resError,
}
var url string
if isTest {
url = fmt.Sprintf(TestSendRawTransUrl, rawTrans, apikey)
} else {
url = fmt.Sprintf(SendRawTransUrl, rawTrans, apikey)
}
//fmt.Println(url)
request, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("sendRawTransaction err:", err)
return "", nil
}
if resp, err := http.DefaultClient.Do(request); err == nil {
respBody, _ := ioutil.ReadAll(resp.Body)
//fmt.Println("sendRawTransaction result:", string(respBody))
json.Unmarshal(respBody, &txhashRes)
if txhashRes.Error.Message != "" {
return txhashRes.Error.Message, errors.New("err")
} else {
return txhashRes.TxHash, nil
}
} else {
fmt.Println("resp error when sendRawTrans,err:", err)
return "", err
}
}