diff --git a/internal/hmyapi/blockchain.go b/internal/hmyapi/blockchain.go index e636b7e62..d753bea36 100644 --- a/internal/hmyapi/blockchain.go +++ b/internal/hmyapi/blockchain.go @@ -541,10 +541,10 @@ func (s *PublicBlockChainAPI) GetValidatorInformation(ctx context.Context, addre return rpcValidator, nil } -// GetDelegationsByDelegator returns information about a validator. +// GetDelegationsByDelegator returns list of delegations for a delegator address. func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, address string) ([]*RPCDelegation, error) { - validatorAddress := internal_common.ParseAddr(address) - validators, delegations := s.b.GetDelegationsByDelegator(validatorAddress) + delegatorAddress := internal_common.ParseAddr(address) + validators, delegations := s.b.GetDelegationsByDelegator(delegatorAddress) result := []*RPCDelegation{} for i := range delegations { delegation := delegations[i] @@ -559,7 +559,7 @@ func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, add } result = append(result, &RPCDelegation{ validators[i], - validatorAddress, + delegatorAddress, delegation.Amount, delegation.Reward, undelegations, @@ -593,3 +593,33 @@ func (s *PublicBlockChainAPI) GetDelegationsByValidator(ctx context.Context, add } 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, + }) + } + return &RPCDelegation{ + validatorAddress, + delegatorAddress, + delegation.Amount, + delegation.Reward, + undelegations, + }, nil + } + return nil, nil +} diff --git a/internal/hmyapi/transactionpool.go b/internal/hmyapi/transactionpool.go index 0ce5b0ef9..a30dcbd07 100644 --- a/internal/hmyapi/transactionpool.go +++ b/internal/hmyapi/transactionpool.go @@ -109,17 +109,44 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context // 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 - if tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash); tx != nil { + tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash) + if tx != nil { return newRPCTransaction(tx, blockHash, blockNumber, index) } // No finalized transaction, try to retrieve it from the pool - if tx := s.b.GetPoolTransaction(hash); tx != nil { + 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) + if stx != nil { + return newRPCStakingTransaction(stx, blockHash, blockNumber, 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) diff --git a/internal/hmyapi/types.go b/internal/hmyapi/types.go index 52fe723e7..62c3858f3 100644 --- a/internal/hmyapi/types.go +++ b/internal/hmyapi/types.go @@ -38,6 +38,23 @@ type RPCTransaction struct { 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"` + 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"` @@ -186,7 +203,6 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber Gas: hexutil.Uint64(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), Hash: tx.Hash(), - Input: hexutil.Bytes(tx.Data()), Nonce: hexutil.Uint64(tx.Nonce()), Value: (*hexutil.Big)(tx.Value()), ShardID: tx.ShardID(), @@ -220,6 +236,96 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber 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, 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()), + 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) @@ -244,6 +350,7 @@ type RPCBlock struct { 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"` @@ -282,6 +389,14 @@ func RPCMarshalBlock(b *types.Block, blockArgs BlockArgs) (map[string]interface{ return newRPCTransactionFromBlockHash(b, tx.Hash()), nil } } + 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 + } + } txs := b.Transactions() transactions := make([]interface{}, len(txs)) var err error @@ -291,6 +406,15 @@ func RPCMarshalBlock(b *types.Block, blockArgs BlockArgs) (map[string]interface{ } } fields["transactions"] = transactions + + 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() @@ -324,6 +448,25 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransacti return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), 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(), index) +} + // CallArgs represents the arguments for a call. type CallArgs struct { From *common.Address `json:"from"` diff --git a/staking/types/transaction.go b/staking/types/transaction.go index bf6b85af6..c7cba8837 100644 --- a/staking/types/transaction.go +++ b/staking/types/transaction.go @@ -209,6 +209,11 @@ func RLPDecodeStakeMsg(payload []byte, d Directive) (interface{}, error) { return ds, nil } +// RawSignatureValues return raw signature values. +func (tx *StakingTransaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { + return tx.data.V, tx.data.R, tx.data.S +} + // StakingType returns the type of staking transaction func (tx *StakingTransaction) StakingType() Directive { return tx.data.Directive