Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rosetta Implementation Cleanup (Stage 3 of Node API Overhaul) #3390

Merged
merged 17 commits into from
Oct 17, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"unsafe"

"github.com/ethereum/go-ethereum/common"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
Expand Down Expand Up @@ -218,3 +219,19 @@ func (r Receipts) ToShardID(i int) uint32 {
func (r Receipts) MaxToShardID() uint32 {
return 0
}

// FindLogsWithTopic returns all the logs that contain the given receipt
func FindLogsWithTopic(
receipt *Receipt, targetTopic ethcommon.Hash,
) []*Log {
logs := []*Log{}
for _, log := range receipt.Logs {
LeoHChen marked this conversation as resolved.
Show resolved Hide resolved
for _, topic := range log.Topics {
if topic == targetTopic {
logs = append(logs, log)
break
}
}
}
return logs
}
114 changes: 114 additions & 0 deletions core/types/receipt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package types

import (
"reflect"
"testing"

ethcommon "github.com/ethereum/go-ethereum/common"

"github.com/ethereum/go-ethereum/crypto"
"github.com/harmony-one/harmony/staking"
)

func TestFindLogsWithTopic(t *testing.T) {
tests := []struct {
receipt *Receipt
topic ethcommon.Hash
expectedResponse []*Log
}{
// test 0
{
receipt: &Receipt{
Logs: []*Log{
{
Topics: []ethcommon.Hash{
staking.IsValidatorKey,
staking.IsValidator,
},
},
{
Topics: []ethcommon.Hash{
crypto.Keccak256Hash([]byte("test")),
},
},
{
Topics: []ethcommon.Hash{
staking.CollectRewardsTopic,
},
},
},
},
topic: staking.IsValidatorKey,
expectedResponse: []*Log{
{
Topics: []ethcommon.Hash{
staking.IsValidatorKey,
staking.IsValidator,
},
},
},
},
// test 1
{
receipt: &Receipt{
Logs: []*Log{
{
Topics: []ethcommon.Hash{
staking.IsValidatorKey,
staking.IsValidator,
},
},
{
Topics: []ethcommon.Hash{
crypto.Keccak256Hash([]byte("test")),
},
},
{
Topics: []ethcommon.Hash{
staking.CollectRewardsTopic,
},
},
},
},
topic: staking.CollectRewardsTopic,
expectedResponse: []*Log{
{
Topics: []ethcommon.Hash{
staking.CollectRewardsTopic,
},
},
},
},
// test 2
{
receipt: &Receipt{
Logs: []*Log{
{
Topics: []ethcommon.Hash{
staking.IsValidatorKey,
},
},
{
Topics: []ethcommon.Hash{
crypto.Keccak256Hash([]byte("test")),
},
},
{
Topics: []ethcommon.Hash{
staking.CollectRewardsTopic,
},
},
},
},
topic: staking.IsValidator,
expectedResponse: []*Log{},
},
}

for i, test := range tests {
response := FindLogsWithTopic(test.receipt, test.topic)
if !reflect.DeepEqual(test.expectedResponse, response) {
t.Errorf("Failed test %v, expected %v, got %v", i, test.expectedResponse, response)
}
}
}
45 changes: 45 additions & 0 deletions hmy/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,51 @@ func (hmy *Harmony) GetBlockSigners(
return committee.Slots, mask, nil
}

// DetailedBlockSignerInfo contains all of the block singing information
type DetailedBlockSignerInfo struct {
// Signers is a map of addresses in the Signers for the block to
// all of the serialized BLS keys that signed said block.
Signers map[common.Address][]bls.SerializedPublicKey
// Committee when the block was signed.
Committee shard.SlotList
// TotalKeysSigned is the total number of bls keys that signed the block.
TotalKeysSigned uint
// Mask is the bitmap Mask for the block.
Mask *bls.Mask
BlockHash common.Hash
}

// GetDetailedBlockSignerInfo fetches the block signer information for any non-genesis block
func (hmy *Harmony) GetDetailedBlockSignerInfo(
ctx context.Context, blk *types.Block,
) (*DetailedBlockSignerInfo, error) {
slotList, mask, err := hmy.GetBlockSigners(
LeoHChen marked this conversation as resolved.
Show resolved Hide resolved
ctx, rpc.BlockNumber(blk.Number().Uint64()),
)
if err != nil {
return nil, err
}

totalSigners := uint(0)
sigInfos := map[common.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 &DetailedBlockSignerInfo{
Signers: sigInfos,
Committee: slotList,
TotalKeysSigned: totalSigners,
Mask: mask,
BlockHash: blk.Hash(),
}, nil
}

// GetLatestChainHeaders ..
func (hmy *Harmony) GetLatestChainHeaders() *block.HeaderPair {
return &block.HeaderPair{
Expand Down
8 changes: 8 additions & 0 deletions hmy/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ func (hmy *Harmony) IsPreStakingEpoch(epoch *big.Int) bool {
return hmy.BlockChain.Config().IsPreStaking(epoch)
}

// IsCommitteeSelectionBlock checks if the given block is the committee selection block
func (hmy *Harmony) IsCommitteeSelectionBlock(blk *types.Block) bool {
isBeaconChain := blk.ShardID() == shard.BeaconChainShardID
isNewEpoch := len(blk.Header().ShardState()) > 0
Daniel-VDM marked this conversation as resolved.
Show resolved Hide resolved
inPreStakingEra := hmy.IsPreStakingEpoch(blk.Epoch())
return isBeaconChain && isNewEpoch && inPreStakingEra
}

// GetDelegationLockingPeriodInEpoch ...
func (hmy *Harmony) GetDelegationLockingPeriodInEpoch(epoch *big.Int) int {
return chain.GetLockPeriodInEpoch(hmy.BlockChain, epoch)
Expand Down
20 changes: 10 additions & 10 deletions rosetta/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ const (
// Blockchain ..
Blockchain = "Harmony"

// Symbol ..
Symbol = "ONE"
// NativeSymbol ..
NativeSymbol = "ONE"

// Decimals ..
Decimals = 18
// NativePrecision in the number of decimal places
NativePrecision = 18

// CurveType ..
CurveType = types.Secp256k1
Expand All @@ -40,14 +40,14 @@ var (
// IdleTimeout ..
IdleTimeout = 120 * time.Second

// Currency ..
Currency = types.Currency{
Symbol: Symbol,
Decimals: Decimals,
// NativeCurrency ..
NativeCurrency = types.Currency{
Symbol: NativeSymbol,
Decimals: NativePrecision,
}

// CurrencyHash for quick equivalent checks
CurrencyHash = types.Hash(Currency)
// NativeCurrencyHash for quick equivalent checks
NativeCurrencyHash = types.Hash(NativeCurrency)
)

// SyncStatus ..
Expand Down
27 changes: 15 additions & 12 deletions rosetta/common/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,39 @@ import (
staking "github.com/harmony-one/harmony/staking/types"
)

// Invariant: A transaction can only contain 1 type of operation(s) other than gas expenditure.
// Invariant: A transaction can only contain 1 type of native operation(s) other than gas expenditure.
const (
// ExpendGasOperation ..
// ExpendGasOperation is an operation that only affects the native currency.
ExpendGasOperation = "Gas"

// TransferOperation ..
TransferOperation = "Transfer"
// TransferNativeOperation is an operation that only affects the native currency.
TransferNativeOperation = "NativeTransfer"

// CrossShardTransferOperation ..
CrossShardTransferOperation = "CrossShardTransfer"
// CrossShardTransferNativeOperation is an operation that only affects the native currency.
CrossShardTransferNativeOperation = "NativeCrossShardTransfer"

// ContractCreationOperation ..
// ContractCreationOperation is an operation that only affects the native currency.
ContractCreationOperation = "ContractCreation"

// GenesisFundsOperation ..
// GenesisFundsOperation is a special operation for genesis block only.
// Note that no transaction can be constructed with this operation.
GenesisFundsOperation = "Genesis"

// PreStakingBlockRewardOperation ..
// PreStakingBlockRewardOperation is a special operation for pre-staking era only.
// Note that no transaction can be constructed with this operation.
PreStakingBlockRewardOperation = "PreStakingBlockReward"

// UndelegationPayoutOperation ..
// UndelegationPayoutOperation is a special operation for committee election block only.
// Note that no transaction can be constructed with this operation.
UndelegationPayoutOperation = "UndelegationPayout"
)

var (
// PlainOperationTypes ..
PlainOperationTypes = []string{
ExpendGasOperation,
TransferOperation,
CrossShardTransferOperation,
TransferNativeOperation,
CrossShardTransferNativeOperation,
ContractCreationOperation,
GenesisFundsOperation,
PreStakingBlockRewardOperation,
Expand Down
4 changes: 2 additions & 2 deletions rosetta/common/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ func TestPlainOperationTypes(t *testing.T) {
plainOperationTypes := PlainOperationTypes
referenceOperationTypes := []string{
ExpendGasOperation,
TransferOperation,
CrossShardTransferOperation,
TransferNativeOperation,
CrossShardTransferNativeOperation,
ContractCreationOperation,
GenesisFundsOperation,
PreStakingBlockRewardOperation,
Expand Down
44 changes: 42 additions & 2 deletions rosetta/services/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package services

import (
"context"
"fmt"

"github.com/coinbase/rosetta-sdk-go/server"
"github.com/coinbase/rosetta-sdk-go/types"
Expand All @@ -10,6 +11,7 @@ import (

hmyTypes "github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/hmy"
internalCommon "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/rosetta/common"
)

Expand All @@ -25,7 +27,7 @@ func NewAccountAPI(hmy *hmy.Harmony) server.AccountAPIServicer {
}
}

// AccountBalance ...
// AccountBalance implements the /account/balance endpoint
func (s *AccountAPI) AccountBalance(
ctx context.Context, request *types.AccountBalanceRequest,
) (*types.AccountBalanceResponse, *types.Error) {
Expand Down Expand Up @@ -73,7 +75,7 @@ func (s *AccountAPI) AccountBalance(

amount := types.Amount{
Value: balance.String(),
Currency: &common.Currency,
Currency: &common.NativeCurrency,
}

respBlock := types.BlockIdentifier{
Expand All @@ -86,3 +88,41 @@ func (s *AccountAPI) AccountBalance(
Balances: []*types.Amount{&amount},
}, nil
}

// AccountMetadata used for account identifiers
type AccountMetadata struct {
Address string `json:"hex_address"`
}

// newAccountIdentifier ..
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(),
})
}
metadata, err := types.MarshalMap(AccountMetadata{Address: address.String()})
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
"message": err.Error(),
})
}

return &types.AccountIdentifier{
Address: b32Address,
Metadata: metadata,
}, nil
}

// getAddress ..
func getAddress(
identifier *types.AccountIdentifier,
) (ethCommon.Address, error) {
if identifier == nil {
return ethCommon.Address{}, fmt.Errorf("identifier cannot be nil")
}
return internalCommon.Bech32ToAddress(identifier.Address)
}
Loading