The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
woop/rosetta/services/block_side_effect.go

169 lines
5.9 KiB

[Rosetta] Make all side effects one transaction (#3458) * [rosetta] Start refactor of side effect transactions * Rename special transactions to side effect transactions * Rename various variables for consistancy within context of side effect transasctions * Remove all individual transaction for side effects, instead start process of batching all side effects under one transaction. * Finish batching of genesis side effect transaction. Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Add `getSideEffectTransaction` & refactor consumers This will hook in the logic to report all side effects under 1 transactions. Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Remove genesis logic path for special effect txs It is not integrated with the normal special effect logic Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Rename helper fns to use 'side effect' Replacing special case Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix special case operation index Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Consolidate side effect operation logic * Update tests for new side effect logic Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix import Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Update inline doc Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix comment for /block logic Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>
4 years ago
package services
import (
"context"
"fmt"
"strings"
"github.com/coinbase/rosetta-sdk-go/types"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/core"
hmytypes "github.com/harmony-one/harmony/core/types"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding"
"github.com/harmony-one/harmony/rosetta/common"
"github.com/harmony-one/harmony/shard"
)
// containsSideEffectTransaction checks if the block contains any side effect operations to report.
func (s *BlockAPI) containsSideEffectTransaction(
ctx context.Context, blk *hmytypes.Block,
) bool {
if blk == nil {
return false
}
return s.hmy.IsCommitteeSelectionBlock(blk.Header()) || !s.hmy.IsStakingEpoch(blk.Epoch()) || blk.NumberU64() == 0
}
const (
// SideEffectTransactionSuffix is use in the transaction identifier for each block that contains
// side-effect operations.
SideEffectTransactionSuffix = "side_effect"
blockHashStrLen = 64
)
// getSideEffectTransactionIdentifier fetches 'transaction identifier' for side effect operations
// for a given block.
// Side effects are genesis funds, pre-staking era block rewards, and undelegation payouts.
// Must include block hash to guarantee uniqueness of tx identifiers.
func getSideEffectTransactionIdentifier(
blockHash ethcommon.Hash,
) *types.TransactionIdentifier {
return &types.TransactionIdentifier{
Hash: fmt.Sprintf("%v_%v",
blockHash.String(), SideEffectTransactionSuffix,
),
}
}
// unpackSideEffectTransactionIdentifier returns the blockHash if the txID is formatted correctly.
func unpackSideEffectTransactionIdentifier(
txID *types.TransactionIdentifier,
) (ethcommon.Hash, *types.Error) {
hash := txID.Hash
hash = strings.TrimPrefix(hash, "0x")
hash = strings.TrimPrefix(hash, "0X")
if len(hash) <= blockHashStrLen || string(hash[blockHashStrLen]) != "_" ||
[Rosetta] Make all side effects one transaction (#3458) * [rosetta] Start refactor of side effect transactions * Rename special transactions to side effect transactions * Rename various variables for consistancy within context of side effect transasctions * Remove all individual transaction for side effects, instead start process of batching all side effects under one transaction. * Finish batching of genesis side effect transaction. Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Add `getSideEffectTransaction` & refactor consumers This will hook in the logic to report all side effects under 1 transactions. Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Remove genesis logic path for special effect txs It is not integrated with the normal special effect logic Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Rename helper fns to use 'side effect' Replacing special case Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix special case operation index Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Consolidate side effect operation logic * Update tests for new side effect logic Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix import Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Update inline doc Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix comment for /block logic Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>
4 years ago
hash[blockHashStrLen+1:] != SideEffectTransactionSuffix {
return ethcommon.Hash{}, common.NewError(common.CatchAllError, map[string]interface{}{
"message": "unknown side effect transaction ID format",
})
}
blkHash := ethcommon.HexToHash(hash[:blockHashStrLen])
return blkHash, nil
}
// getSideEffectTransaction returns the side effect transaction for a block if said block has one.
// Side effects to reports are: genesis funds, undelegation payouts, permissioned-phase block rewards.
func (s *BlockAPI) getSideEffectTransaction(
ctx context.Context, blk *hmytypes.Block,
) (*types.Transaction, *types.Error) {
if !s.containsSideEffectTransaction(ctx, blk) {
return nil, common.NewError(common.TransactionNotFoundError, map[string]interface{}{
"message": "no side effect transaction found for given block",
})
}
var startingOpIndex *int64
txOperations := []*types.Operation{}
updateStartingOpIndex := func(newOperations []*types.Operation) {
if len(newOperations) > 0 {
index := newOperations[len(newOperations)-1].OperationIdentifier.Index + 1
startingOpIndex = &index
}
txOperations = append(txOperations, newOperations...)
}
// Handle genesis funds
if blk.NumberU64() == 0 {
ops, rosettaError := GetSideEffectOperationsFromGenesisSpec(getGenesisSpec(s.hmy.ShardID), startingOpIndex)
if rosettaError != nil {
return nil, rosettaError
}
updateStartingOpIndex(ops)
}
// Handle block rewards for epoch < staking epoch (permissioned-phase block rewards)
// Note that block rewards don't start until the second block.
if !s.hmy.IsStakingEpoch(blk.Epoch()) && blk.NumberU64() > 1 {
rewards, err := s.hmy.GetPreStakingBlockRewards(ctx, blk)
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
"message": err.Error(),
})
}
ops, rosettaError := GetSideEffectOperationsFromPreStakingRewards(rewards, startingOpIndex)
if rosettaError != nil {
return nil, rosettaError
}
updateStartingOpIndex(ops)
}
// Handle undelegation payout
if s.hmy.IsCommitteeSelectionBlock(blk.Header()) && s.hmy.IsPreStakingEpoch(blk.Epoch()) {
payouts, err := s.hmy.GetUndelegationPayouts(ctx, blk.Epoch())
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
"message": err.Error(),
})
}
ops, rosettaError := GetSideEffectOperationsFromUndelegationPayouts(payouts, startingOpIndex)
if rosettaError != nil {
return nil, rosettaError
}
updateStartingOpIndex(ops)
}
return &types.Transaction{
TransactionIdentifier: getSideEffectTransactionIdentifier(blk.Hash()),
Operations: txOperations,
}, nil
}
// sideEffectBlockTransaction is a formatter for side effect transactions
func (s *BlockAPI) sideEffectBlockTransaction(
ctx context.Context, request *types.BlockTransactionRequest,
) (*types.BlockTransactionResponse, *types.Error) {
// If no transaction info is found, check for special case transactions.
blk, rosettaError := getBlock(ctx, s.hmy, &types.PartialBlockIdentifier{Index: &request.BlockIdentifier.Index})
if rosettaError != nil {
return nil, rosettaError
}
blkHash, rosettaError := unpackSideEffectTransactionIdentifier(
request.TransactionIdentifier,
)
if rosettaError != nil {
return nil, rosettaError
}
if blkHash.String() != blk.Hash().String() {
return nil, common.NewError(common.TransactionNotFoundError, map[string]interface{}{
"message": fmt.Sprintf("side effect transaction is not for block: %v", blk.NumberU64()),
})
}
tx, rosettaError := s.getSideEffectTransaction(ctx, blk)
if rosettaError != nil {
return nil, rosettaError
}
return &types.BlockTransactionResponse{Transaction: tx}, nil
}
// getGenesisSpec ..
func getGenesisSpec(shardID uint32) *core.Genesis {
if shard.Schedule.GetNetworkID() == shardingconfig.MainNet {
return core.NewGenesisSpec(nodeconfig.Mainnet, shardID)
}
if shard.Schedule.GetNetworkID() == shardingconfig.LocalNet {
return core.NewGenesisSpec(nodeconfig.Localnet, shardID)
}
return core.NewGenesisSpec(nodeconfig.Testnet, shardID)
}