github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/integration/rpcinfo/info_server_test.go (about)

     1  // +build integration
     2  
     3  // Space above here matters
     4  // Copyright Monax Industries Limited
     5  // SPDX-License-Identifier: Apache-2.0
     6  
     7  package rpcinfo
     8  
     9  import (
    10  	"context"
    11  	"encoding/json"
    12  	"sort"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/hyperledger/burrow/rpc"
    17  	"github.com/hyperledger/burrow/rpc/lib/jsonrpc"
    18  
    19  	tmjson "github.com/tendermint/tendermint/libs/json"
    20  
    21  	"github.com/hyperledger/burrow/integration"
    22  	"github.com/hyperledger/burrow/txs/payload"
    23  
    24  	"github.com/hyperledger/burrow/core"
    25  
    26  	"github.com/hyperledger/burrow/binary"
    27  	"github.com/hyperledger/burrow/event"
    28  	"github.com/hyperledger/burrow/execution/exec"
    29  	"github.com/hyperledger/burrow/integration/rpctest"
    30  	"github.com/hyperledger/burrow/rpc/rpcinfo/infoclient"
    31  	"github.com/hyperledger/burrow/rpc/rpctransact"
    32  	"github.com/hyperledger/burrow/txs"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/require"
    35  	ctypes "github.com/tendermint/tendermint/consensus/types"
    36  )
    37  
    38  const timeout = 5 * time.Second
    39  
    40  func TestInfoServer(t *testing.T) {
    41  	kern, shutdown := integration.RunNode(t, rpctest.GenesisDoc, rpctest.PrivateAccounts)
    42  	defer shutdown()
    43  	inputAddress := rpctest.PrivateAccounts[0].GetAddress()
    44  	infoAddress := "http://" + kern.InfoListenAddress().String()
    45  	var clients = map[string]rpc.Client{
    46  		"JSON RPC": jsonrpc.NewClient(infoAddress),
    47  		"URI":      jsonrpc.NewURIClient(infoAddress),
    48  	}
    49  	cli := rpctest.NewTransactClient(t, kern.GRPCListenAddress().String())
    50  	for clientName, rpcClient := range clients {
    51  		t.Run(clientName, func(t *testing.T) {
    52  			t.Run("Status", func(t *testing.T) {
    53  				t.Parallel()
    54  				resp, err := infoclient.Status(rpcClient)
    55  				require.NoError(t, err)
    56  				assert.Contains(t, resp.GetNodeInfo().GetMoniker(), "node")
    57  				assert.Equal(t, rpctest.GenesisDoc.GetChainID(), resp.NodeInfo.Network,
    58  					"ChainID should match NodeInfo.Network")
    59  			})
    60  
    61  			t.Run("Account", func(t *testing.T) {
    62  				t.Parallel()
    63  				acc := rpctest.GetAccount(t, rpcClient, rpctest.PrivateAccounts[0].GetAddress())
    64  				if acc == nil {
    65  					t.Fatal("Account was nil")
    66  				}
    67  				if acc.GetAddress() != rpctest.PrivateAccounts[0].GetAddress() {
    68  					t.Fatalf("Failed to get correct account. Got %s, expected %s", acc.GetAddress(),
    69  						rpctest.PrivateAccounts[0].GetAddress())
    70  				}
    71  			})
    72  
    73  			t.Run("Storage", func(t *testing.T) {
    74  				t.Parallel()
    75  				amt, gasLim, fee := uint64(1100), uint64(1000), uint64(1000)
    76  				code := []byte{0x60, 0x5, 0x60, 0x1, 0x55}
    77  				// Call with nil address will create a contract
    78  				txe, err := cli.CallTxSync(context.Background(), &payload.CallTx{
    79  					Input: &payload.TxInput{
    80  						Address: inputAddress,
    81  						Amount:  amt,
    82  					},
    83  					Data:     code,
    84  					GasLimit: gasLim,
    85  					Fee:      fee,
    86  				})
    87  				require.NoError(t, err)
    88  				assert.Equal(t, true, txe.Receipt.CreatesContract, "This transaction should"+
    89  					" create a contract")
    90  				assert.NotEqual(t, 0, len(txe.TxHash), "Receipt should contain a"+
    91  					" transaction hash")
    92  				contractAddr := txe.Receipt.ContractAddress
    93  				assert.NotEqual(t, 0, len(contractAddr), "Transactions claims to have"+
    94  					" created a contract but the contract address is empty")
    95  
    96  				v := rpctest.GetStorage(t, rpcClient, contractAddr, []byte{0x1})
    97  				got := binary.LeftPadWord256(v)
    98  				expected := binary.LeftPadWord256([]byte{0x5})
    99  				if got.Compare(expected) != 0 {
   100  					t.Fatalf("Wrong storage value. Got %x, expected %x", got.Bytes(),
   101  						expected.Bytes())
   102  				}
   103  			})
   104  
   105  			t.Run("Block", func(t *testing.T) {
   106  				t.Parallel()
   107  				waitNBlocks(t, kern, 1)
   108  				res, err := infoclient.Block(rpcClient, 1)
   109  				require.NoError(t, err)
   110  				assert.Equal(t, int64(1), res.Block.Height)
   111  			})
   112  
   113  			t.Run("WaitBlocks", func(t *testing.T) {
   114  				t.Parallel()
   115  				waitNBlocks(t, kern, 5)
   116  			})
   117  
   118  			t.Run("BlockchainInfo", func(t *testing.T) {
   119  				t.Parallel()
   120  				// wait a mimimal number of blocks to ensure that the later query for block
   121  				// headers has a non-trivial length
   122  				nBlocks := 4
   123  				waitNBlocks(t, kern, nBlocks)
   124  
   125  				resp, err := infoclient.Blocks(rpcClient, 1, 0)
   126  				if err != nil {
   127  					t.Fatalf("Failed to get blockchain info: %v", err)
   128  				}
   129  				lastBlockHeight := resp.LastHeight
   130  				nMetaBlocks := len(resp.BlockMetas)
   131  				assert.True(t, uint64(nMetaBlocks) <= lastBlockHeight,
   132  					"Logically number of block metas should be equal or less than block height.")
   133  				assert.True(t, nBlocks <= len(resp.BlockMetas),
   134  					"Should see at least %v BlockMetas after waiting for %v blocks but saw %v",
   135  					nBlocks, nBlocks, len(resp.BlockMetas))
   136  				// For the maximum number (default to 20) of retrieved block headers,
   137  				// check that they correctly chain to each other.
   138  				lastBlockHash := resp.BlockMetas[0].Header.Hash()
   139  				for i := 1; i < nMetaBlocks-1; i++ {
   140  					// the blockhash in header of height h should be identical to the hash
   141  					// in the LastBlockID of the header of block height h+1.
   142  					assert.Equal(t, lastBlockHash, resp.BlockMetas[i].Header.LastBlockID.Hash,
   143  						"Blockchain should be a hash tree!")
   144  					lastBlockHash = resp.BlockMetas[i].Header.Hash()
   145  				}
   146  
   147  				// Now retrieve only two blockheaders (h=1, and h=2) and check that we got
   148  				// two results.
   149  				resp, err = infoclient.Blocks(rpcClient, 1, 2)
   150  				assert.NoError(t, err)
   151  				assert.Equal(t, 2, len(resp.BlockMetas),
   152  					"Should see 2 BlockMetas after extracting 2 blocks")
   153  			})
   154  
   155  			t.Run("UnconfirmedTxs", func(t *testing.T) {
   156  				amt, gasLim, fee := uint64(1100), uint64(1000), uint64(1000)
   157  				code := []byte{0x60, 0x5, 0x60, 0x1, 0x55}
   158  				// Call with nil address will create a contract
   159  				txEnv := rpctest.MakeDefaultCallTx(t, rpcClient, nil, code, amt, gasLim, fee)
   160  				txChan := make(chan []*txs.Envelope)
   161  
   162  				// We want to catch the Tx in mempool before it gets reaped by tendermint
   163  				// consensus. We should be able to do this almost always if we broadcast our
   164  				// transaction immediately after a block has been committed. There is about
   165  				// 1 second between blocks, and we will have the lock on Reap
   166  				// So we wait for a block here
   167  				waitNBlocks(t, kern, 1)
   168  
   169  				go func() {
   170  					for {
   171  						resp, err := infoclient.UnconfirmedTxs(rpcClient, -1)
   172  						if err != nil {
   173  							// We get an error on exit
   174  							return
   175  						}
   176  						if resp.NumTxs > 0 {
   177  							txChan <- resp.Txs
   178  						}
   179  					}
   180  				}()
   181  
   182  				broadcastTxSync(t, cli, txEnv)
   183  				select {
   184  				case <-time.After(time.Second * timeout):
   185  					t.Fatal("Timeout out waiting for unconfirmed transactions to appear")
   186  				case transactions := <-txChan:
   187  					assert.Len(t, transactions, 1, "There should only be a single transaction in the "+
   188  						"mempool during this test (previous txs should have made it into a block)")
   189  					assert.Contains(t, transactions, txEnv, "Transaction should be returned by ListUnconfirmedTxs")
   190  				}
   191  			})
   192  
   193  			t.Run("Validators", func(t *testing.T) {
   194  				t.Parallel()
   195  				resp, err := infoclient.Validators(rpcClient)
   196  				assert.NoError(t, err)
   197  				assert.Len(t, resp.BondedValidators, 1)
   198  				validator := resp.BondedValidators[0]
   199  				assert.Equal(t, rpctest.GenesisDoc.Validators[0].PublicKey, validator.PublicKey)
   200  			})
   201  
   202  			t.Run("Consensus", func(t *testing.T) {
   203  				t.Parallel()
   204  				resp, err := infoclient.Consensus(rpcClient)
   205  				require.NoError(t, err)
   206  
   207  				// Now I do a special dance... because the votes section of RoundState has will Marshal but not Unmarshal yet
   208  				// TODO: put in a PR in tendermint to fix thiss
   209  				rawMap := make(map[string]json.RawMessage)
   210  				err = json.Unmarshal(resp.RoundState, &rawMap)
   211  				require.NoError(t, err)
   212  				delete(rawMap, "votes")
   213  
   214  				bs, err := json.Marshal(rawMap)
   215  				require.NoError(t, err)
   216  
   217  				rs := new(ctypes.RoundState)
   218  				err = tmjson.Unmarshal(bs, rs)
   219  				require.NoError(t, err)
   220  
   221  				assert.Equal(t, rs.Validators.Validators[0].Address, rs.Validators.Proposer.Address)
   222  			})
   223  
   224  			t.Run("Names", func(t *testing.T) {
   225  				t.Parallel()
   226  				names := []string{"bib", "flub", "flib"}
   227  				sort.Strings(names)
   228  				for _, name := range names {
   229  					_, err := rpctest.UpdateName(cli, inputAddress, name, name, 99999)
   230  					require.NoError(t, err)
   231  				}
   232  
   233  				entry, err := infoclient.Name(rpcClient, names[0])
   234  				require.NoError(t, err)
   235  				assert.Equal(t, names[0], entry.Name)
   236  				assert.Equal(t, names[0], entry.Data)
   237  
   238  				entry, err = infoclient.Name(rpcClient, "asdasdas")
   239  				require.NoError(t, err)
   240  				require.Nil(t, entry)
   241  
   242  				var namesOut []string
   243  				entries, err := infoclient.Names(rpcClient, "")
   244  				require.NoError(t, err)
   245  				for _, entry := range entries {
   246  					namesOut = append(namesOut, entry.Name)
   247  				}
   248  				require.Equal(t, names, namesOut)
   249  
   250  				namesOut = namesOut[:0]
   251  				entries, err = infoclient.Names(rpcClient, "fl")
   252  				require.NoError(t, err)
   253  				for _, entry := range entries {
   254  					namesOut = append(namesOut, entry.Name)
   255  				}
   256  				require.Equal(t, []string{"flib", "flub"}, namesOut)
   257  			})
   258  		})
   259  	}
   260  
   261  }
   262  
   263  func waitNBlocks(t testing.TB, kern *core.Kernel, n int) {
   264  	subID := event.GenSubID()
   265  	ch, err := kern.Emitter.Subscribe(context.Background(), subID, exec.QueryForBlockExecution(), 10)
   266  	require.NoError(t, err)
   267  	defer kern.Emitter.UnsubscribeAll(context.Background(), subID)
   268  	for i := 0; i < n; i++ {
   269  		<-ch
   270  	}
   271  }
   272  
   273  func broadcastTxSync(t testing.TB, cli rpctransact.TransactClient, txEnv *txs.Envelope) *exec.TxExecution {
   274  	txe, err := cli.BroadcastTxSync(context.Background(), &rpctransact.TxEnvelopeParam{
   275  		Envelope: txEnv,
   276  	})
   277  	require.NoError(t, err)
   278  	return txe
   279  }