Divide apis into v1 and v2

pull/2048/head
flicker-harmony 5 years ago
parent f58f22c5e1
commit a4479d62d0
  1. 4
      internal/hmyapi/README.md
  2. 2
      internal/hmyapi/apiv1/addrlock.go
  3. 84
      internal/hmyapi/apiv1/backend.go
  4. 635
      internal/hmyapi/apiv1/blockchain.go
  5. 2
      internal/hmyapi/apiv1/debug.go
  6. 2
      internal/hmyapi/apiv1/error.go
  7. 4
      internal/hmyapi/apiv1/harmony.go
  8. 2
      internal/hmyapi/apiv1/net.go
  9. 6
      internal/hmyapi/apiv1/private_account.go
  10. 2
      internal/hmyapi/apiv1/public_account.go
  11. 25
      internal/hmyapi/apiv1/sendtxargs.go
  12. 368
      internal/hmyapi/apiv1/transactionpool.go
  13. 447
      internal/hmyapi/apiv1/types.go
  14. 2
      internal/hmyapi/apiv1/util.go
  15. 54
      internal/hmyapi/apiv2/addrlock.go
  16. 84
      internal/hmyapi/apiv2/backend.go
  17. 19
      internal/hmyapi/apiv2/blockchain.go
  18. 32
      internal/hmyapi/apiv2/debug.go
  19. 8
      internal/hmyapi/apiv2/error.go
  20. 78
      internal/hmyapi/apiv2/harmony.go
  21. 28
      internal/hmyapi/apiv2/net.go
  22. 70
      internal/hmyapi/apiv2/private_account.go
  23. 28
      internal/hmyapi/apiv2/public_account.go
  24. 83
      internal/hmyapi/apiv2/sendtxargs.go
  25. 4
      internal/hmyapi/apiv2/transactionpool.go
  26. 8
      internal/hmyapi/apiv2/types.go
  27. 71
      internal/hmyapi/apiv2/util.go
  28. 54
      internal/hmyapi/backend.go
  29. 10
      node/rpc.go

@ -89,3 +89,7 @@ The ``shh`` is for the whisper protocol to communicate p2p and broadcast
* [ ] shh_uninstallFilter * [ ] shh_uninstallFilter
* [ ] shh_getFilterChanges * [ ] shh_getFilterChanges
* [ ] shh_getMessages * [ ] shh_getMessages
### API Versions
* For API V1 you specify 1.0 version in curl
* For API V2 you specify 2.0 version in curl, V2 has output numbers were changed to decimals and also fixed few errors

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package hmyapi package apiv1
import ( import (
"sync" "sync"

@ -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 (
"context" "context"
@ -53,6 +53,7 @@ type NodeMetadata struct {
ChainID string `json:"chainid"` ChainID string `json:"chainid"`
IsLeader bool `json:"is-leader"` IsLeader bool `json:"is-leader"`
ShardID uint32 `json:"shard-id"` ShardID uint32 `json:"shard-id"`
Role string `json:"role"`
} }
// GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node // GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node
@ -65,6 +66,7 @@ func (s *PublicHarmonyAPI) GetNodeMetadata() NodeMetadata {
s.b.ChainConfig().ChainID.String(), s.b.ChainConfig().ChainID.String(),
s.b.IsLeader(), s.b.IsLeader(),
s.b.GetShardID(), s.b.GetShardID(),
cfg.Role().String(),
} }
} }

