merge with main

pull/3405/head
Rongjian Lan 4 years ago
commit a36da0c28d
  1. 2
      api/service/syncing/syncing.go
  2. 2
      cmd/harmony/main.go
  3. 16
      common/ntp/ntp_test.go
  4. 6
      consensus/consensus_service.go
  5. 2
      consensus/consensus_v2.go
  6. 10
      consensus/construct_test.go
  7. 2
      consensus/quorum/one-node-one-vote.go
  8. 3
      consensus/quorum/one-node-staked-vote.go
  9. 14
      consensus/quorum/quorom_test.go
  10. 6
      consensus/quorum/quorum.go
  11. 27
      consensus/validator.go
  12. 7
      consensus/view_change.go
  13. 2
      go.mod
  14. 52
      node/node.go
  15. 2
      rosetta/common/config.go
  16. 12
      rosetta/common/operations.go
  17. 4
      rosetta/common/operations_test.go
  18. 27
      rosetta/services/account.go
  19. 65
      rosetta/services/block.go
  20. 2
      rosetta/services/block_special.go
  21. 103
      rosetta/services/construction_check.go
  22. 8
      rosetta/services/construction_check_test.go
  23. 35
      rosetta/services/construction_create.go
  24. 22
      rosetta/services/construction_parse.go
  25. 4
      rosetta/services/construction_parse_test.go
  26. 11
      rosetta/services/construction_submit.go
  27. 6
      rosetta/services/mempool.go
  28. 10
      rosetta/services/tx_construction.go
  29. 22
      rosetta/services/tx_construction_test.go
  30. 23
      rosetta/services/tx_format.go
  31. 12
      rosetta/services/tx_format_test.go
  32. 67
      rosetta/services/tx_operation.go
  33. 6
      rosetta/services/tx_operation_components.go
  34. 30
      rosetta/services/tx_operation_components_test.go
  35. 25
      rosetta/services/tx_operation_test.go
  36. 6
      rpc/contract.go
  37. 2
      rpc/transaction.go
  38. 2
      shard/committee/assignment.go

