From c0b3ecddd6f20babe43cf26ae46009c7e2d8ab6c Mon Sep 17 00:00:00 2001 From: peekpi <894646171@qq.com> Date: Sat, 28 May 2022 21:27:12 +0800 Subject: [PATCH 1/3] align eip-1898: support block number or hash on state-related methods --- core/blockchain.go | 5 +++ core/headerchain.go | 4 +++ eth/rpc/types.go | 17 ++++++++++ eth/rpc/types_test.go | 31 +++++++++++++++++ hmy/blockchain.go | 46 ++++++++++++++++++++++++-- rosetta/services/account.go | 4 +-- rosetta/services/call_service.go | 6 ++-- rosetta/services/construction_check.go | 7 ++-- rpc/blockchain.go | 22 +++--------- rpc/contract.go | 24 +++++--------- rpc/eth/rpc.go | 4 +-- rpc/staking.go | 2 +- rpc/transaction.go | 26 +++++++-------- rpc/v1/legacy.go | 4 +-- rpc/v2/legacy.go | 2 +- 15 files changed, 141 insertions(+), 63 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 40ab953ecb..1c82a79f24 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1838,6 +1838,11 @@ func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com return bc.hc.GetBlockHashesFromHash(hash, max) } +// GetCanonicalHash returns the canonical hash for a given block number +func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash { + return bc.hc.GetCanonicalHash(number) +} + // GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or // a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the // number of blocks to be individually checked before we reach the canonical chain. diff --git a/core/headerchain.go b/core/headerchain.go index ab4dbf5714..08739adb3e 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -460,6 +460,10 @@ func (hc *HeaderChain) getHashByNumber(number uint64) common.Hash { return hash } +func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash { + return rawdb.ReadCanonicalHash(hc.chainDb, number) +} + // CurrentHeader retrieves the current head header of the canonical chain. The // header is retrieved from the HeaderChain's internal cache. func (hc *HeaderChain) CurrentHeader() *block.Header { diff --git a/eth/rpc/types.go b/eth/rpc/types.go index dc9248d0fe..b6c48fdcc6 100644 --- a/eth/rpc/types.go +++ b/eth/rpc/types.go @@ -103,6 +103,22 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { return nil } +// MarshalText implements encoding.TextMarshaler. It marshals: +// - "latest", "earliest" or "pending" as strings +// - other numbers as hex +func (bn BlockNumber) MarshalText() ([]byte, error) { + switch bn { + case EarliestBlockNumber: + return []byte("earliest"), nil + case LatestBlockNumber: + return []byte("latest"), nil + case PendingBlockNumber: + return []byte("pending"), nil + default: + return hexutil.Uint64(bn).MarshalText() + } +} + func (bn BlockNumber) Int64() int64 { return (int64)(bn) } @@ -114,6 +130,7 @@ type BlockNumberOrHash struct { } func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { + fmt.Println("UnmarshalJSON: bnh") type erased BlockNumberOrHash e := erased{} err := json.Unmarshal(data, &e) diff --git a/eth/rpc/types_test.go b/eth/rpc/types_test.go index 89b0c9171a..f110dee7c6 100644 --- a/eth/rpc/types_test.go +++ b/eth/rpc/types_test.go @@ -18,6 +18,7 @@ package rpc import ( "encoding/json" + "reflect" "testing" "github.com/ethereum/go-ethereum/common" @@ -122,3 +123,33 @@ func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { } } } + +func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { + tests := []struct { + name string + number int64 + }{ + {"max", math.MaxInt64}, + {"pending", int64(PendingBlockNumber)}, + {"latest", int64(LatestBlockNumber)}, + {"earliest", int64(EarliestBlockNumber)}, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number)) + marshalled, err := json.Marshal(bnh) + if err != nil { + t.Fatal("cannot marshal:", err) + } + var unmarshalled BlockNumberOrHash + err = json.Unmarshal(marshalled, &unmarshalled) + if err != nil { + t.Fatal("cannot unmarshal:", err) + } + if !reflect.DeepEqual(bnh, unmarshalled) { + t.Fatalf("wrong result: expected %v, got %v", bnh, unmarshalled) + } + }) + } +} diff --git a/hmy/blockchain.go b/hmy/blockchain.go index 0a637ebbec..1a306671a9 100644 --- a/hmy/blockchain.go +++ b/hmy/blockchain.go @@ -209,9 +209,30 @@ func (hmy *Harmony) GetCurrentBadBlocks() []core.BadBlock { return hmy.BlockChain.BadBlocks() } +func (hmy *Harmony) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return hmy.BlockByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + header := hmy.BlockChain.GetHeaderByHash(hash) + if header == nil { + return nil, errors.New("header for hash not found") + } + if blockNrOrHash.RequireCanonical && hmy.BlockChain.GetCanonicalHash(header.Number().Uint64()) != hash { + return nil, errors.New("hash is not currently canonical") + } + block := hmy.BlockChain.GetBlock(hash, header.Number().Uint64()) + if block == nil { + return nil, errors.New("header found, but block body is missing") + } + return block, nil + } + return nil, errors.New("invalid arguments; neither block nor hash specified") +} + // GetBalance returns balance of an given address. -func (hmy *Harmony) GetBalance(ctx context.Context, address common.Address, blockNum rpc.BlockNumber) (*big.Int, error) { - s, _, err := hmy.StateAndHeaderByNumber(ctx, blockNum) +func (hmy *Harmony) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*big.Int, error) { + s, _, err := hmy.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if s == nil || err != nil { return nil, err } @@ -268,6 +289,27 @@ func (hmy *Harmony) StateAndHeaderByNumber(ctx context.Context, blockNum rpc.Blo return stateDb, header, err } +func (hmy *Harmony) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.DB, *block.Header, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return hmy.StateAndHeaderByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + header, err := hmy.HeaderByHash(ctx, hash) + if err != nil { + return nil, nil, err + } + if header == nil { + return nil, nil, errors.New("header for hash not found") + } + if blockNrOrHash.RequireCanonical && hmy.BlockChain.GetCanonicalHash(header.Number().Uint64()) != hash { + return nil, nil, errors.New("hash is not currently canonical") + } + stateDb, err := hmy.BlockChain.StateAt(header.Root()) + return stateDb, header, err + } + return nil, nil, errors.New("invalid arguments; neither block nor hash specified") +} + // GetLeaderAddress returns the one address of the leader, given the coinbaseAddr. // Note that the coinbaseAddr is overloaded with the BLS pub key hash in staking era. func (hmy *Harmony) GetLeaderAddress(coinbaseAddr common.Address, epoch *big.Int) string { diff --git a/rosetta/services/account.go b/rosetta/services/account.go index 61fc9111ce..da9a67c226 100644 --- a/rosetta/services/account.go +++ b/rosetta/services/account.go @@ -60,7 +60,7 @@ func (s *AccountAPI) AccountBalance( }) } blockNum := rpc.BlockNumber(block.Header().Header.Number().Int64()) - balance := new(big.Int) + var balance *big.Int if request.AccountIdentifier.SubAccount != nil { // indicate it may be a request for delegated balance @@ -69,7 +69,7 @@ func (s *AccountAPI) AccountBalance( return nil, rosettaError } } else { - balance, err = s.hmy.GetBalance(ctx, addr, blockNum) + balance, err = s.hmy.GetBalance(ctx, addr, rpc.BlockNumberOrHashWithNumber(blockNum)) if err != nil { return nil, common.NewError(common.SanityCheckError, map[string]interface{}{ "message": "invalid address", diff --git a/rosetta/services/call_service.go b/rosetta/services/call_service.go index ab0b11c3a0..6bee731fed 100644 --- a/rosetta/services/call_service.go +++ b/rosetta/services/call_service.go @@ -343,7 +343,7 @@ func (c *CallAPIService) getStorageAt( }) } - res, err := contractAPI.GetStorageAt(ctx, args.Addr, args.Key, rpc2.BlockNumber(args.BlockNum)) + res, err := contractAPI.GetStorageAt(ctx, args.Addr, args.Key, rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(args.BlockNum))) if err != nil { return nil, common.NewError(common.ErrCallExecute, map[string]interface{}{ "message": errors.WithMessage(err, "get storage at error").Error(), @@ -366,7 +366,7 @@ func (c *CallAPIService) getCode( "message": errors.WithMessage(err, "invalid parameters").Error(), }) } - code, err := contractAPI.GetCode(ctx, args.Addr, rpc2.BlockNumber(args.BlockNum)) + code, err := contractAPI.GetCode(ctx, args.Addr, rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(args.BlockNum))) if err != nil { return nil, common.NewError(common.ErrCallExecute, map[string]interface{}{ "message": errors.WithMessage(err, "get code error").Error(), @@ -389,7 +389,7 @@ func (c *CallAPIService) call( "message": errors.WithMessage(err, "invalid parameters").Error(), }) } - data, err := contractAPI.Call(ctx, args.CallArgs, rpc2.BlockNumber(args.BlockNum)) + data, err := contractAPI.Call(ctx, args.CallArgs, rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(args.BlockNum))) if err != nil { return nil, common.NewError(common.ErrCallExecute, map[string]interface{}{ "message": errors.WithMessage(err, "call smart contract error").Error(), diff --git a/rosetta/services/construction_check.go b/rosetta/services/construction_check.go index d850b64f5f..1c3e1db699 100644 --- a/rosetta/services/construction_check.go +++ b/rosetta/services/construction_check.go @@ -228,14 +228,15 @@ func (s *ConstructAPI) ConstructionMetadata( }) } + latest := ethRpc.BlockNumberOrHashWithNumber(ethRpc.LatestBlockNumber) var estGasUsed uint64 if !isStakingOperation(options.OperationType) { if options.OperationType == common.ContractCreationOperation { - estGasUsed, err = rpc.EstimateGas(ctx, s.hmy, rpc.CallArgs{From: senderAddr, Data: &data}, nil) + estGasUsed, err = rpc.EstimateGas(ctx, s.hmy, rpc.CallArgs{From: senderAddr, Data: &data}, latest, nil) estGasUsed *= 2 // HACK to account for imperfect contract creation estimation } else { estGasUsed, err = rpc.EstimateGas( - ctx, s.hmy, rpc.CallArgs{From: senderAddr, To: &contractAddress, Data: &data}, nil, + ctx, s.hmy, rpc.CallArgs{From: senderAddr, To: &contractAddress, Data: &data}, latest, nil, ) } } else { @@ -269,7 +270,7 @@ func (s *ConstructAPI) ConstructionMetadata( callArgs.To = &contractAddress } evmExe, err := rpc.DoEVMCall( - ctx, s.hmy, callArgs, ethRpc.LatestBlockNumber, rpc.CallTimeout, + ctx, s.hmy, callArgs, latest, rpc.CallTimeout, ) if err != nil { return nil, common.NewError(common.CatchAllError, map[string]interface{}{ diff --git a/rpc/blockchain.go b/rpc/blockchain.go index a14676f1ba..6f3e2293f7 100644 --- a/rpc/blockchain.go +++ b/rpc/blockchain.go @@ -126,7 +126,7 @@ func (s *PublicBlockchainService) getBalanceByBlockNumber( if err != nil { return nil, err } - balance, err := s.hmy.GetBalance(ctx, addr, blockNum) + balance, err := s.hmy.GetBalance(ctx, addr, rpc.BlockNumberOrHashWithNumber(blockNum)) if err != nil { return nil, err } @@ -787,7 +787,7 @@ type StorageResult struct { // GetHeaderByNumberRLPHex returns block header at given number by `hex(rlp(header))` func (s *PublicBlockchainService) GetProof( - ctx context.Context, address common.Address, storageKeys []string, blockNumber BlockNumber) (ret *AccountResult, err error) { + ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (ret *AccountResult, err error) { timer := DoMetricRPCRequest(GetProof) defer DoRPCRequestDuration(GetProof, timer) @@ -803,23 +803,9 @@ func (s *PublicBlockchainService) GetProof( return } - // Process number based on version - blockNum := blockNumber.EthBlockNumber() - - // Ensure valid block number - if s.version != Eth && isBlockGreaterThanLatest(s.hmy, blockNum) { - err = ErrRequestedBlockTooHigh - return - } - - // Fetch Header - header, err := s.hmy.HeaderByNumber(ctx, blockNum) - if header == nil && err != nil { - return - } - state, err := s.hmy.BeaconChain.StateAt(header.Root()) + state, _, err := s.hmy.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { - return + return nil, err } storageTrie := state.StorageTrie(address) diff --git a/rpc/contract.go b/rpc/contract.go index 87ac6c5aa8..6bf3a393eb 100644 --- a/rpc/contract.go +++ b/rpc/contract.go @@ -63,14 +63,11 @@ func (s *PublicContractService) wait(limiter *rate.Limiter, ctx context.Context) // Call executes the given transaction on the state for the given block number. // It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. func (s *PublicContractService) Call( - ctx context.Context, args CallArgs, blockNumber BlockNumber, + ctx context.Context, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, ) (hexutil.Bytes, error) { timer := DoMetricRPCRequest(Call) defer DoRPCRequestDuration(Call, timer) - // Process number based on version - blockNum := blockNumber.EthBlockNumber() - err := s.wait(s.limiterCall, ctx) if err != nil { DoMetricRPCQueryInfo(Call, RateLimitedNumber) @@ -78,7 +75,7 @@ func (s *PublicContractService) Call( } // Execute call - result, err := DoEVMCall(ctx, s.hmy, args, blockNum, CallTimeout) + result, err := DoEVMCall(ctx, s.hmy, args, blockNrOrHash, CallTimeout) if err != nil { return nil, err } @@ -93,21 +90,18 @@ func (s *PublicContractService) Call( // GetCode returns the code stored at the given address in the state for the given block number. func (s *PublicContractService) GetCode( - ctx context.Context, addr string, blockNumber BlockNumber, + ctx context.Context, addr string, blockNrOrHash rpc.BlockNumberOrHash, ) (hexutil.Bytes, error) { timer := DoMetricRPCRequest(GetCode) defer DoRPCRequestDuration(GetCode, timer) - // Process number based on version - blockNum := blockNumber.EthBlockNumber() - // Fetch state address, err := hmyCommon.ParseAddr(addr) if err != nil { DoMetricRPCQueryInfo(GetCode, FailedNumber) return nil, err } - state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNum) + state, _, err := s.hmy.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { DoMetricRPCQueryInfo(GetCode, FailedNumber) return nil, err @@ -122,15 +116,13 @@ func (s *PublicContractService) GetCode( // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. func (s *PublicContractService) GetStorageAt( - ctx context.Context, addr string, key string, blockNumber BlockNumber, + ctx context.Context, addr string, key string, blockNrOrHash rpc.BlockNumberOrHash, ) (hexutil.Bytes, error) { timer := DoMetricRPCRequest(GetStorageAt) defer DoRPCRequestDuration(GetStorageAt, timer) - // Process number based on version - blockNum := blockNumber.EthBlockNumber() // Fetch state - state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNum) + state, _, err := s.hmy.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { DoMetricRPCQueryInfo(GetStorageAt, FailedNumber) return nil, err @@ -148,7 +140,7 @@ func (s *PublicContractService) GetStorageAt( // DoEVMCall executes an EVM call func DoEVMCall( - ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNum rpc.BlockNumber, + ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, timeout time.Duration, ) (core.ExecutionResult, error) { defer func(start time.Time) { @@ -158,7 +150,7 @@ func DoEVMCall( }(time.Now()) // Fetch state - state, header, err := hmy.StateAndHeaderByNumber(ctx, blockNum) + state, header, err := hmy.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { DoMetricRPCQueryInfo(DoEvmCall, FailedNumber) return core.ExecutionResult{}, err diff --git a/rpc/eth/rpc.go b/rpc/eth/rpc.go index 1725b41077..618c6dd00c 100644 --- a/rpc/eth/rpc.go +++ b/rpc/eth/rpc.go @@ -32,13 +32,13 @@ func NewPublicEthService(hmy *hmy.Harmony, namespace string) rpc.API { // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta // block numbers are also allowed. func (s *PublicEthService) GetBalance( - ctx context.Context, address string, blockNr rpc.BlockNumber, + ctx context.Context, address string, blockNrOrHash rpc.BlockNumberOrHash, ) (*hexutil.Big, error) { addr, err := internal_common.ParseAddr(address) if err != nil { return nil, err } - balance, err := s.hmy.GetBalance(ctx, addr, blockNr) + balance, err := s.hmy.GetBalance(ctx, addr, blockNrOrHash) if err != nil { return nil, err } diff --git a/rpc/staking.go b/rpc/staking.go index 99319afe7b..e3a61721f6 100644 --- a/rpc/staking.go +++ b/rpc/staking.go @@ -80,7 +80,7 @@ func (s *PublicStakingService) getBalanceByBlockNumber( if err != nil { return nil, err } - balance, err := s.hmy.GetBalance(ctx, addr, blockNum) + balance, err := s.hmy.GetBalance(ctx, addr, rpc.BlockNumberOrHashWithNumber(blockNum)) if err != nil { return nil, err } diff --git a/rpc/transaction.go b/rpc/transaction.go index 34d9d172fb..5448935fdf 100644 --- a/rpc/transaction.go +++ b/rpc/transaction.go @@ -70,13 +70,11 @@ func (s *PublicTransactionService) GetAccountNonce( // more granular transaction counts queries // Note that the return type is an interface to account for the different versions func (s *PublicTransactionService) GetTransactionCount( - ctx context.Context, addr string, blockNumber BlockNumber, + ctx context.Context, addr string, blockNrOrHash rpc.BlockNumberOrHash, ) (response interface{}, err error) { timer := DoMetricRPCRequest(GetTransactionCount) defer DoRPCRequestDuration(GetTransactionCount, timer) - // Process arguments based on version - blockNum := blockNumber.EthBlockNumber() address, err := internal_common.ParseAddr(addr) if err != nil { return nil, err @@ -84,7 +82,7 @@ func (s *PublicTransactionService) GetTransactionCount( // Fetch transaction count var nonce uint64 - if blockNum == rpc.PendingBlockNumber { + if blockNr, ok := blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber { // Ask transaction pool for the nonce which includes pending transactions nonce, err = s.hmy.GetPoolNonce(ctx, address) if err != nil { @@ -92,7 +90,7 @@ func (s *PublicTransactionService) GetTransactionCount( } } else { // Resolve block number and use its state to ask for the nonce - state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNum) + state, _, err := s.hmy.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if err != nil { return nil, err } @@ -165,12 +163,15 @@ func (s *PublicTransactionService) GetStakingTransactionsCount( // EstimateGas returns an estimate of the amount of gas needed to execute the // given transaction against the current pending block. func (s *PublicTransactionService) EstimateGas( - ctx context.Context, args CallArgs, + ctx context.Context, args CallArgs, blockNrOrHash *rpc.BlockNumberOrHash, ) (hexutil.Uint64, error) { timer := DoMetricRPCRequest(RpcEstimateGas) defer DoRPCRequestDuration(RpcEstimateGas, timer) - - gas, err := EstimateGas(ctx, s.hmy, args, nil) + bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + if blockNrOrHash != nil { + bNrOrHash = *blockNrOrHash + } + gas, err := EstimateGas(ctx, s.hmy, args, bNrOrHash, nil) if err != nil { return 0, err } @@ -836,14 +837,13 @@ func returnHashesWithPagination(hashes []common.Hash, pageIndex uint32, pageSize } // EstimateGas - estimate gas cost for a given operation -func EstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *big.Int) (uint64, error) { +func EstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap *big.Int) (uint64, error) { // Binary search the gas requirement, as it may be higher than the amount used var ( lo uint64 = params.TxGas - 1 hi uint64 cap uint64 ) - blockNum := rpc.LatestBlockNumber // Use zero address if sender unspecified. if args.From == nil { args.From = new(common.Address) @@ -854,7 +854,7 @@ func EstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *b } else { // Retrieve the block to act as the gas ceiling - blk, err := hmy.BlockByNumber(ctx, blockNum) + blk, err := hmy.BlockByNumberOrHash(ctx, blockNrOrHash) if err != nil { return 0, err } @@ -862,7 +862,7 @@ func EstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *b } // Recap the highest gas limit with account's available balance. if args.GasPrice != nil && args.GasPrice.ToInt().BitLen() != 0 { - state, _, err := hmy.StateAndHeaderByNumber(ctx, blockNum) + state, _, err := hmy.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if err != nil { return 0, err } @@ -898,7 +898,7 @@ func EstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *b executable := func(gas uint64) (bool, *core.ExecutionResult, error) { args.Gas = (*hexutil.Uint64)(&gas) - result, err := DoEVMCall(ctx, hmy, args, blockNum, 0) + result, err := DoEVMCall(ctx, hmy, args, blockNrOrHash, 0) if err != nil { if errors.Is(err, core.ErrIntrinsicGas) { return true, nil, nil // Special case, raise gas limit diff --git a/rpc/v1/legacy.go b/rpc/v1/legacy.go index 408715b298..9fd99711de 100644 --- a/rpc/v1/legacy.go +++ b/rpc/v1/legacy.go @@ -33,13 +33,13 @@ func NewPublicLegacyAPI(hmy *hmy.Harmony, namespace string) rpc.API { // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta // block numbers are also allowed. func (s *PublicLegacyService) GetBalance( - ctx context.Context, address string, blockNr rpc.BlockNumber, + ctx context.Context, address string, blockNrOrHash rpc.BlockNumberOrHash, ) (*hexutil.Big, error) { addr, err := internal_common.ParseAddr(address) if err != nil { return nil, err } - balance, err := s.hmy.GetBalance(ctx, addr, blockNr) + balance, err := s.hmy.GetBalance(ctx, addr, blockNrOrHash) if err != nil { return nil, err } diff --git a/rpc/v2/legacy.go b/rpc/v2/legacy.go index b08f5ce9cc..d3796403fe 100644 --- a/rpc/v2/legacy.go +++ b/rpc/v2/legacy.go @@ -38,7 +38,7 @@ func (s *PublicLegacyService) GetBalance( if err != nil { return nil, err } - balance, err := s.hmy.GetBalance(ctx, addr, rpc.BlockNumber(-1)) + balance, err := s.hmy.GetBalance(ctx, addr, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)) if err != nil { return nil, err } From 8eef45eeb7e3ae29f5468658a6c76c8275468fd1 Mon Sep 17 00:00:00 2001 From: peekpi <894646171@qq.com> Date: Sat, 28 May 2022 22:35:59 +0800 Subject: [PATCH 2/3] fix compatibility of BlockNumberOrHash --- eth/rpc/types.go | 12 ++++++++---- eth/rpc/types_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/eth/rpc/types.go b/eth/rpc/types.go index b6c48fdcc6..894834885a 100644 --- a/eth/rpc/types.go +++ b/eth/rpc/types.go @@ -130,7 +130,6 @@ type BlockNumberOrHash struct { } func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { - fmt.Println("UnmarshalJSON: bnh") type erased BlockNumberOrHash e := erased{} err := json.Unmarshal(data, &e) @@ -144,9 +143,14 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { return nil } var input string - err = json.Unmarshal(data, &input) - if err != nil { - return err + if err := json.Unmarshal(data, &input); err != nil { + var numInput int64 // old hmy rpc use number type as input + if err := json.Unmarshal(data, &numInput); err != nil { + return err + } + bn := BlockNumber(numInput) + bnh.BlockNumber = &bn + return nil } switch input { case "earliest": diff --git a/eth/rpc/types_test.go b/eth/rpc/types_test.go index f110dee7c6..461a92912d 100644 --- a/eth/rpc/types_test.go +++ b/eth/rpc/types_test.go @@ -153,3 +153,30 @@ func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { }) } } + +func TestBlockNumberOrHash_Unmarshal_Compatibility(t *testing.T) { + tests := []struct { + name string + number int64 + }{ + {"max", math.MaxInt64}, + {"pending", int64(PendingBlockNumber)}, + {"latest", int64(LatestBlockNumber)}, + {"earliest", int64(EarliestBlockNumber)}, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + input, _ := json.Marshal(test.number) + var unmarshalled BlockNumberOrHash + err := json.Unmarshal([]byte(input), &unmarshalled) + if err != nil { + t.Fatal("cannot unmarshal:", err) + } + if number, isNumber := unmarshalled.Number(); !isNumber || int64(number) != test.number { + t.Fatalf("wrong result: expected %v, got %v", test.number, unmarshalled) + } + }) + } +} From cd0b2fb3d253de27a0e3ff1a5f388c03f399f9bf Mon Sep 17 00:00:00 2001 From: peekpi <894646171@qq.com> Date: Sat, 28 May 2022 23:36:53 +0800 Subject: [PATCH 3/3] fix unit test --- eth/rpc/types_test.go | 2 +- scripts/travis_rpc_checker.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/rpc/types_test.go b/eth/rpc/types_test.go index 461a92912d..601ef805f5 100644 --- a/eth/rpc/types_test.go +++ b/eth/rpc/types_test.go @@ -82,7 +82,7 @@ func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { 6: {`"0x12"`, false, BlockNumberOrHashWithNumber(18)}, 7: {`"0x7fffffffffffffff"`, false, BlockNumberOrHashWithNumber(math.MaxInt64)}, 8: {`"0x8000000000000000"`, true, BlockNumberOrHash{}}, - 9: {"0", true, BlockNumberOrHash{}}, + 9: {"0", false, BlockNumberOrHashWithNumber(0)}, // different from eth, because we need to keep compatibility with old hmy rpc 10: {`"ff"`, true, BlockNumberOrHash{}}, 11: {`"pending"`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, 12: {`"latest"`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, diff --git a/scripts/travis_rpc_checker.sh b/scripts/travis_rpc_checker.sh index 6f0529bdb0..23aedb528a 100755 --- a/scripts/travis_rpc_checker.sh +++ b/scripts/travis_rpc_checker.sh @@ -5,7 +5,7 @@ CACHE_DIR="docker_images" mkdir -p $CACHE_DIR echo "pulling cached docker img" docker load -i $CACHE_DIR/images.tar || true -docker pull harmonyone/localnet-test +#docker pull harmonyone/localnet-test echo "saving cached docker img" docker save -o $CACHE_DIR/images.tar harmonyone/localnet-test docker run -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test -n \ No newline at end of file