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 ( |
||||
"context" |
@ -1,4 +1,4 @@ |
||||
package hmyapi |
||||
package apiv1 |
||||
|
||||
import "errors" |
||||
|
@ -1,4 +1,4 @@ |
||||
package hmyapi |
||||
package apiv1 |
||||
|
||||
import ( |
||||
"fmt" |
@ -1,4 +1,4 @@ |
||||
package hmyapi |
||||
package apiv1 |
||||
|
||||
import ( |
||||
"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 ( |
||||
"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