@ -32,7 +32,7 @@ const (
TimesToFail = 5 // downloadBlocks service retry limit
RegistrationNumber = 3
SyncingPortDifference = 3000
inSyncThreshold = 0 // when peerBlockHeight - myBlockHeight <= inSyncThreshold, it's ready to join consensus
inSyncThreshold = 1 // when peerBlockHeight - myBlockHeight <= inSyncThreshold, it's ready to join consensus
SyncLoopBatchSize uint32 = 1000 // maximum size for one query of block hashes
verifyHeaderBatchSize uint64 = 100 // block chain header verification batch size
SyncLoopFrequency = 1 // unit in second

@ -315,7 +315,7 @@ func setupNodeAndRun(hc harmonyConfig) {
WSPort: hc.WS.Port,
DebugEnabled: hc.RPCOpt.DebugEnabled,
}
if nodeConfig.ShardID != shard.BeaconChainShardID {
if nodeConfig.ShardID != shard.BeaconChainShardID && hc.General.NodeType != nodeTypeExplorer {
utils.Logger().Info().
Uint32("shardID", currentNode.Blockchain().ShardID()).
Uint32("shardID", nodeConfig.ShardID).Msg("SupportBeaconSyncing")

@ -2,19 +2,23 @@ package ntp
import (
"fmt"
"os"
"testing"
)
func TestCheckLocalTimeAccurate(t *testing.T) {
accurate, err := CheckLocalTimeAccurate("0.pool.ntp.org")
if !accurate {
t.Fatalf("local time is not accurate: %v\n", err)
}
accurate, err = CheckLocalTimeAccurate("wrong.ip")
accurate, err := CheckLocalTimeAccurate("wrong.ip")
if accurate {
t.Fatalf("query ntp pool should failed: %v\n", err)
}
accurate, err = CheckLocalTimeAccurate("0.pool.ntp.org")
if !accurate {
if os.IsTimeout(err) {
t.Skip(err)
}
t.Fatal(err)
}
}
func TestCurrentTime(t *testing.T) {

@ -421,6 +421,12 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode {
Msg("[UpdateConsensusInformation] changing committee")
// take care of possible leader change during the epoch
// TODO: in a very rare case, when a M1 view change happened, the block contains coinbase for last leader
// but the new leader is actually recognized by most of the nodes. At this time, if a node sync to this
// exact block and set its leader, it will set with the failed leader as in the coinbase of the block.
// This is a very rare case scenario and not likely to cause any issue in mainnet. But we need to think about
// a solution to take care of this case because the coinbase of the latest block doesn't really represent the
// the real current leader in case of M1 view change.
if !curHeader.IsLastBlockInEpoch() && curHeader.Number().Uint64() != 0 {
leaderPubKey, err := consensus.getLeaderPubKeyFromCoinbase(curHeader)
if err != nil || leaderPubKey == nil {

@ -608,7 +608,7 @@ func (consensus *Consensus) commitBlock(blk *types.Block, committedMsg *FBFTMess
func (consensus *Consensus) SetupForNewConsensus(blk *types.Block, committedMsg *FBFTMessage) {
atomic.AddUint64(&consensus.blockNum, 1)
consensus.SetCurBlockViewID(committedMsg.ViewID + 1)
consensus.SetViewIDs(committedMsg.ViewID + 1)
consensus.LeaderPubKey = committedMsg.SenderPubkeys[0]
// Update consensus keys at last so the change of leader status doesn't mess up normal flow
if blk.IsLastBlockInEpoch() {

@ -71,19 +71,21 @@ func TestConstructPreparedMessage(test *testing.T) {
message := "test string"
leaderKey := bls.SerializedPublicKey{}
leaderKey.FromLibBLSPublicKey(leaderPubKey)
leaderKeyWrapper := bls.PublicKeyWrapper{Object: leaderPubKey, Bytes: leaderKey}
validatorKey := bls.SerializedPublicKey{}
validatorKey.FromLibBLSPublicKey(validatorPubKey)
consensus.Decider.SubmitVote(
validatorKeyWrapper := bls.PublicKeyWrapper{Object: validatorPubKey, Bytes: validatorKey}
consensus.Decider.AddNewVote(
quorum.Prepare,
[]bls.SerializedPublicKey{leaderKey},
[]*bls.PublicKeyWrapper{&leaderKeyWrapper},
leaderPriKey.Sign(message),
common.BytesToHash(consensus.blockHash[:]),
consensus.blockNum,
consensus.GetCurBlockViewID(),
)
if _, err := consensus.Decider.SubmitVote(
if _, err := consensus.Decider.AddNewVote(
quorum.Prepare,
[]bls.SerializedPublicKey{validatorKey},
[]*bls.PublicKeyWrapper{&validatorKeyWrapper},
validatorPriKey.Sign(message),
common.BytesToHash(consensus.blockHash[:]),
consensus.blockNum,

@ -36,7 +36,7 @@ func (v *uniformVoteWeight) AddNewVote(
for i, pubKey := range pubKeys {
pubKeysBytes[i] = pubKey.Bytes
}
return v.SubmitVote(p, pubKeysBytes, sig, headerHash, height, viewID)
return v.submitVote(p, pubKeysBytes, sig, headerHash, height, viewID)
}
// IsQuorumAchieved ..

@ -82,7 +82,7 @@ func (v *stakedVoteWeight) AddNewVote(
pubKeysBytes[i] = pubKey.Bytes
}
ballet, err := v.SubmitVote(p, pubKeysBytes, sig, headerHash, height, viewID)
ballet, err := v.submitVote(p, pubKeysBytes, sig, headerHash, height, viewID)
if err != nil {
return ballet, err
@ -188,7 +188,6 @@ func (v *stakedVoteWeight) QuorumThreshold() numeric.Dec {
// IsAllSigsCollected ..
func (v *stakedVoteWeight) IsAllSigsCollected() bool {
utils.Logger().Info().Msgf("ALL SIGS %s", v.voteTally.Commit.tally)
return v.voteTally.Commit.tally.Equal(numeric.NewDec(1))
}

@ -88,7 +88,7 @@ func TestSubmitVote(test *testing.T) {
decider.UpdateParticipants([]bls.PublicKeyWrapper{pubKeyWrapper1, pubKeyWrapper2})
if _, err := decider.SubmitVote(
if _, err := decider.submitVote(
Prepare,
[]bls.SerializedPublicKey{pubKeyWrapper1.Bytes},
blsPriKey1.Sign(message),
@ -99,7 +99,7 @@ func TestSubmitVote(test *testing.T) {
test.Log(err)
}
if _, err := decider.SubmitVote(
if _, err := decider.submitVote(
Prepare,
[]bls.SerializedPublicKey{pubKeyWrapper2.Bytes},
blsPriKey2.Sign(message),
@ -110,7 +110,7 @@ func TestSubmitVote(test *testing.T) {
test.Log(err)
}
if decider.SignersCount(Prepare) != 2 {
test.Fatal("SubmitVote failed")
test.Fatal("submitVote failed")
}
aggSig := &bls_core.Sign{}
@ -145,7 +145,7 @@ func TestSubmitVoteAggregateSig(test *testing.T) {
decider.UpdateParticipants([]bls.PublicKeyWrapper{pubKeyWrapper1, pubKeyWrapper2})
decider.SubmitVote(
decider.submitVote(
Prepare,
[]bls.SerializedPublicKey{pubKeyWrapper1.Bytes},
blsPriKey1.SignHash(blockHash[:]),
@ -160,7 +160,7 @@ func TestSubmitVoteAggregateSig(test *testing.T) {
aggSig.Add(s)
}
}
if _, err := decider.SubmitVote(
if _, err := decider.submitVote(
Prepare,
[]bls.SerializedPublicKey{pubKeyWrapper2.Bytes, pubKeyWrapper3.Bytes},
aggSig,
@ -172,7 +172,7 @@ func TestSubmitVoteAggregateSig(test *testing.T) {
}
if decider.SignersCount(Prepare) != 3 {
test.Fatal("SubmitVote failed")
test.Fatal("submitVote failed")
}
aggSig.Add(blsPriKey1.SignHash(blockHash[:]))
@ -180,7 +180,7 @@ func TestSubmitVoteAggregateSig(test *testing.T) {
test.Fatal("AggregateVotes failed")
}
if _, err := decider.SubmitVote(
if _, err := decider.submitVote(
Prepare,
[]bls.SerializedPublicKey{pubKeyWrapper2.Bytes},
aggSig,

@ -81,7 +81,8 @@ type ParticipantTracker interface {
// SignatoryTracker ..
type SignatoryTracker interface {
ParticipantTracker
SubmitVote(
// This func shouldn't be called directly from outside of quorum. Use AddNewVote instead.
submitVote(
p Phase, pubkeys []bls.SerializedPublicKey,
sig *bls_core.Sign, headerHash common.Hash,
height, viewID uint64,
@ -118,6 +119,7 @@ type Decider interface {
DependencyInjectionWriter
SetVoters(subCommittee *shard.Committee, epoch *big.Int) (*TallyResult, error)
Policy() Policy
// Add new vote will add the signature in the memory and increase the cumulative voting power
AddNewVote(
p Phase, pubkeys []*bls_cosi.PublicKeyWrapper,
sig *bls_core.Sign, headerHash common.Hash,
@ -273,7 +275,7 @@ func (s *cIdentities) SignersCount(p Phase) int64 {
}
}
func (s *cIdentities) SubmitVote(
func (s *cIdentities) submitVote(
p Phase, pubkeys []bls.SerializedPublicKey,
sig *bls_core.Sign, headerHash common.Hash,
height, viewID uint64,

@ -114,10 +114,6 @@ func (consensus *Consensus) onPrepared(msg *msg_pb.Message) {
Msg("Wrong BlockNum Received, ignoring!")
return
}
if recvMsg.BlockNum > consensus.blockNum+1 {
consensus.getLogger().Warn().Msgf("[OnPrepared] low consensus block number. Spin sync")
consensus.spinUpStateSync()
}
// check validity of prepared signature
blockHash := recvMsg.BlockHash
@ -153,10 +149,12 @@ func (consensus *Consensus) onPrepared(msg *msg_pb.Message) {
if !consensus.onPreparedSanityChecks(&blockObj, recvMsg) {
return
}
consensus.getLogger().Info().
Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("MsgViewID", recvMsg.ViewID).
Msg("[OnPrepared] Received OnPrepared message11111111")
if recvMsg.BlockNum > consensus.blockNum {
consensus.getLogger().Warn().Msgf("[OnPrepared] low consensus block number. Spin sync")
consensus.spinUpStateSync()
}
consensus.mutex.Lock()
defer consensus.mutex.Unlock()
consensus.getLogger().Info().
@ -248,7 +246,6 @@ func (consensus *Consensus) onCommitted(msg *msg_pb.Message) {
consensus.getLogger().Warn().Msg("[OnCommitted] unable to parse msg")
return
}
consensus.getLogger().Info().
Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("MsgViewID", recvMsg.ViewID).
@ -298,10 +295,6 @@ func (consensus *Consensus) onCommitted(msg *msg_pb.Message) {
Msg("[OnCommitted] Failed finding a matching block for committed message")
return
}
consensus.getLogger().Info().
Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("MsgViewID", recvMsg.ViewID).
Msg("[OnCommitted] Received committed message333333")
commitPayload := signature.ConstructCommitPayload(consensus.Blockchain,
blockObj.Epoch(), blockObj.Hash(), blockObj.NumberU64(), blockObj.Header().ViewID().Uint64())
if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) {
@ -317,10 +310,10 @@ func (consensus *Consensus) onCommitted(msg *msg_pb.Message) {
Msg("[OnCommitted] Received committed message444444")
consensus.FBFTLog.AddMessage(recvMsg)
consensus.getLogger().Info().
Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("MsgViewID", recvMsg.ViewID).
Msg("[OnCommitted] Received committed message555555")
if recvMsg.BlockNum > consensus.blockNum {
consensus.getLogger().Info().Msg("[OnCommitted] low consensus block number. Spin up state sync")
consensus.spinUpStateSync()
}
consensus.getLogger().Info().
Uint64("MsgBlockNum", recvMsg.BlockNum).

@ -131,9 +131,8 @@ func (consensus *Consensus) getNextViewID() (uint64, time.Duration) {
if curTimestamp <= blockTimestamp {
return consensus.fallbackNextViewID()
}
totalNode := consensus.Decider.ParticipantsCount()
// diff is at least 1, and it won't exceed the totalNode
diff := uint64(((curTimestamp - blockTimestamp) / viewChangeTimeout) % int64(totalNode))
// diff only increases
diff := uint64((curTimestamp - blockTimestamp) / viewChangeTimeout)
nextViewID := diff + consensus.GetCurBlockViewID()
consensus.getLogger().Info().
@ -161,7 +160,7 @@ func (consensus *Consensus) getNextLeaderKey(viewID uint64) *bls.PublicKeyWrappe
var err error
epoch := big.NewInt(0)
if consensus.Blockchain == nil {
consensus.getLogger().Error().Msg("[getNextLeaderKey] ChainReader is nil. Use consensus.LeaderPubKey")
consensus.getLogger().Error().Msg("[getNextLeaderKey] Blockchain is nil. Use consensus.LeaderPubKey")
lastLeaderPubKey = consensus.LeaderPubKey
} else {
curHeader := consensus.Blockchain.CurrentHeader()

@ -11,7 +11,7 @@ require (
github.com/beevik/ntp v0.3.0
github.com/btcsuite/btcutil v1.0.2
github.com/cespare/cp v1.1.1
github.com/coinbase/rosetta-sdk-go v0.4.4
github.com/coinbase/rosetta-sdk-go v0.4.6
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v1.7.1
github.com/edsrzf/mmap-go v1.0.0 // indirect

@ -204,10 +204,16 @@ func (node *Node) tryBroadcastStaking(stakingTx *staking.StakingTransaction) {
// Add new transactions to the pending transaction list.
func (node *Node) addPendingTransactions(newTxs types.Transactions) []error {
poolTxs := types.PoolTransactions{}
errs := []error{}
acceptCx := node.Blockchain().Config().AcceptsCrossTx(node.Blockchain().CurrentHeader().Epoch())
for _, tx := range newTxs {
if tx.ShardID() != tx.ToShardID() && !acceptCx {
errs = append(errs, errors.WithMessage(errInvalidEpoch, "cross-shard tx not accepted yet"))
continue
}
poolTxs = append(poolTxs, tx)
}
errs := node.TxPool.AddRemotes(poolTxs)
errs = append(errs, node.TxPool.AddRemotes(poolTxs)...)
pendingCount, queueCount := node.TxPool.Stats()
utils.Logger().Info().
@ -221,22 +227,28 @@ func (node *Node) addPendingTransactions(newTxs types.Transactions) []error {
// Add new staking transactions to the pending staking transaction list.
func (node *Node) addPendingStakingTransactions(newStakingTxs staking.StakingTransactions) []error {
if node.NodeConfig.ShardID == shard.BeaconChainShardID &&
node.Blockchain().Config().IsPreStaking(node.Blockchain().CurrentHeader().Epoch()) {
poolTxs := types.PoolTransactions{}
for _, tx := range newStakingTxs {
poolTxs = append(poolTxs, tx)
if node.NodeConfig.ShardID == shard.BeaconChainShardID {
if node.Blockchain().Config().IsPreStaking(node.Blockchain().CurrentHeader().Epoch()) {
poolTxs := types.PoolTransactions{}
for _, tx := range newStakingTxs {
poolTxs = append(poolTxs, tx)
}
errs := node.TxPool.AddRemotes(poolTxs)
pendingCount, queueCount := node.TxPool.Stats()
utils.Logger().Info().
Int("length of newStakingTxs", len(poolTxs)).
Int("totalPending", pendingCount).
Int("totalQueued", queueCount).
Msg("Got more staking transactions")
return errs
}
errs := node.TxPool.AddRemotes(poolTxs)
pendingCount, queueCount := node.TxPool.Stats()
utils.Logger().Info().
Int("length of newStakingTxs", len(poolTxs)).
Int("totalPending", pendingCount).
Int("totalQueued", queueCount).
Msg("Got more staking transactions")
return errs
return []error{
errors.WithMessage(errInvalidEpoch, "staking txs not accepted yet"),
}
}
return []error{
errors.WithMessage(errInvalidShard, fmt.Sprintf("txs only valid on shard %v", shard.BeaconChainShardID)),
}
return make([]error, len(newStakingTxs))
}
// AddPendingStakingTransaction staking transactions
@ -248,13 +260,17 @@ func (node *Node) AddPendingStakingTransaction(
var err error
for i := range errs {
if errs[i] != nil {
utils.Logger().Info().Err(errs[i]).Msg("[AddPendingStakingTransaction] Failed adding new staking transaction")
utils.Logger().Info().
Err(errs[i]).
Msg("[AddPendingStakingTransaction] Failed adding new staking transaction")
err = errs[i]
break
}
}
if err == nil || node.BroadcastInvalidTx {
utils.Logger().Info().Str("Hash", newStakingTx.Hash().Hex()).Msg("Broadcasting Staking Tx")
utils.Logger().Info().
Str("Hash", newStakingTx.Hash().Hex()).
Msg("Broadcasting Staking Tx")
node.tryBroadcastStaking(newStakingTx)
}
return err
@ -361,6 +377,8 @@ var (
errWrongShardID = errors.New("wrong shard id")
errInvalidNodeMsg = errors.New("invalid node message")
errIgnoreBeaconMsg = errors.New("ignore beacon sync block")
errInvalidEpoch = errors.New("invalid epoch for transaction")
errInvalidShard = errors.New("invalid shard")
)
// validateNodeMessage validate node message

@ -12,7 +12,7 @@ import (
const (
// RosettaVersion tied back to the version of the rosetta go-sdk
RosettaVersion = "0.4.4" // TODO (dm): set variable via build flags
RosettaVersion = "1.4.6" // TODO (dm): set variable via build flags
// Blockchain ..
Blockchain = "Harmony"

@ -15,11 +15,11 @@ const (
// ExpendGasOperation is an operation that only affects the native currency.
ExpendGasOperation = "Gas"
// TransferNativeOperation is an operation that only affects the native currency.
TransferNativeOperation = "NativeTransfer"
// NativeTransferOperation is an operation that only affects the native currency.
NativeTransferOperation = "NativeTransfer"
// CrossShardTransferNativeOperation is an operation that only affects the native currency.
CrossShardTransferNativeOperation = "NativeCrossShardTransfer"
// NativeCrossShardTransferOperation is an operation that only affects the native currency.
NativeCrossShardTransferOperation = "NativeCrossShardTransfer"
// ContractCreationOperation is an operation that only affects the native currency.
ContractCreationOperation = "ContractCreation"
@ -41,8 +41,8 @@ var (
// PlainOperationTypes ..
PlainOperationTypes = []string{
ExpendGasOperation,
TransferNativeOperation,
CrossShardTransferNativeOperation,
NativeTransferOperation,
NativeCrossShardTransferOperation,
ContractCreationOperation,
GenesisFundsOperation,
PreStakingBlockRewardOperation,

@ -50,8 +50,8 @@ func TestPlainOperationTypes(t *testing.T) {
plainOperationTypes := PlainOperationTypes
referenceOperationTypes := []string{
ExpendGasOperation,
TransferNativeOperation,
CrossShardTransferNativeOperation,
NativeTransferOperation,
NativeCrossShardTransferOperation,
ContractCreationOperation,
GenesisFundsOperation,
PreStakingBlockRewardOperation,

@ -36,39 +36,26 @@ func (s *AccountAPI) AccountBalance(
}
var block *hmyTypes.Block
var rosettaError *types.Error
if request.BlockIdentifier == nil {
block = s.hmy.CurrentBlock()
} else {
var err error
if request.BlockIdentifier.Hash != nil {
blockHash := ethCommon.HexToHash(*request.BlockIdentifier.Hash)
block, err = s.hmy.GetBlock(ctx, blockHash)
if err != nil {
return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{
"message": "block hash not found",
})
}
} else {
blockNum := rpc.BlockNumber(*request.BlockIdentifier.Index)
block, err = s.hmy.BlockByNumber(ctx, blockNum)
if err != nil {
return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{
"message": "block index not found",
})
}
block, rosettaError = getBlock(ctx, s.hmy, request.BlockIdentifier)
if rosettaError != nil {
return nil, rosettaError
}
}
addr, err := getAddress(request.AccountIdentifier)
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
return nil, common.NewError(common.SanityCheckError, map[string]interface{}{
"message": err.Error(),
})
}
blockNum := rpc.BlockNumber(block.Header().Header.Number().Int64())
balance, err := s.hmy.GetBalance(ctx, addr, blockNum)
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
return nil, common.NewError(common.SanityCheckError, map[string]interface{}{
"message": "invalid address",
})
}
@ -100,7 +87,7 @@ func newAccountIdentifier(
) (*types.AccountIdentifier, *types.Error) {
b32Address, err := internalCommon.AddressToBech32(address)
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
return nil, common.NewError(common.SanityCheckError, map[string]interface{}{
"message": err.Error(),
})
}

@ -2,6 +2,7 @@ package services
import (
"context"
"fmt"
"math/big"
"github.com/coinbase/rosetta-sdk-go/server"
@ -43,7 +44,7 @@ func (s *BlockAPI) Block(
var blk *hmytypes.Block
var currBlockID, prevBlockID *types.BlockIdentifier
if blk, rosettaError = s.getBlock(ctx, request.BlockIdentifier); rosettaError != nil {
if blk, rosettaError = getBlock(ctx, s.hmy, request.BlockIdentifier); rosettaError != nil {
return nil, rosettaError
}
@ -124,30 +125,6 @@ func (s *BlockAPI) Block(
}, nil
}
// getBlock ..
func (s *BlockAPI) getBlock(
ctx context.Context, request *types.PartialBlockIdentifier,
) (blk *hmytypes.Block, rosettaError *types.Error) {
var err error
if request.Hash != nil {
requestBlockHash := ethcommon.HexToHash(*request.Hash)
blk, err = s.hmy.GetBlock(ctx, requestBlockHash)
} else if request.Index != nil {
blk, err = s.hmy.BlockByNumber(ctx, rpc.BlockNumber(*request.Index).EthBlockNumber())
} else {
return nil, &common.BlockNotFoundError
}
if err != nil {
return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{
"message": err.Error(),
})
}
if blk == nil {
return nil, &common.BlockNotFoundError
}
return blk, nil
}
// BlockTransaction implements the /block/transaction endpoint
func (s *BlockAPI) BlockTransaction(
ctx context.Context, request *types.BlockTransactionRequest,
@ -174,10 +151,20 @@ func (s *BlockAPI) BlockTransaction(
}
return response, rosettaError2
}
state, _, err := s.hmy.StateAndHeaderByNumber(ctx, rpc.BlockNumber(request.BlockIdentifier.Index).EthBlockNumber())
if state == nil || err != nil {
return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{
"message": fmt.Sprintf("block state not found for block %v", request.BlockIdentifier.Index),
})
}
var transaction *types.Transaction
if txInfo.tx != nil && txInfo.receipt != nil {
transaction, rosettaError = FormatTransaction(txInfo.tx, txInfo.receipt)
contractCode := []byte{}
if txInfo.tx.To() != nil {
contractCode = state.GetCode(*txInfo.tx.To())
}
transaction, rosettaError = FormatTransaction(txInfo.tx, txInfo.receipt, contractCode)
if rosettaError != nil {
return nil, rosettaError
}
@ -245,3 +232,29 @@ func (s *BlockAPI) getTransactionInfo(
cxReceipt: cxReceipt,
}, nil
}
// getBlock ..
func getBlock(
ctx context.Context, hmy *hmy.Harmony, blockID *types.PartialBlockIdentifier,
) (blk *hmytypes.Block, rosettaError *types.Error) {
var err error
if blockID.Hash != nil {
requestBlockHash := ethcommon.HexToHash(*blockID.Hash)
blk, err = hmy.GetBlock(ctx, requestBlockHash)
} else if blockID.Index != nil {
blk, err = hmy.BlockByNumber(ctx, rpc.BlockNumber(*blockID.Index).EthBlockNumber())
} else {
return nil, &common.BlockNotFoundError
}
if err != nil {
return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{
"message": err.Error(),
})
}
if blk == nil {
return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{
"message": "block not found for given block identifier",
})
}
return blk, nil
}

@ -181,7 +181,7 @@ 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})
blk, rosettaError := getBlock(ctx, s.hmy, &types.PartialBlockIdentifier{Index: &request.BlockIdentifier.Index})
if rosettaError != nil {
return nil, rosettaError
}

@ -9,8 +9,10 @@ import (
"github.com/coinbase/rosetta-sdk-go/types"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethRpc "github.com/ethereum/go-ethereum/rpc"
"github.com/pkg/errors"
"github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/rosetta/common"
"github.com/harmony-one/harmony/rpc"
@ -74,6 +76,17 @@ func (s *ConstructAPI) ConstructionPreprocess(
"message": "sender address is not found for given operations",
})
}
if txMetadata.ToShardID != nil && txMetadata.FromShardID != nil &&
components.Type != common.NativeCrossShardTransferOperation && *txMetadata.ToShardID != *txMetadata.FromShardID {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "given from & to shard are different for a native same shard transfer",
})
}
if request.SuggestedFeeMultiplier != nil && *request.SuggestedFeeMultiplier < 1 {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "given gas price multiplier must be at least 1",
})
}
options, err := types.MarshalMap(ConstructMetadataOptions{
TransactionMetadata: txMetadata,
@ -85,6 +98,11 @@ func (s *ConstructAPI) ConstructionPreprocess(
"message": err.Error(),
})
}
if _, err := getAddress(components.From); err != nil {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": err.Error(),
})
}
return &types.ConstructionPreprocessResponse{
Options: options,
RequiredPublicKeys: []*types.AccountIdentifier{
@ -95,10 +113,13 @@ func (s *ConstructAPI) ConstructionPreprocess(
// ConstructMetadata with a set of operations will construct a valid transaction
type ConstructMetadata struct {
Nonce uint64 `json:"nonce"`
GasLimit uint64 `json:"gas_limit"`
GasPrice *big.Int `json:"gas_price"`
Transaction *TransactionMetadata `json:"transaction_metadata"`
Nonce uint64 `json:"nonce"`
GasLimit uint64 `json:"gas_limit"`
GasPrice *big.Int `json:"gas_price"`
ContractCode hexutil.Bytes `json:"contract_code"`
EvmReturn hexutil.Bytes `json:"evm_return"`
EvmErrorMessage string `json:"evm_error_message"`
Transaction *TransactionMetadata `json:"transaction_metadata"`
}
// UnmarshalFromInterface ..
@ -151,6 +172,20 @@ func (s *ConstructAPI) ConstructionMetadata(
})
}
currBlock, err := s.hmy.BlockByNumber(ctx, ethRpc.LatestBlockNumber)
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
"message": err.Error(),
})
}
if options.OperationType == common.NativeCrossShardTransferOperation &&
!s.hmy.BlockChain.Config().AcceptsCrossTx(currBlock.Epoch()) {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "cross-shard transaction is not accepted yet",
})
}
data := hexutil.Bytes{}
if options.TransactionMetadata.Data != nil {
var err error
@ -161,13 +196,31 @@ func (s *ConstructAPI) ConstructionMetadata(
}
}
var contractAddress ethCommon.Address
if options.TransactionMetadata.ContractAccountIdentifier != nil {
contractAddress, err = getAddress(options.TransactionMetadata.ContractAccountIdentifier)
if err != nil {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": errors.WithMessage(err, "unable to get provided contract address").Error(),
})
}
}
state, _, err := s.hmy.StateAndHeaderByNumber(ctx, ethRpc.LatestBlockNumber)
if state == nil || err != nil {
return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{
"message": "block state not found for latest block",
})
}
var estGasUsed uint64
if !isStakingOperation(options.OperationType) {
if options.OperationType == common.ContractCreationOperation {
estGasUsed, err = rpc.EstimateGas(ctx, s.hmy, rpc.CallArgs{Data: &data}, nil)
estGasUsed, err = rpc.EstimateGas(ctx, s.hmy, rpc.CallArgs{From: senderAddr, Data: &data}, nil)
estGasUsed *= 2 // HACK to account for imperfect contract creation estimation
} else {
estGasUsed, err = rpc.EstimateGas(ctx, s.hmy, rpc.CallArgs{To: &ethCommon.Address{}, Data: &data}, nil)
estGasUsed, err = rpc.EstimateGas(
ctx, s.hmy, rpc.CallArgs{From: senderAddr, To: &contractAddress, Data: &data}, nil,
)
}
} else {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
@ -185,11 +238,41 @@ func (s *ConstructAPI) ConstructionMetadata(
}
sugNativeFee, sugNativePrice := getSuggestedNativeFeeAndPrice(gasMul, new(big.Int).SetUint64(estGasUsed))
evmErrorMsg := ""
evmReturn := hexutil.Bytes{}
if !isStakingOperation(options.OperationType) &&
options.OperationType != common.ContractCreationOperation &&
len(data) > 0 {
gas := hexutil.Uint64(estGasUsed)
callArgs := rpc.CallArgs{
From: senderAddr,
To: &contractAddress,
Data: &data,
Gas: &gas,
}
evmExe, err := rpc.DoEVMCall(
ctx, s.hmy, callArgs, ethRpc.LatestBlockNumber, vm.Config{}, rpc.CallTimeout, s.hmy.RPCGasCap,
)
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
"message": errors.WithMessage(err, "unable to execute EVM").Error(),
})
}
if evmExe.VMErr != nil {
evmErrorMsg = evmExe.VMErr.Error()
}
evmReturn = evmExe.ReturnData
sugNativeFee, sugNativePrice = getSuggestedNativeFeeAndPrice(gasMul, new(big.Int).SetUint64(evmExe.UsedGas))
}
metadata, err := types.MarshalMap(ConstructMetadata{
Nonce: nonce,
GasPrice: sugNativePrice,
GasLimit: estGasUsed,
Transaction: options.TransactionMetadata,
Nonce: nonce,
GasPrice: sugNativePrice,
GasLimit: estGasUsed,
Transaction: options.TransactionMetadata,
ContractCode: state.GetCode(contractAddress),
EvmErrorMessage: evmErrorMsg,
EvmReturn: evmReturn,
})
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{

@ -28,7 +28,7 @@ func TestConstructMetadataOptions(t *testing.T) {
{
Metadata: ConstructMetadataOptions{
TransactionMetadata: refTxMedata,
OperationType: common.TransferNativeOperation,
OperationType: common.NativeTransferOperation,
GasPriceMultiplier: nil,
},
ExpectError: false,
@ -36,7 +36,7 @@ func TestConstructMetadataOptions(t *testing.T) {
{
Metadata: ConstructMetadataOptions{
TransactionMetadata: refTxMedata,
OperationType: common.TransferNativeOperation,
OperationType: common.NativeTransferOperation,
GasPriceMultiplier: &refGasPrice,
},
ExpectError: false,
@ -44,7 +44,7 @@ func TestConstructMetadataOptions(t *testing.T) {
{
Metadata: ConstructMetadataOptions{
TransactionMetadata: nil,
OperationType: common.TransferNativeOperation,
OperationType: common.NativeTransferOperation,
GasPriceMultiplier: &refGasPrice,
},
ExpectError: true,
@ -52,7 +52,7 @@ func TestConstructMetadataOptions(t *testing.T) {
{
Metadata: ConstructMetadataOptions{
TransactionMetadata: nil,
OperationType: common.TransferNativeOperation,
OperationType: common.NativeTransferOperation,
GasPriceMultiplier: nil,
},
ExpectError: true,

@ -7,6 +7,7 @@ import (
"fmt"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rlp"
"github.com/pkg/errors"
@ -23,9 +24,10 @@ const (
// WrappedTransaction is a wrapper for a transaction that includes all relevant
// data to parse a transaction.
type WrappedTransaction struct {
RLPBytes []byte `json:"rlp_bytes"`
IsStaking bool `json:"is_staking"`
From *types.AccountIdentifier `json:"from"`
RLPBytes []byte `json:"rlp_bytes"`
IsStaking bool `json:"is_staking"`
ContractCode hexutil.Bytes `json:"contract_code"`
From *types.AccountIdentifier `json:"from"`
}
// unpackWrappedTransactionFromString ..
@ -111,11 +113,18 @@ func (s *ConstructAPI) ConstructionPayloads(
"message": "sender address is not found for given operations",
})
}
if types.Hash(senderID) != types.Hash(components.From) {
if senderID.Address != components.From.Address {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "sender account identifier from operations does not match account identifier from public key",
})
}
if metadata.Transaction.FromShardID != nil && *metadata.Transaction.FromShardID != s.hmy.ShardID {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": fmt.Sprintf("transaction is for shard %v != shard %v",
*metadata.Transaction.FromShardID, s.hmy.ShardID,
),
})
}
unsignedTx, rosettaError := ConstructTransaction(components, metadata, s.hmy.ShardID)
if rosettaError != nil {
@ -132,9 +141,10 @@ func (s *ConstructAPI) ConstructionPayloads(
})
}
wrappedTxMarshalledBytes, err := json.Marshal(WrappedTransaction{
RLPBytes: buf.Bytes(),
From: senderID,
IsStaking: components.IsStaking(),
RLPBytes: buf.Bytes(),
From: senderID,
ContractCode: metadata.ContractCode,
IsStaking: components.IsStaking(),
})
if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
@ -184,11 +194,16 @@ func (s *ConstructAPI) ConstructionCombine(
"message": "require exactly 1 signature",
})
}
if tx.ShardID() != s.hmy.ShardID {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": fmt.Sprintf("transaction is for shard %v != shard %v", tx.ShardID(), s.hmy.ShardID),
})
}
sig := request.Signatures[0]
if sig.SignatureType != common.SignatureType {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": fmt.Sprintf("invalid transaction type, currently only support %v", common.SignatureType),
"message": fmt.Sprintf("invalid signature type, currently only support %v", common.SignatureType),
})
}
sigAddress, rosettaError := getAddressFromPublicKey(sig.PublicKey)
@ -199,7 +214,7 @@ func (s *ConstructAPI) ConstructionCombine(
if rosettaError != nil {
return nil, rosettaError
}
if wrappedTransaction.From == nil || types.Hash(wrappedTransaction.From) != types.Hash(sigAccountID) {
if wrappedTransaction.From == nil || wrappedTransaction.From.Address != sigAccountID.Address {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "signer public key does not match unsigned transaction's sender",
})
@ -239,7 +254,7 @@ func (s *ConstructAPI) ConstructionCombine(
senderAddress, err := signedTx.SenderAddress()
if err != nil {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": errors.WithMessage(err, "unable to get sender address with signed transaction").Error(),
"message": errors.WithMessage(err, "bad signature payload").Error(),
})
}
if *sigAddress != senderAddress {

@ -2,6 +2,7 @@ package services
import (
"context"
"fmt"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/pkg/errors"
@ -21,6 +22,11 @@ func (s *ConstructAPI) ConstructionParse(
if rosettaError != nil {
return nil, rosettaError
}
if tx.ShardID() != s.hmy.ShardID {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": fmt.Sprintf("transaction is for shard %v != shard %v", tx.ShardID(), s.hmy.ShardID),
})
}
if request.Signed {
return parseSignedTransaction(ctx, wrappedTransaction, tx)
}
@ -37,11 +43,17 @@ func parseUnsignedTransaction(
})
}
if _, err := getAddress(wrappedTransaction.From); err != nil {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": err.Error(),
})
}
// TODO (dm): implement intended receipt for staking transactions
intendedReceipt := &hmyTypes.Receipt{
GasUsed: tx.Gas(),
}
formattedTx, rosettaError := FormatTransaction(tx, intendedReceipt)
formattedTx, rosettaError := FormatTransaction(tx, intendedReceipt, wrappedTransaction.ContractCode)
if rosettaError != nil {
return nil, rosettaError
}
@ -52,7 +64,7 @@ func parseUnsignedTransaction(
foundSender := false
operations := formattedTx.Operations
for _, op := range operations {
if types.Hash(op.Account) == types.Hash(tempAccID) {
if op.Account.Address == tempAccID.Address {
foundSender = true
op.Account = wrappedTransaction.From
}
@ -82,21 +94,21 @@ func parseSignedTransaction(
intendedReceipt := &hmyTypes.Receipt{
GasUsed: tx.Gas(),
}
formattedTx, rosettaError := FormatTransaction(tx, intendedReceipt)
formattedTx, rosettaError := FormatTransaction(tx, intendedReceipt, wrappedTransaction.ContractCode)
if rosettaError != nil {
return nil, rosettaError
}
sender, err := tx.SenderAddress()
if err != nil {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": errors.WithMessage(err, "unable to get sender address, invalid signed transaction"),
"message": errors.WithMessage(err, "unable to get sender address for signed transaction").Error(),
})
}
senderID, rosettaError := newAccountIdentifier(sender)
if rosettaError != nil {
return nil, rosettaError
}
if types.Hash(senderID) != types.Hash(wrappedTransaction.From) {
if senderID.Address != wrappedTransaction.From.Address {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "wrapped transaction sender/from does not match transaction signer",
})

@ -38,7 +38,7 @@ func TestParseUnsignedTransaction(t *testing.T) {
refTestReceipt := &hmytypes.Receipt{
GasUsed: testTx.Gas(),
}
refFormattedTx, rosettaError := FormatTransaction(testTx, refTestReceipt)
refFormattedTx, rosettaError := FormatTransaction(testTx, refTestReceipt, []byte{})
if rosettaError != nil {
t.Fatal(rosettaError)
}
@ -101,7 +101,7 @@ func TestParseSignedTransaction(t *testing.T) {
refTestReceipt := &hmytypes.Receipt{
GasUsed: testTx.Gas(),
}
refFormattedTx, rosettaError := FormatTransaction(testTx, refTestReceipt)
refFormattedTx, rosettaError := FormatTransaction(testTx, refTestReceipt, []byte{})
if rosettaError != nil {
t.Fatal(rosettaError)
}

@ -2,6 +2,7 @@ package services
import (
"context"
"fmt"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/pkg/errors"
@ -27,6 +28,11 @@ func (s *ConstructAPI) ConstructionHash(
"message": "nil transaction",
})
}
if tx.ShardID() != s.hmy.ShardID {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": fmt.Sprintf("transaction is for shard %v != shard %v", tx.ShardID(), s.hmy.ShardID),
})
}
return &types.TransactionIdentifierResponse{
TransactionIdentifier: &types.TransactionIdentifier{Hash: tx.Hash().String()},
}, nil
@ -48,6 +54,11 @@ func (s *ConstructAPI) ConstructionSubmit(
"message": "nil wrapped transaction or nil unwrapped transaction",
})
}
if tx.ShardID() != s.hmy.ShardID {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": fmt.Sprintf("transaction is for shard %v != shard %v", tx.ShardID(), s.hmy.ShardID),
})
}
wrappedSenderAddress, err := getAddress(wrappedTransaction.From)
if err != nil {

@ -64,7 +64,6 @@ func (s *MempoolAPI) MempoolTransaction(
return nil, &common.TransactionNotFoundError
}
var nilAddress ethCommon.Address
senderAddr, _ := poolTx.SenderAddress()
estLog := &hmyTypes.Log{
Address: senderAddr,
@ -73,6 +72,7 @@ func (s *MempoolAPI) MempoolTransaction(
BlockNumber: s.hmy.CurrentBlock().NumberU64(),
}
// Contract related information for pending transactions is not reported
estReceipt := &hmyTypes.Receipt{
PostState: []byte{},
Status: hmyTypes.ReceiptStatusSuccessful, // Assume transaction will succeed
@ -80,11 +80,11 @@ func (s *MempoolAPI) MempoolTransaction(
Bloom: [256]byte{},
Logs: []*hmyTypes.Log{estLog},
TxHash: poolTx.Hash(),
ContractAddress: nilAddress, // ContractAddress is only for smart contract creation & can not be determined until transaction is finalized
ContractAddress: ethCommon.Address{},
GasUsed: poolTx.Gas(),
}
respTx, err := FormatTransaction(poolTx, estReceipt)
respTx, err := FormatTransaction(poolTx, estReceipt, []byte{})
if err != nil {
return nil, err
}

@ -18,8 +18,10 @@ type TransactionMetadata struct {
CrossShardIdentifier *types.TransactionIdentifier `json:"cross_shard_transaction_identifier,omitempty"`
ToShardID *uint32 `json:"to_shard,omitempty"`
FromShardID *uint32 `json:"from_shard,omitempty"`
Data *string `json:"data,omitempty"`
Logs []*hmyTypes.Log `json:"logs,omitempty"`
// ContractAccountIdentifier is the 'main' contract account ID associated with a transaction
ContractAccountIdentifier *types.AccountIdentifier `json:"contract_account_identifier,omitempty"`
Data *string `json:"data,omitempty"`
Logs []*hmyTypes.Log `json:"logs,omitempty"`
}
// UnmarshalFromInterface ..
@ -55,7 +57,7 @@ func ConstructTransaction(
var tx hmyTypes.PoolTransaction
switch components.Type {
case common.CrossShardTransferNativeOperation:
case common.NativeCrossShardTransferOperation:
if tx, rosettaError = constructCrossShardTransaction(components, metadata, sourceShardID); rosettaError != nil {
return nil, rosettaError
}
@ -63,7 +65,7 @@ func ConstructTransaction(
if tx, rosettaError = constructContractCreationTransaction(components, metadata, sourceShardID); rosettaError != nil {
return nil, rosettaError
}
case common.TransferNativeOperation:
case common.NativeTransferOperation:
if tx, rosettaError = constructPlainTransaction(components, metadata, sourceShardID); rosettaError != nil {
return nil, rosettaError
}

@ -27,7 +27,7 @@ func TestConstructPlainTransaction(t *testing.T) {
refDataBytes := []byte{0xEE, 0xEE, 0xEE}
refData := hexutil.Encode(refDataBytes)
refComponents := &OperationComponents{
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
From: refFrom,
To: refTo,
Amount: big.NewInt(12000),
@ -116,7 +116,7 @@ func TestConstructPlainTransaction(t *testing.T) {
// test invalid receiver
_, rosettaError = constructPlainTransaction(&OperationComponents{
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
From: refFrom,
To: nil,
Amount: big.NewInt(12000),
@ -126,7 +126,7 @@ func TestConstructPlainTransaction(t *testing.T) {
t.Error("expected error")
}
_, rosettaError = constructPlainTransaction(&OperationComponents{
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
From: refFrom,
To: &types.AccountIdentifier{
Address: "",
@ -140,7 +140,7 @@ func TestConstructPlainTransaction(t *testing.T) {
// test valid nil sender
_, rosettaError = constructPlainTransaction(&OperationComponents{
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
From: nil,
To: refTo,
Amount: big.NewInt(12000),
@ -179,7 +179,7 @@ func TestConstructCrossShardTransaction(t *testing.T) {
refDataBytes := []byte{0xEE, 0xEE, 0xEE}
refData := hexutil.Encode(refDataBytes)
refComponents := &OperationComponents{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
From: refFrom,
To: refTo,
Amount: big.NewInt(12000),
@ -238,7 +238,7 @@ func TestConstructCrossShardTransaction(t *testing.T) {
// test invalid receiver
_, rosettaError = constructCrossShardTransaction(&OperationComponents{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
From: refFrom,
To: nil,
Amount: big.NewInt(12000),
@ -248,7 +248,7 @@ func TestConstructCrossShardTransaction(t *testing.T) {
t.Error("expected error")
}
_, rosettaError = constructCrossShardTransaction(&OperationComponents{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
From: refFrom,
To: &types.AccountIdentifier{
Address: "",
@ -262,7 +262,7 @@ func TestConstructCrossShardTransaction(t *testing.T) {
// test valid nil sender
_, rosettaError = constructCrossShardTransaction(&OperationComponents{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
From: nil,
To: refTo,
Amount: big.NewInt(12000),
@ -432,7 +432,7 @@ func TestConstructTransaction(t *testing.T) {
// test valid cross-shard transfer (negative test cases are in TestConstructCrossShardTransaction)
generalTx, rosettaError := ConstructTransaction(&OperationComponents{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
From: refFrom,
To: refTo,
Amount: big.NewInt(12000),
@ -485,7 +485,7 @@ func TestConstructTransaction(t *testing.T) {
// test valid transfer (negative test cases are in TestConstructPlainTransaction)
generalTx, rosettaError = ConstructTransaction(&OperationComponents{
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
From: refFrom,
To: refTo,
Amount: big.NewInt(12000),
@ -512,7 +512,7 @@ func TestConstructTransaction(t *testing.T) {
// test invalid sender shard
badShard := refShard + refToShard + 1
_, rosettaError = ConstructTransaction(&OperationComponents{
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
From: refFrom,
To: refTo,
Amount: big.NewInt(12000),

@ -22,10 +22,10 @@ var (
// FormatTransaction for staking, cross-shard sender, and plain transactions
func FormatTransaction(
tx hmytypes.PoolTransaction, receipt *hmytypes.Receipt,
tx hmytypes.PoolTransaction, receipt *hmytypes.Receipt, contractCode []byte,
) (fmtTx *types.Transaction, rosettaError *types.Error) {
var operations []*types.Operation
var isCrossShard, isStaking bool
var isCrossShard, isStaking, isContractCreation bool
var toShard uint32
switch tx.(type) {
@ -36,7 +36,7 @@ func FormatTransaction(
if rosettaError != nil {
return nil, rosettaError
}
isCrossShard = false
isCrossShard, isContractCreation = false, false
toShard = stakingTx.ShardID()
case *hmytypes.Transaction:
isStaking = false
@ -46,6 +46,7 @@ func FormatTransaction(
return nil, rosettaError
}
isCrossShard = plainTx.ShardID() != plainTx.ToShardID()
isContractCreation = tx.To() == nil
toShard = plainTx.ToShardID()
default:
return nil, common.NewError(common.CatchAllError, map[string]interface{}{
@ -57,6 +58,20 @@ func FormatTransaction(
// Set all possible metadata
var txMetadata TransactionMetadata
if isContractCreation {
contractID, rosettaError := newAccountIdentifier(receipt.ContractAddress)
if rosettaError != nil {
return nil, rosettaError
}
txMetadata.ContractAccountIdentifier = contractID
} else if len(contractCode) > 0 && tx.To() != nil {
// Contract code was found, so receiving account must be the contract address
contractID, rosettaError := newAccountIdentifier(*tx.To())
if rosettaError != nil {
return nil, rosettaError
}
txMetadata.ContractAccountIdentifier = contractID
}
if isCrossShard {
txMetadata.CrossShardIdentifier = txID
txMetadata.ToShardID = &toShard
@ -122,7 +137,7 @@ func FormatCrossShardReceiverTransaction(
OperationIdentifier: &types.OperationIdentifier{
Index: 0, // There is no gas expenditure for cross-shard transaction payout
},
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Status: common.SuccessOperationStatus.Status,
Account: receiverAccountID,
Amount: &types.Amount{

@ -81,7 +81,7 @@ func testFormatStakingTransaction(
Status: hmytypes.ReceiptStatusSuccessful,
GasUsed: gasUsed,
}
rosettaTx, rosettaError := FormatTransaction(tx, receipt)
rosettaTx, rosettaError := FormatTransaction(tx, receipt, []byte{})
if rosettaError != nil {
t.Fatal(rosettaError)
}
@ -136,7 +136,7 @@ func testFormatPlainTransaction(
Status: hmytypes.ReceiptStatusSuccessful,
GasUsed: gasUsed,
}
rosettaTx, rosettaError := FormatTransaction(tx, receipt)
rosettaTx, rosettaError := FormatTransaction(tx, receipt, []byte{})
if rosettaError != nil {
t.Fatal(rosettaError)
}
@ -152,7 +152,7 @@ func testFormatPlainTransaction(
if rosettaTx.Operations[0].Type != common.ExpendGasOperation {
t.Error("Expected 1st operation to be gas")
}
if rosettaTx.Operations[1].Type != common.TransferNativeOperation {
if rosettaTx.Operations[1].Type != common.NativeTransferOperation {
t.Error("Expected 2nd operation to transfer related")
}
if rosettaTx.Operations[1].Metadata != nil {
@ -324,7 +324,7 @@ func testFormatCrossShardSenderTransaction(
Status: hmytypes.ReceiptStatusSuccessful,
GasUsed: gasUsed,
}
rosettaTx, rosettaError := FormatTransaction(tx, receipt)
rosettaTx, rosettaError := FormatTransaction(tx, receipt, []byte{})
if rosettaError != nil {
t.Fatal(rosettaError)
}
@ -340,7 +340,7 @@ func testFormatCrossShardSenderTransaction(
if rosettaTx.Operations[0].Type != common.ExpendGasOperation {
t.Error("Expected 1st operation to be gas")
}
if rosettaTx.Operations[1].Type != common.CrossShardTransferNativeOperation {
if rosettaTx.Operations[1].Type != common.NativeCrossShardTransferOperation {
t.Error("Expected 2nd operation to cross-shard transfer related")
}
if reflect.DeepEqual(rosettaTx.Operations[1].Metadata, map[string]interface{}{}) {
@ -396,7 +396,7 @@ func TestFormatCrossShardReceiverTransaction(t *testing.T) {
OperationIdentifier: &types.OperationIdentifier{
Index: 0, // There is no gas expenditure for cross-shard payout
},
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Status: common.SuccessOperationStatus.Status,
Account: receiverAccID,
Amount: &types.Amount{

@ -210,7 +210,7 @@ func newTransferNativeOperations(
receiverAddress := *tx.To()
// Common elements
opType := common.TransferNativeOperation
opType := common.NativeTransferOperation
opStatus := common.SuccessOperationStatus.Status
if receipt.Status == hmytypes.ReceiptStatusFailed {
if len(tx.Data()) > 0 {
@ -309,7 +309,7 @@ func newCrossShardSenderTransferNativeOperations(
RelatedOperations: []*types.OperationIdentifier{
startingOperationID,
},
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Status: common.SuccessOperationStatus.Status,
Account: senderAccountID,
Amount: &types.Amount{
@ -326,10 +326,7 @@ func newContractCreationNativeOperations(
startingOperationID *types.OperationIdentifier,
tx *hmytypes.Transaction, txReceipt *hmytypes.Receipt, senderAddress ethcommon.Address,
) ([]*types.Operation, *types.Error) {
senderAccountID, rosettaError := newAccountIdentifier(senderAddress)
if rosettaError != nil {
return nil, rosettaError
}
// TODO: correct the contract creation transaction...
// Set execution status as necessary
status := common.SuccessOperationStatus.Status
@ -341,24 +338,50 @@ func newContractCreationNativeOperations(
return nil, rosettaError
}
// Subtraction operation elements
subOperationID := &types.OperationIdentifier{
Index: startingOperationID.Index + 1,
}
subRelatedID := []*types.OperationIdentifier{
startingOperationID,
}
subAccountID, rosettaError := newAccountIdentifier(senderAddress)
if rosettaError != nil {
return nil, rosettaError
}
subAmount := &types.Amount{
Value: negativeBigValue(tx.Value()),
Currency: &common.NativeCurrency,
}
// Addition operation elements
addOperationID := &types.OperationIdentifier{
Index: subOperationID.Index + 1,
}
addRelatedID := []*types.OperationIdentifier{
subOperationID,
}
addAmount := &types.Amount{
Value: tx.Value().String(),
Currency: &common.NativeCurrency,
}
return []*types.Operation{
{
OperationIdentifier: &types.OperationIdentifier{
Index: startingOperationID.Index + 1,
},
RelatedOperations: []*types.OperationIdentifier{
startingOperationID,
},
Type: common.ContractCreationOperation,
Status: status,
Account: senderAccountID,
Amount: &types.Amount{
Value: negativeBigValue(tx.Value()),
Currency: &common.NativeCurrency,
},
Metadata: map[string]interface{}{
"contract_address": contractAddressID,
},
OperationIdentifier: subOperationID,
RelatedOperations: subRelatedID,
Type: common.ContractCreationOperation,
Status: status,
Account: subAccountID,
Amount: subAmount,
},
{
OperationIdentifier: addOperationID,
RelatedOperations: addRelatedID,
Type: common.ContractCreationOperation,
Status: status,
Account: contractAddressID,
Amount: addAmount,
},
}, nil
}

@ -57,7 +57,7 @@ func GetOperationComponents(
return getTransferOperationComponents(operations)
}
switch operations[0].Type {
case common.CrossShardTransferNativeOperation:
case common.NativeCrossShardTransferOperation:
return getCrossShardOperationComponents(operations[0])
case common.ContractCreationOperation:
return getContractCreationOperationComponents(operations[0])
@ -78,7 +78,7 @@ func getTransferOperationComponents(
})
}
op0, op1 := operations[0], operations[1]
if op0.Type != common.TransferNativeOperation || op1.Type != common.TransferNativeOperation {
if op0.Type != common.NativeTransferOperation || op1.Type != common.NativeTransferOperation {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "invalid operation type(s) for same shard transfer",
})
@ -187,7 +187,7 @@ func getCrossShardOperationComponents(
"message": "operation must have account sender/from & receiver/to identifiers for cross shard transfer",
})
}
if types.Hash(operation.Account) != types.Hash(components.From) {
if operation.Account.Address != components.From.Address {
return nil, common.NewError(common.InvalidTransactionConstructionError, map[string]interface{}{
"message": "operation account identifier does not match sender/from identifiers for cross shard transfer",
})

@ -124,7 +124,7 @@ func TestGetCrossShardOperationComponents(t *testing.T) {
// test valid operations
refOperation := &types.Operation{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: refAmount,
Account: refFrom,
Metadata: refMetadataMap,
@ -148,7 +148,7 @@ func TestGetCrossShardOperationComponents(t *testing.T) {
// test nil amount
_, rosettaError = getCrossShardOperationComponents(&types.Operation{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: nil,
Account: refFrom,
Metadata: refMetadataMap,
@ -159,7 +159,7 @@ func TestGetCrossShardOperationComponents(t *testing.T) {
// test positive amount
_, rosettaError = getCrossShardOperationComponents(&types.Operation{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: &types.Amount{
Value: "12000",
Currency: &common.NativeCurrency,
@ -173,7 +173,7 @@ func TestGetCrossShardOperationComponents(t *testing.T) {
// test different/unsupported currency
_, rosettaError = getCrossShardOperationComponents(&types.Operation{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: &types.Amount{
Value: "-12000",
Currency: &types.Currency{
@ -190,7 +190,7 @@ func TestGetCrossShardOperationComponents(t *testing.T) {
// test nil account
_, rosettaError = getCrossShardOperationComponents(&types.Operation{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: refAmount,
Account: nil,
Metadata: refMetadataMap,
@ -201,7 +201,7 @@ func TestGetCrossShardOperationComponents(t *testing.T) {
// test no metadata
_, rosettaError = getCrossShardOperationComponents(&types.Operation{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: refAmount,
Account: refFrom,
})
@ -224,7 +224,7 @@ func TestGetCrossShardOperationComponents(t *testing.T) {
t.Fatal(err)
}
_, rosettaError = getCrossShardOperationComponents(&types.Operation{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: refAmount,
Account: refFrom,
Metadata: badMetadataMap,
@ -266,7 +266,7 @@ func TestGetTransferOperationComponents(t *testing.T) {
OperationIdentifier: &types.OperationIdentifier{
Index: 0,
},
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
Amount: refFromAmount,
Account: refFrom,
},
@ -279,7 +279,7 @@ func TestGetTransferOperationComponents(t *testing.T) {
Index: 0,
},
},
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
Amount: refToAmount,
Account: refTo,
},
@ -345,20 +345,20 @@ func TestGetTransferOperationComponents(t *testing.T) {
// test invalid operation
refOperations[0].Type = common.ExpendGasOperation
refOperations[1].Type = common.TransferNativeOperation
refOperations[1].Type = common.NativeTransferOperation
_, rosettaError = getTransferOperationComponents(refOperations)
if rosettaError == nil {
t.Error("expected error")
}
// test invalid operation sender
refOperations[0].Type = common.TransferNativeOperation
refOperations[0].Type = common.NativeTransferOperation
refOperations[1].Type = common.ExpendGasOperation
_, rosettaError = getTransferOperationComponents(refOperations)
if rosettaError == nil {
t.Error("expected error")
}
refOperations[1].Type = common.TransferNativeOperation
refOperations[1].Type = common.NativeTransferOperation
// test nil amount
refOperations[0].Amount = nil
@ -517,7 +517,7 @@ func TestGetOperationComponents(t *testing.T) {
OperationIdentifier: &types.OperationIdentifier{
Index: 0,
},
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
Amount: refFromAmount,
Account: refFrom,
},
@ -530,7 +530,7 @@ func TestGetOperationComponents(t *testing.T) {
Index: 0,
},
},
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
Amount: refToAmount,
Account: refTo,
},
@ -551,7 +551,7 @@ func TestGetOperationComponents(t *testing.T) {
}
_, rosettaError = GetOperationComponents([]*types.Operation{
{
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Amount: refFromAmount,
Account: refFrom,
Metadata: refMetadataMap,

@ -368,7 +368,7 @@ func TestNewTransferNativeOperations(t *testing.T) {
Index: startingOpID.Index,
},
},
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
Status: common.ContractFailureOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
@ -385,7 +385,7 @@ func TestNewTransferNativeOperations(t *testing.T) {
Index: startingOpID.Index + 1,
},
},
Type: common.TransferNativeOperation,
Type: common.NativeTransferOperation,
Status: common.ContractFailureOperationStatus.Status,
Account: receiverAccID,
Amount: &types.Amount{
@ -461,7 +461,7 @@ func TestNewCrossShardSenderTransferNativeOperations(t *testing.T) {
RelatedOperations: []*types.OperationIdentifier{
startingOpID,
},
Type: common.CrossShardTransferNativeOperation,
Type: common.NativeCrossShardTransferOperation,
Status: common.SuccessOperationStatus.Status,
Account: senderAccID,
Amount: &types.Amount{
@ -527,8 +527,22 @@ func TestNewContractCreationNativeOperations(t *testing.T) {
Value: negativeBigValue(tx.Value()),
Currency: &common.NativeCurrency,
},
Metadata: map[string]interface{}{
"contract_address": contractAddressID,
},
{
OperationIdentifier: &types.OperationIdentifier{
Index: startingOpID.Index + 2,
},
RelatedOperations: []*types.OperationIdentifier{
{
Index: startingOpID.Index + 1,
},
},
Type: common.ContractCreationOperation,
Status: common.ContractFailureOperationStatus.Status,
Account: contractAddressID,
Amount: &types.Amount{
Value: tx.Value().String(),
Currency: &common.NativeCurrency,
},
},
}
@ -549,6 +563,7 @@ func TestNewContractCreationNativeOperations(t *testing.T) {
// Test successful contract creation
refOperations[0].Status = common.SuccessOperationStatus.Status
refOperations[1].Status = common.SuccessOperationStatus.Status
receipt.Status = hmytypes.ReceiptStatusSuccessful // Indicate successful tx
operations, rosettaError = newContractCreationNativeOperations(startingOpID, tx, receipt, senderAddr)
if rosettaError != nil {

@ -50,7 +50,7 @@ func (s *PublicContractService) Call(
blockNum := blockNumber.EthBlockNumber()
// Execute call
result, err := doCall(ctx, s.hmy, args, blockNum, vm.Config{}, CallTimeout, s.hmy.RPCGasCap)
result, err := DoEVMCall(ctx, s.hmy, args, blockNum, vm.Config{}, CallTimeout, s.hmy.RPCGasCap)
if err != nil {
return nil, err
}
@ -99,8 +99,8 @@ func (s *PublicContractService) GetStorageAt(
return res[:], state.Error()
}
// docall executes an EVM call
func doCall(
// DoEVMCall executes an EVM call
func DoEVMCall(
ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNum rpc.BlockNumber,
vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int,
) (core.ExecutionResult, error) {

@ -725,7 +725,7 @@ func EstimateGas(
executable := func(gas uint64) bool {
args.Gas = (*hexutil.Uint64)(&gas)
result, err := doCall(ctx, hmy, args, blockNum, vm.Config{}, 0, big.NewInt(int64(max)))
result, err := DoEVMCall(ctx, hmy, args, blockNum, vm.Config{}, 0, big.NewInt(int64(max)))
if err != nil || result.VMErr == vm.ErrCodeStoreOutOfGas || result.VMErr == vm.ErrOutOfGas {
return false
}

@ -227,7 +227,7 @@ func IsEligibleForEPoSAuction(snapshot *staking.ValidatorSnapshot, validator *st
}
}
// Blockchain is a subset of Engine.Blockchain, just enough to do assignment
// ChainReader is a subset of Engine.Blockchain, just enough to do assignment
type ChainReader interface {
// ReadShardState retrieves sharding state given the epoch number.
// This api reads the shard state cached or saved on the chaindb.

Loading…
Cancel
Save