@ -29,10 +29,6 @@ import (
stakingTypes "github.com/harmony-one/harmony/staking/types"
)
const (
blockHashLen = 64
)
// BlockAPI implements the server.BlockAPIServicer interface.
type BlockAPI struct {
hmy * hmy . Harmony
@ -79,11 +75,18 @@ func (s *BlockAPI) Block(
Hash : prevBlock . Hash ( ) . String ( ) ,
}
// Report undelegation payouts as transactions to fit API.
// Report all transactions here since all undelegation payout amounts are known after fetching payouts.
transactions , rosettaError := s . getAllUndelegationPayoutTransactions ( ctx , blk )
if rosettaError != nil {
return nil , rosettaError
}
responseBlock := & types . Block {
BlockIdentifier : currBlockID ,
ParentBlockIdentifier : prevBlockID ,
Timestamp : blk . Time ( ) . Int64 ( ) * 1e3 , // Timestamp must be in ms.
Transactions : [ ] * types . Transaction { } , // Do not return tx details as it is optional.
Transactions : transactions ,
}
otherTransactions := [ ] * types . TransactionIdentifier { }
@ -99,6 +102,7 @@ func (s *BlockAPI) Block(
}
// Report cross-shard transaction payouts.
for _ , cxReceipts := range blk . IncomingReceipts ( ) {
// Report cross-shard transaction payouts.
for _ , cxReceipt := range cxReceipts . Receipts {
otherTransactions = append ( otherTransactions , & types . TransactionIdentifier {
Hash : cxReceipt . TxHash . String ( ) ,
@ -107,21 +111,11 @@ func (s *BlockAPI) Block(
}
// Report pre-staking era block rewards as transactions to fit API.
if ! s . hmy . IsStakingEpoch ( blk . Epoch ( ) ) {
blockSigInfo , rosettaError := getBlockSignerInfo ( ctx , s . hmy , blk )
preStakingRewardTxIDs , rosettaError := s . getAllPreStakingRewardTransactionIdentifiers ( ctx , blk )
if rosettaError != nil {
return nil , rosettaError
}
for acc , signedBlsKeys := range blockSigInfo . signers {
if len ( signedBlsKeys ) > 0 {
b32Addr , err := internalCommon . AddressToBech32 ( acc )
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
otherTransactions = append ( otherTransactions , getSpecialCaseTransactionIdentifier ( blk . Hash ( ) , b32Addr ) )
}
}
otherTransactions = append ( otherTransactions , preStakingRewardTxIDs ... )
}
return & types . BlockResponse {
@ -157,25 +151,122 @@ func (s *BlockAPI) genesisBlock(
otherTransactions := [ ] * types . TransactionIdentifier { }
// Report initial genesis funds as transactions to fit API.
for _ , tx := range getPseudoTransactionForGenesis ( getGenesisSpec ( blk . ShardID ( ) ) ) {
b32Addr , err := internalCommon . AddressToBech32 ( * tx . To ( ) )
otherTransactions = append (
otherTransactions , getSpecialCaseTransactionIdentifier ( blk . Hash ( ) , * tx . To ( ) , SpecialGenesisTxID ) ,
)
}
return & types . BlockResponse {
Block : responseBlock ,
OtherTransactions : otherTransactions ,
} , nil
}
// getAllPreStakingRewardTransactionIdentifiers is only used for the /block endpoint
func ( s * BlockAPI ) getAllPreStakingRewardTransactionIdentifiers (
ctx context . Context , blk * hmytypes . Block ,
) ( [ ] * types . TransactionIdentifier , * types . Error ) {
txIDs := [ ] * types . TransactionIdentifier { }
blockSigInfo , rosettaError := s . getBlockSignerInfo ( ctx , blk )
if rosettaError != nil {
return nil , rosettaError
}
for acc , signedBlsKeys := range blockSigInfo . signers {
if len ( signedBlsKeys ) > 0 {
txIDs = append ( txIDs , getSpecialCaseTransactionIdentifier ( blk . Hash ( ) , acc , SpecialPreStakingRewardTxID ) )
}
}
return txIDs , nil
}
// isCommitteeSelectionBlock ..
func ( s * BlockAPI ) isCommitteeSelectionBlock ( blk * hmytypes . Block ) bool {
isBeaconChain := blk . ShardID ( ) == shard . BeaconChainShardID
isNewEpoch := len ( blk . Header ( ) . ShardState ( ) ) > 0
inPreStakingEra := s . hmy . IsPreStakingEpoch ( blk . Epoch ( ) )
return isBeaconChain && isNewEpoch && inPreStakingEra
}
// getAllUndelegationPayoutTransactions is only used for the /block endpoint
func ( s * BlockAPI ) getAllUndelegationPayoutTransactions (
ctx context . Context , blk * hmytypes . Block ,
) ( [ ] * types . Transaction , * types . Error ) {
if ! s . isCommitteeSelectionBlock ( blk ) {
return [ ] * types . Transaction { } , nil
}
delegatorPayouts , err := s . hmy . GetUndelegationPayouts ( ctx , blk . Epoch ( ) )
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
otherTransactions = append ( otherTransactions , getSpecialCaseTransactionIdentifier ( blk . Hash ( ) , b32Addr ) )
transactions := [ ] * types . Transaction { }
for delegator , payout := range delegatorPayouts {
accID , rosettaError := newAccountIdentifier ( delegator )
if rosettaError != nil {
return nil , rosettaError
}
transactions = append ( transactions , & types . Transaction {
TransactionIdentifier : getSpecialCaseTransactionIdentifier (
blk . Hash ( ) , delegator , SpecialUndelegationPayoutTxID ,
) ,
Operations : [ ] * types . Operation {
{
OperationIdentifier : & types . OperationIdentifier {
Index : 0 , // There is no gas expenditure for undelegation payout
} ,
Type : common . UndelegationPayoutOperation ,
Status : common . SuccessOperationStatus . Status ,
Account : accID ,
Amount : & types . Amount {
Value : fmt . Sprintf ( "%v" , payout ) ,
Currency : & common . Currency ,
} ,
} ,
} ,
} )
}
return transactions , nil
}
return & types . BlockResponse {
Block : responseBlock ,
OtherTransactions : otherTransactions ,
// getBlockSignerInfo fetches the block signer information for any non-genesis block
func ( s * BlockAPI ) getBlockSignerInfo (
ctx context . Context , blk * hmytypes . Block ,
) ( * blockSignerInfo , * types . Error ) {
slotList , mask , err := s . hmy . GetBlockSigners (
ctx , rpc . BlockNumber ( blk . Number ( ) . Uint64 ( ) ) . EthBlockNumber ( ) ,
)
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
totalSigners := uint ( 0 )
sigInfos := map [ ethcommon . Address ] [ ] bls . SerializedPublicKey { }
for _ , slot := range slotList {
if _ , ok := sigInfos [ slot . EcdsaAddress ] ; ! ok {
sigInfos [ slot . EcdsaAddress ] = [ ] bls . SerializedPublicKey { }
}
if ok , err := mask . KeyEnabled ( slot . BLSPublicKey ) ; ok && err == nil {
sigInfos [ slot . EcdsaAddress ] = append ( sigInfos [ slot . EcdsaAddress ] , slot . BLSPublicKey )
totalSigners ++
}
}
return & blockSignerInfo {
signers : sigInfos ,
totalKeysSigned : totalSigners ,
mask : mask ,
blockHash : blk . Hash ( ) ,
} , nil
}
// BlockTransaction implements the /block/transaction endpoint
func ( s * BlockAPI ) BlockTransaction (
ctx context . Context , request * types . BlockTransactionRequest ,
) ( response * types . BlockTransactionResponse , rosettaError * types . Error ) {
) ( * types . BlockTransactionResponse , * types . Error ) {
if err := assertValidNetworkIdentifier ( request . NetworkIdentifier , s . hmy . ShardID ) ; err != nil {
return nil , err
}
@ -189,17 +280,14 @@ func (s *BlockAPI) BlockTransaction(
txHash := ethcommon . HexToHash ( request . TransactionIdentifier . Hash )
txInfo , rosettaError := s . getTransactionInfo ( ctx , blockHash , txHash )
if rosettaError != nil {
blk , rosettaError2 := s . getBlock ( ctx , & types . PartialBlockIdentifier { Index : & request . BlockIdentifier . Index } )
if rosettaError2 != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"error" : rosettaError2 ,
"base _error" : rosettaError ,
// If no transaction info is found, check for special case transactions.
response , rosettaError2 := s . specialBlockTransaction ( ctx , request )
if rosettaError2 != nil && rosettaError2 . Code != common . TransactionNotFoundError . Code {
return nil , common . NewError ( common . TransactionNotFoundError , map [ string ] interface { } {
"from _error" : rosettaError2 ,
} )
}
if s . hmy . IsStakingEpoch ( blk . Epoch ( ) ) {
return nil , rosettaError
}
return s . preStakingEraBlockRewardTransaction ( ctx , request . TransactionIdentifier , blk )
return response , rosettaError2
}
var transaction * types . Transaction
@ -229,25 +317,51 @@ func (s *BlockAPI) genesisBlockTransaction(
"message" : err . Error ( ) ,
} )
}
blkHash , b32Addr , rosettaError := unpackSpecialCaseTransactionIdentifier ( request . TransactionIdentifier )
blkHash , address , rosettaError := unpackSpecialCaseTransactionIdentifier (
request . TransactionIdentifier , SpecialGenesisTxID ,
)
if rosettaError != nil {
return nil , rosettaError
}
if blkHash . String ( ) != genesisBlock . Hash ( ) . String ( ) {
return nil , & common . TransactionNotFoundError
}
txs , rosettaError := formatGenesisTransaction ( request . TransactionIdentifier , b32Addr , s . hmy . ShardID )
txs , rosettaError := formatGenesisTransaction ( request . TransactionIdentifier , address , s . hmy . ShardID )
if rosettaError != nil {
return nil , rosettaError
}
return & types . BlockTransactionResponse { Transaction : txs } , nil
}
// preStakingEraBlockRewardTransaction is a special handler for pre-staking era block reward transactions
func ( s * BlockAPI ) preStakingEraBlockRewardTransaction (
// specialBlockTransaction is a formatter for special, non-genesis, transactions
func ( s * BlockAPI ) specialBlockTransaction (
ctx context . Context , request * types . BlockTransactionRequest ,
) ( * types . BlockTransactionResponse , * types . Error ) {
// If no transaction info is found, check for special case transactions.
blk , rosettaError := s . getBlock ( ctx , & types . PartialBlockIdentifier { Index : & request . BlockIdentifier . Index } )
if rosettaError != nil {
return nil , rosettaError
}
if s . isCommitteeSelectionBlock ( blk ) {
// Note that undelegation payout MUST be checked before reporting error in pre-staking & staking era.
response , rosettaError := s . undelegationPayoutBlockTransaction ( ctx , request . TransactionIdentifier , blk )
if rosettaError != nil && ! s . hmy . IsStakingEpoch ( blk . Epoch ( ) ) && s . hmy . IsPreStakingEpoch ( blk . Epoch ( ) ) {
// Handle edge case special transaction for pre-staking era
return s . preStakingRewardBlockTransaction ( ctx , request . TransactionIdentifier , blk )
}
return response , rosettaError
}
if ! s . hmy . IsStakingEpoch ( blk . Epoch ( ) ) {
return s . preStakingRewardBlockTransaction ( ctx , request . TransactionIdentifier , blk )
}
return nil , & common . TransactionNotFoundError
}
// preStakingRewardBlockTransaction is a special handler for pre-staking era
func ( s * BlockAPI ) preStakingRewardBlockTransaction (
ctx context . Context , txID * types . TransactionIdentifier , blk * hmytypes . Block ,
) ( * types . BlockTransactionResponse , * types . Error ) {
blkHash , b32Address , rosettaError := unpackSpecialCaseTransactionIdentifier ( txID )
blkHash , a ddress, rosettaError := unpackSpecialCaseTransactionIdentifier ( txID , SpecialPreStakingRewardT xID )
if rosettaError != nil {
return nil , rosettaError
}
@ -258,11 +372,41 @@ func (s *BlockAPI) preStakingEraBlockRewardTransaction(
) ,
} )
}
blockSignerInfo , rosettaError := getBlockSignerInfo ( ctx , s . hmy , blk )
blockSignerInfo , rosettaError := s . getBlockSignerInfo ( ctx , blk )
if rosettaError != nil {
return nil , rosettaError
}
transactions , rosettaError := formatPreStakingBlockRewardsTransaction ( b32Address , blockSignerInfo )
transactions , rosettaError := formatPreStakingRewardTransaction ( txID , blockSignerInfo , address )
if rosettaError != nil {
return nil , rosettaError
}
return & types . BlockTransactionResponse { Transaction : transactions } , nil
}
// undelegationPayoutBlockTransaction is a special handler for undelegation payout transactions
func ( s * BlockAPI ) undelegationPayoutBlockTransaction (
ctx context . Context , txID * types . TransactionIdentifier , blk * hmytypes . Block ,
) ( * types . BlockTransactionResponse , * types . Error ) {
blkHash , address , rosettaError := unpackSpecialCaseTransactionIdentifier ( txID , SpecialUndelegationPayoutTxID )
if rosettaError != nil {
return nil , rosettaError
}
if blkHash . String ( ) != blk . Hash ( ) . String ( ) {
return nil , common . NewError ( common . SanityCheckError , map [ string ] interface { } {
"message" : fmt . Sprintf (
"block hash %v != requested block hash %v in tx ID" , blkHash . String ( ) , blk . Hash ( ) . String ( ) ,
) ,
} )
}
delegatorPayouts , err := s . hmy . GetUndelegationPayouts ( ctx , blk . Epoch ( ) )
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
transactions , rosettaError := formatUndelegationPayoutTransaction ( txID , delegatorPayouts , address )
if rosettaError != nil {
return nil , rosettaError
}
@ -287,6 +431,9 @@ func (s *BlockAPI) getBlock(
"message" : err . Error ( ) ,
} )
}
if blk == nil {
return nil , & common . BlockNotFoundError
}
return blk , nil
}
@ -372,30 +519,56 @@ func getPseudoTransactionForGenesis(spec *core.Genesis) []*hmytypes.Transaction
return txs
}
// SpecialTransactionSuffix ..
type SpecialTransactionSuffix uint
// Special transaction suffixes that are specific to the rosetta package
const (
SpecialGenesisTxID SpecialTransactionSuffix = iota
SpecialPreStakingRewardTxID
SpecialUndelegationPayoutTxID
)
// String ..
func ( s SpecialTransactionSuffix ) String ( ) string {
return [ ... ] string { "genesis" , "reward" , "undelegation" } [ s ]
}
// getSpecialCaseTransactionIdentifier fetches 'transaction identifiers' for a given block-hash and suffix.
// Special cases include genesis transactions & pre-staking era block rewards.
// Special cases include genesis transactions, pre-staking era block rewards, and undelegation payout s.
// Must include block hash to guarantee uniqueness of tx identifiers.
func getSpecialCaseTransactionIdentifier (
blockHash ethcommon . Hash , suffix string ,
blockHash ethcommon . Hash , address ethcommon . Address , suffix SpecialTransactionSuffix ,
) * types . TransactionIdentifier {
return & types . TransactionIdentifier {
Hash : fmt . Sprintf ( "%v_%v" , blockHash . String ( ) , suffix ) ,
Hash : fmt . Sprintf ( "%v_%v_%v" ,
blockHash . String ( ) , internalCommon . MustAddressToBech32 ( address ) , suffix . String ( ) ,
) ,
}
}
const (
blockHashStrLen = 64
b32AddrStrLen = 42
)
// unpackSpecialCaseTransactionIdentifier returns the suffix & blockHash if the txID is formatted correctly.
func unpackSpecialCaseTransactionIdentifier (
txID * types . TransactionIdentifier ,
) ( ethcommon . Hash , string , * types . Error ) {
txID * types . TransactionIdentifier , expectedSuffix SpecialTransactionSuffix ,
) ( ethcommon . Hash , ethcommon . Address , * types . Error ) {
hash := txID . Hash
hash = strings . TrimPrefix ( hash , "0x" )
hash = strings . TrimPrefix ( hash , "0X" )
if len ( hash ) < blockHashLen + 1 || string ( hash [ blockHashLen ] ) != "_" {
return ethcommon . Hash { } , "" , common . NewError ( common . CatchAllError , map [ string ] interface { } {
minCharCount := blockHashStrLen + b32AddrStrLen + 2
if len ( hash ) < minCharCount || string ( hash [ blockHashStrLen ] ) != "_" ||
string ( hash [ minCharCount - 1 ] ) != "_" || expectedSuffix . String ( ) != hash [ minCharCount : ] {
return ethcommon . Hash { } , ethcommon . Address { } , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : "unknown special case transaction ID format" ,
} )
}
return ethcommon . HexToHash ( hash [ : blockHashLen ] ) , hash [ blockHashLen + 1 : ] , nil
blkHash := ethcommon . HexToHash ( hash [ : blockHashStrLen ] )
addr := internalCommon . MustBech32ToAddress ( hash [ blockHashStrLen + 1 : minCharCount - 1 ] )
return blkHash , addr , nil
}
// blockSignerInfo contains all of the block singing information
@ -410,38 +583,6 @@ type blockSignerInfo struct {
blockHash ethcommon . Hash
}
// getBlockSignerInfo fetches the block signer information for any non-genesis block
func getBlockSignerInfo (
ctx context . Context , hmy * hmy . Harmony , blk * hmytypes . Block ,
) ( * blockSignerInfo , * types . Error ) {
slotList , mask , err := hmy . GetBlockSigners (
ctx , rpc . BlockNumber ( blk . Number ( ) . Uint64 ( ) ) . EthBlockNumber ( ) ,
)
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
totalSigners := uint ( 0 )
sigInfos := map [ ethcommon . Address ] [ ] bls . SerializedPublicKey { }
for _ , slot := range slotList {
if _ , ok := sigInfos [ slot . EcdsaAddress ] ; ! ok {
sigInfos [ slot . EcdsaAddress ] = [ ] bls . SerializedPublicKey { }
}
if ok , err := mask . KeyEnabled ( slot . BLSPublicKey ) ; ok && err == nil {
sigInfos [ slot . EcdsaAddress ] = append ( sigInfos [ slot . EcdsaAddress ] , slot . BLSPublicKey )
totalSigners ++
}
}
return & blockSignerInfo {
signers : sigInfos ,
totalKeysSigned : totalSigners ,
mask : mask ,
blockHash : blk . Hash ( ) ,
} , nil
}
// TransactionMetadata ..
type TransactionMetadata struct {
CrossShardIdentifier * types . TransactionIdentifier ` json:"cross_shard_transaction_identifier,omitempty" `
@ -497,17 +638,13 @@ func formatCrossShardReceiverTransaction(
// formatGenesisTransaction for genesis block's initial balances
func formatGenesisTransaction (
txID * types . TransactionIdentifier , targetB32Addr string , shardID uint32 ,
txID * types . TransactionIdentifier , targetAddr ethcommon . Address , shardID uint32 ,
) ( fmtTx * types . Transaction , rosettaError * types . Error ) {
var b32Addr string
var err error
targetB32Addr := internalCommon . MustAddressToBech32 ( targetAddr )
genesisSpec := getGenesisSpec ( shardID )
for _ , tx := range getPseudoTransactionForGenesis ( genesisSpec ) {
if b32Addr , err = internalCommon . AddressToBech32 ( * tx . To ( ) ) ; err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
b32Addr , _ = internalCommon . AddressToBech32 ( * tx . To ( ) )
if targetB32Addr == b32Addr {
accID , rosettaError := newAccountIdentifier ( * tx . To ( ) )
if rosettaError != nil {
@ -538,22 +675,15 @@ func formatGenesisTransaction(
return nil , & common . TransactionNotFoundError
}
// formatPreStakingBlock Rewards Transaction for block rewards in pre-staking era for a given Bech-32 address
func formatPreStakingBlock Rewards Transaction (
b32Address string , blockSigInfo * blockSignerInfo ,
// formatPreStakingRewardTransaction for block rewards in pre-staking era for a given Bech-32 address.
func formatPreStakingRewardTransaction (
txID * types . TransactionIdentifier , blockSigInfo * blockSignerInfo , address ethcommon . Address ,
) ( * types . Transaction , * types . Error ) {
addr , err := internalCommon . Bech32ToAddress ( b32Address )
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
signatures , ok := blockSigInfo . signers [ addr ]
signatures , ok := blockSigInfo . signers [ address ]
if ! ok || len ( signatures ) == 0 {
return nil , & common . TransactionNotFoundError
}
accID , rosettaError := newAccountIdentifier ( addr )
accID , rosettaError := newAccountIdentifier ( address )
if rosettaError != nil {
return nil , rosettaError
}
@ -573,11 +703,11 @@ func formatPreStakingBlockRewardsTransaction(
last = cur
i ++
}
if sigAddr == addr {
if sigAddr == address {
rewardsForThisBlock = rewardsForThisAddr
if ! ( rewardsForThisAddr . Cmp ( big . NewInt ( 0 ) ) > 0 ) {
return nil , common . NewError ( common . SanityCheckError , map [ string ] interface { } {
"message" : "expected non-zero block reward in pre-staking ea r for block signer" ,
"message" : "expected non-zero block reward in pre-staking era for block signer" ,
} )
}
break
@ -585,13 +715,13 @@ func formatPreStakingBlockRewardsTransaction(
}
return & types . Transaction {
TransactionIdentifier : getSpecialCaseTransactionIdentifier ( blockSigInfo . blockHash , b32Address ) ,
TransactionIdentifier : txID ,
Operations : [ ] * types . Operation {
{
OperationIdentifier : & types . OperationIdentifier {
Index : 0 ,
} ,
Type : common . PreStakingEra BlockRewardOperation ,
Type : common . PreStakingBlockRewardOperation ,
Status : common . SuccessOperationStatus . Status ,
Account : accID ,
Amount : & types . Amount {
@ -603,6 +733,38 @@ func formatPreStakingBlockRewardsTransaction(
} , nil
}
// formatUndelegationPayoutTransaction for undelegation payouts at committee selection block
func formatUndelegationPayoutTransaction (
txID * types . TransactionIdentifier , delegatorPayouts hmy . UndelegationPayouts , address ethcommon . Address ,
) ( * types . Transaction , * types . Error ) {
accID , rosettaError := newAccountIdentifier ( address )
if rosettaError != nil {
return nil , rosettaError
}
payout , ok := delegatorPayouts [ address ]
if ! ok {
return nil , & common . TransactionNotFoundError
}
return & types . Transaction {
TransactionIdentifier : txID ,
Operations : [ ] * types . Operation {
{
OperationIdentifier : & types . OperationIdentifier {
Index : 0 ,
} ,
Type : common . UndelegationPayoutOperation ,
Status : common . SuccessOperationStatus . Status ,
Account : accID ,
Amount : & types . Amount {
Value : fmt . Sprintf ( "%v" , payout ) ,
Currency : & common . Currency ,
} ,
} ,
} ,
} , nil
}
// formatTransaction for staking, cross-shard sender, and plain transactions
func formatTransaction (
tx hmytypes . PoolTransaction , receipt * hmytypes . Receipt ,
@ -732,7 +894,7 @@ func getStakingOperations(
} )
}
// Set correct amount depending on staking message directive
// Set correct amount depending on staking message directive that apply balance changes INSTANTLY
var amount * types . Amount
switch tx . StakingType ( ) {
case stakingTypes . DirectiveCreateValidator :
@ -740,11 +902,7 @@ func getStakingOperations(
return nil , rosettaError
}
case stakingTypes . DirectiveDelegate :
if amount , rosettaError = getAmountFromDelegateMessage ( tx . Data ( ) ) ; rosettaError != nil {
return nil , rosettaError
}
case stakingTypes . DirectiveUndelegate :
if amount , rosettaError = getAmountFromUndelegateMessage ( tx . Data ( ) ) ; rosettaError != nil {
if amount , rosettaError = getAmountFromDelegateMessage ( receipt , tx . Data ( ) ) ; rosettaError != nil {
return nil , rosettaError
}
case stakingTypes . DirectiveCollectRewards :
@ -753,7 +911,7 @@ func getStakingOperations(
}
default :
amount = & types . Amount {
Value : fmt . Sprintf ( "-%v" , tx . Value ( ) ) ,
Value : "0" , // All other staking transactions do not apply balance changes instantly or at all
Currency : & common . Currency ,
}
}
@ -792,7 +950,7 @@ func getAmountFromCreateValidatorMessage(data []byte) (*types.Amount, *types.Err
} , nil
}
func getAmountFromDelegateMessage ( data [ ] byte ) ( * types . Amount , * types . Error ) {
func getAmountFromDelegateMessage ( receipt * hmytypes . Receipt , data [ ] byte ) ( * types . Amount , * types . Error ) {
msg , err := stakingTypes . RLPDecodeStakeMsg ( data , stakingTypes . DirectiveDelegate )
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
@ -805,8 +963,21 @@ func getAmountFromDelegateMessage(data []byte) (*types.Amount, *types.Error) {
"message" : "unable to parse staking message for delegate tx" ,
} )
}
stkAmount := stkMsg . Amount
logs := findLogsWithTopic ( receipt , staking . DelegateTopic )
for _ , log := range logs {
if len ( log . Data ) > ethcommon . AddressLength {
validatorAddress := ethcommon . BytesToAddress ( log . Data [ : ethcommon . AddressLength ] )
if log . Address == stkMsg . DelegatorAddress && stkMsg . ValidatorAddress == validatorAddress {
// Remove re-delegation amount as funds were never credited to account's balance.
stkAmount = new ( big . Int ) . Sub ( stkAmount , new ( big . Int ) . SetBytes ( log . Data [ ethcommon . AddressLength : ] ) )
break
}
}
}
return & types . Amount {
Value : fmt . Sprintf ( "-%v" , stkMsg . Amount ) ,
Value : fmt . Sprintf ( "-%v" , stkAmount ) ,
Currency : & common . Currency ,
} , nil
}
@ -1018,12 +1189,7 @@ type AccountMetadata struct {
func newAccountIdentifier (
address ethcommon . Address ,
) ( * types . AccountIdentifier , * types . Error ) {
b32Address , err := internalCommon . AddressToBech32 ( address )
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {
"message" : err . Error ( ) ,
} )
}
b32Address , _ := internalCommon . AddressToBech32 ( address )
metadata , err := rpc . NewStructuredResponse ( AccountMetadata { Address : address . String ( ) } )
if err != nil {
return nil , common . NewError ( common . CatchAllError , map [ string ] interface { } {