@ -1,4 +1,4 @@
package hmyapi package apiv1
import ( import (
"fmt" "fmt"

@ -1,4 +1,4 @@
package hmyapi package apiv1
import ( import (
"context" "context"
@ -30,7 +30,7 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error)
// tries to sign it with the key associated with args.To. If the given passwd isn't // tries to sign it with the key associated with args.To. If the given passwd isn't
// able to decrypt the key it fails. // able to decrypt the key it fails.
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
if &args.Nonce == nil { if args.Nonce == nil {
// Hold the addresse's mutex around signing to prevent concurrent assignment of // Hold the addresse's mutex around signing to prevent concurrent assignment of
// the same nonce to multiple accounts. // the same nonce to multiple accounts.
s.nonceLock.LockAddr(args.From) s.nonceLock.LockAddr(args.From)
@ -41,7 +41,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
utils.Logger().Warn(). utils.Logger().Warn().
Str("from", args.From.Hex()). Str("from", args.From.Hex()).
Str("to", args.To.Hex()). Str("to", args.To.Hex()).
Uint64("value", args.Value.Uint64()). Uint64("value", args.Value.ToInt().Uint64()).
AnErr("err", err). AnErr("err", err).
Msg("Failed transaction send attempt") Msg("Failed transaction send attempt")
return common.Hash{}, err return common.Hash{}, err

@ -1,4 +1,4 @@
package hmyapi package apiv1
import ( import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"

@ -1,4 +1,4 @@
package hmyapi package apiv1
import ( import (
"bytes" "bytes"
@ -16,10 +16,10 @@ type SendTxArgs struct {
From common.Address `json:"from"` From common.Address `json:"from"`
To *common.Address `json:"to"` To *common.Address `json:"to"`
ShardID uint32 `json:"shardID"` ShardID uint32 `json:"shardID"`
Gas uint64 `json:"gas"` Gas *hexutil.Uint64 `json:"gas"`
GasPrice *big.Int `json:"gasPrice"` GasPrice *hexutil.Big `json:"gasPrice"`
Value *big.Int `json:"value"` Value *hexutil.Big `json:"value"`
Nonce uint64 `json:"nonce"` Nonce *hexutil.Uint64 `json:"nonce"`
// We accept "data" and "input" for backwards-compatibility reasons. "input" is the // We accept "data" and "input" for backwards-compatibility reasons. "input" is the
// newer name and should be preferred by clients. // newer name and should be preferred by clients.
Data *hexutil.Bytes `json:"data"` Data *hexutil.Bytes `json:"data"`
@ -28,8 +28,9 @@ type SendTxArgs struct {
// setDefaults is a helper function that fills in default values for unspecified tx fields. // setDefaults is a helper function that fills in default values for unspecified tx fields.
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
if args.Gas == 0 { if args.Gas == nil {
args.Gas = 90000 args.Gas = new(hexutil.Uint64)
*(*uint64)(args.Gas) = 90000
} }
// TODO(ricl): add check for shardID // TODO(ricl): add check for shardID
if args.GasPrice == nil { if args.GasPrice == nil {
@ -41,14 +42,14 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
// args.GasPrice = (*hexutil.Big)(price) // args.GasPrice = (*hexutil.Big)(price)
} }
if args.Value == nil { if args.Value == nil {
args.Value = big.NewInt(0) args.Value = new(hexutil.Big)
} }
if &args.Nonce == nil { if args.Nonce == nil {
nonce, err := b.GetPoolNonce(ctx, args.From) nonce, err := b.GetPoolNonce(ctx, args.From)
if err != nil { if err != nil {
return err return err
} }
args.Nonce = nonce args.Nonce = (*hexutil.Uint64)(&nonce)
} }
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { 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`) return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
@ -76,7 +77,7 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
input = *args.Input input = *args.Input
} }
if args.To == nil { if args.To == nil {
return types.NewContractCreation(args.Nonce, args.ShardID, args.Value, args.Gas, args.GasPrice, input) return types.NewContractCreation(uint64(*args.Nonce), args.ShardID, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
} }
return types.NewTransaction(args.Nonce, *args.To, args.ShardID, args.Value, args.Gas, 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,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
}

@ -1,4 +1,4 @@
package hmyapi package apiv2
import ( import (
"context" "context"
@ -299,19 +299,18 @@ func (s *PublicBlockChainAPI) GetLeader(ctx context.Context) string {
} }
// GetValidatorSelfDelegation returns validator stake. // GetValidatorSelfDelegation returns validator stake.
func (s *PublicBlockChainAPI) GetValidatorSelfDelegation(ctx context.Context, address string) uint64 { func (s *PublicBlockChainAPI) GetValidatorSelfDelegation(ctx context.Context, address string) *big.Int {
return s.b.GetValidatorSelfDelegation(internal_common.ParseAddr(address)).Uint64() return s.b.GetValidatorSelfDelegation(internal_common.ParseAddr(address))
} }
// GetValidatorTotalDelegation returns total balace stacking for validator with delegation. // GetValidatorTotalDelegation returns total balace stacking for validator with delegation.
func (s *PublicBlockChainAPI) GetValidatorTotalDelegation(ctx context.Context, address string) uint64 { func (s *PublicBlockChainAPI) GetValidatorTotalDelegation(ctx context.Context, address string) *big.Int {
delegations := s.b.GetDelegationsByValidator(internal_common.ParseAddr(address)) delegations := s.b.GetDelegationsByValidator(internal_common.ParseAddr(address))
totalStake := big.NewInt(0) totalStake := big.NewInt(0)
for _, delegation := range delegations { for _, delegation := range delegations {
totalStake.Add(totalStake, delegation.Amount) totalStake.Add(totalStake, delegation.Amount)
} }
// TODO: return more than uint64 return totalStake
return totalStake.Uint64()
} }
// GetShardingStructure returns an array of sharding structures. // GetShardingStructure returns an array of sharding structures.
@ -416,8 +415,8 @@ func doCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb
} }
// Set default gas & gas price if none were set // Set default gas & gas price if none were set
gas := uint64(math.MaxUint64 / 2) gas := uint64(math.MaxUint64 / 2)
if args.Gas != 0 { if args.Gas != nil {
gas = args.Gas gas = uint64(*args.Gas)
} }
if globalGasCap != nil && globalGasCap.Uint64() < gas { if globalGasCap != nil && globalGasCap.Uint64() < gas {
utils.Logger().Warn(). utils.Logger().Warn().
@ -428,12 +427,12 @@ func doCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb
} }
gasPrice := new(big.Int).SetUint64(defaultGasPrice) gasPrice := new(big.Int).SetUint64(defaultGasPrice)
if args.GasPrice != nil { if args.GasPrice != nil {
gasPrice = args.GasPrice gasPrice = args.GasPrice.ToInt()
} }
value := new(big.Int) value := new(big.Int)
if args.Value != nil { if args.Value != nil {
value = args.Value value = args.Value.ToInt()
} }
var data []byte var data []byte

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

@ -1,4 +1,4 @@
package hmyapi package apiv2
import ( import (
"context" "context"
@ -184,7 +184,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
return common.Hash{}, err return common.Hash{}, err
} }
if &args.Nonce == nil { if args.Nonce == nil {
// Hold the addresse's mutex around signing to prevent concurrent assignment of // Hold the addresse's mutex around signing to prevent concurrent assignment of
// the same nonce to multiple accounts. // the same nonce to multiple accounts.
s.nonceLock.LockAddr(args.From) s.nonceLock.LockAddr(args.From)

@ -1,4 +1,4 @@
package hmyapi package apiv2
import ( import (
"encoding/hex" "encoding/hex"
@ -441,8 +441,8 @@ func newRPCStakingTransactionFromBlockIndex(b *types.Block, index uint64) *RPCSt
type CallArgs struct { type CallArgs struct {
From *common.Address `json:"from"` From *common.Address `json:"from"`
To *common.Address `json:"to"` To *common.Address `json:"to"`
Gas uint64 `json:"gas"` Gas *hexutil.Uint64 `json:"gas"`
GasPrice *big.Int `json:"gasPrice"` GasPrice *hexutil.Big `json:"gasPrice"`
Value *big.Int `json:"value"` Value *hexutil.Big `json:"value"`
Data *hexutil.Bytes `json:"data"` Data *hexutil.Bytes `json:"data"`
} }

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

@ -14,6 +14,8 @@ import (
"github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/hmyapi/apiv1"
"github.com/harmony-one/harmony/internal/hmyapi/apiv2"
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/shard" "github.com/harmony-one/harmony/shard"
staking "github.com/harmony-one/harmony/staking/types" staking "github.com/harmony-one/harmony/staking/types"
@ -85,33 +87,67 @@ type Backend interface {
// GetAPIs returns all the APIs. // GetAPIs returns all the APIs.
func GetAPIs(b Backend) []rpc.API { func GetAPIs(b Backend) []rpc.API {
nonceLock := new(AddrLocker) nonceLock := new(apiv1.AddrLocker)
nonceLockV2 := new(apiv2.AddrLocker)
return []rpc.API{ return []rpc.API{
{ {
Namespace: "hmy", Namespace: "hmy",
Version: "1.0", Version: "1.0",
Service: NewPublicHarmonyAPI(b), Service: apiv1.NewPublicHarmonyAPI(b),
Public: true, Public: true,
}, },
{ {
Namespace: "hmy", Namespace: "hmy",
Version: "1.0", Version: "1.0",
Service: NewPublicBlockChainAPI(b), Service: apiv1.NewPublicBlockChainAPI(b),
Public: true, Public: true,
}, { },
{
Namespace: "hmy", Namespace: "hmy",
Version: "1.0", Version: "1.0",
Service: NewPublicTransactionPoolAPI(b, nonceLock), Service: apiv1.NewPublicTransactionPoolAPI(b, nonceLock),
Public: true, Public: true,
}, { },
{
Namespace: "hmy", Namespace: "hmy",
Version: "1.0", Version: "1.0",
Service: NewPublicAccountAPI(b.AccountManager()), Service: apiv1.NewPublicAccountAPI(b.AccountManager()),
Public: true, Public: true,
}, { },
{
Namespace: "hmy", Namespace: "hmy",
Version: "1.0", Version: "1.0",
Service: NewDebugAPI(b), Service: apiv1.NewDebugAPI(b),
Public: true, // FIXME: change to false once IPC implemented
},
{
Namespace: "hmyv2",
Version: "1.0",
Service: apiv2.NewPublicHarmonyAPI(b),
Public: true,
},
{
Namespace: "hmyv2",
Version: "1.0",
Service: apiv2.NewPublicBlockChainAPI(b),
Public: true,
},
{
Namespace: "hmyv2",
Version: "1.0",
Service: apiv2.NewPublicTransactionPoolAPI(b, nonceLockV2),
Public: true,
},
{
Namespace: "hmyv2",
Version: "1.0",
Service: apiv2.NewPublicAccountAPI(b.AccountManager()),
Public: true,
},
{
Namespace: "hmyv2",
Version: "1.0",
Service: apiv2.NewDebugAPI(b),
Public: true, // FIXME: change to false once IPC implemented Public: true, // FIXME: change to false once IPC implemented
}, },
} }

@ -12,6 +12,8 @@ import (
"github.com/harmony-one/harmony/hmy" "github.com/harmony-one/harmony/hmy"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/hmyapi" "github.com/harmony-one/harmony/internal/hmyapi"
"github.com/harmony-one/harmony/internal/hmyapi/apiv1"
"github.com/harmony-one/harmony/internal/hmyapi/apiv2"
"github.com/harmony-one/harmony/internal/hmyapi/filters" "github.com/harmony-one/harmony/internal/hmyapi/filters"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard" "github.com/harmony-one/harmony/shard"
@ -192,7 +194,13 @@ func (node *Node) APIs() []rpc.API {
{ {
Namespace: "net", Namespace: "net",
Version: "1.0", Version: "1.0",
Service: hmyapi.NewPublicNetAPI(node.host, harmony.APIBackend.NetVersion()), Service: apiv1.NewPublicNetAPI(node.host, harmony.APIBackend.NetVersion()),
Public: true,
},
{
Namespace: "netv2",
Version: "1.0",
Service: apiv2.NewPublicNetAPI(node.host, harmony.APIBackend.NetVersion()),
Public: true, Public: true,
}, },
}...) }...)

Loading…
Cancel
Save