parent
f58f22c5e1
commit
a4479d62d0
@ -0,0 +1,84 @@ |
|||||||
|
package apiv1 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/ethdb" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
"github.com/harmony-one/harmony/accounts" |
||||||
|
"github.com/harmony-one/harmony/block" |
||||||
|
"github.com/harmony-one/harmony/core" |
||||||
|
"github.com/harmony-one/harmony/core/state" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
"github.com/harmony-one/harmony/core/vm" |
||||||
|
"github.com/harmony-one/harmony/internal/params" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
) |
||||||
|
|
||||||
|
// Backend interface provides the common API services (that are provided by
|
||||||
|
// both full and light clients) with access to necessary functions.
|
||||||
|
// implementations:
|
||||||
|
// * hmy/api_backend.go
|
||||||
|
type Backend interface { |
||||||
|
// NOTE(ricl): this is not in ETH Backend inteface. They put it directly in eth object.
|
||||||
|
NetVersion() uint64 |
||||||
|
// General Ethereum API
|
||||||
|
// Downloader() *downloader.Downloader
|
||||||
|
ProtocolVersion() int |
||||||
|
// SuggestPrice(ctx context.Context) (*big.Int, error)
|
||||||
|
ChainDb() ethdb.Database |
||||||
|
EventMux() *event.TypeMux |
||||||
|
AccountManager() *accounts.Manager |
||||||
|
// ExtRPCEnabled() bool
|
||||||
|
RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection
|
||||||
|
// BlockChain API
|
||||||
|
// SetHead(number uint64)
|
||||||
|
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) |
||||||
|
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) |
||||||
|
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.DB, *block.Header, error) |
||||||
|
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) |
||||||
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) |
||||||
|
// GetTd(blockHash common.Hash) *big.Int
|
||||||
|
GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) |
||||||
|
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription |
||||||
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription |
||||||
|
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription |
||||||
|
// TxPool API
|
||||||
|
SendTx(ctx context.Context, signedTx *types.Transaction) error |
||||||
|
// GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
||||||
|
GetPoolTransactions() (types.Transactions, error) |
||||||
|
GetPoolTransaction(txHash common.Hash) *types.Transaction |
||||||
|
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) |
||||||
|
// Stats() (pending int, queued int)
|
||||||
|
// TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
||||||
|
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription |
||||||
|
ChainConfig() *params.ChainConfig |
||||||
|
CurrentBlock() *types.Block |
||||||
|
// Get balance
|
||||||
|
GetBalance(address common.Address) (*big.Int, error) |
||||||
|
// Get validators for a particular epoch
|
||||||
|
GetValidators(epoch *big.Int) (*shard.Committee, error) |
||||||
|
GetShardID() uint32 |
||||||
|
// Get transactions history for an address
|
||||||
|
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
||||||
|
// retrieve the blockHash using txID and add blockHash to CxPool for resending
|
||||||
|
ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) |
||||||
|
IsLeader() bool |
||||||
|
SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error |
||||||
|
GetActiveValidatorAddresses() []common.Address |
||||||
|
GetAllValidatorAddresses() []common.Address |
||||||
|
GetValidatorInformation(addr common.Address) *staking.Validator |
||||||
|
GetValidatorStats(addr common.Address) *staking.ValidatorStats |
||||||
|
GetDelegationsByValidator(validator common.Address) []*staking.Delegation |
||||||
|
GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) |
||||||
|
GetValidatorSelfDelegation(addr common.Address) *big.Int |
||||||
|
GetShardState() (*shard.State, error) |
||||||
|
GetCurrentStakingErrorSink() []staking.RPCTransactionError |
||||||
|
GetCurrentTransactionErrorSink() []types.RPCTransactionError |
||||||
|
IsBeaconChainExplorerNode() bool |
||||||
|
GetMedianRawStakeSnapshot() *big.Int |
||||||
|
} |
@ -0,0 +1,635 @@ |
|||||||
|
package apiv1 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/common/math" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
"github.com/harmony-one/bls/ffi/go/bls" |
||||||
|
"github.com/harmony-one/harmony/common/denominations" |
||||||
|
"github.com/harmony-one/harmony/core" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
"github.com/harmony-one/harmony/core/vm" |
||||||
|
internal_bls "github.com/harmony-one/harmony/crypto/bls" |
||||||
|
internal_common "github.com/harmony-one/harmony/internal/common" |
||||||
|
"github.com/harmony-one/harmony/internal/utils" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
defaultGasPrice = denominations.Nano |
||||||
|
defaultFromAddress = "0x0000000000000000000000000000000000000000" |
||||||
|
defaultBlocksPeriod = 15000 |
||||||
|
) |
||||||
|
|
||||||
|
// PublicBlockChainAPI provides an API to access the Harmony blockchain.
|
||||||
|
// It offers only methods that operate on public data that is freely available to anyone.
|
||||||
|
type PublicBlockChainAPI struct { |
||||||
|
b Backend |
||||||
|
} |
||||||
|
|
||||||
|
// NewPublicBlockChainAPI creates a new Harmony blockchain API.
|
||||||
|
func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { |
||||||
|
return &PublicBlockChainAPI{b} |
||||||
|
} |
||||||
|
|
||||||
|
// BlockArgs is struct to include optional block formatting params.
|
||||||
|
type BlockArgs struct { |
||||||
|
WithSigners bool `json:"withSigners"` |
||||||
|
InclTx bool `json:"inclTx"` |
||||||
|
FullTx bool `json:"fullTx"` |
||||||
|
Signers []string `json:"signers"` |
||||||
|
InclStaking bool `json:"inclStaking"` |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all
|
||||||
|
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
|
||||||
|
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { |
||||||
|
block, err := s.b.BlockByNumber(ctx, blockNr) |
||||||
|
if block != nil { |
||||||
|
blockArgs := BlockArgs{WithSigners: false, InclTx: true, FullTx: fullTx, InclStaking: true} |
||||||
|
response, err := RPCMarshalBlock(block, blockArgs) |
||||||
|
if err == nil && blockNr == rpc.PendingBlockNumber { |
||||||
|
// Pending blocks need to nil out a few fields
|
||||||
|
for _, field := range []string{"hash", "nonce", "miner"} { |
||||||
|
response[field] = nil |
||||||
|
} |
||||||
|
} |
||||||
|
return response, err |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
|
||||||
|
// detail, otherwise only the transaction hash is returned.
|
||||||
|
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { |
||||||
|
block, err := s.b.GetBlock(ctx, blockHash) |
||||||
|
if block != nil { |
||||||
|
blockArgs := BlockArgs{WithSigners: false, InclTx: true, FullTx: fullTx, InclStaking: true} |
||||||
|
return RPCMarshalBlock(block, blockArgs) |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockByNumberNew returns the requested block. When blockNr is -1 the chain head is returned. When fullTx in blockArgs is true all
|
||||||
|
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned. When withSigners in BlocksArgs is true
|
||||||
|
// it shows block signers for this block in list of one addresses.
|
||||||
|
func (s *PublicBlockChainAPI) GetBlockByNumberNew(ctx context.Context, blockNr rpc.BlockNumber, blockArgs BlockArgs) (map[string]interface{}, error) { |
||||||
|
block, err := s.b.BlockByNumber(ctx, blockNr) |
||||||
|
blockArgs.InclTx = true |
||||||
|
if blockArgs.WithSigners { |
||||||
|
blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNr) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
if block != nil { |
||||||
|
response, err := RPCMarshalBlock(block, blockArgs) |
||||||
|
if err == nil && blockNr == rpc.PendingBlockNumber { |
||||||
|
// Pending blocks need to nil out a few fields
|
||||||
|
for _, field := range []string{"hash", "nonce", "miner"} { |
||||||
|
response[field] = nil |
||||||
|
} |
||||||
|
} |
||||||
|
return response, err |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockByHashNew returns the requested block. When fullTx in blockArgs is true all transactions in the block are returned in full
|
||||||
|
// detail, otherwise only the transaction hash is returned. When withSigners in BlocksArgs is true
|
||||||
|
// it shows block signers for this block in list of one addresses.
|
||||||
|
func (s *PublicBlockChainAPI) GetBlockByHashNew(ctx context.Context, blockHash common.Hash, blockArgs BlockArgs) (map[string]interface{}, error) { |
||||||
|
block, err := s.b.GetBlock(ctx, blockHash) |
||||||
|
blockArgs.InclTx = true |
||||||
|
if blockArgs.WithSigners { |
||||||
|
blockArgs.Signers, err = s.GetBlockSigners(ctx, rpc.BlockNumber(block.NumberU64())) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
if block != nil { |
||||||
|
return RPCMarshalBlock(block, blockArgs) |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlocks method returns blocks in range blockStart, blockEnd just like GetBlockByNumber but all at once.
|
||||||
|
func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart rpc.BlockNumber, blockEnd rpc.BlockNumber, blockArgs BlockArgs) ([]map[string]interface{}, error) { |
||||||
|
result := make([]map[string]interface{}, 0) |
||||||
|
for i := blockStart; i <= blockEnd; i++ { |
||||||
|
block, err := s.b.BlockByNumber(ctx, i) |
||||||
|
blockArgs.InclTx = true |
||||||
|
if blockArgs.WithSigners { |
||||||
|
blockArgs.Signers, err = s.GetBlockSigners(ctx, rpc.BlockNumber(i)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
if block != nil { |
||||||
|
rpcBlock, err := RPCMarshalBlock(block, blockArgs) |
||||||
|
if err == nil && i == rpc.PendingBlockNumber { |
||||||
|
// Pending blocks need to nil out a few fields
|
||||||
|
for _, field := range []string{"hash", "nonce", "miner"} { |
||||||
|
rpcBlock[field] = nil |
||||||
|
} |
||||||
|
} |
||||||
|
result = append(result, rpcBlock) |
||||||
|
} |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidators returns validators list for a particular epoch.
|
||||||
|
func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (map[string]interface{}, error) { |
||||||
|
committee, err := s.b.GetValidators(big.NewInt(epoch)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
validators := make([]map[string]interface{}, 0) |
||||||
|
for _, validator := range committee.Slots { |
||||||
|
validatorBalance, err := s.b.GetBalance(validator.EcdsaAddress) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
validatorsFields := map[string]interface{}{ |
||||||
|
"address": oneAddress, |
||||||
|
"balance": (*hexutil.Big)(validatorBalance), |
||||||
|
} |
||||||
|
validators = append(validators, validatorsFields) |
||||||
|
} |
||||||
|
result := map[string]interface{}{ |
||||||
|
"shardID": committee.ShardID, |
||||||
|
"validators": validators, |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockSigners returns signers for a particular block.
|
||||||
|
func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) ([]string, error) { |
||||||
|
if uint64(blockNr) == 0 || uint64(blockNr) >= uint64(s.BlockNumber()) { |
||||||
|
return make([]string, 0), nil |
||||||
|
} |
||||||
|
block, err := s.b.BlockByNumber(ctx, blockNr) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
blockWithSigners, err := s.b.BlockByNumber(ctx, blockNr+1) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
committee, err := s.b.GetValidators(block.Epoch()) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
pubkeys := make([]*bls.PublicKey, len(committee.Slots)) |
||||||
|
for i, validator := range committee.Slots { |
||||||
|
pubkeys[i] = new(bls.PublicKey) |
||||||
|
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i]) |
||||||
|
} |
||||||
|
result := make([]string, 0) |
||||||
|
mask, err := internal_bls.NewMask(pubkeys, nil) |
||||||
|
if err != nil { |
||||||
|
return result, err |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return result, err |
||||||
|
} |
||||||
|
err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) |
||||||
|
if err != nil { |
||||||
|
return result, err |
||||||
|
} |
||||||
|
for _, validator := range committee.Slots { |
||||||
|
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) |
||||||
|
if err != nil { |
||||||
|
return result, err |
||||||
|
} |
||||||
|
blsPublicKey := new(bls.PublicKey) |
||||||
|
validator.BlsPublicKey.ToLibBLSPublicKey(blsPublicKey) |
||||||
|
if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok { |
||||||
|
result = append(result, oneAddress) |
||||||
|
} |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// IsBlockSigner returns true if validator with address signed blockNr block.
|
||||||
|
func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr rpc.BlockNumber, address string) (bool, error) { |
||||||
|
if uint64(blockNr) == 0 || uint64(blockNr) >= uint64(s.BlockNumber()) { |
||||||
|
return false, nil |
||||||
|
} |
||||||
|
block, err := s.b.BlockByNumber(ctx, blockNr) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
blockWithSigners, err := s.b.BlockByNumber(ctx, blockNr+1) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
committee, err := s.b.GetValidators(block.Epoch()) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
pubkeys := make([]*bls.PublicKey, len(committee.Slots)) |
||||||
|
for i, validator := range committee.Slots { |
||||||
|
pubkeys[i] = new(bls.PublicKey) |
||||||
|
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i]) |
||||||
|
} |
||||||
|
mask, err := internal_bls.NewMask(pubkeys, nil) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
for _, validator := range committee.Slots { |
||||||
|
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
if oneAddress != address { |
||||||
|
continue |
||||||
|
} |
||||||
|
blsPublicKey := new(bls.PublicKey) |
||||||
|
validator.BlsPublicKey.ToLibBLSPublicKey(blsPublicKey) |
||||||
|
if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok { |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
} |
||||||
|
return false, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetSignedBlocks returns how many blocks a particular validator signed for last defaultBlocksPeriod (3 hours ~ 1500 blocks).
|
||||||
|
func (s *PublicBlockChainAPI) GetSignedBlocks(ctx context.Context, address string) hexutil.Uint64 { |
||||||
|
totalSigned := uint64(0) |
||||||
|
lastBlock := uint64(0) |
||||||
|
blockHeight := uint64(s.BlockNumber()) |
||||||
|
if blockHeight >= defaultBlocksPeriod { |
||||||
|
lastBlock = blockHeight - defaultBlocksPeriod + 1 |
||||||
|
} |
||||||
|
for i := lastBlock; i <= blockHeight; i++ { |
||||||
|
signed, err := s.IsBlockSigner(ctx, rpc.BlockNumber(i), address) |
||||||
|
if err == nil && signed { |
||||||
|
totalSigned++ |
||||||
|
} |
||||||
|
} |
||||||
|
return hexutil.Uint64(totalSigned) |
||||||
|
} |
||||||
|
|
||||||
|
// GetEpoch returns current epoch.
|
||||||
|
func (s *PublicBlockChainAPI) GetEpoch(ctx context.Context) hexutil.Uint64 { |
||||||
|
return hexutil.Uint64(s.LatestHeader(ctx).Epoch) |
||||||
|
} |
||||||
|
|
||||||
|
// GetLeader returns current shard leader.
|
||||||
|
func (s *PublicBlockChainAPI) GetLeader(ctx context.Context) string { |
||||||
|
return s.LatestHeader(ctx).Leader |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidatorSelfDelegation returns validator stake.
|
||||||
|
func (s *PublicBlockChainAPI) GetValidatorSelfDelegation(ctx context.Context, address string) hexutil.Uint64 { |
||||||
|
return hexutil.Uint64(s.b.GetValidatorSelfDelegation(internal_common.ParseAddr(address)).Uint64()) |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidatorTotalDelegation returns total balace stacking for validator with delegation.
|
||||||
|
func (s *PublicBlockChainAPI) GetValidatorTotalDelegation(ctx context.Context, address string) hexutil.Uint64 { |
||||||
|
delegations := s.b.GetDelegationsByValidator(internal_common.ParseAddr(address)) |
||||||
|
totalStake := big.NewInt(0) |
||||||
|
for _, delegation := range delegations { |
||||||
|
totalStake.Add(totalStake, delegation.Amount) |
||||||
|
} |
||||||
|
// TODO: return more than uint64
|
||||||
|
return hexutil.Uint64(totalStake.Uint64()) |
||||||
|
} |
||||||
|
|
||||||
|
// GetShardingStructure returns an array of sharding structures.
|
||||||
|
func (s *PublicBlockChainAPI) GetShardingStructure(ctx context.Context) ([]map[string]interface{}, error) { |
||||||
|
// Get header and number of shards.
|
||||||
|
epoch := s.GetEpoch(ctx) |
||||||
|
numShard := shard.Schedule.InstanceForEpoch(big.NewInt(int64(epoch))).NumShards() |
||||||
|
|
||||||
|
// Return shareding structure for each case.
|
||||||
|
return shard.Schedule.GetShardingStructure(int(numShard), int(s.b.GetShardID())), nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetShardID returns shard ID of the requested node.
|
||||||
|
func (s *PublicBlockChainAPI) GetShardID(ctx context.Context) (int, error) { |
||||||
|
return int(s.b.GetShardID()), nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetCode returns the code stored at the given address in the state for the given block number.
|
||||||
|
func (s *PublicBlockChainAPI) GetCode(ctx context.Context, addr string, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { |
||||||
|
address := internal_common.ParseAddr(addr) |
||||||
|
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) |
||||||
|
if state == nil || err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
code := state.GetCode(address) |
||||||
|
return code, state.Error() |
||||||
|
} |
||||||
|
|
||||||
|
// GetStorageAt returns the storage from the state at the given address, key and
|
||||||
|
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
|
||||||
|
// numbers are also allowed.
|
||||||
|
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, addr string, key string, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { |
||||||
|
address := internal_common.ParseAddr(addr) |
||||||
|
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) |
||||||
|
if state == nil || err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
res := state.GetState(address, common.HexToHash(key)) |
||||||
|
return res[:], state.Error() |
||||||
|
} |
||||||
|
|
||||||
|
// GetBalance returns the amount of Nano for the given address in the state of the
|
||||||
|
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
|
||||||
|
// block numbers are also allowed.
|
||||||
|
func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address string, blockNr rpc.BlockNumber) (*hexutil.Big, error) { |
||||||
|
// TODO: currently only get latest balance. Will add complete logic later.
|
||||||
|
addr := internal_common.ParseAddr(address) |
||||||
|
balance, err := s.b.GetBalance(addr) |
||||||
|
if balance == nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return (*hexutil.Big)(balance), err |
||||||
|
} |
||||||
|
|
||||||
|
// BlockNumber returns the block number of the chain head.
|
||||||
|
func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 { |
||||||
|
header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
|
||||||
|
return hexutil.Uint64(header.Number().Uint64()) |
||||||
|
} |
||||||
|
|
||||||
|
// ResendCx requests that the egress receipt for the given cross-shard
|
||||||
|
// transaction be sent to the destination shard for credit. This is used for
|
||||||
|
// unblocking a half-complete cross-shard transaction whose fund has been
|
||||||
|
// withdrawn already from the source shard but not credited yet in the
|
||||||
|
// destination account due to transient failures.
|
||||||
|
func (s *PublicBlockChainAPI) ResendCx(ctx context.Context, txID common.Hash) (bool, error) { |
||||||
|
_, success := s.b.ResendCx(ctx, txID) |
||||||
|
return success, nil |
||||||
|
} |
||||||
|
|
||||||
|
// 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 *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { |
||||||
|
result, _, _, err := doCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second, s.b.RPCGasCap()) |
||||||
|
return (hexutil.Bytes)(result), err |
||||||
|
} |
||||||
|
|
||||||
|
func doCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { |
||||||
|
defer func(start time.Time) { |
||||||
|
utils.Logger().Debug(). |
||||||
|
Dur("runtime", time.Since(start)). |
||||||
|
Msg("Executing EVM call finished") |
||||||
|
}(time.Now()) |
||||||
|
|
||||||
|
state, header, err := b.StateAndHeaderByNumber(ctx, blockNr) |
||||||
|
if state == nil || err != nil { |
||||||
|
return nil, 0, false, err |
||||||
|
} |
||||||
|
// Set sender address or use a default if none specified
|
||||||
|
var addr common.Address |
||||||
|
if args.From == nil { |
||||||
|
// TODO(ricl): this logic was borrowed from [go-ethereum](https://github.com/ethereum/go-ethereum/blob/f578d41ee6b3087f8021fd561a0b5665aea3dba6/internal/ethapi/api.go#L738)
|
||||||
|
// [question](https://ethereum.stackexchange.com/questions/72979/why-does-the-docall-function-use-the-first-account-by-default)
|
||||||
|
// Might need to reconsider the logic
|
||||||
|
// if wallets := b.AccountManager().Wallets(); len(wallets) > 0 {
|
||||||
|
// if accounts := wallets[0].Accounts(); len(accounts) > 0 {
|
||||||
|
// addr = accounts[0].Address
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// The logic in ethereum is to pick a random address managed under the account manager.
|
||||||
|
// Currently Harmony no longers support the account manager.
|
||||||
|
// Any address does not affect the logic of this call.
|
||||||
|
addr = common.HexToAddress(defaultFromAddress) |
||||||
|
} else { |
||||||
|
addr = *args.From |
||||||
|
} |
||||||
|
// Set default gas & gas price if none were set
|
||||||
|
gas := uint64(math.MaxUint64 / 2) |
||||||
|
if args.Gas != nil { |
||||||
|
gas = uint64(*args.Gas) |
||||||
|
} |
||||||
|
if globalGasCap != nil && globalGasCap.Uint64() < gas { |
||||||
|
utils.Logger().Warn(). |
||||||
|
Uint64("requested", gas). |
||||||
|
Uint64("cap", globalGasCap.Uint64()). |
||||||
|
Msg("Caller gas above allowance, capping") |
||||||
|
gas = globalGasCap.Uint64() |
||||||
|
} |
||||||
|
gasPrice := new(big.Int).SetUint64(defaultGasPrice) |
||||||
|
if args.GasPrice != nil { |
||||||
|
gasPrice = args.GasPrice.ToInt() |
||||||
|
} |
||||||
|
|
||||||
|
value := new(big.Int) |
||||||
|
if args.Value != nil { |
||||||
|
value = args.Value.ToInt() |
||||||
|
} |
||||||
|
|
||||||
|
var data []byte |
||||||
|
if args.Data != nil { |
||||||
|
data = []byte(*args.Data) |
||||||
|
} |
||||||
|
|
||||||
|
// Create new call message
|
||||||
|
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) |
||||||
|
|
||||||
|
// Setup context so it may be cancelled the call has completed
|
||||||
|
// or, in case of unmetered gas, setup a context with a timeout.
|
||||||
|
var cancel context.CancelFunc |
||||||
|
if timeout > 0 { |
||||||
|
ctx, cancel = context.WithTimeout(ctx, timeout) |
||||||
|
} else { |
||||||
|
ctx, cancel = context.WithCancel(ctx) |
||||||
|
} |
||||||
|
// Make sure the context is cancelled when the call has completed
|
||||||
|
// this makes sure resources are cleaned up.
|
||||||
|
defer cancel() |
||||||
|
|
||||||
|
// Get a new instance of the EVM.
|
||||||
|
evm, vmError, err := b.GetEVM(ctx, msg, state, header) |
||||||
|
if err != nil { |
||||||
|
return nil, 0, false, err |
||||||
|
} |
||||||
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
|
go func() { |
||||||
|
<-ctx.Done() |
||||||
|
evm.Cancel() |
||||||
|
}() |
||||||
|
|
||||||
|
// Setup the gas pool (also for unmetered requests)
|
||||||
|
// and apply the message.
|
||||||
|
gp := new(core.GasPool).AddGas(math.MaxUint64) |
||||||
|
res, gas, failed, err := core.ApplyMessage(evm, msg, gp) |
||||||
|
if err := vmError(); err != nil { |
||||||
|
return nil, 0, false, err |
||||||
|
} |
||||||
|
// If the timer caused an abort, return an appropriate error message
|
||||||
|
if evm.Cancelled() { |
||||||
|
return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout) |
||||||
|
} |
||||||
|
return res, gas, failed, err |
||||||
|
} |
||||||
|
|
||||||
|
// LatestHeader returns the latest header information
|
||||||
|
func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformation { |
||||||
|
header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
|
||||||
|
return newHeaderInformation(header) |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
errNotExplorerNode = errors.New("cannot call this rpc on non beaconchain explorer node") |
||||||
|
) |
||||||
|
|
||||||
|
// GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain
|
||||||
|
// explorer node
|
||||||
|
func (s *PublicBlockChainAPI) GetMedianRawStakeSnapshot() (uint64, error) { |
||||||
|
if s.b.IsBeaconChainExplorerNode() { |
||||||
|
return s.GetMedianRawStakeSnapshot() |
||||||
|
} |
||||||
|
return 0, errNotExplorerNode |
||||||
|
} |
||||||
|
|
||||||
|
// GetAllValidatorAddresses returns all validator addresses.
|
||||||
|
func (s *PublicBlockChainAPI) GetAllValidatorAddresses() ([]string, error) { |
||||||
|
addresses := []string{} |
||||||
|
for _, addr := range s.b.GetAllValidatorAddresses() { |
||||||
|
oneAddr, _ := internal_common.AddressToBech32(addr) |
||||||
|
addresses = append(addresses, oneAddr) |
||||||
|
} |
||||||
|
return addresses, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetActiveValidatorAddresses returns active validator addresses.
|
||||||
|
func (s *PublicBlockChainAPI) GetActiveValidatorAddresses() ([]string, error) { |
||||||
|
addresses := []string{} |
||||||
|
for _, addr := range s.b.GetActiveValidatorAddresses() { |
||||||
|
oneAddr, _ := internal_common.AddressToBech32(addr) |
||||||
|
addresses = append(addresses, oneAddr) |
||||||
|
} |
||||||
|
return addresses, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidatorMetrics ..
|
||||||
|
func (s *PublicBlockChainAPI) GetValidatorMetrics(ctx context.Context, address string) (*staking.ValidatorStats, error) { |
||||||
|
validatorAddress := internal_common.ParseAddr(address) |
||||||
|
stats := s.b.GetValidatorStats(validatorAddress) |
||||||
|
if stats == nil { |
||||||
|
return nil, fmt.Errorf("validator stats not found: %s", validatorAddress.Hex()) |
||||||
|
} |
||||||
|
return stats, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidatorInformation returns information about a validator.
|
||||||
|
func (s *PublicBlockChainAPI) GetValidatorInformation(ctx context.Context, address string) (*staking.Validator, error) { |
||||||
|
validatorAddress := internal_common.ParseAddr(address) |
||||||
|
validator := s.b.GetValidatorInformation(validatorAddress) |
||||||
|
if validator == nil { |
||||||
|
return nil, fmt.Errorf("validator not found: %s", validatorAddress.Hex()) |
||||||
|
} |
||||||
|
return validator, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetDelegationsByDelegator returns list of delegations for a delegator address.
|
||||||
|
func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, address string) ([]*RPCDelegation, error) { |
||||||
|
delegatorAddress := internal_common.ParseAddr(address) |
||||||
|
validators, delegations := s.b.GetDelegationsByDelegator(delegatorAddress) |
||||||
|
result := []*RPCDelegation{} |
||||||
|
for i := range delegations { |
||||||
|
delegation := delegations[i] |
||||||
|
|
||||||
|
undelegations := []RPCUndelegation{} |
||||||
|
|
||||||
|
for j := range delegation.Undelegations { |
||||||
|
undelegations = append(undelegations, RPCUndelegation{ |
||||||
|
delegation.Undelegations[j].Amount, |
||||||
|
delegation.Undelegations[j].Epoch, |
||||||
|
}) |
||||||
|
} |
||||||
|
valAddr, _ := internal_common.AddressToBech32(validators[i]) |
||||||
|
delAddr, _ := internal_common.AddressToBech32(delegatorAddress) |
||||||
|
result = append(result, &RPCDelegation{ |
||||||
|
valAddr, |
||||||
|
delAddr, |
||||||
|
delegation.Amount, |
||||||
|
delegation.Reward, |
||||||
|
undelegations, |
||||||
|
}) |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetDelegationsByValidator returns list of delegations for a validator address.
|
||||||
|
func (s *PublicBlockChainAPI) GetDelegationsByValidator(ctx context.Context, address string) ([]*RPCDelegation, error) { |
||||||
|
validatorAddress := internal_common.ParseAddr(address) |
||||||
|
delegations := s.b.GetDelegationsByValidator(validatorAddress) |
||||||
|
result := make([]*RPCDelegation, 0) |
||||||
|
for _, delegation := range delegations { |
||||||
|
|
||||||
|
undelegations := []RPCUndelegation{} |
||||||
|
|
||||||
|
for j := range delegation.Undelegations { |
||||||
|
undelegations = append(undelegations, RPCUndelegation{ |
||||||
|
delegation.Undelegations[j].Amount, |
||||||
|
delegation.Undelegations[j].Epoch, |
||||||
|
}) |
||||||
|
} |
||||||
|
valAddr, _ := internal_common.AddressToBech32(validatorAddress) |
||||||
|
delAddr, _ := internal_common.AddressToBech32(delegation.DelegatorAddress) |
||||||
|
result = append(result, &RPCDelegation{ |
||||||
|
valAddr, |
||||||
|
delAddr, |
||||||
|
delegation.Amount, |
||||||
|
delegation.Reward, |
||||||
|
undelegations, |
||||||
|
}) |
||||||
|
} |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetDelegationByDelegatorAndValidator returns a delegation for delegator and validator.
|
||||||
|
func (s *PublicBlockChainAPI) GetDelegationByDelegatorAndValidator(ctx context.Context, address string, validator string) (*RPCDelegation, error) { |
||||||
|
delegatorAddress := internal_common.ParseAddr(address) |
||||||
|
validatorAddress := internal_common.ParseAddr(validator) |
||||||
|
validators, delegations := s.b.GetDelegationsByDelegator(delegatorAddress) |
||||||
|
for i := range delegations { |
||||||
|
if validators[i] != validatorAddress { |
||||||
|
continue |
||||||
|
} |
||||||
|
delegation := delegations[i] |
||||||
|
|
||||||
|
undelegations := []RPCUndelegation{} |
||||||
|
|
||||||
|
for j := range delegation.Undelegations { |
||||||
|
undelegations = append(undelegations, RPCUndelegation{ |
||||||
|
delegation.Undelegations[j].Amount, |
||||||
|
delegation.Undelegations[j].Epoch, |
||||||
|
}) |
||||||
|
} |
||||||
|
valAddr, _ := internal_common.AddressToBech32(validatorAddress) |
||||||
|
delAddr, _ := internal_common.AddressToBech32(delegatorAddress) |
||||||
|
return &RPCDelegation{ |
||||||
|
valAddr, |
||||||
|
delAddr, |
||||||
|
delegation.Amount, |
||||||
|
delegation.Reward, |
||||||
|
undelegations, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
return nil, nil |
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package hmyapi |
package apiv1 |
||||||
|
|
||||||
import ( |
import ( |
||||||
"context" |
"context" |
@ -1,4 +1,4 @@ |
|||||||
package hmyapi |
package apiv1 |
||||||
|
|
||||||
import "errors" |
import "errors" |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package hmyapi |
package apiv1 |
||||||
|
|
||||||
import ( |
import ( |
||||||
"fmt" |
"fmt" |
@ -1,4 +1,4 @@ |
|||||||
package hmyapi |
package apiv1 |
||||||
|
|
||||||
import ( |
import ( |
||||||
"github.com/ethereum/go-ethereum/common" |
"github.com/ethereum/go-ethereum/common" |
@ -0,0 +1,368 @@ |
|||||||
|
package apiv1 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
"github.com/harmony-one/harmony/accounts" |
||||||
|
"github.com/harmony-one/harmony/core/rawdb" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
internal_common "github.com/harmony-one/harmony/internal/common" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
"github.com/pkg/errors" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrInvalidChainID when ChainID of signer does not match that of running node
|
||||||
|
errInvalidChainID = errors.New("invalid chain id for signer") |
||||||
|
) |
||||||
|
|
||||||
|
// TxHistoryArgs is struct to make GetTransactionsHistory request
|
||||||
|
type TxHistoryArgs struct { |
||||||
|
Address string `json:"address"` |
||||||
|
PageIndex uint32 `json:"pageIndex"` |
||||||
|
PageSize uint32 `json:"pageSize"` |
||||||
|
FullTx bool `json:"fullTx"` |
||||||
|
TxType string `json:"txType"` |
||||||
|
Order string `json:"order"` |
||||||
|
} |
||||||
|
|
||||||
|
// PublicTransactionPoolAPI exposes methods for the RPC interface
|
||||||
|
type PublicTransactionPoolAPI struct { |
||||||
|
b Backend |
||||||
|
nonceLock *AddrLocker |
||||||
|
} |
||||||
|
|
||||||
|
// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
|
||||||
|
func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI { |
||||||
|
return &PublicTransactionPoolAPI{b, nonceLock} |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionsHistory returns the list of transactions hashes that involve a particular address.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, args TxHistoryArgs) (map[string]interface{}, error) { |
||||||
|
address := args.Address |
||||||
|
result := []common.Hash{} |
||||||
|
var err error |
||||||
|
if strings.HasPrefix(args.Address, "one1") { |
||||||
|
address = args.Address |
||||||
|
} else { |
||||||
|
addr := internal_common.ParseAddr(args.Address) |
||||||
|
address, err = internal_common.AddressToBech32(addr) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
hashes, err := s.b.GetTransactionsHistory(address, args.TxType, args.Order) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
result = ReturnWithPagination(hashes, args) |
||||||
|
if !args.FullTx { |
||||||
|
return map[string]interface{}{"transactions": result}, nil |
||||||
|
} |
||||||
|
txs := []*RPCTransaction{} |
||||||
|
for _, hash := range result { |
||||||
|
tx := s.GetTransactionByHash(ctx, hash) |
||||||
|
txs = append(txs, tx) |
||||||
|
} |
||||||
|
return map[string]interface{}{"transactions": txs}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { |
||||||
|
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { |
||||||
|
n := hexutil.Uint(len(block.Transactions())) |
||||||
|
return &n |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { |
||||||
|
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { |
||||||
|
n := hexutil.Uint(len(block.Transactions())) |
||||||
|
return &n |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { |
||||||
|
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { |
||||||
|
return newRPCTransactionFromBlockIndex(block, uint64(index)) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { |
||||||
|
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { |
||||||
|
return newRPCTransactionFromBlockIndex(block, uint64(index)) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionByHash returns the transaction for the given hash
|
||||||
|
func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction { |
||||||
|
// Try to return an already finalized transaction
|
||||||
|
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash) |
||||||
|
block, _ := s.b.GetBlock(ctx, blockHash) |
||||||
|
if block == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
if tx != nil { |
||||||
|
return newRPCTransaction(tx, blockHash, blockNumber, block.Time().Uint64(), index) |
||||||
|
} |
||||||
|
// No finalized transaction, try to retrieve it from the pool
|
||||||
|
if tx = s.b.GetPoolTransaction(hash); tx != nil { |
||||||
|
return newRPCPendingTransaction(tx) |
||||||
|
} |
||||||
|
// Transaction unknown, return as such
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetStakingTransactionByHash returns the transaction for the given hash
|
||||||
|
func (s *PublicTransactionPoolAPI) GetStakingTransactionByHash(ctx context.Context, hash common.Hash) *RPCStakingTransaction { |
||||||
|
// Try to return an already finalized transaction
|
||||||
|
stx, blockHash, blockNumber, index := rawdb.ReadStakingTransaction(s.b.ChainDb(), hash) |
||||||
|
block, _ := s.b.GetBlock(ctx, blockHash) |
||||||
|
if block == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
if stx != nil { |
||||||
|
return newRPCStakingTransaction(stx, blockHash, blockNumber, block.Time().Uint64(), index) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetStakingTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCStakingTransaction { |
||||||
|
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { |
||||||
|
return newRPCStakingTransactionFromBlockIndex(block, uint64(index)) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetStakingTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCStakingTransaction { |
||||||
|
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { |
||||||
|
return newRPCStakingTransactionFromBlockIndex(block, uint64(index)) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
|
||||||
|
func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr string, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) { |
||||||
|
address := internal_common.ParseAddr(addr) |
||||||
|
// Ask transaction pool for the nonce which includes pending transactions
|
||||||
|
if blockNr == rpc.PendingBlockNumber { |
||||||
|
nonce, err := s.b.GetPoolNonce(ctx, address) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return (*hexutil.Uint64)(&nonce), nil |
||||||
|
} |
||||||
|
// Resolve block number and use its state to ask for the nonce
|
||||||
|
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) |
||||||
|
if state == nil || err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
nonce := state.GetNonce(address) |
||||||
|
return (*hexutil.Uint64)(&nonce), state.Error() |
||||||
|
} |
||||||
|
|
||||||
|
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
|
||||||
|
// transaction pool.
|
||||||
|
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) { |
||||||
|
// Look up the wallet containing the requested signer
|
||||||
|
account := accounts.Account{Address: args.From} |
||||||
|
|
||||||
|
wallet, err := s.b.AccountManager().Find(account) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
|
||||||
|
if args.Nonce == nil { |
||||||
|
// Hold the addresse's mutex around signing to prevent concurrent assignment of
|
||||||
|
// the same nonce to multiple accounts.
|
||||||
|
s.nonceLock.LockAddr(args.From) |
||||||
|
defer s.nonceLock.UnlockAddr(args.From) |
||||||
|
} |
||||||
|
|
||||||
|
// Set some sanity defaults and terminate on failure
|
||||||
|
if err := args.setDefaults(ctx, s.b); err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
// Assemble the transaction and sign with the wallet
|
||||||
|
tx := args.toTransaction() |
||||||
|
|
||||||
|
signed, err := wallet.SignTx(account, tx, s.b.ChainConfig().ChainID) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
return SubmitTransaction(ctx, s.b, signed) |
||||||
|
} |
||||||
|
|
||||||
|
// SendRawStakingTransaction will add the signed transaction to the transaction pool.
|
||||||
|
// The sender is responsible for signing the transaction and using the correct nonce.
|
||||||
|
func (s *PublicTransactionPoolAPI) SendRawStakingTransaction( |
||||||
|
ctx context.Context, encodedTx hexutil.Bytes, |
||||||
|
) (common.Hash, error) { |
||||||
|
tx := new(staking.StakingTransaction) |
||||||
|
if err := rlp.DecodeBytes(encodedTx, tx); err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
c := s.b.ChainConfig().ChainID |
||||||
|
if tx.ChainID().Cmp(c) != 0 { |
||||||
|
e := errors.Wrapf(errInvalidChainID, "current chain id:%s", c.String()) |
||||||
|
return common.Hash{}, e |
||||||
|
} |
||||||
|
return SubmitStakingTransaction(ctx, s.b, tx) |
||||||
|
} |
||||||
|
|
||||||
|
// SendRawTransaction will add the signed transaction to the transaction pool.
|
||||||
|
// The sender is responsible for signing the transaction and using the correct nonce.
|
||||||
|
func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { |
||||||
|
tx := new(types.Transaction) |
||||||
|
if err := rlp.DecodeBytes(encodedTx, tx); err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
c := s.b.ChainConfig().ChainID |
||||||
|
if tx.ChainID().Cmp(c) != 0 { |
||||||
|
e := errors.Wrapf(errInvalidChainID, "current chain id:%s", c.String()) |
||||||
|
return common.Hash{}, e |
||||||
|
} |
||||||
|
return SubmitTransaction(ctx, s.b, tx) |
||||||
|
} |
||||||
|
|
||||||
|
func (s *PublicTransactionPoolAPI) fillTransactionFields(tx *types.Transaction, fields map[string]interface{}) error { |
||||||
|
var err error |
||||||
|
fields["shardID"] = tx.ShardID() |
||||||
|
var signer types.Signer = types.FrontierSigner{} |
||||||
|
if tx.Protected() { |
||||||
|
signer = types.NewEIP155Signer(tx.ChainID()) |
||||||
|
} |
||||||
|
from, _ := types.Sender(signer, tx) |
||||||
|
fields["from"] = from |
||||||
|
fields["to"] = "" |
||||||
|
if tx.To() != nil { |
||||||
|
fields["to"], err = internal_common.AddressToBech32(*tx.To()) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
fields["from"], err = internal_common.AddressToBech32(from) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (s *PublicTransactionPoolAPI) fillStakingTransactionFields(stx *staking.StakingTransaction, fields map[string]interface{}) error { |
||||||
|
from, err := stx.SenderAddress() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
fields["sender"], err = internal_common.AddressToBech32(from) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
fields["type"] = stx.StakingType() |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
||||||
|
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { |
||||||
|
var tx *types.Transaction |
||||||
|
var stx *staking.StakingTransaction |
||||||
|
var blockHash common.Hash |
||||||
|
var blockNumber, index uint64 |
||||||
|
tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.b.ChainDb(), hash) |
||||||
|
if tx == nil { |
||||||
|
stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.b.ChainDb(), hash) |
||||||
|
if stx == nil { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
} |
||||||
|
receipts, err := s.b.GetReceipts(ctx, blockHash) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
if len(receipts) <= int(index) { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
receipt := receipts[index] |
||||||
|
fields := map[string]interface{}{ |
||||||
|
"blockHash": blockHash, |
||||||
|
"blockNumber": hexutil.Uint64(blockNumber), |
||||||
|
"transactionHash": hash, |
||||||
|
"transactionIndex": hexutil.Uint64(index), |
||||||
|
"gasUsed": hexutil.Uint64(receipt.GasUsed), |
||||||
|
"cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed), |
||||||
|
"contractAddress": nil, |
||||||
|
"logs": receipt.Logs, |
||||||
|
"logsBloom": receipt.Bloom, |
||||||
|
} |
||||||
|
if tx != nil { |
||||||
|
if err = s.fillTransactionFields(tx, fields); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} else { // stx not nil
|
||||||
|
if err = s.fillStakingTransactionFields(stx, fields); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
// Assign receipt status or post state.
|
||||||
|
if len(receipt.PostState) > 0 { |
||||||
|
fields["root"] = hexutil.Bytes(receipt.PostState) |
||||||
|
} else { |
||||||
|
fields["status"] = hexutil.Uint(receipt.Status) |
||||||
|
} |
||||||
|
if receipt.Logs == nil { |
||||||
|
fields["logs"] = [][]*types.Log{} |
||||||
|
} |
||||||
|
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||||
|
if receipt.ContractAddress != (common.Address{}) { |
||||||
|
fields["contractAddress"] = receipt.ContractAddress |
||||||
|
} |
||||||
|
return fields, nil |
||||||
|
} |
||||||
|
|
||||||
|
// PendingTransactions returns the transactions that are in the transaction pool
|
||||||
|
// and have a from address that is one of the accounts this node manages.
|
||||||
|
func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) { |
||||||
|
pending, err := s.b.GetPoolTransactions() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
accounts := make(map[common.Address]struct{}) |
||||||
|
for _, wallet := range s.b.AccountManager().Wallets() { |
||||||
|
for _, account := range wallet.Accounts() { |
||||||
|
accounts[account.Address] = struct{}{} |
||||||
|
} |
||||||
|
} |
||||||
|
transactions := make([]*RPCTransaction, 0, len(pending)) |
||||||
|
for _, tx := range pending { |
||||||
|
var signer types.Signer = types.HomesteadSigner{} |
||||||
|
if tx.Protected() { |
||||||
|
signer = types.NewEIP155Signer(tx.ChainID()) |
||||||
|
} |
||||||
|
from, _ := types.Sender(signer, tx) |
||||||
|
if _, exists := accounts[from]; exists { |
||||||
|
transactions = append(transactions, newRPCPendingTransaction(tx)) |
||||||
|
} |
||||||
|
} |
||||||
|
return transactions, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetCXReceiptByHash returns the transaction for the given hash
|
||||||
|
func (s *PublicTransactionPoolAPI) GetCXReceiptByHash(ctx context.Context, hash common.Hash) *RPCCXReceipt { |
||||||
|
if cx, blockHash, blockNumber, _ := rawdb.ReadCXReceipt(s.b.ChainDb(), hash); cx != nil { |
||||||
|
return newRPCCXReceipt(cx, blockHash, blockNumber) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,447 @@ |
|||||||
|
package apiv1 |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/hex" |
||||||
|
"math/big" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
types2 "github.com/harmony-one/harmony/staking/types" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
ethtypes "github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/harmony-one/harmony/block" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
internal_common "github.com/harmony-one/harmony/internal/common" |
||||||
|
) |
||||||
|
|
||||||
|
// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
|
||||||
|
type RPCTransaction struct { |
||||||
|
BlockHash common.Hash `json:"blockHash"` |
||||||
|
BlockNumber *hexutil.Big `json:"blockNumber"` |
||||||
|
From string `json:"from"` |
||||||
|
Timestamp hexutil.Uint64 `json:"timestamp"` |
||||||
|
Gas hexutil.Uint64 `json:"gas"` |
||||||
|
GasPrice *hexutil.Big `json:"gasPrice"` |
||||||
|
Hash common.Hash `json:"hash"` |
||||||
|
Input hexutil.Bytes `json:"input"` |
||||||
|
Nonce hexutil.Uint64 `json:"nonce"` |
||||||
|
To string `json:"to"` |
||||||
|
TransactionIndex hexutil.Uint `json:"transactionIndex"` |
||||||
|
Value *hexutil.Big `json:"value"` |
||||||
|
ShardID uint32 `json:"shardID"` |
||||||
|
ToShardID uint32 `json:"toShardID"` |
||||||
|
V *hexutil.Big `json:"v"` |
||||||
|
R *hexutil.Big `json:"r"` |
||||||
|
S *hexutil.Big `json:"s"` |
||||||
|
} |
||||||
|
|
||||||
|
// RPCStakingTransaction represents a transaction that will serialize to the RPC representation of a staking transaction
|
||||||
|
type RPCStakingTransaction struct { |
||||||
|
BlockHash common.Hash `json:"blockHash"` |
||||||
|
BlockNumber *hexutil.Big `json:"blockNumber"` |
||||||
|
From string `json:"from"` |
||||||
|
Timestamp hexutil.Uint64 `json:"timestamp"` |
||||||
|
Gas hexutil.Uint64 `json:"gas"` |
||||||
|
GasPrice *hexutil.Big `json:"gasPrice"` |
||||||
|
Hash common.Hash `json:"hash"` |
||||||
|
Nonce hexutil.Uint64 `json:"nonce"` |
||||||
|
TransactionIndex hexutil.Uint `json:"transactionIndex"` |
||||||
|
V *hexutil.Big `json:"v"` |
||||||
|
R *hexutil.Big `json:"r"` |
||||||
|
S *hexutil.Big `json:"s"` |
||||||
|
Type string `json:"type"` |
||||||
|
Msg map[string]interface{} `json:"msg"` |
||||||
|
} |
||||||
|
|
||||||
|
// RPCCXReceipt represents a CXReceipt that will serialize to the RPC representation of a CXReceipt
|
||||||
|
type RPCCXReceipt struct { |
||||||
|
BlockHash common.Hash `json:"blockHash"` |
||||||
|
BlockNumber *hexutil.Big `json:"blockNumber"` |
||||||
|
TxHash common.Hash `json:"hash"` |
||||||
|
From string `json:"from"` |
||||||
|
To string `json:"to"` |
||||||
|
ShardID uint32 `json:"shardID"` |
||||||
|
ToShardID uint32 `json:"toShardID"` |
||||||
|
Amount *hexutil.Big `json:"value"` |
||||||
|
} |
||||||
|
|
||||||
|
// HeaderInformation represents the latest consensus information
|
||||||
|
type HeaderInformation struct { |
||||||
|
BlockHash common.Hash `json:"blockHash"` |
||||||
|
BlockNumber uint64 `json:"blockNumber"` |
||||||
|
ShardID uint32 `json:"shardID"` |
||||||
|
Leader string `json:"leader"` |
||||||
|
ViewID uint64 `json:"viewID"` |
||||||
|
Epoch uint64 `json:"epoch"` |
||||||
|
Timestamp string `json:"timestamp"` |
||||||
|
UnixTime uint64 `json:"unixtime"` |
||||||
|
LastCommitSig string `json:"lastCommitSig"` |
||||||
|
LastCommitBitmap string `json:"lastCommitBitmap"` |
||||||
|
} |
||||||
|
|
||||||
|
// RPCDelegation represents a particular delegation to a validator
|
||||||
|
type RPCDelegation struct { |
||||||
|
ValidatorAddress string `json:"validator_address" yaml:"validator_address"` |
||||||
|
DelegatorAddress string `json:"delegator_address" yaml:"delegator_address"` |
||||||
|
Amount *big.Int `json:"amount" yaml:"amount"` |
||||||
|
Reward *big.Int `json:"reward" yaml:"reward"` |
||||||
|
Undelegations []RPCUndelegation `json:"Undelegations" yaml:"Undelegations"` |
||||||
|
} |
||||||
|
|
||||||
|
// RPCUndelegation represents one undelegation entry
|
||||||
|
type RPCUndelegation struct { |
||||||
|
Amount *big.Int |
||||||
|
Epoch *big.Int |
||||||
|
} |
||||||
|
|
||||||
|
func newHeaderInformation(header *block.Header) *HeaderInformation { |
||||||
|
if header == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
result := &HeaderInformation{ |
||||||
|
BlockHash: header.Hash(), |
||||||
|
BlockNumber: header.Number().Uint64(), |
||||||
|
ShardID: header.ShardID(), |
||||||
|
ViewID: header.ViewID().Uint64(), |
||||||
|
Epoch: header.Epoch().Uint64(), |
||||||
|
UnixTime: header.Time().Uint64(), |
||||||
|
Timestamp: time.Unix(header.Time().Int64(), 0).UTC().String(), |
||||||
|
LastCommitBitmap: hex.EncodeToString(header.LastCommitBitmap()), |
||||||
|
} |
||||||
|
|
||||||
|
sig := header.LastCommitSignature() |
||||||
|
result.LastCommitSig = hex.EncodeToString(sig[:]) |
||||||
|
|
||||||
|
bechAddr, err := internal_common.AddressToBech32(header.Coinbase()) |
||||||
|
if err != nil { |
||||||
|
bechAddr = header.Coinbase().Hex() |
||||||
|
} |
||||||
|
|
||||||
|
result.Leader = bechAddr |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCCXReceipt returns a CXReceipt that will serialize to the RPC representation
|
||||||
|
func newRPCCXReceipt(cx *types.CXReceipt, blockHash common.Hash, blockNumber uint64) *RPCCXReceipt { |
||||||
|
result := &RPCCXReceipt{ |
||||||
|
BlockHash: blockHash, |
||||||
|
TxHash: cx.TxHash, |
||||||
|
Amount: (*hexutil.Big)(cx.Amount), |
||||||
|
ShardID: cx.ShardID, |
||||||
|
ToShardID: cx.ToShardID, |
||||||
|
} |
||||||
|
if blockHash != (common.Hash{}) { |
||||||
|
result.BlockHash = blockHash |
||||||
|
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) |
||||||
|
} |
||||||
|
|
||||||
|
fromAddr, err := internal_common.AddressToBech32(cx.From) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
toAddr := "" |
||||||
|
if cx.To != nil { |
||||||
|
if toAddr, err = internal_common.AddressToBech32(*cx.To); err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
result.From = fromAddr |
||||||
|
result.To = toAddr |
||||||
|
|
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||||
|
// representation, with the given location metadata set (if available).
|
||||||
|
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, timestamp uint64, index uint64) *RPCTransaction { |
||||||
|
var signer types.Signer = types.FrontierSigner{} |
||||||
|
if tx.Protected() { |
||||||
|
signer = types.NewEIP155Signer(tx.ChainID()) |
||||||
|
} |
||||||
|
from, _ := types.Sender(signer, tx) |
||||||
|
v, r, s := tx.RawSignatureValues() |
||||||
|
|
||||||
|
result := &RPCTransaction{ |
||||||
|
Gas: hexutil.Uint64(tx.Gas()), |
||||||
|
GasPrice: (*hexutil.Big)(tx.GasPrice()), |
||||||
|
Hash: tx.Hash(), |
||||||
|
Nonce: hexutil.Uint64(tx.Nonce()), |
||||||
|
Value: (*hexutil.Big)(tx.Value()), |
||||||
|
ShardID: tx.ShardID(), |
||||||
|
ToShardID: tx.ToShardID(), |
||||||
|
Timestamp: hexutil.Uint64(timestamp), |
||||||
|
V: (*hexutil.Big)(v), |
||||||
|
R: (*hexutil.Big)(r), |
||||||
|
S: (*hexutil.Big)(s), |
||||||
|
} |
||||||
|
if blockHash != (common.Hash{}) { |
||||||
|
result.BlockHash = blockHash |
||||||
|
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) |
||||||
|
result.TransactionIndex = hexutil.Uint(index) |
||||||
|
} |
||||||
|
|
||||||
|
fromAddr, err := internal_common.AddressToBech32(from) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
toAddr := "" |
||||||
|
|
||||||
|
if tx.To() != nil { |
||||||
|
if toAddr, err = internal_common.AddressToBech32(*tx.To()); err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
result.From = fromAddr |
||||||
|
} else { |
||||||
|
result.From = strings.ToLower(from.Hex()) |
||||||
|
} |
||||||
|
result.To = toAddr |
||||||
|
|
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCStakingTransaction returns a transaction that will serialize to the RPC
|
||||||
|
// representation, with the given location metadata set (if available).
|
||||||
|
func newRPCStakingTransaction(tx *types2.StakingTransaction, blockHash common.Hash, blockNumber uint64, timestamp uint64, index uint64) *RPCStakingTransaction { |
||||||
|
from, _ := tx.SenderAddress() |
||||||
|
v, r, s := tx.RawSignatureValues() |
||||||
|
|
||||||
|
stakingTxType := tx.StakingType().String() |
||||||
|
message := tx.StakingMessage() |
||||||
|
fields := make(map[string]interface{}, 0) |
||||||
|
|
||||||
|
switch stakingTxType { |
||||||
|
case "CreateValidator": |
||||||
|
msg := message.(types2.CreateValidator) |
||||||
|
fields = map[string]interface{}{ |
||||||
|
"validatorAddress": msg.ValidatorAddress, |
||||||
|
"name": msg.Description.Name, |
||||||
|
"commissionRate": (*hexutil.Big)(msg.CommissionRates.Rate.Int), |
||||||
|
"maxCommissionRate": (*hexutil.Big)(msg.CommissionRates.MaxRate.Int), |
||||||
|
"maxChangeRate": (*hexutil.Big)(msg.CommissionRates.MaxChangeRate.Int), |
||||||
|
"minSelfDelegation": (*hexutil.Big)(msg.MinSelfDelegation), |
||||||
|
"maxTotalDelegation": (*hexutil.Big)(msg.MaxTotalDelegation), |
||||||
|
"amount": (*hexutil.Big)(msg.Amount), |
||||||
|
"website": msg.Description.Website, |
||||||
|
"identity": msg.Description.Identity, |
||||||
|
"securityContact": msg.Description.SecurityContact, |
||||||
|
"details": msg.Description.Details, |
||||||
|
"slotPubKeys": msg.SlotPubKeys, |
||||||
|
} |
||||||
|
case "EditValidator": |
||||||
|
msg := message.(types2.EditValidator) |
||||||
|
fields = map[string]interface{}{ |
||||||
|
"validatorAddress": msg.ValidatorAddress, |
||||||
|
"commisionRate": (*hexutil.Big)(msg.CommissionRate.Int), |
||||||
|
"name": msg.Description.Name, |
||||||
|
"minSelfDelegation": (*hexutil.Big)(msg.MinSelfDelegation), |
||||||
|
"maxTotalDelegation": (*hexutil.Big)(msg.MaxTotalDelegation), |
||||||
|
"website": msg.Description.Website, |
||||||
|
"identity": msg.Description.Identity, |
||||||
|
"securityContact": msg.Description.SecurityContact, |
||||||
|
"details": msg.Description.Details, |
||||||
|
"slotPubKeyToAdd": msg.SlotKeyToAdd, |
||||||
|
"slotPubKeyToRemove": msg.SlotKeyToRemove, |
||||||
|
} |
||||||
|
case "CollectRewards": |
||||||
|
msg := message.(types2.CollectRewards) |
||||||
|
fields = map[string]interface{}{ |
||||||
|
"delegatorAddress": msg.DelegatorAddress, |
||||||
|
} |
||||||
|
case "Delegate": |
||||||
|
msg := message.(types2.Delegate) |
||||||
|
fields = map[string]interface{}{ |
||||||
|
"delegatorAddress": msg.DelegatorAddress, |
||||||
|
"validatorAddress": msg.ValidatorAddress, |
||||||
|
"amount": (*hexutil.Big)(msg.Amount), |
||||||
|
} |
||||||
|
case "Undelegate": |
||||||
|
msg := message.(types2.Undelegate) |
||||||
|
fields = map[string]interface{}{ |
||||||
|
"delegatorAddress": msg.DelegatorAddress, |
||||||
|
"validatorAddress": msg.ValidatorAddress, |
||||||
|
"amount": (*hexutil.Big)(msg.Amount), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
result := &RPCStakingTransaction{ |
||||||
|
Gas: hexutil.Uint64(tx.Gas()), |
||||||
|
GasPrice: (*hexutil.Big)(tx.Price()), |
||||||
|
Hash: tx.Hash(), |
||||||
|
Nonce: hexutil.Uint64(tx.Nonce()), |
||||||
|
Timestamp: hexutil.Uint64(timestamp), |
||||||
|
V: (*hexutil.Big)(v), |
||||||
|
R: (*hexutil.Big)(r), |
||||||
|
S: (*hexutil.Big)(s), |
||||||
|
Type: stakingTxType, |
||||||
|
Msg: fields, |
||||||
|
} |
||||||
|
if blockHash != (common.Hash{}) { |
||||||
|
result.BlockHash = blockHash |
||||||
|
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) |
||||||
|
result.TransactionIndex = hexutil.Uint(index) |
||||||
|
} |
||||||
|
|
||||||
|
fromAddr, err := internal_common.AddressToBech32(from) |
||||||
|
if err != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
result.From = fromAddr |
||||||
|
|
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
|
||||||
|
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { |
||||||
|
return newRPCTransaction(tx, common.Hash{}, 0, 0, 0) |
||||||
|
} |
||||||
|
|
||||||
|
// RPCBlock represents a block that will serialize to the RPC representation of a block
|
||||||
|
type RPCBlock struct { |
||||||
|
Number *hexutil.Big `json:"number"` |
||||||
|
Hash common.Hash `json:"hash"` |
||||||
|
ParentHash common.Hash `json:"parentHash"` |
||||||
|
Nonce types.BlockNonce `json:"nonce"` |
||||||
|
MixHash common.Hash `json:"mixHash"` |
||||||
|
LogsBloom ethtypes.Bloom `json:"logsBloom"` |
||||||
|
StateRoot common.Hash `json:"stateRoot"` |
||||||
|
Miner common.Address `json:"miner"` |
||||||
|
Difficulty *hexutil.Big `json:"difficulty"` |
||||||
|
ExtraData []byte `json:"extraData"` |
||||||
|
Size hexutil.Uint64 `json:"size"` |
||||||
|
GasLimit hexutil.Uint64 `json:"gasLimit"` |
||||||
|
GasUsed hexutil.Uint64 `json:"gasUsed"` |
||||||
|
Timestamp *big.Int `json:"timestamp"` |
||||||
|
TransactionsRoot common.Hash `json:"transactionsRoot"` |
||||||
|
ReceiptsRoot common.Hash `json:"receiptsRoot"` |
||||||
|
Transactions []interface{} `json:"transactions"` |
||||||
|
StakingTxs []interface{} `json:"stakingTxs` |
||||||
|
Uncles []common.Hash `json:"uncles"` |
||||||
|
TotalDifficulty *big.Int `json:"totalDifficulty"` |
||||||
|
Signers []string `json:"signers"` |
||||||
|
} |
||||||
|
|
||||||
|
// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
|
||||||
|
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
|
||||||
|
// transaction hashes.
|
||||||
|
func RPCMarshalBlock(b *types.Block, blockArgs BlockArgs) (map[string]interface{}, error) { |
||||||
|
head := b.Header() // copies the header once
|
||||||
|
fields := map[string]interface{}{ |
||||||
|
"number": (*hexutil.Big)(head.Number()), |
||||||
|
"hash": b.Hash(), |
||||||
|
"parentHash": head.ParentHash(), |
||||||
|
"nonce": 0, // Remove this because we don't have it in our header
|
||||||
|
"mixHash": head.MixDigest(), |
||||||
|
"logsBloom": head.Bloom(), |
||||||
|
"stateRoot": head.Root(), |
||||||
|
"miner": head.Coinbase(), |
||||||
|
"difficulty": 0, // Remove this because we don't have it in our header
|
||||||
|
"extraData": hexutil.Bytes(head.Extra()), |
||||||
|
"size": hexutil.Uint64(b.Size()), |
||||||
|
"gasLimit": hexutil.Uint64(head.GasLimit()), |
||||||
|
"gasUsed": hexutil.Uint64(head.GasUsed()), |
||||||
|
"timestamp": hexutil.Uint64(head.Time().Uint64()), |
||||||
|
"transactionsRoot": head.TxHash(), |
||||||
|
"receiptsRoot": head.ReceiptHash(), |
||||||
|
} |
||||||
|
|
||||||
|
if blockArgs.InclTx { |
||||||
|
formatTx := func(tx *types.Transaction) (interface{}, error) { |
||||||
|
return tx.Hash(), nil |
||||||
|
} |
||||||
|
if blockArgs.FullTx { |
||||||
|
formatTx = func(tx *types.Transaction) (interface{}, error) { |
||||||
|
return newRPCTransactionFromBlockHash(b, tx.Hash()), nil |
||||||
|
} |
||||||
|
} |
||||||
|
txs := b.Transactions() |
||||||
|
transactions := make([]interface{}, len(txs)) |
||||||
|
var err error |
||||||
|
for i, tx := range txs { |
||||||
|
if transactions[i], err = formatTx(tx); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
fields["transactions"] = transactions |
||||||
|
|
||||||
|
if blockArgs.InclStaking { |
||||||
|
formatStakingTx := func(tx *types2.StakingTransaction) (interface{}, error) { |
||||||
|
return tx.Hash(), nil |
||||||
|
} |
||||||
|
if blockArgs.FullTx { |
||||||
|
formatStakingTx = func(tx *types2.StakingTransaction) (interface{}, error) { |
||||||
|
return newRPCStakingTransactionFromBlockHash(b, tx.Hash()), nil |
||||||
|
} |
||||||
|
} |
||||||
|
stakingTxs := b.StakingTransactions() |
||||||
|
stakingTransactions := make([]interface{}, len(stakingTxs)) |
||||||
|
for i, tx := range stakingTxs { |
||||||
|
if stakingTransactions[i], err = formatStakingTx(tx); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
fields["stakingTransactions"] = stakingTransactions |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uncles := b.Uncles() |
||||||
|
uncleHashes := make([]common.Hash, len(uncles)) |
||||||
|
for i, uncle := range uncles { |
||||||
|
uncleHashes[i] = uncle.Hash() |
||||||
|
} |
||||||
|
fields["uncles"] = uncleHashes |
||||||
|
if blockArgs.WithSigners { |
||||||
|
fields["signers"] = blockArgs.Signers |
||||||
|
} |
||||||
|
return fields, nil |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
|
||||||
|
func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction { |
||||||
|
for idx, tx := range b.Transactions() { |
||||||
|
if tx.Hash() == hash { |
||||||
|
return newRPCTransactionFromBlockIndex(b, uint64(idx)) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
|
||||||
|
func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction { |
||||||
|
txs := b.Transactions() |
||||||
|
if index >= uint64(len(txs)) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index) |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCStakingTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
|
||||||
|
func newRPCStakingTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCStakingTransaction { |
||||||
|
for idx, tx := range b.StakingTransactions() { |
||||||
|
if tx.Hash() == hash { |
||||||
|
return newRPCStakingTransactionFromBlockIndex(b, uint64(idx)) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// newRPCStakingTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
|
||||||
|
func newRPCStakingTransactionFromBlockIndex(b *types.Block, index uint64) *RPCStakingTransaction { |
||||||
|
txs := b.StakingTransactions() |
||||||
|
if index >= uint64(len(txs)) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return newRPCStakingTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index) |
||||||
|
} |
||||||
|
|
||||||
|
// CallArgs represents the arguments for a call.
|
||||||
|
type CallArgs struct { |
||||||
|
From *common.Address `json:"from"` |
||||||
|
To *common.Address `json:"to"` |
||||||
|
Gas *hexutil.Uint64 `json:"gas"` |
||||||
|
GasPrice *hexutil.Big `json:"gasPrice"` |
||||||
|
Value *hexutil.Big `json:"value"` |
||||||
|
Data *hexutil.Bytes `json:"data"` |
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package hmyapi |
package apiv1 |
||||||
|
|
||||||
import ( |
import ( |
||||||
"context" |
"context" |
@ -0,0 +1,54 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"sync" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
) |
||||||
|
|
||||||
|
// AddrLocker ...
|
||||||
|
type AddrLocker struct { |
||||||
|
mu sync.Mutex |
||||||
|
locks map[common.Address]*sync.Mutex |
||||||
|
} |
||||||
|
|
||||||
|
// lock returns the lock of the given address.
|
||||||
|
func (l *AddrLocker) lock(address common.Address) *sync.Mutex { |
||||||
|
l.mu.Lock() |
||||||
|
defer l.mu.Unlock() |
||||||
|
if l.locks == nil { |
||||||
|
l.locks = make(map[common.Address]*sync.Mutex) |
||||||
|
} |
||||||
|
if _, ok := l.locks[address]; !ok { |
||||||
|
l.locks[address] = new(sync.Mutex) |
||||||
|
} |
||||||
|
return l.locks[address] |
||||||
|
} |
||||||
|
|
||||||
|
// LockAddr locks an account's mutex. This is used to prevent another tx getting the
|
||||||
|
// same nonce until the lock is released. The mutex prevents the (an identical nonce) from
|
||||||
|
// being read again during the time that the first transaction is being signed.
|
||||||
|
func (l *AddrLocker) LockAddr(address common.Address) { |
||||||
|
l.lock(address).Lock() |
||||||
|
} |
||||||
|
|
||||||
|
// UnlockAddr unlocks the mutex of the given account.
|
||||||
|
func (l *AddrLocker) UnlockAddr(address common.Address) { |
||||||
|
l.lock(address).Unlock() |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/ethdb" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
"github.com/harmony-one/harmony/accounts" |
||||||
|
"github.com/harmony-one/harmony/block" |
||||||
|
"github.com/harmony-one/harmony/core" |
||||||
|
"github.com/harmony-one/harmony/core/state" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
"github.com/harmony-one/harmony/core/vm" |
||||||
|
"github.com/harmony-one/harmony/internal/params" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
) |
||||||
|
|
||||||
|
// Backend interface provides the common API services (that are provided by
|
||||||
|
// both full and light clients) with access to necessary functions.
|
||||||
|
// implementations:
|
||||||
|
// * hmy/api_backend.go
|
||||||
|
type Backend interface { |
||||||
|
// NOTE(ricl): this is not in ETH Backend inteface. They put it directly in eth object.
|
||||||
|
NetVersion() uint64 |
||||||
|
// General Ethereum API
|
||||||
|
// Downloader() *downloader.Downloader
|
||||||
|
ProtocolVersion() int |
||||||
|
// SuggestPrice(ctx context.Context) (*big.Int, error)
|
||||||
|
ChainDb() ethdb.Database |
||||||
|
EventMux() *event.TypeMux |
||||||
|
AccountManager() *accounts.Manager |
||||||
|
// ExtRPCEnabled() bool
|
||||||
|
RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection
|
||||||
|
// BlockChain API
|
||||||
|
// SetHead(number uint64)
|
||||||
|
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) |
||||||
|
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) |
||||||
|
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.DB, *block.Header, error) |
||||||
|
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) |
||||||
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) |
||||||
|
// GetTd(blockHash common.Hash) *big.Int
|
||||||
|
GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) |
||||||
|
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription |
||||||
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription |
||||||
|
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription |
||||||
|
// TxPool API
|
||||||
|
SendTx(ctx context.Context, signedTx *types.Transaction) error |
||||||
|
// GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
||||||
|
GetPoolTransactions() (types.Transactions, error) |
||||||
|
GetPoolTransaction(txHash common.Hash) *types.Transaction |
||||||
|
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) |
||||||
|
// Stats() (pending int, queued int)
|
||||||
|
// TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
||||||
|
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription |
||||||
|
ChainConfig() *params.ChainConfig |
||||||
|
CurrentBlock() *types.Block |
||||||
|
// Get balance
|
||||||
|
GetBalance(address common.Address) (*big.Int, error) |
||||||
|
// Get validators for a particular epoch
|
||||||
|
GetValidators(epoch *big.Int) (*shard.Committee, error) |
||||||
|
GetShardID() uint32 |
||||||
|
// Get transactions history for an address
|
||||||
|
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
||||||
|
// retrieve the blockHash using txID and add blockHash to CxPool for resending
|
||||||
|
ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) |
||||||
|
IsLeader() bool |
||||||
|
SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error |
||||||
|
GetActiveValidatorAddresses() []common.Address |
||||||
|
GetAllValidatorAddresses() []common.Address |
||||||
|
GetValidatorInformation(addr common.Address) *staking.Validator |
||||||
|
GetValidatorStats(addr common.Address) *staking.ValidatorStats |
||||||
|
GetDelegationsByValidator(validator common.Address) []*staking.Delegation |
||||||
|
GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) |
||||||
|
GetValidatorSelfDelegation(addr common.Address) *big.Int |
||||||
|
GetShardState() (*shard.State, error) |
||||||
|
GetCurrentStakingErrorSink() []staking.RPCTransactionError |
||||||
|
GetCurrentTransactionErrorSink() []types.RPCTransactionError |
||||||
|
IsBeaconChainExplorerNode() bool |
||||||
|
GetMedianRawStakeSnapshot() *big.Int |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"errors" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log" |
||||||
|
"github.com/harmony-one/harmony/internal/utils" |
||||||
|
) |
||||||
|
|
||||||
|
// DebugAPI Internal JSON RPC for debugging purpose
|
||||||
|
type DebugAPI struct { |
||||||
|
b Backend |
||||||
|
} |
||||||
|
|
||||||
|
// NewDebugAPI Creates a new DebugAPI instance
|
||||||
|
func NewDebugAPI(b Backend) *DebugAPI { |
||||||
|
return &DebugAPI{b} |
||||||
|
} |
||||||
|
|
||||||
|
// SetLogVerbosity Sets log verbosity on runtime
|
||||||
|
// Example usage:
|
||||||
|
// curl -H "Content-Type: application/json" -d '{"method":"debug_setLogVerbosity","params":[0],"id":1}' http://localhost:9123
|
||||||
|
func (*DebugAPI) SetLogVerbosity(ctx context.Context, level int) (map[string]interface{}, error) { |
||||||
|
if level < int(log.LvlCrit) || level > int(log.LvlTrace) { |
||||||
|
return nil, errors.New("invalid log level") |
||||||
|
} |
||||||
|
|
||||||
|
verbosity := log.Lvl(level) |
||||||
|
utils.SetLogVerbosity(verbosity) |
||||||
|
return map[string]interface{}{"verbosity": verbosity.String()}, nil |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import "errors" |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrIncorrectChainID is an incorrect chain ID.
|
||||||
|
ErrIncorrectChainID = errors.New("Incorrect chain ID") |
||||||
|
) |
@ -0,0 +1,78 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/harmony-one/harmony/api/proto" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
) |
||||||
|
|
||||||
|
// PublicHarmonyAPI provides an API to access Harmony related information.
|
||||||
|
// It offers only methods that operate on public data that is freely available to anyone.
|
||||||
|
type PublicHarmonyAPI struct { |
||||||
|
b Backend |
||||||
|
} |
||||||
|
|
||||||
|
// NewPublicHarmonyAPI ...
|
||||||
|
func NewPublicHarmonyAPI(b Backend) *PublicHarmonyAPI { |
||||||
|
return &PublicHarmonyAPI{b} |
||||||
|
} |
||||||
|
|
||||||
|
// ProtocolVersion returns the current Harmony protocol version this node supports
|
||||||
|
func (s *PublicHarmonyAPI) ProtocolVersion() int { |
||||||
|
return proto.ProtocolVersion |
||||||
|
} |
||||||
|
|
||||||
|
// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
|
||||||
|
// yet received the latest block headers from its pears. In case it is synchronizing:
|
||||||
|
// - startingBlock: block number this node started to synchronise from
|
||||||
|
// - currentBlock: block number this node is currently importing
|
||||||
|
// - highestBlock: block number of the highest block header this node has received from peers
|
||||||
|
// - pulledStates: number of state entries processed until now
|
||||||
|
// - knownStates: number of known state entries that still need to be pulled
|
||||||
|
func (s *PublicHarmonyAPI) Syncing() (interface{}, error) { |
||||||
|
// TODO(ricl): find our Downloader module for syncing blocks
|
||||||
|
return false, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GasPrice returns a suggestion for a gas price.
|
||||||
|
func (s *PublicHarmonyAPI) GasPrice(ctx context.Context) (*big.Int, error) { |
||||||
|
// TODO(ricl): add SuggestPrice API
|
||||||
|
return big.NewInt(1), nil |
||||||
|
} |
||||||
|
|
||||||
|
// NodeMetadata captures select metadata of the RPC answering node
|
||||||
|
type NodeMetadata struct { |
||||||
|
BLSPublicKey string `json:"blskey"` |
||||||
|
Version string `json:"version"` |
||||||
|
NetworkType string `json:"network"` |
||||||
|
ChainID string `json:"chainid"` |
||||||
|
IsLeader bool `json:"is-leader"` |
||||||
|
ShardID uint32 `json:"shard-id"` |
||||||
|
} |
||||||
|
|
||||||
|
// GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node
|
||||||
|
func (s *PublicHarmonyAPI) GetNodeMetadata() NodeMetadata { |
||||||
|
cfg := nodeconfig.GetDefaultConfig() |
||||||
|
return NodeMetadata{ |
||||||
|
cfg.ConsensusPubKey.SerializeToHexStr(), |
||||||
|
nodeconfig.GetVersion(), |
||||||
|
string(cfg.GetNetworkType()), |
||||||
|
s.b.ChainConfig().ChainID.String(), |
||||||
|
s.b.IsLeader(), |
||||||
|
s.b.GetShardID(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// GetCurrentTransactionErrorSink ..
|
||||||
|
func (s *PublicHarmonyAPI) GetCurrentTransactionErrorSink() []types.RPCTransactionError { |
||||||
|
return s.b.GetCurrentTransactionErrorSink() |
||||||
|
} |
||||||
|
|
||||||
|
// GetCurrentStakingErrorSink ..
|
||||||
|
func (s *PublicHarmonyAPI) GetCurrentStakingErrorSink() []staking.RPCTransactionError { |
||||||
|
return s.b.GetCurrentStakingErrorSink() |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/harmony-one/harmony/p2p" |
||||||
|
) |
||||||
|
|
||||||
|
// PublicNetAPI offers network related RPC methods
|
||||||
|
type PublicNetAPI struct { |
||||||
|
net p2p.Host |
||||||
|
networkVersion uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// NewPublicNetAPI creates a new net API instance.
|
||||||
|
func NewPublicNetAPI(net p2p.Host, networkVersion uint64) *PublicNetAPI { |
||||||
|
return &PublicNetAPI{net, networkVersion} |
||||||
|
} |
||||||
|
|
||||||
|
// PeerCount returns the number of connected peers
|
||||||
|
func (s *PublicNetAPI) PeerCount() int { |
||||||
|
return s.net.GetPeerCount() |
||||||
|
} |
||||||
|
|
||||||
|
// Version returns the network version, i.e. network ID identifying which network we are using
|
||||||
|
func (s *PublicNetAPI) Version() string { |
||||||
|
return fmt.Sprintf("%d", s.networkVersion) // TODO(ricl): we should add support for network id (https://github.com/ethereum/wiki/wiki/JSON-RPC#net_version)
|
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
|
||||||
|
"github.com/harmony-one/harmony/accounts" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
"github.com/harmony-one/harmony/hmy" |
||||||
|
"github.com/harmony-one/harmony/internal/utils" |
||||||
|
) |
||||||
|
|
||||||
|
// PrivateAccountAPI provides an API to access accounts managed by this node.
|
||||||
|
// It offers methods to create, (un)lock en list accounts. Some methods accept
|
||||||
|
// passwords and are therefore considered private by default.
|
||||||
|
type PrivateAccountAPI struct { |
||||||
|
am *accounts.Manager |
||||||
|
nonceLock *AddrLocker |
||||||
|
b *hmy.APIBackend |
||||||
|
} |
||||||
|
|
||||||
|
// NewAccount will create a new account and returns the address for the new account.
|
||||||
|
func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) { |
||||||
|
// TODO: port
|
||||||
|
return common.Address{}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// SendTransaction will create a transaction from the given arguments and
|
||||||
|
// tries to sign it with the key associated with args.To. If the given passwd isn't
|
||||||
|
// able to decrypt the key it fails.
|
||||||
|
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { |
||||||
|
if args.Nonce == nil { |
||||||
|
// Hold the addresse's mutex around signing to prevent concurrent assignment of
|
||||||
|
// the same nonce to multiple accounts.
|
||||||
|
s.nonceLock.LockAddr(args.From) |
||||||
|
defer s.nonceLock.UnlockAddr(args.From) |
||||||
|
} |
||||||
|
signed, err := s.signTransaction(ctx, &args, passwd) |
||||||
|
if err != nil { |
||||||
|
utils.Logger().Warn(). |
||||||
|
Str("from", args.From.Hex()). |
||||||
|
Str("to", args.To.Hex()). |
||||||
|
Uint64("value", args.Value.ToInt().Uint64()). |
||||||
|
AnErr("err", err). |
||||||
|
Msg("Failed transaction send attempt") |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
return SubmitTransaction(ctx, s.b, signed) |
||||||
|
} |
||||||
|
|
||||||
|
// signTransaction sets defaults and signs the given transaction
|
||||||
|
// NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
|
||||||
|
// and release it after the transaction has been submitted to the tx pool
|
||||||
|
func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *SendTxArgs, passwd string) (*types.Transaction, error) { |
||||||
|
// Look up the wallet containing the requested signer
|
||||||
|
account := accounts.Account{Address: args.From} |
||||||
|
wallet, err := s.am.Find(account) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
// Set some sanity defaults and terminate on failure
|
||||||
|
if err := args.setDefaults(ctx, s.b); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
// Assemble the transaction and sign with the wallet
|
||||||
|
tx := args.toTransaction() |
||||||
|
|
||||||
|
return wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID) |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/harmony-one/harmony/accounts" |
||||||
|
) |
||||||
|
|
||||||
|
// PublicAccountAPI provides an API to access accounts managed by this node.
|
||||||
|
// It offers only methods that can retrieve accounts.
|
||||||
|
type PublicAccountAPI struct { |
||||||
|
am *accounts.Manager |
||||||
|
} |
||||||
|
|
||||||
|
// NewPublicAccountAPI creates a new PublicAccountAPI.
|
||||||
|
func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI { |
||||||
|
return &PublicAccountAPI{am: am} |
||||||
|
} |
||||||
|
|
||||||
|
// Accounts returns the collection of accounts this node manages
|
||||||
|
func (s *PublicAccountAPI) Accounts() []common.Address { |
||||||
|
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
||||||
|
for _, wallet := range s.am.Wallets() { |
||||||
|
for _, account := range wallet.Accounts() { |
||||||
|
addresses = append(addresses, account.Address) |
||||||
|
} |
||||||
|
} |
||||||
|
return addresses |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"context" |
||||||
|
"errors" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
) |
||||||
|
|
||||||
|
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
||||||
|
type SendTxArgs struct { |
||||||
|
From common.Address `json:"from"` |
||||||
|
To *common.Address `json:"to"` |
||||||
|
ShardID uint32 `json:"shardID"` |
||||||
|
Gas *hexutil.Uint64 `json:"gas"` |
||||||
|
GasPrice *hexutil.Big `json:"gasPrice"` |
||||||
|
Value *hexutil.Big `json:"value"` |
||||||
|
Nonce *hexutil.Uint64 `json:"nonce"` |
||||||
|
// We accept "data" and "input" for backwards-compatibility reasons. "input" is the
|
||||||
|
// newer name and should be preferred by clients.
|
||||||
|
Data *hexutil.Bytes `json:"data"` |
||||||
|
Input *hexutil.Bytes `json:"input"` |
||||||
|
} |
||||||
|
|
||||||
|
// setDefaults is a helper function that fills in default values for unspecified tx fields.
|
||||||
|
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { |
||||||
|
if args.Gas == nil { |
||||||
|
args.Gas = new(hexutil.Uint64) |
||||||
|
*(*uint64)(args.Gas) = 90000 |
||||||
|
} |
||||||
|
// TODO(ricl): add check for shardID
|
||||||
|
if args.GasPrice == nil { |
||||||
|
// TODO(ricl): port
|
||||||
|
// price, err := b.SuggestPrice(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// args.GasPrice = (*hexutil.Big)(price)
|
||||||
|
} |
||||||
|
if args.Value == nil { |
||||||
|
args.Value = new(hexutil.Big) |
||||||
|
} |
||||||
|
if args.Nonce == nil { |
||||||
|
nonce, err := b.GetPoolNonce(ctx, args.From) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
args.Nonce = (*hexutil.Uint64)(&nonce) |
||||||
|
} |
||||||
|
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { |
||||||
|
return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) |
||||||
|
} |
||||||
|
if args.To == nil { |
||||||
|
// Contract creation
|
||||||
|
var input []byte |
||||||
|
if args.Data != nil { |
||||||
|
input = *args.Data |
||||||
|
} else if args.Input != nil { |
||||||
|
input = *args.Input |
||||||
|
} |
||||||
|
if len(input) == 0 { |
||||||
|
return errors.New(`contract creation without any data provided`) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (args *SendTxArgs) toTransaction() *types.Transaction { |
||||||
|
var input []byte |
||||||
|
if args.Data != nil { |
||||||
|
input = *args.Data |
||||||
|
} else if args.Input != nil { |
||||||
|
input = *args.Input |
||||||
|
} |
||||||
|
if args.To == nil { |
||||||
|
return types.NewContractCreation(uint64(*args.Nonce), args.ShardID, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) |
||||||
|
} |
||||||
|
return types.NewTransaction(uint64(*args.Nonce), *args.To, args.ShardID, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
package apiv2 |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
common2 "github.com/harmony-one/harmony/internal/common" |
||||||
|
"github.com/harmony-one/harmony/internal/utils" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
) |
||||||
|
|
||||||
|
// defaultPageSize is to have default pagination.
|
||||||
|
const ( |
||||||
|
defaultPageSize = uint32(100) |
||||||
|
) |
||||||
|
|
||||||
|
// ReturnWithPagination returns result with pagination (offset, page in TxHistoryArgs).
|
||||||
|
func ReturnWithPagination(hashes []common.Hash, args TxHistoryArgs) []common.Hash { |
||||||
|
pageSize := defaultPageSize |
||||||
|
pageIndex := args.PageIndex |
||||||
|
if args.PageSize > 0 { |
||||||
|
pageSize = args.PageSize |
||||||
|
} |
||||||
|
if uint64(pageSize)*uint64(pageIndex) >= uint64(len(hashes)) { |
||||||
|
return make([]common.Hash, 0) |
||||||
|
} |
||||||
|
if uint64(pageSize)*uint64(pageIndex)+uint64(pageSize) > uint64(len(hashes)) { |
||||||
|
return hashes[pageSize*pageIndex:] |
||||||
|
} |
||||||
|
return hashes[pageSize*pageIndex : pageSize*pageIndex+pageSize] |
||||||
|
} |
||||||
|
|
||||||
|
// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
|
||||||
|
func SubmitTransaction( |
||||||
|
ctx context.Context, b Backend, tx *types.Transaction, |
||||||
|
) (common.Hash, error) { |
||||||
|
if err := b.SendTx(ctx, tx); err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
if tx.To() == nil { |
||||||
|
signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Epoch()) |
||||||
|
from, err := types.Sender(signer, tx) |
||||||
|
if err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
addr := crypto.CreateAddress(from, tx.Nonce()) |
||||||
|
utils.Logger().Info(). |
||||||
|
Str("fullhash", tx.Hash().Hex()). |
||||||
|
Str("contract", common2.MustAddressToBech32(addr)). |
||||||
|
Msg("Submitted contract creation") |
||||||
|
} else { |
||||||
|
utils.Logger().Info(). |
||||||
|
Str("fullhash", tx.Hash().Hex()). |
||||||
|
Str("recipient", tx.To().Hex()). |
||||||
|
Msg("Submitted transaction") |
||||||
|
} |
||||||
|
return tx.Hash(), nil |
||||||
|
} |
||||||
|
|
||||||
|
// SubmitStakingTransaction is a helper function that submits tx to txPool and logs a message.
|
||||||
|
func SubmitStakingTransaction( |
||||||
|
ctx context.Context, b Backend, tx *staking.StakingTransaction, |
||||||
|
) (common.Hash, error) { |
||||||
|
if err := b.SendStakingTx(ctx, tx); err != nil { |
||||||
|
return common.Hash{}, err |
||||||
|
} |
||||||
|
utils.Logger().Info().Str("fullhash", tx.Hash().Hex()).Msg("Submitted Staking transaction") |
||||||
|
return tx.Hash(), nil |
||||||
|
} |
Loading…
Reference in new issue