github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/rpc/web3/eth_service_test.go (about)

     1  package web3_test
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/hyperledger/burrow/acm/balance"
    12  	"github.com/hyperledger/burrow/crypto"
    13  	"github.com/hyperledger/burrow/encoding"
    14  	"github.com/hyperledger/burrow/encoding/web3hex"
    15  	"github.com/hyperledger/burrow/execution/evm/abi"
    16  	"github.com/hyperledger/burrow/integration"
    17  	"github.com/hyperledger/burrow/keys"
    18  	"github.com/hyperledger/burrow/logging"
    19  	"github.com/hyperledger/burrow/project"
    20  	"github.com/hyperledger/burrow/rpc"
    21  	"github.com/hyperledger/burrow/rpc/web3"
    22  	"github.com/hyperledger/burrow/txs"
    23  	"github.com/hyperledger/burrow/txs/payload"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  var d = new(web3hex.Decoder).Must()
    28  
    29  // Check we can force set a decimal ChainID
    30  const chainID = "15321"
    31  
    32  func TestWeb3Service(t *testing.T) {
    33  	ctx := context.Background()
    34  	genesisAccounts := integration.MakePrivateAccounts("burrow", 1)
    35  	genesisAccounts = append(genesisAccounts, integration.MakeEthereumAccounts("ethereum", 3)...)
    36  	genesisDoc := integration.TestGenesisDoc(genesisAccounts, 0)
    37  	genesisDoc.ChainID = chainID
    38  	config, _ := integration.NewTestConfig(genesisDoc)
    39  	logger := logging.NewNoopLogger()
    40  	kern, err := integration.TestKernel(genesisAccounts[0], genesisAccounts, config)
    41  	require.NoError(t, err)
    42  	err = kern.Boot()
    43  	defer kern.Shutdown(ctx)
    44  
    45  	dir, err := ioutil.TempDir(os.TempDir(), "")
    46  	require.NoError(t, err)
    47  	defer os.RemoveAll(dir)
    48  
    49  	store := keys.NewFilesystemKeyStore(dir, true)
    50  	for _, acc := range genesisAccounts {
    51  		err = store.StoreKeyPlain(&keys.Key{
    52  			CurveType:  acc.PrivateKey().CurveType,
    53  			Address:    acc.GetAddress(),
    54  			PublicKey:  *acc.GetPublicKey(),
    55  			PrivateKey: acc.PrivateKey(),
    56  		})
    57  		require.NoError(t, err)
    58  	}
    59  
    60  	nodeView, err := kern.GetNodeView()
    61  	require.NoError(t, err)
    62  
    63  	accountState := kern.State
    64  	eventsState := kern.State
    65  	validatorState := kern.State
    66  	eth := web3.NewEthService(accountState, eventsState, kern.Blockchain, validatorState,
    67  		nodeView, kern.Transactor, store, kern.Logger)
    68  
    69  	t.Run("Web3Sha3", func(t *testing.T) {
    70  		result, err := eth.Web3Sha3(&web3.Web3Sha3Params{"0x68656c6c6f20776f726c64"}) // hello world
    71  		require.NoError(t, err)
    72  		// hex encoded
    73  		require.Equal(t, "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad", result.HashedData)
    74  	})
    75  
    76  	t.Run("NetListening", func(t *testing.T) {
    77  		result, err := eth.NetListening()
    78  		require.NoError(t, err)
    79  		require.Equal(t, true, result.IsNetListening)
    80  	})
    81  
    82  	t.Run("NetPeerCount", func(t *testing.T) {
    83  		result, err := eth.NetPeerCount()
    84  		require.NoError(t, err)
    85  		require.Equal(t, "0x0", result.NumConnectedPeers)
    86  	})
    87  
    88  	t.Run("Version+ID", func(t *testing.T) {
    89  		t.Run("Web3ClientVersion", func(t *testing.T) {
    90  			result, err := eth.Web3ClientVersion()
    91  			require.NoError(t, err)
    92  			require.Equal(t, project.FullVersion(), result.ClientVersion)
    93  		})
    94  
    95  		t.Run("NetVersion", func(t *testing.T) {
    96  			result, err := eth.NetVersion()
    97  			require.NoError(t, err)
    98  			require.Equal(t, web3hex.Encoder.BigInt(encoding.GetEthChainID(genesisDoc.GetChainID())), result.ChainID)
    99  		})
   100  
   101  		t.Run("EthProtocolVersion", func(t *testing.T) {
   102  			result, err := eth.EthProtocolVersion()
   103  			require.NoError(t, err)
   104  			require.NotEmpty(t, result.ProtocolVersion)
   105  		})
   106  
   107  		t.Run("EthChainId", func(t *testing.T) {
   108  			result, err := eth.EthChainId()
   109  			require.NoError(t, err)
   110  			doc := config.GenesisDoc
   111  			require.Equal(t, web3hex.Encoder.BigInt(encoding.GetEthChainID(doc.GetChainID())), result.ChainId)
   112  		})
   113  	})
   114  
   115  	t.Run("EthTransactions", func(t *testing.T) {
   116  		var txHash, contractAddress string
   117  
   118  		sender := genesisAccounts[1]
   119  		receivee := genesisAccounts[2].GetPublicKey().GetAddress()
   120  		acc, err := kern.State.GetAccount(receivee)
   121  		require.NoError(t, err)
   122  		before := acc.GetBalance()
   123  
   124  		t.Run("EthSendRawTransaction", func(t *testing.T) {
   125  			txEnv := txs.Enclose(chainID, &payload.CallTx{
   126  				Input: &payload.TxInput{
   127  					Address:  sender.GetAddress(),
   128  					Amount:   1,
   129  					Sequence: 0,
   130  				},
   131  				Address: &receivee,
   132  				Data:    nil,
   133  			})
   134  			txEnv.Encoding = txs.Envelope_RLP
   135  			err = txEnv.Sign(sender)
   136  			require.NoError(t, err)
   137  
   138  			rawTx, err := txs.EthRawTxFromEnvelope(txEnv)
   139  			require.NoError(t, err)
   140  
   141  			bs, err := rawTx.Marshal()
   142  			require.NoError(t, err)
   143  
   144  			raw := web3hex.Encoder.BytesTrim(bs)
   145  
   146  			_, err = eth.EthSendRawTransaction(&web3.EthSendRawTransactionParams{
   147  				SignedTransactionData: raw,
   148  			})
   149  			require.NoError(t, err)
   150  		})
   151  
   152  		t.Run("EthGetBalance", func(t *testing.T) {
   153  			result, err := eth.EthGetBalance(&web3.EthGetBalanceParams{
   154  				Address:     web3hex.Encoder.BytesTrim(receivee.Bytes()),
   155  				BlockNumber: "latest",
   156  			})
   157  			require.NoError(t, err)
   158  			after := d.BigInt(result.GetBalanceResult)
   159  			after = balance.WeiToNative(after)
   160  			require.Equal(t, after.Uint64(), before+1)
   161  		})
   162  
   163  		t.Run("EthGetTransactionCount", func(t *testing.T) {
   164  			result, err := eth.EthGetTransactionCount(&web3.EthGetTransactionCountParams{
   165  				Address: genesisAccounts[1].GetAddress().String(),
   166  			})
   167  			require.NoError(t, err)
   168  			require.Equal(t, web3hex.Encoder.Uint64(1), result.NonceOrNull)
   169  		})
   170  
   171  		// create contract on chain
   172  		t.Run("EthSendTransaction", func(t *testing.T) {
   173  			type ret struct {
   174  				*web3.EthSendTransactionResult
   175  				error
   176  			}
   177  			ch := make(chan ret)
   178  			numSends := 5
   179  			for i := 0; i < numSends; i++ {
   180  				idx := i
   181  				go func() {
   182  					tx := &web3.EthSendTransactionParams{
   183  						Transaction: web3.Transaction{
   184  							From: web3hex.Encoder.BytesTrim(genesisAccounts[3].GetAddress().Bytes()),
   185  							Gas:  web3hex.Encoder.Uint64(uint64(40 + idx)), // make tx unique in mempool
   186  							Data: web3hex.Encoder.BytesTrim(rpc.Bytecode_HelloWorld),
   187  						},
   188  					}
   189  					result, err := eth.EthSendTransaction(tx)
   190  					ch <- ret{result, err}
   191  				}()
   192  			}
   193  			for i := 0; i < numSends; i++ {
   194  				select {
   195  				case r := <-ch:
   196  					require.NoError(t, r.error)
   197  					require.NotEmpty(t, r.TransactionHash)
   198  				case <-time.After(2 * time.Second):
   199  					t.Fatalf("timed out waiting for EthSendTransaction result")
   200  				}
   201  			}
   202  		})
   203  
   204  		t.Run("EthGetTransactionReceipt", func(t *testing.T) {
   205  			sendResult, err := eth.EthSendTransaction(&web3.EthSendTransactionParams{
   206  				Transaction: web3.Transaction{
   207  					From: web3hex.Encoder.BytesTrim(genesisAccounts[3].GetAddress().Bytes()),
   208  					Gas:  web3hex.Encoder.Uint64(40),
   209  					Data: web3hex.Encoder.BytesTrim(rpc.Bytecode_HelloWorld),
   210  				},
   211  			})
   212  			require.NoError(t, err)
   213  			txHash = sendResult.TransactionHash
   214  			require.NotEmpty(t, txHash, "need tx hash to get tx receipt")
   215  			receiptResult, err := eth.EthGetTransactionReceipt(&web3.EthGetTransactionReceiptParams{
   216  				TransactionHash: txHash,
   217  			})
   218  			require.NoError(t, err)
   219  			contractAddress = receiptResult.Receipt.ContractAddress
   220  			require.NotEmpty(t, contractAddress)
   221  		})
   222  
   223  		t.Run("EthCall", func(t *testing.T) {
   224  			require.NotEmpty(t, contractAddress, "need contract address to call")
   225  
   226  			packed, _, err := abi.EncodeFunctionCall(string(rpc.Abi_HelloWorld), "Hello", logger)
   227  			require.NoError(t, err)
   228  
   229  			result, err := eth.EthCall(&web3.EthCallParams{
   230  				Transaction: web3.Transaction{
   231  					From: web3hex.Encoder.BytesTrim(genesisAccounts[1].GetAddress().Bytes()),
   232  					To:   contractAddress,
   233  					Data: web3hex.Encoder.BytesTrim(packed),
   234  				},
   235  			})
   236  			require.NoError(t, err)
   237  
   238  			value := d.Bytes(result.ReturnValue)
   239  			vars, err := abi.DecodeFunctionReturn(string(rpc.Abi_HelloWorld), "Hello", value)
   240  			require.NoError(t, err)
   241  			require.Len(t, vars, 1)
   242  			require.Equal(t, "Hello, World", vars[0].Value)
   243  		})
   244  
   245  		t.Run("EthGetCode", func(t *testing.T) {
   246  			require.NotEmpty(t, contractAddress, "need contract address get code")
   247  			result, err := eth.EthGetCode(&web3.EthGetCodeParams{
   248  				Address: contractAddress,
   249  			})
   250  			require.NoError(t, err)
   251  			require.Equal(t, web3hex.Encoder.BytesTrim(rpc.DeployedBytecode_HelloWorld), strings.ToLower(result.Bytes))
   252  		})
   253  	})
   254  
   255  	t.Run("EthMining", func(t *testing.T) {
   256  		result, err := eth.EthMining()
   257  		require.NoError(t, err)
   258  		require.Equal(t, true, result.Mining)
   259  	})
   260  
   261  	t.Run("EthAccounts", func(t *testing.T) {
   262  		result, err := eth.EthAccounts()
   263  		require.NoError(t, err)
   264  		require.Len(t, result.Addresses, len(genesisAccounts)-1)
   265  		for _, acc := range genesisAccounts {
   266  			if acc.PrivateKey().CurveType == crypto.CurveTypeSecp256k1 {
   267  				require.Contains(t, result.Addresses, web3hex.Encoder.BytesTrim(acc.GetAddress().Bytes()))
   268  			}
   269  		}
   270  	})
   271  
   272  	t.Run("EthSign", func(t *testing.T) {
   273  		result, err := eth.EthSign(&web3.EthSignParams{
   274  			Address: "0x" + genesisAccounts[1].GetAddress().String(),
   275  			Bytes:   "0xdeadbeaf",
   276  		})
   277  		require.NoError(t, err)
   278  		require.Equal(t, `0x1ba96f3dd6cbbc27aaaafe9d68a5368653f72a5677e365e2505ad207a5e8741949717cfc1cc107583142bfe54b9ba4840f5ad7cb12b224dd97b2fb6a735b93c57a`, result.Signature)
   279  	})
   280  
   281  	t.Run("EthGetBlock", func(t *testing.T) {
   282  		numberResult, err := eth.EthGetBlockByNumber(&web3.EthGetBlockByNumberParams{BlockNumber: web3hex.Encoder.Uint64(1)})
   283  		require.NoError(t, err)
   284  		hashResult, err := eth.EthGetBlockByHash(&web3.EthGetBlockByHashParams{BlockHash: numberResult.GetBlockByNumberResult.Hash})
   285  		require.NoError(t, err)
   286  		require.Equal(t, numberResult.GetBlockByNumberResult, hashResult.GetBlockByHashResult)
   287  	})
   288  
   289  }