align eip-1898: support block number or hash on state-related methods (#4181)

* align eip-1898: support block number or hash on state-related methods

* fix compatibility of BlockNumberOrHash

* fix unit test
pull/4185/head
PeekPI 2 years ago committed by GitHub
parent 1a3018db1b
commit 2367a0da23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      core/blockchain.go
  2. 4
      core/headerchain.go
  3. 27
      eth/rpc/types.go
  4. 60
      eth/rpc/types_test.go
  5. 46
      hmy/blockchain.go
  6. 4
      rosetta/services/account.go
  7. 6
      rosetta/services/call_service.go
  8. 7
      rosetta/services/construction_check.go
  9. 22
      rpc/blockchain.go
  10. 24
      rpc/contract.go
  11. 4
      rpc/eth/rpc.go
  12. 2
      rpc/staking.go
  13. 26
      rpc/transaction.go
  14. 4
      rpc/v1/legacy.go
  15. 2
      rpc/v2/legacy.go
  16. 2
      scripts/travis_rpc_checker.sh

@ -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.

@ -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 {

@ -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)
}
@ -127,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":

@ -18,6 +18,7 @@ package rpc
import (
"encoding/json"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
@ -81,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)},
@ -122,3 +123,60 @@ 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)
}
})
}
}
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)
}
})
}
}

@ -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 {

@ -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",

@ -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(),

@ -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{}{

@ -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)

@ -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

@ -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
}

@ -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
}

@ -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

@ -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
}

@ -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
}

@ -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
Loading…
Cancel
Save