Skip to content

Instantly share code, notes, and snippets.

@thanasik
Created March 27, 2017 20:43
Show Gist options
  • Save thanasik/3c7c1ab9d3ae736e5c63a2bbe4c47ac2 to your computer and use it in GitHub Desktop.
Save thanasik/3c7c1ab9d3ae736e5c63a2bbe4c47ac2 to your computer and use it in GitHub Desktop.
Sending an Ethereum transaction in Go
package main
import (
"fmt"
"math/big"
"context"
"io/ioutil"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
const (
KEYJSON_FILEDIR = `/home/someuser/.privatechain/keystore/UTC--2017-03-22T21-46-08.966825919Z--547d093502fe2760b0ca88edac5bcfa42e8d9e51`
SIGN_PASSPHRASE = `test`
KEYSTORE_DIR = `/home/someuser/.privatechain/keystore`
COINBASE_ADDR_HEX = `0x547d093502fe2760b0ca88edac5bcfa42e8d9e51`
ALTER_ADDR_HEX = `0x970e8128ab834e8eac17ab8e3812f010678cf791`
CHAIN_ID = 0 // From geth logs
)
func main() {
// Init a keystore
ks := keystore.NewKeyStore(
KEYSTORE_DIR,
keystore.LightScryptN,
keystore.LightScryptP)
fmt.Println()
// Create account definitions
fromAccDef := accounts.Account{
Address: common.HexToAddress(COINBASE_ADDR_HEX),
}
toAccDef := accounts.Account{
Address: common.HexToAddress(ALTER_ADDR_HEX),
}
// Find the signing account
signAcc, err := ks.Find(fromAccDef)
if err != nil {
fmt.Println("account keystore find error:")
panic(err)
}
fmt.Printf("account found: signAcc.addr=%s; signAcc.url=%s\n", signAcc.Address.String(), signAcc.URL)
fmt.Println()
// Unlock the signing account
errUnlock := ks.Unlock(signAcc, SIGN_PASSPHRASE)
if errUnlock != nil {
fmt.Println("account unlock error:")
panic(err)
}
fmt.Printf("account unlocked: signAcc.addr=%s; signAcc.url=%s\n", signAcc.Address.String(), signAcc.URL)
fmt.Println()
// Construct the transaction
tx := types.NewTransaction(
0x0,
toAccDef.Address,
new(big.Int),
new(big.Int),
new(big.Int),
[]byte(`cooldatahere`))
// Open the account key file
keyJson, readErr := ioutil.ReadFile(KEYJSON_FILEDIR)
if readErr != nil {
fmt.Println("key json read error:")
panic(readErr)
}
// Get the private key
keyWrapper, keyErr := keystore.DecryptKey(keyJson, SIGN_PASSPHRASE)
if keyErr != nil {
fmt.Println("key decrypt error:")
panic(keyErr)
}
fmt.Printf("key extracted: addr=%s", keyWrapper.Address.String())
// Define signer and chain id
// chainID := big.NewInt(CHAIN_ID)
// signer := types.NewEIP155Signer(chainID)
signer := types.HomesteadSigner{}
// Sign the transaction signature with the private key
signature, signatureErr := crypto.Sign(tx.SigHash(signer).Bytes(), keyWrapper.PrivateKey)
if signatureErr != nil {
fmt.Println("signature create error:")
panic(signatureErr)
}
signedTx, signErr := tx.WithSignature(signer, signature)
if signErr != nil {
fmt.Println("signer with signature error:")
panic(signErr)
}
// Connect the client
client, err := ethclient.Dial("http://localhost:8000") // 8000=geth RPC port
if err != nil {
fmt.Println("client connection error:")
panic(err)
}
fmt.Println("client connected")
fmt.Println()
// Send the transaction to the network
txErr := client.SendTransaction(context.Background(), signedTx)
if txErr != nil {
fmt.Println("send tx error:")
panic(txErr)
}
fmt.Printf("send success tx.hash=%s\n", signedTx.Hash().String())
}
@clowdrgn
Copy link

thanks for your help! what I need is that signature, signatureErr := crypto.Sign(common.FromHex("0x..."), keyWrapper.PrivateKey) . it's painful since I try to use eth.sign() to pass solidity's ecovery(msg,v,r,s) method.It always been different.This is very helpful. thank you angin!

@thanasik
Copy link
Author

Glad it helped you out!

@huahuayu
Copy link

huahuayu commented Aug 23, 2018

hi @karysto, line 91 get two warnings:
Unresolved reference 'SigHash'
Unresolved reference 'Bytes'
please check

change line 91 to:
signature, signatureErr := crypto.Sign(signer.Hash(tx).Bytes(), keyWrapper.PrivateKey) will do the trick

@huahuayu
Copy link

Hi @karysto the geth rpc response (result field) is hex data, how to transform it into readable data?

@AndreiD
Copy link

AndreiD commented Oct 21, 2018

here's my variant based on the above code.

package you_package

import (
	"context"
	"io/ioutil"
	"math/big"
	"net/http"
	"strings"

	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/accounts/keystore"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
)

// maybe move them to config ?
const (
	// KeyJSONFile the coinbase account
	KeyJSONFile = "./account/account.json"
	// WalletPasswordFile password file
	WalletPasswordFile = "./account/password.txt"
	// KeystoreDir keystore dir
	KeystoreDir = "./account"
	// CoinBaseAddress address
	CoinBaseAddress = "0x5d924b2d34643b4eb7d4291fdcb17236963f040f"
	// From geth logs
	ChainID = 1555
)



	address := 0x212312312321  //this is the address that receives it

	ks := keystore.NewKeyStore(
		KeystoreDir,
		keystore.LightScryptN,
		keystore.LightScryptP)

	// Create account definitions
	fromAccDef := accounts.Account{
		Address: common.HexToAddress(CoinBaseAddress),
	}

	// Find the signing account
	signAcc, err := ks.Find(fromAccDef)
	//check error

	b, err := ioutil.ReadFile(WalletPasswordFile)
	//check error
	signPassphrase := strings.TrimRight(string(b), "\r\n")

	// Unlock the signing account
	err = ks.Unlock(signAcc, signPassphrase)
	//check error

	nonce, err := client.PendingNonceAt(context.Background(), fromAccDef.Address)
	//check error
	value := big.NewInt(100000000000000000) // in wei (0.1 eth)
	gasLimit := uint64(21000)               // in units
	gasPrice := big.NewInt(20000000)
	
	var data []byte //nil
	toAddress := common.HexToAddress(address)
	tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)

	// Open the account key file
	keyJSON, err := ioutil.ReadFile(KeyJSONFile)
	//check error

	// Get the private key
	keyWrapper, keyErr := keystore.DecryptKey(keyJSON, signPassphrase)
	//check error

	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(big.NewInt(ChainID)), keyWrapper.PrivateKey)
	//check error

	// Final Step
	txErr := client.SendTransaction(context.Background(), signedTx)
	//check error
	 you now have the hash in ->>>> signedTx.Hash().String()}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment