From a4980008f03181ebc9f90cff7d342fffd4e6227e Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Tue, 27 Oct 2020 15:24:17 -0700 Subject: [PATCH 01/14] Fix quorum check with multi-key --- consensus/quorum/one-node-staked-vote.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/quorum/one-node-staked-vote.go b/consensus/quorum/one-node-staked-vote.go index 7b79f4de8..8023b03d8 100644 --- a/consensus/quorum/one-node-staked-vote.go +++ b/consensus/quorum/one-node-staked-vote.go @@ -188,7 +188,7 @@ func (v *stakedVoteWeight) QuorumThreshold() numeric.Dec { // IsAllSigsCollected .. func (v *stakedVoteWeight) IsAllSigsCollected() bool { - return v.SignersCount(Commit) == v.ParticipantsCount() + return v.voteTally.Commit.tally.Equal(numeric.NewDec(1)) } func (v *stakedVoteWeight) SetVoters( From 46bbe894089ca1965bed291a3b5bcb26d33909a5 Mon Sep 17 00:00:00 2001 From: Daniel Van Der Maden Date: Tue, 27 Oct 2020 23:18:29 -0700 Subject: [PATCH 02/14] [rosetta] Update to v1.4.6 of rosetta SDK * Set rosetta release version Signed-off-by: Daniel Van Der Maden --- go.mod | 2 +- rosetta/common/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 2dd46e09a..5cf37b42d 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/rosetta/common/config.go b/rosetta/common/config.go index 30058d574..feb30d67a 100644 --- a/rosetta/common/config.go +++ b/rosetta/common/config.go @@ -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" From 9d95b9844d7b9b6c3bdbc6413dd8c75451645fba Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 28 Oct 2020 10:37:34 -0700 Subject: [PATCH 03/14] rename ChainReader to Blockchain --- cmd/harmony/main.go | 2 +- consensus/consensus.go | 4 ++-- consensus/consensus_service.go | 24 ++++++++++++------------ consensus/consensus_v2.go | 28 ++++++++++++++-------------- consensus/double_sign.go | 4 ++-- consensus/leader.go | 2 +- consensus/threshold.go | 2 +- consensus/validator.go | 4 ++-- consensus/view_change.go | 10 +++++----- node/node.go | 4 ++-- node/node_handler.go | 2 +- shard/committee/assignment.go | 2 +- 12 files changed, 44 insertions(+), 44 deletions(-) diff --git a/cmd/harmony/main.go b/cmd/harmony/main.go index 195d48c23..07971a601 100644 --- a/cmd/harmony/main.go +++ b/cmd/harmony/main.go @@ -615,7 +615,7 @@ func setupConsensusAndNode(hc harmonyConfig, nodeConfig *nodeconfig.ConfigType) } // TODO: refactor the creation of blockchain out of node.New() - currentConsensus.ChainReader = currentNode.Blockchain() + currentConsensus.Blockchain = currentNode.Blockchain() currentNode.NodeConfig.DNSZone = hc.Network.DNSZone currentNode.NodeConfig.SetBeaconGroupID( diff --git a/consensus/consensus.go b/consensus/consensus.go index ee4e41102..5449c612c 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -54,8 +54,8 @@ type Consensus struct { multiSigBitmap *bls_cosi.Mask // Bitmap for parsing multisig bitmap from validators multiSigMutex sync.RWMutex - // The chain reader for the blockchain this consensus is working on - ChainReader *core.BlockChain + // The blockchain this consensus is working on + Blockchain *core.BlockChain // Minimal number of peers in the shard // If the number of validators is less than minPeers, the consensus won't start MinPeers int diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index 9ac3611a8..17a220de0 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -246,7 +246,7 @@ func (consensus *Consensus) ReadSignatureBitmapPayload( func (consensus *Consensus) getLeaderPubKeyFromCoinbase( header *block.Header, ) (*bls.PublicKeyWrapper, error) { - shardState, err := consensus.ChainReader.ReadShardState(header.Epoch()) + shardState, err := consensus.Blockchain.ReadShardState(header.Epoch()) if err != nil { return nil, errors.Wrapf(err, "cannot read shard state %v %s", header.Epoch(), @@ -260,7 +260,7 @@ func (consensus *Consensus) getLeaderPubKeyFromCoinbase( } committerKey := new(bls_core.PublicKey) - isStaking := consensus.ChainReader.Config().IsStaking(header.Epoch()) + isStaking := consensus.Blockchain.Config().IsStaking(header.Epoch()) for _, member := range committee.Slots { if isStaking { // After staking the coinbase address will be the address of bls public key @@ -296,7 +296,7 @@ func (consensus *Consensus) getLeaderPubKeyFromCoinbase( // (b) node in committed but has any err during processing: Syncing mode // (c) node in committed and everything looks good: Normal mode func (consensus *Consensus) UpdateConsensusInformation() Mode { - curHeader := consensus.ChainReader.CurrentHeader() + curHeader := consensus.Blockchain.CurrentHeader() curEpoch := curHeader.Epoch() nextEpoch := new(big.Int).Add(curHeader.Epoch(), common.Big1) @@ -314,15 +314,15 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { consensus.BlockPeriod = 5 * time.Second // Enable aggregate sig at epoch 1000 for mainnet, at epoch 53000 for testnet, and always for other nets. - if (consensus.ChainReader.Config().ChainID == params.MainnetChainID && curEpoch.Cmp(big.NewInt(1000)) > 0) || - (consensus.ChainReader.Config().ChainID == params.TestnetChainID && curEpoch.Cmp(big.NewInt(54500)) > 0) || - (consensus.ChainReader.Config().ChainID != params.MainnetChainID && consensus.ChainReader.Config().ChainID != params.TestChainID) { + if (consensus.Blockchain.Config().ChainID == params.MainnetChainID && curEpoch.Cmp(big.NewInt(1000)) > 0) || + (consensus.Blockchain.Config().ChainID == params.TestnetChainID && curEpoch.Cmp(big.NewInt(54500)) > 0) || + (consensus.Blockchain.Config().ChainID != params.MainnetChainID && consensus.Blockchain.Config().ChainID != params.TestChainID) { consensus.AggregateSig = true } - isFirstTimeStaking := consensus.ChainReader.Config().IsStaking(nextEpoch) && - curHeader.IsLastBlockInEpoch() && !consensus.ChainReader.Config().IsStaking(curEpoch) - haventUpdatedDecider := consensus.ChainReader.Config().IsStaking(curEpoch) && + isFirstTimeStaking := consensus.Blockchain.Config().IsStaking(nextEpoch) && + curHeader.IsLastBlockInEpoch() && !consensus.Blockchain.Config().IsStaking(curEpoch) + haventUpdatedDecider := consensus.Blockchain.Config().IsStaking(curEpoch) && consensus.Decider.Policy() != quorum.SuperMajorityStake // Only happens once, the flip-over to a new Decider policy @@ -338,7 +338,7 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { epochToSet := curEpoch hasError := false curShardState, err := committee.WithStakingEnabled.ReadFromDB( - curEpoch, consensus.ChainReader, + curEpoch, consensus.Blockchain, ) if err != nil { utils.Logger().Error(). @@ -354,7 +354,7 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode { if curHeader.IsLastBlockInEpoch() && isNotGenesisBlock { nextShardState, err := committee.WithStakingEnabled.ReadFromDB( - nextEpoch, consensus.ChainReader, + nextEpoch, consensus.Blockchain, ) if err != nil { utils.Logger().Error(). @@ -561,7 +561,7 @@ func (consensus *Consensus) selfCommit(payload []byte) error { consensus.switchPhase("selfCommit", FBFTCommit) consensus.aggregatedPrepareSig = aggSig consensus.prepareBitmap = mask - commitPayload := signature.ConstructCommitPayload(consensus.ChainReader, + commitPayload := signature.ConstructCommitPayload(consensus.Blockchain, block.Epoch(), block.Hash(), block.NumberU64(), block.Header().ViewID().Uint64()) for i, key := range consensus.priKey { if err := consensus.commitBitmap.SetKey(key.Pub.Bytes, true); err != nil { diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index 9cb7577c5..6360d77a0 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -196,7 +196,7 @@ func (consensus *Consensus) BlockCommitSigs(blockNum uint64) ([]byte, error) { if consensus.blockNum <= 1 { return nil, nil } - lastCommits, err := consensus.ChainReader.ReadCommitSig(blockNum) + lastCommits, err := consensus.Blockchain.ReadCommitSig(blockNum) if err != nil || len(lastCommits) < bls.BLSSignatureSizeInBytes { msgs := consensus.FBFTLog.GetMessagesByTypeSeq( @@ -274,15 +274,15 @@ func (consensus *Consensus) Start( } case <-consensus.syncReadyChan: consensus.getLogger().Info().Msg("[ConsensusMainLoop] syncReadyChan") - consensus.SetBlockNum(consensus.ChainReader.CurrentHeader().Number().Uint64() + 1) - consensus.SetViewIDs(consensus.ChainReader.CurrentHeader().ViewID().Uint64() + 1) + consensus.SetBlockNum(consensus.Blockchain.CurrentHeader().Number().Uint64() + 1) + consensus.SetViewIDs(consensus.Blockchain.CurrentHeader().ViewID().Uint64() + 1) mode := consensus.UpdateConsensusInformation() consensus.current.SetMode(mode) consensus.getLogger().Info().Str("Mode", mode.String()).Msg("Node is IN SYNC") case <-consensus.syncNotReadyChan: consensus.getLogger().Info().Msg("[ConsensusMainLoop] syncNotReadyChan") - consensus.SetBlockNum(consensus.ChainReader.CurrentHeader().Number().Uint64() + 1) + consensus.SetBlockNum(consensus.Blockchain.CurrentHeader().Number().Uint64() + 1) consensus.current.SetMode(Syncing) consensus.getLogger().Info().Msg("[ConsensusMainLoop] Node is OUT OF SYNC") @@ -294,8 +294,8 @@ func (consensus *Consensus) Start( //VRF/VDF is only generated in the beacon chain if consensus.NeedsRandomNumberGeneration(newBlock.Header().Epoch()) { // generate VRF if the current block has a new leader - if !consensus.ChainReader.IsSameLeaderAsPreviousBlock(newBlock) { - vrfBlockNumbers, err := consensus.ChainReader.ReadEpochVrfBlockNums(newBlock.Header().Epoch()) + if !consensus.Blockchain.IsSameLeaderAsPreviousBlock(newBlock) { + vrfBlockNumbers, err := consensus.Blockchain.ReadEpochVrfBlockNums(newBlock.Header().Epoch()) if err != nil { consensus.getLogger().Info(). Uint64("MsgBlockNum", newBlock.NumberU64()). @@ -326,7 +326,7 @@ func (consensus *Consensus) Start( if (!vdfInProgress) && len(vrfBlockNumbers) >= consensus.VdfSeedSize() { //check local database to see if there's a VDF generated for this epoch //generate a VDF if no blocknum is available - _, err := consensus.ChainReader.ReadEpochVdfBlockNum(newBlock.Header().Epoch()) + _, err := consensus.Blockchain.ReadEpochVdfBlockNum(newBlock.Header().Epoch()) if err != nil { consensus.GenerateVdfAndProof(newBlock, vrfBlockNumbers) vdfInProgress = true @@ -347,7 +347,7 @@ func (consensus *Consensus) Start( Msg("[ConsensusMainLoop] failed to verify the VDF output") } else { //write the VDF only if VDF has not been generated - _, err := consensus.ChainReader.ReadEpochVdfBlockNum(newBlock.Header().Epoch()) + _, err := consensus.Blockchain.ReadEpochVdfBlockNum(newBlock.Header().Epoch()) if err == nil { consensus.getLogger().Info(). Uint64("MsgBlockNum", newBlock.NumberU64()). @@ -555,7 +555,7 @@ func (consensus *Consensus) GenerateVrfAndProof(newBlock *types.Block, vrfBlockN } sk := vrf_bls.NewVRFSigner(key.Pri) blockHash := [32]byte{} - previousHeader := consensus.ChainReader.GetHeaderByNumber( + previousHeader := consensus.Blockchain.GetHeaderByNumber( newBlock.NumberU64() - 1, ) if previousHeader == nil { @@ -580,7 +580,7 @@ func (consensus *Consensus) GenerateVrfAndProof(newBlock *types.Block, vrfBlockN func (consensus *Consensus) ValidateVrfAndProof(headerObj *block.Header) bool { vrfPk := vrf_bls.NewVRFVerifier(consensus.LeaderPubKey.Object) var blockHash [32]byte - previousHeader := consensus.ChainReader.GetHeaderByNumber( + previousHeader := consensus.Blockchain.GetHeaderByNumber( headerObj.Number().Uint64() - 1, ) if previousHeader == nil { @@ -608,7 +608,7 @@ func (consensus *Consensus) ValidateVrfAndProof(headerObj *block.Header) bool { return false } - vrfBlockNumbers, _ := consensus.ChainReader.ReadEpochVrfBlockNums( + vrfBlockNumbers, _ := consensus.Blockchain.ReadEpochVrfBlockNums( headerObj.Epoch(), ) consensus.getLogger().Info(). @@ -624,7 +624,7 @@ func (consensus *Consensus) GenerateVdfAndProof(newBlock *types.Block, vrfBlockN //derive VDF seed from VRFs generated in the current epoch seed := [32]byte{} for i := 0; i < consensus.VdfSeedSize(); i++ { - previousVrf := consensus.ChainReader.GetVrfByNumber(vrfBlockNumbers[i]) + previousVrf := consensus.Blockchain.GetVrfByNumber(vrfBlockNumbers[i]) for j := 0; j < len(seed); j++ { seed[j] = seed[j] ^ previousVrf[j] } @@ -658,7 +658,7 @@ func (consensus *Consensus) GenerateVdfAndProof(newBlock *types.Block, vrfBlockN // ValidateVdfAndProof validates the VDF/proof in the current epoch func (consensus *Consensus) ValidateVdfAndProof(headerObj *block.Header) bool { - vrfBlockNumbers, err := consensus.ChainReader.ReadEpochVrfBlockNums(headerObj.Epoch()) + vrfBlockNumbers, err := consensus.Blockchain.ReadEpochVrfBlockNums(headerObj.Epoch()) if err != nil { consensus.getLogger().Error().Err(err). Str("MsgBlockNum", headerObj.Number().String()). @@ -673,7 +673,7 @@ func (consensus *Consensus) ValidateVdfAndProof(headerObj *block.Header) bool { seed := [32]byte{} for i := 0; i < consensus.VdfSeedSize(); i++ { - previousVrf := consensus.ChainReader.GetVrfByNumber(vrfBlockNumbers[i]) + previousVrf := consensus.Blockchain.GetVrfByNumber(vrfBlockNumbers[i]) for j := 0; j < len(seed); j++ { seed[j] = seed[j] ^ previousVrf[j] } diff --git a/consensus/double_sign.go b/consensus/double_sign.go index d11044afd..86dfd050e 100644 --- a/consensus/double_sign.go +++ b/consensus/double_sign.go @@ -40,8 +40,8 @@ func (consensus *Consensus) checkDoubleSign(recvMsg *FBFTMessage) bool { return true } - curHeader := consensus.ChainReader.CurrentHeader() - committee, err := consensus.ChainReader.ReadShardState(curHeader.Epoch()) + curHeader := consensus.Blockchain.CurrentHeader() + committee, err := consensus.Blockchain.ReadShardState(curHeader.Epoch()) if err != nil { consensus.getLogger().Err(err). Uint32("shard", consensus.ShardID). diff --git a/consensus/leader.go b/consensus/leader.go index c473d5138..988380ffa 100644 --- a/consensus/leader.go +++ b/consensus/leader.go @@ -242,7 +242,7 @@ func (consensus *Consensus) onCommit(msg *msg_pb.Message) { Msg("[OnCommit] Failed finding a matching block for committed message") return } - commitPayload := signature.ConstructCommitPayload(consensus.ChainReader, + commitPayload := signature.ConstructCommitPayload(consensus.Blockchain, blockObj.Epoch(), blockObj.Hash(), blockObj.NumberU64(), blockObj.Header().ViewID().Uint64()) logger = logger.With(). Uint64("MsgViewID", recvMsg.ViewID). diff --git a/consensus/threshold.go b/consensus/threshold.go index c1ca8c6f1..503185f3b 100644 --- a/consensus/threshold.go +++ b/consensus/threshold.go @@ -46,7 +46,7 @@ func (consensus *Consensus) didReachPrepareQuorum() error { Msg("[didReachPrepareQuorum] Unparseable block data") return err } - commitPayload := signature.ConstructCommitPayload(consensus.ChainReader, + commitPayload := signature.ConstructCommitPayload(consensus.Blockchain, blockObj.Epoch(), blockObj.Hash(), blockObj.NumberU64(), blockObj.Header().ViewID().Uint64()) // so by this point, everyone has committed to the blockhash of this block diff --git a/consensus/validator.go b/consensus/validator.go index 1f378c7bd..dd89ed72a 100644 --- a/consensus/validator.go +++ b/consensus/validator.go @@ -81,7 +81,7 @@ func (consensus *Consensus) sendCommitMessages(blockObj *types.Block) { priKeys := consensus.getPriKeysInCommittee() // Sign commit signature on the received block and construct the p2p messages - commitPayload := signature.ConstructCommitPayload(consensus.ChainReader, + commitPayload := signature.ConstructCommitPayload(consensus.Blockchain, blockObj.Epoch(), blockObj.Hash(), blockObj.NumberU64(), blockObj.Header().ViewID().Uint64()) p2pMsgs := consensus.constructP2pMessages(msg_pb.MessageType_COMMIT, commitPayload, priKeys) @@ -256,7 +256,7 @@ func (consensus *Consensus) onCommitted(msg *msg_pb.Message) { Msg("[OnCommitted] Failed finding a matching block for committed message") return } - commitPayload := signature.ConstructCommitPayload(consensus.ChainReader, + commitPayload := signature.ConstructCommitPayload(consensus.Blockchain, blockObj.Epoch(), blockObj.Hash(), blockObj.NumberU64(), blockObj.Header().ViewID().Uint64()) if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) { consensus.getLogger().Error(). diff --git a/consensus/view_change.go b/consensus/view_change.go index 6ec0b1386..37df69712 100644 --- a/consensus/view_change.go +++ b/consensus/view_change.go @@ -117,10 +117,10 @@ func (consensus *Consensus) fallbackNextViewID() (uint64, time.Duration) { // viewID is only used as the fallback mechansim to determine the nextViewID func (consensus *Consensus) getNextViewID() (uint64, time.Duration) { // handle corner case at first - if consensus.ChainReader == nil { + if consensus.Blockchain == nil { return consensus.fallbackNextViewID() } - curHeader := consensus.ChainReader.CurrentHeader() + curHeader := consensus.Blockchain.CurrentHeader() if curHeader == nil { return consensus.fallbackNextViewID() } @@ -160,11 +160,11 @@ func (consensus *Consensus) getNextLeaderKey(viewID uint64) *bls.PublicKeyWrappe var lastLeaderPubKey *bls.PublicKeyWrapper var err error epoch := big.NewInt(0) - if consensus.ChainReader == nil { - consensus.getLogger().Error().Msg("[getNextLeaderKey] ChainReader is nil. Use consensus.LeaderPubKey") + if consensus.Blockchain == nil { + consensus.getLogger().Error().Msg("[getNextLeaderKey] Blockchain is nil. Use consensus.LeaderPubKey") lastLeaderPubKey = consensus.LeaderPubKey } else { - curHeader := consensus.ChainReader.CurrentHeader() + curHeader := consensus.Blockchain.CurrentHeader() if curHeader == nil { consensus.getLogger().Error().Msg("[getNextLeaderKey] Failed to get current header from blockchain") lastLeaderPubKey = consensus.LeaderPubKey diff --git a/node/node.go b/node/node.go index 3fa0ef3e7..2111fa0ee 100644 --- a/node/node.go +++ b/node/node.go @@ -1053,7 +1053,7 @@ func (node *Node) InitConsensusWithValidators() (err error) { Uint64("epoch", epoch.Uint64()). Msg("[InitConsensusWithValidators] Try To Get PublicKeys") shardState, err := committee.WithStakingEnabled.Compute( - epoch, node.Consensus.ChainReader, + epoch, node.Consensus.Blockchain, ) if err != nil { utils.Logger().Err(err). @@ -1159,7 +1159,7 @@ func (node *Node) populateSelfAddresses(epoch *big.Int) { node.keysToAddrsEpoch = epoch shardID := node.Consensus.ShardID - shardState, err := node.Consensus.ChainReader.ReadShardState(epoch) + shardState, err := node.Consensus.Blockchain.ReadShardState(epoch) if err != nil { utils.Logger().Error().Err(err). Int64("epoch", epoch.Int64()). diff --git a/node/node_handler.go b/node/node_handler.go index 037a667c1..88265722a 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -241,7 +241,7 @@ func (node *Node) BroadcastCrossLink() { node.host.SendMessageToGroups( []nodeconfig.GroupID{nodeconfig.NewGroupIDByShardID(shard.BeaconChainShardID)}, p2p.ConstructMessage( - proto_node.ConstructCrossLinkMessage(node.Consensus.ChainReader, headers)), + proto_node.ConstructCrossLinkMessage(node.Consensus.Blockchain, headers)), ) } diff --git a/shard/committee/assignment.go b/shard/committee/assignment.go index 32c1f9fa7..b9507f0d2 100644 --- a/shard/committee/assignment.go +++ b/shard/committee/assignment.go @@ -227,7 +227,7 @@ func IsEligibleForEPoSAuction(snapshot *staking.ValidatorSnapshot, validator *st } } -// ChainReader is a subset of Engine.ChainReader, 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. From 7c9a2a2f3bf5b4b1acbd728c1eecd3a73b2e09ec Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 28 Oct 2020 10:50:00 -0700 Subject: [PATCH 04/14] more refactors; add logic to update commit sigs --- consensus/construct.go | 46 +++++++++++++++++++++++++----------------- consensus/validator.go | 20 ++++++++++++++++++ internal/chain/sig.go | 6 +++--- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/consensus/construct.go b/consensus/construct.go index 1bb334f62..f405cbdf1 100644 --- a/consensus/construct.go +++ b/consensus/construct.go @@ -98,16 +98,8 @@ func (consensus *Consensus) construct( // Do the signing, 96 byte of bls signature needMsgSig := true switch p { - case msg_pb.MessageType_PREPARED: - consensusMsg.Block = consensus.block - // Payload - buffer := bytes.Buffer{} - // 96 bytes aggregated signature - aggSig = consensus.Decider.AggregateVotes(quorum.Prepare) - buffer.Write(aggSig.Serialize()) - // Bitmap - buffer.Write(consensus.prepareBitmap.Bitmap) - consensusMsg.Payload = buffer.Bytes() + case msg_pb.MessageType_ANNOUNCE: + consensusMsg.Payload = consensus.blockHash[:] case msg_pb.MessageType_PREPARE: needMsgSig = false sig := bls_core.Sign{} @@ -126,16 +118,11 @@ func (consensus *Consensus) construct( } } consensusMsg.Payload = sig.Serialize() + case msg_pb.MessageType_PREPARED: + consensusMsg.Block = consensus.block + consensusMsg.Payload = consensus.constructQuorumSigAndBitmap(quorum.Prepare) case msg_pb.MessageType_COMMITTED: - buffer := bytes.Buffer{} - // 96 bytes aggregated signature - aggSig = consensus.Decider.AggregateVotes(quorum.Commit) - buffer.Write(aggSig.Serialize()) - // Bitmap - buffer.Write(consensus.commitBitmap.Bitmap) - consensusMsg.Payload = buffer.Bytes() - case msg_pb.MessageType_ANNOUNCE: - consensusMsg.Payload = consensus.blockHash[:] + consensusMsg.Payload = consensus.constructQuorumSigAndBitmap(quorum.Commit) } var marshaledMessage []byte @@ -171,3 +158,24 @@ func (consensus *Consensus) construct( OptionalAggregateSignature: aggSig, }, nil } + +// constructQuorumSigAndBitmap constructs the aggregated sig and bitmap as +// a byte slice in format of: [[aggregated sig], [sig bitmap]] +func (consensus *Consensus) constructQuorumSigAndBitmap(p quorum.Phase) []byte { + buffer := bytes.Buffer{} + // 96 bytes aggregated signature + aggSig := consensus.Decider.AggregateVotes(p) + buffer.Write(aggSig.Serialize()) + // Bitmap + if p == quorum.Prepare { + buffer.Write(consensus.prepareBitmap.Bitmap) + } else if p == quorum.Commit { + buffer.Write(consensus.commitBitmap.Bitmap) + } else { + utils.Logger().Error(). + Str("phase", p.String()). + Msg("[constructQuorumSigAndBitmap] Invalid phase is supplied.") + return []byte{} + } + return buffer.Bytes() +} diff --git a/consensus/validator.go b/consensus/validator.go index dd89ed72a..bff01506e 100644 --- a/consensus/validator.go +++ b/consensus/validator.go @@ -227,6 +227,11 @@ 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). + Msg("[OnCommitted] Received committed message") + // NOTE let it handle its own logs if !consensus.isRightBlockNumCheck(recvMsg) { return @@ -273,6 +278,21 @@ func (consensus *Consensus) onCommitted(msg *msg_pb.Message) { consensus.aggregatedCommitSig = aggSig consensus.commitBitmap = mask + // If we already have a committed signature received before, check whether the new one + // has more signatures and if yes, override the old data. + // Otherwise, simply write the commit signature in db. + commitSigBitmap, err := consensus.Blockchain.ReadCommitSig(blockObj.NumberU64()) + if err == nil && len(commitSigBitmap) == len(recvMsg.Payload) { + new := mask.CountEnabled() + mask.SetMask(commitSigBitmap[bls.BLSSignatureSizeInBytes:]) + cur := mask.CountEnabled() + if new > cur { + consensus.Blockchain.WriteCommitSig(blockObj.NumberU64(), recvMsg.Payload) + } + } else { + consensus.Blockchain.WriteCommitSig(blockObj.NumberU64(), recvMsg.Payload) + } + consensus.tryCatchup() if recvMsg.BlockNum > consensus.blockNum { consensus.getLogger().Info().Uint64("MsgBlockNum", recvMsg.BlockNum).Msg("[OnCommitted] OUT OF SYNC") diff --git a/internal/chain/sig.go b/internal/chain/sig.go index 38d2b461d..98bb47b3e 100644 --- a/internal/chain/sig.go +++ b/internal/chain/sig.go @@ -11,15 +11,15 @@ import ( // ReadSignatureBitmapByPublicKeys read the payload of signature and bitmap based on public keys func ReadSignatureBitmapByPublicKeys(recvPayload []byte, publicKeys []bls.PublicKeyWrapper) (*bls_core.Sign, *bls.Mask, error) { - if len(recvPayload) < 96 { + if len(recvPayload) < bls.BLSSignatureSizeInBytes { return nil, nil, errors.New("payload not have enough length") } payload := append(recvPayload[:0:0], recvPayload...) //#### Read payload data // 96 byte of multi-sig offset := 0 - multiSig := payload[offset : offset+96] - offset += 96 + multiSig := payload[offset : offset+bls.BLSSignatureSizeInBytes] + offset += bls.BLSSignatureSizeInBytes // bitmap bitmap := payload[offset:] //#### END Read payload data From 9018d10bc98d52800e3fbc17afbd1496f0f94fc8 Mon Sep 17 00:00:00 2001 From: Daniel Van Der Maden Date: Fri, 30 Oct 2020 01:08:52 -0700 Subject: [PATCH 05/14] Rosetta Contract Operations Fix (#3417) * [rosetta] Update to v1.4.6 of rosetta SDK * Set rosetta release version Signed-off-by: Daniel Van Der Maden * [rpc] Expose DoEVMCall Signed-off-by: Daniel Van Der Maden * [rosetta] Fix contract related operations * Make contract creation be a 2 operation pair, with contract addr as receiver * Add Contract address to all transaction metadata that involving contract operations * Update construction API to include framework for contract functionality * Update tests Signed-off-by: Daniel Van Der Maden * [rosetta] Fix imports Signed-off-by: Daniel Van Der Maden * [rosetta] Fix construction metadata check for contracts Signed-off-by: Daniel Van Der Maden * [ntp] Make TestCheckLocalTimeAccurate fault tolerant Signed-off-by: Daniel Van Der Maden * [rosetta] Fix nil block ptr crash & generalize getBlock Signed-off-by: Daniel Van Der Maden * [rosetta] Correct error type for account rosetta errors Signed-off-by: Daniel Van Der Maden --- common/ntp/ntp_test.go | 16 +++-- rosetta/services/account.go | 27 ++------ rosetta/services/block.go | 65 ++++++++++-------- rosetta/services/block_special.go | 2 +- rosetta/services/construction_check.go | 73 ++++++++++++++++++--- rosetta/services/construction_create.go | 15 +++-- rosetta/services/construction_parse.go | 4 +- rosetta/services/construction_parse_test.go | 4 +- rosetta/services/mempool.go | 6 +- rosetta/services/tx_construction.go | 6 +- rosetta/services/tx_format.go | 21 +++++- rosetta/services/tx_format_test.go | 6 +- rosetta/services/tx_operation.go | 63 ++++++++++++------ rosetta/services/tx_operation_test.go | 19 +++++- rpc/contract.go | 6 +- rpc/transaction.go | 2 +- 16 files changed, 225 insertions(+), 110 deletions(-) diff --git a/common/ntp/ntp_test.go b/common/ntp/ntp_test.go index ddd9ff432..2e8b148d7 100644 --- a/common/ntp/ntp_test.go +++ b/common/ntp/ntp_test.go @@ -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) { diff --git a/rosetta/services/account.go b/rosetta/services/account.go index f52626195..231e5f2c6 100644 --- a/rosetta/services/account.go +++ b/rosetta/services/account.go @@ -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(), }) } diff --git a/rosetta/services/block.go b/rosetta/services/block.go index a3c0b7785..8dcb15f61 100644 --- a/rosetta/services/block.go +++ b/rosetta/services/block.go @@ -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 +} diff --git a/rosetta/services/block_special.go b/rosetta/services/block_special.go index 8b6dbe17d..4ac2e0de6 100644 --- a/rosetta/services/block_special.go +++ b/rosetta/services/block_special.go @@ -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 } diff --git a/rosetta/services/construction_check.go b/rosetta/services/construction_check.go index 7ae31bb8e..33d43368d 100644 --- a/rosetta/services/construction_check.go +++ b/rosetta/services/construction_check.go @@ -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" @@ -95,10 +97,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 .. @@ -161,13 +166,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: ðCommon.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 +208,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{}{ diff --git a/rosetta/services/construction_create.go b/rosetta/services/construction_create.go index 1151032a5..0fc2293ea 100644 --- a/rosetta/services/construction_create.go +++ b/rosetta/services/construction_create.go @@ -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 .. @@ -132,9 +134,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{}{ diff --git a/rosetta/services/construction_parse.go b/rosetta/services/construction_parse.go index 259ff0156..5ead22257 100644 --- a/rosetta/services/construction_parse.go +++ b/rosetta/services/construction_parse.go @@ -41,7 +41,7 @@ func parseUnsignedTransaction( intendedReceipt := &hmyTypes.Receipt{ GasUsed: tx.Gas(), } - formattedTx, rosettaError := FormatTransaction(tx, intendedReceipt) + formattedTx, rosettaError := FormatTransaction(tx, intendedReceipt, wrappedTransaction.ContractCode) if rosettaError != nil { return nil, rosettaError } @@ -82,7 +82,7 @@ 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 } diff --git a/rosetta/services/construction_parse_test.go b/rosetta/services/construction_parse_test.go index 1c7ed3c6e..7b4714117 100644 --- a/rosetta/services/construction_parse_test.go +++ b/rosetta/services/construction_parse_test.go @@ -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) } diff --git a/rosetta/services/mempool.go b/rosetta/services/mempool.go index 54b525280..c46895e3f 100644 --- a/rosetta/services/mempool.go +++ b/rosetta/services/mempool.go @@ -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 } diff --git a/rosetta/services/tx_construction.go b/rosetta/services/tx_construction.go index 887486754..8dbf257df 100644 --- a/rosetta/services/tx_construction.go +++ b/rosetta/services/tx_construction.go @@ -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 .. diff --git a/rosetta/services/tx_format.go b/rosetta/services/tx_format.go index 26b731d33..b1d66bbdb 100644 --- a/rosetta/services/tx_format.go +++ b/rosetta/services/tx_format.go @@ -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 diff --git a/rosetta/services/tx_format_test.go b/rosetta/services/tx_format_test.go index a3944d6f2..e86b09b8a 100644 --- a/rosetta/services/tx_format_test.go +++ b/rosetta/services/tx_format_test.go @@ -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) } @@ -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) } diff --git a/rosetta/services/tx_operation.go b/rosetta/services/tx_operation.go index 4d65d90d1..7fd2c1947 100644 --- a/rosetta/services/tx_operation.go +++ b/rosetta/services/tx_operation.go @@ -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 } diff --git a/rosetta/services/tx_operation_test.go b/rosetta/services/tx_operation_test.go index 854ce5ce7..421df2b71 100644 --- a/rosetta/services/tx_operation_test.go +++ b/rosetta/services/tx_operation_test.go @@ -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 { diff --git a/rpc/contract.go b/rpc/contract.go index 4cc3df90b..43d9fb6df 100644 --- a/rpc/contract.go +++ b/rpc/contract.go @@ -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) { diff --git a/rpc/transaction.go b/rpc/transaction.go index aa78166a0..f95e6589b 100644 --- a/rpc/transaction.go +++ b/rpc/transaction.go @@ -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 } From ff9a85ac4889222c2f78ec7ebe65bee4693e21fb Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Fri, 30 Oct 2020 17:09:34 -0700 Subject: [PATCH 06/14] Fix view change stuck issue --- consensus/consensus_service.go | 4 ++-- consensus/consensus_v2.go | 2 +- consensus/view_change.go | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index 17a220de0..ffbb2f370 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -573,9 +573,9 @@ func (consensus *Consensus) selfCommit(payload []byte) error { continue } - if _, err := consensus.Decider.SubmitVote( + if _, err := consensus.Decider.AddNewVote( quorum.Commit, - []bls.SerializedPublicKey{key.Pub.Bytes}, + []*bls_cosi.PublicKeyWrapper{key.Pub}, key.Pri.SignHash(commitPayload), common.BytesToHash(consensus.blockHash[:]), block.NumberU64(), diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index 6360d77a0..ea1d1b9a0 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -517,7 +517,7 @@ func (consensus *Consensus) commitBlock(blk *types.Block, committedMsg *FBFTMess } 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() { diff --git a/consensus/view_change.go b/consensus/view_change.go index 37df69712..2603f0e53 100644 --- a/consensus/view_change.go +++ b/consensus/view_change.go @@ -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 := uint64((curTimestamp - blockTimestamp) / viewChangeTimeout) nextViewID := diff + consensus.GetCurBlockViewID() consensus.getLogger().Info(). From d3eeb15ce79dc7f743d1cb35494b94cbe234eb23 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Fri, 30 Oct 2020 17:18:12 -0700 Subject: [PATCH 07/14] make submitVote private --- consensus/construct_test.go | 4 ++-- consensus/quorum/one-node-one-vote.go | 2 +- consensus/quorum/one-node-staked-vote.go | 2 +- consensus/quorum/quorom_test.go | 14 +++++++------- consensus/quorum/quorum.go | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/consensus/construct_test.go b/consensus/construct_test.go index 24b45c11a..a0111fb5d 100644 --- a/consensus/construct_test.go +++ b/consensus/construct_test.go @@ -73,7 +73,7 @@ func TestConstructPreparedMessage(test *testing.T) { leaderKey.FromLibBLSPublicKey(leaderPubKey) validatorKey := bls.SerializedPublicKey{} validatorKey.FromLibBLSPublicKey(validatorPubKey) - consensus.Decider.SubmitVote( + consensus.Decider.submitVote( quorum.Prepare, []bls.SerializedPublicKey{leaderKey}, leaderPriKey.Sign(message), @@ -81,7 +81,7 @@ func TestConstructPreparedMessage(test *testing.T) { consensus.blockNum, consensus.GetCurBlockViewID(), ) - if _, err := consensus.Decider.SubmitVote( + if _, err := consensus.Decider.submitVote( quorum.Prepare, []bls.SerializedPublicKey{validatorKey}, validatorPriKey.Sign(message), diff --git a/consensus/quorum/one-node-one-vote.go b/consensus/quorum/one-node-one-vote.go index 45db9bcd3..7455d6e72 100644 --- a/consensus/quorum/one-node-one-vote.go +++ b/consensus/quorum/one-node-one-vote.go @@ -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 .. diff --git a/consensus/quorum/one-node-staked-vote.go b/consensus/quorum/one-node-staked-vote.go index 8023b03d8..0f65257c9 100644 --- a/consensus/quorum/one-node-staked-vote.go +++ b/consensus/quorum/one-node-staked-vote.go @@ -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 diff --git a/consensus/quorum/quorom_test.go b/consensus/quorum/quorom_test.go index d6b91a5bf..73767ea25 100644 --- a/consensus/quorum/quorom_test.go +++ b/consensus/quorum/quorom_test.go @@ -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, diff --git a/consensus/quorum/quorum.go b/consensus/quorum/quorum.go index f41d47ec1..ae82d7ef3 100644 --- a/consensus/quorum/quorum.go +++ b/consensus/quorum/quorum.go @@ -81,7 +81,7 @@ type ParticipantTracker interface { // SignatoryTracker .. type SignatoryTracker interface { ParticipantTracker - SubmitVote( + submitVote( p Phase, pubkeys []bls.SerializedPublicKey, sig *bls_core.Sign, headerHash common.Hash, height, viewID uint64, From 500142719f6457c167db3db13870052023dcfb62 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Fri, 30 Oct 2020 17:25:39 -0700 Subject: [PATCH 08/14] change submitVote on quorum --- consensus/quorum/quorum.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/quorum/quorum.go b/consensus/quorum/quorum.go index ae82d7ef3..785aea4ac 100644 --- a/consensus/quorum/quorum.go +++ b/consensus/quorum/quorum.go @@ -273,7 +273,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, From 6a72ca14ad6afafc2ae7747ee6becacbd7050b8b Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Fri, 30 Oct 2020 18:12:16 -0700 Subject: [PATCH 09/14] make block sync trigger less frequent --- api/service/syncing/syncing.go | 2 +- consensus/consensus_service.go | 6 ++++++ consensus/construct_test.go | 10 ++++++---- consensus/validator.go | 19 +++++++++++-------- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/api/service/syncing/syncing.go b/api/service/syncing/syncing.go index 873da9e90..03a3b46ca 100644 --- a/api/service/syncing/syncing.go +++ b/api/service/syncing/syncing.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 diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index ffbb2f370..b3ede1404 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -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 { diff --git a/consensus/construct_test.go b/consensus/construct_test.go index a0111fb5d..27d1e82f4 100644 --- a/consensus/construct_test.go +++ b/consensus/construct_test.go @@ -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, diff --git a/consensus/validator.go b/consensus/validator.go index bff01506e..a1f83de65 100644 --- a/consensus/validator.go +++ b/consensus/validator.go @@ -114,10 +114,6 @@ func (consensus *Consensus) onPrepared(msg *msg_pb.Message) { Msg("Wrong BlockNum Received, ignoring!") return } - if recvMsg.BlockNum > consensus.blockNum { - consensus.getLogger().Warn().Msgf("[OnPrepared] low consensus block number. Spin sync") - consensus.spinUpStateSync() - } // check validity of prepared signature blockHash := recvMsg.BlockHash @@ -153,6 +149,12 @@ func (consensus *Consensus) onPrepared(msg *msg_pb.Message) { if !consensus.onPreparedSanityChecks(&blockObj, recvMsg) { return } + + 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() @@ -236,10 +238,6 @@ func (consensus *Consensus) onCommitted(msg *msg_pb.Message) { if !consensus.isRightBlockNumCheck(recvMsg) { return } - if recvMsg.BlockNum > consensus.blockNum { - consensus.getLogger().Info().Msg("[OnCommitted] low consensus block number. Spin up state sync") - consensus.spinUpStateSync() - } aggSig, mask, err := consensus.ReadSignatureBitmapPayload(recvMsg.Payload, 0) if err != nil { @@ -272,6 +270,11 @@ func (consensus *Consensus) onCommitted(msg *msg_pb.Message) { consensus.FBFTLog.AddMessage(recvMsg) + if recvMsg.BlockNum > consensus.blockNum { + consensus.getLogger().Info().Msg("[OnCommitted] low consensus block number. Spin up state sync") + consensus.spinUpStateSync() + } + consensus.mutex.Lock() defer consensus.mutex.Unlock() From 786162c0a7c60a9bd97da48d7d4fb4172e38b21a Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Fri, 30 Oct 2020 18:31:42 -0700 Subject: [PATCH 10/14] Add more comments --- consensus/quorum/quorum.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/consensus/quorum/quorum.go b/consensus/quorum/quorum.go index 785aea4ac..674ea96c0 100644 --- a/consensus/quorum/quorum.go +++ b/consensus/quorum/quorum.go @@ -81,6 +81,7 @@ type ParticipantTracker interface { // SignatoryTracker .. type SignatoryTracker interface { ParticipantTracker + // 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, @@ -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, From 38fe2a4422e07216b9506169373380b7a31ed989 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Fri, 30 Oct 2020 18:34:52 -0700 Subject: [PATCH 11/14] fix comment --- consensus/view_change.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/view_change.go b/consensus/view_change.go index 2603f0e53..3ef66eb9d 100644 --- a/consensus/view_change.go +++ b/consensus/view_change.go @@ -131,7 +131,7 @@ func (consensus *Consensus) getNextViewID() (uint64, time.Duration) { if curTimestamp <= blockTimestamp { return consensus.fallbackNextViewID() } - // diff is at least 1, and it won't exceed the totalNode + // diff only increases diff := uint64((curTimestamp - blockTimestamp) / viewChangeTimeout) nextViewID := diff + consensus.GetCurBlockViewID() From 1336e063bacfa7e9e7aa83af824718d16cfcab3e Mon Sep 17 00:00:00 2001 From: Daniel Van Der Maden Date: Fri, 30 Oct 2020 21:27:56 -0700 Subject: [PATCH 12/14] QOL Fixes for Rosetta (#3418) * [rosetta] Make nonsensical native tx construction error Signed-off-by: Daniel Van Der Maden * [rosetta] Refactor native constant names to match value Signed-off-by: Daniel Van Der Maden * [rosetta] Remove hex address comparison This is done since the hex address in the metadata is more of a QOL feature, all that matters is the b32 address. Signed-off-by: Daniel Van Der Maden * [rosetta] Fix error message for incorrect signed tx parse * Add check for sender address when parsing unsigned tx Signed-off-by: Daniel Van Der Maden * [rosetta] Make bad signature error message clearer Signed-off-by: Daniel Van Der Maden * [rosetta] Add shard ID check when combining transactions Signed-off-by: Daniel Van Der Maden * [rosetta] Add shard ID check for tx Hash & Submission Signed-off-by: Daniel Van Der Maden * [rosetta] Fix invalid signature type err msg Signed-off-by: Daniel Van Der Maden * [rosetta] Add shard check for tx parse Signed-off-by: Daniel Van Der Maden * [rosetta] Add check for shard ID when creating Tx payload Signed-off-by: Daniel Van Der Maden --- rosetta/common/operations.go | 12 ++++---- rosetta/common/operations_test.go | 4 +-- rosetta/services/construction_check.go | 16 ++++++++++ rosetta/services/construction_check_test.go | 8 ++--- rosetta/services/construction_create.go | 20 ++++++++++--- rosetta/services/construction_parse.go | 18 +++++++++-- rosetta/services/construction_submit.go | 11 +++++++ rosetta/services/tx_construction.go | 4 +-- rosetta/services/tx_construction_test.go | 22 +++++++------- rosetta/services/tx_format.go | 2 +- rosetta/services/tx_format_test.go | 6 ++-- rosetta/services/tx_operation.go | 4 +-- rosetta/services/tx_operation_components.go | 6 ++-- .../services/tx_operation_components_test.go | 30 +++++++++---------- rosetta/services/tx_operation_test.go | 6 ++-- 15 files changed, 110 insertions(+), 59 deletions(-) diff --git a/rosetta/common/operations.go b/rosetta/common/operations.go index 42eff80bf..82fbf5a44 100644 --- a/rosetta/common/operations.go +++ b/rosetta/common/operations.go @@ -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, diff --git a/rosetta/common/operations_test.go b/rosetta/common/operations_test.go index ac297c68a..a2e9c08f1 100644 --- a/rosetta/common/operations_test.go +++ b/rosetta/common/operations_test.go @@ -50,8 +50,8 @@ func TestPlainOperationTypes(t *testing.T) { plainOperationTypes := PlainOperationTypes referenceOperationTypes := []string{ ExpendGasOperation, - TransferNativeOperation, - CrossShardTransferNativeOperation, + NativeTransferOperation, + NativeCrossShardTransferOperation, ContractCreationOperation, GenesisFundsOperation, PreStakingBlockRewardOperation, diff --git a/rosetta/services/construction_check.go b/rosetta/services/construction_check.go index 33d43368d..9b96ea70a 100644 --- a/rosetta/services/construction_check.go +++ b/rosetta/services/construction_check.go @@ -76,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, @@ -87,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{ diff --git a/rosetta/services/construction_check_test.go b/rosetta/services/construction_check_test.go index 548c5531e..47879e935 100644 --- a/rosetta/services/construction_check_test.go +++ b/rosetta/services/construction_check_test.go @@ -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, diff --git a/rosetta/services/construction_create.go b/rosetta/services/construction_create.go index 0fc2293ea..8c5d01c30 100644 --- a/rosetta/services/construction_create.go +++ b/rosetta/services/construction_create.go @@ -113,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 { @@ -187,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) @@ -202,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", }) @@ -242,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 { diff --git a/rosetta/services/construction_parse.go b/rosetta/services/construction_parse.go index 5ead22257..69212530e 100644 --- a/rosetta/services/construction_parse.go +++ b/rosetta/services/construction_parse.go @@ -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,6 +43,12 @@ 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(), @@ -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 } @@ -89,14 +101,14 @@ func parseSignedTransaction( 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", }) diff --git a/rosetta/services/construction_submit.go b/rosetta/services/construction_submit.go index ec73b5a61..3276ff5b5 100644 --- a/rosetta/services/construction_submit.go +++ b/rosetta/services/construction_submit.go @@ -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 { diff --git a/rosetta/services/tx_construction.go b/rosetta/services/tx_construction.go index 8dbf257df..14177c269 100644 --- a/rosetta/services/tx_construction.go +++ b/rosetta/services/tx_construction.go @@ -57,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 } @@ -65,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 } diff --git a/rosetta/services/tx_construction_test.go b/rosetta/services/tx_construction_test.go index 966f1e17c..e8e8c9455 100644 --- a/rosetta/services/tx_construction_test.go +++ b/rosetta/services/tx_construction_test.go @@ -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), diff --git a/rosetta/services/tx_format.go b/rosetta/services/tx_format.go index b1d66bbdb..9995e346e 100644 --- a/rosetta/services/tx_format.go +++ b/rosetta/services/tx_format.go @@ -137,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{ diff --git a/rosetta/services/tx_format_test.go b/rosetta/services/tx_format_test.go index e86b09b8a..37ccaff1b 100644 --- a/rosetta/services/tx_format_test.go +++ b/rosetta/services/tx_format_test.go @@ -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 { @@ -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{ diff --git a/rosetta/services/tx_operation.go b/rosetta/services/tx_operation.go index 7fd2c1947..682e6645c 100644 --- a/rosetta/services/tx_operation.go +++ b/rosetta/services/tx_operation.go @@ -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{ diff --git a/rosetta/services/tx_operation_components.go b/rosetta/services/tx_operation_components.go index 405aaa899..285af55a4 100644 --- a/rosetta/services/tx_operation_components.go +++ b/rosetta/services/tx_operation_components.go @@ -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", }) diff --git a/rosetta/services/tx_operation_components_test.go b/rosetta/services/tx_operation_components_test.go index dff96ad3f..416c3ecc4 100644 --- a/rosetta/services/tx_operation_components_test.go +++ b/rosetta/services/tx_operation_components_test.go @@ -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, diff --git a/rosetta/services/tx_operation_test.go b/rosetta/services/tx_operation_test.go index 421df2b71..de29462f3 100644 --- a/rosetta/services/tx_operation_test.go +++ b/rosetta/services/tx_operation_test.go @@ -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{ From 13051c4cf8fa5562376db10135234f3bc371aef6 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Fri, 30 Oct 2020 22:11:36 -0700 Subject: [PATCH 13/14] remove beacon sync for explorer node --- cmd/harmony/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/harmony/main.go b/cmd/harmony/main.go index 07971a601..bd078f765 100644 --- a/cmd/harmony/main.go +++ b/cmd/harmony/main.go @@ -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") From 2ead9d7d336ce94ff302ae76f2c3c8fc28484445 Mon Sep 17 00:00:00 2001 From: Daniel Van Der Maden Date: Sat, 31 Oct 2020 12:17:27 -0700 Subject: [PATCH 14/14] Tx submission epoch checks (#3421) * [node] Add Cx epoch check * Report invalid epoch for staking tx Signed-off-by: Daniel Van Der Maden * [rosetta] Add Cx epoch check in Metadata request Signed-off-by: Daniel Van Der Maden * [node] Fix lint Signed-off-by: Daniel Van Der Maden --- node/node.go | 52 +++++++++++++++++--------- rosetta/services/construction_check.go | 14 +++++++ 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/node/node.go b/node/node.go index 2111fa0ee..59b82aa1a 100644 --- a/node/node.go +++ b/node/node.go @@ -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 diff --git a/rosetta/services/construction_check.go b/rosetta/services/construction_check.go index 9b96ea70a..53d1b5f2a 100644 --- a/rosetta/services/construction_check.go +++ b/rosetta/services/construction_check.go @@ -172,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