Introduction
In this tutorial, we'll walk through how to use tests to ensure the integrity of Ethereum data extraction in a Go application. We'll be using the Go-Ethereum client to retrieve block and transaction data and using the testify package for our tests.
Prerequisites
- Basic understanding of Go programming language.
- Go installed on your system.
- geth (Go-Ethereum) client installed.
- testify package installed (go get github.com/stretchr/testify).
Project Setup
- Create a Go Project:
mkdir ethereum-data-extraction
cd ethereum-data-extraction
go mod init ethereum-data-extraction
- Install Dependencies:
go get github.com/ethereum/go-ethereum
go get github.com/stretchr/testify
Code for Data Extraction
Block Data Extraction
Let's start with the function to extract block data.
package blockchain
import (
"context"
"log"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
)
type BlockData struct {
Number *big.Int
Hash string
ParentHash string
Nonce uint64
Sha3Uncles string
Miner string
Difficulty *big.Int
ExtraData string
Size uint64
GasLimit uint64
GasUsed uint64
Timestamp uint64
Transactions []string
}
func getClient() (*ethclient.Client, error) {
client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID")
if err != nil {
return nil, err
}
return client, nil
}
func GetBlockData(blockNumber *big.Int) (*BlockData, error) {
client, err := getClient()
if err != nil {
log.Fatal("Failed to get client:", err)
}
block, err := client.BlockByNumber(context.Background(), blockNumber)
if err != nil {
log.Fatalf("Failed to retrieve the block: %v", err)
return nil, err
}
transactionHashes := make([]string, len(block.Body().Transactions))
for i, tx := range block.Body().Transactions {
transactionHashes[i] = tx.Hash().Hex()
}
blockData := &BlockData{
Number: block.Number(),
Hash: block.Hash().Hex(),
ParentHash: block.ParentHash().Hex(),
Nonce: block.Nonce(),
Sha3Uncles: block.UncleHash().Hex(),
Miner: block.Coinbase().Hex(),
Difficulty: block.Difficulty(),
ExtraData: string(block.Extra()),
Size: block.Size(),
GasLimit: block.GasLimit(),
GasUsed: block.GasUsed(),
Timestamp: block.Time(),
Transactions: transactionHashes,
}
return blockData, nil
}
Transaction Data Extraction
Next, we define the function to extract transaction data.
package blockchain
import (
"context"
"log"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
)
type TransactionData struct {
Hash string
Nonce uint64
From string
To string
Value *big.Int
Gas uint64
GasPrice *big.Int
Data string
}
func GetTxData(txHash string) (*TransactionData, error) {
client, err := getClient()
if err != nil {
log.Fatal("Failed to get client:", err)
return nil, err
}
tx, _, err := client.TransactionByHash(context.Background(), common.HexToHash(txHash))
if err != nil {
log.Fatal("Failed to retrieve transaction:", err)
return nil, err
}
sender, err := getTxSender(tx)
if err != nil {
log.Fatalf("Failed to get sender: %v", err)
return nil, err
}
decodedData := decodeTxData(tx.Data())
return &TransactionData{
Hash: tx.Hash().Hex(),
Nonce: tx.Nonce(),
From: sender,
To: tx.To().Hex(),
Value: tx.Value(),
Gas: tx.Gas(),
GasPrice: tx.GasPrice(),
Data: decodedData,
}, nil
}
func getChainId() (*big.Int, error) {
client, err := getClient()
if err != nil {
log.Fatal("Failed to get client:", err)
return big.NewInt(0), err
}
chainId, err := client.NetworkID(context.Background())
if err != nil {
log.Fatal("Failed to get chainId:", err)
return big.NewInt(0), err
}
return chainId, nil
}
func getTxSender(tx *types.Transaction) (string, error) {
chainId, err := getChainId()
if err != nil {
log.Fatal("Failed to get chainId:", err)
return "", err
}
sender, err := types.Sender(types.NewLondonSigner(chainId), tx)
if err != nil {
log.Fatal("Not able to retrieve sender:", err)
return "", err
}
return sender.Hex(), nil
}
func decodeTxData(data []byte) string {
dataHex := common.Bytes2Hex(data)
return dataHex
}
Writing Tests
Now, let's write tests to assert the integrity of the data extraction functions.
package blockchain
import (
"fmt"
"math/big"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetBlockData(t *testing.T) {
blockNumber := big.NewInt(20076728)
blockData, err := GetBlockData(blockNumber)
// Use fmt.Sprintf to format the log message
t.Log(fmt.Sprintf("Block Data: \n%+v\nError: \n%v", blockData, err))
assert.Nil(t, err)
assert.NotNil(t, blockData)
}
func TestGetTransactionData(t *testing.T) {
blockNumber := big.NewInt(20076728)
blockData, err := GetBlockData(blockNumber)
assert.Nil(t, err)
assert.NotNil(t, blockData)
if len(blockData.Transactions) > 0 {
tx := blockData.Transactions[0]
txData, err := GetTxData(tx)
// Use fmt.Sprintf to format the log message
t.Log(fmt.Sprintf("Transaction Data: \n%+v\nError: \n%v", txData, err))
assert.Nil(t, err)
assert.NotNil(t, txData)
} else {
t.Skip("No transactions found in the block")
}
}
Run the tests using the following command:
go test -v
The output:
--- PASS: TestGetBlockData (0.95s)
=== RUN TestGetTransactionData
Current working directory: /mnt/c/github/dfbeat-v2/pkg/blockchain
Current working directory: /mnt/c/github/dfbeat-v2/pkg/blockchain
Current working directory: /mnt/c/github/dfbeat-v2/pkg/blockchain
blockchain_test.go:33: Transaction Data:
&{Hash:0x47e80ad8fa5f3bc94160f7eb1a3357f87b2756190007ab815b1a1890ddb65949 Nonce:249 BlockHash: BlockNumber:0 TransactionIndex:0 From:0x71B8CF59230bb295ade15506efd258eb458056A1 To:0x80a64c6D7f12C47B7c66c5B4E20E72bc1FCd5d9e Value:+50000000000000000 Gas:297438 GasPrice:+81175867687 Data:088890dc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000071b8cf59230bb295ade15506efd258eb458056a1000000000000000000000000000000000000000000000000000000006669c2ac0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000f3fd27ffb4712278e6d51d7d526a0ce431d454c5}
Error:
<nil>
--- PASS: TestGetTransactionData (0.56s)
Conclusion
In this tutorial, we have demonstrated how to extract block and transaction data from the Ethereum blockchain using Go and the Go-Ethereum client. We also covered how to write and run tests to ensure the integrity of the data extraction process. By following these steps, you can reliably retrieve and verify blockchain data in your Go applications.
Top comments (0)