diff --git a/.travis.yml b/.travis.yml index 9c4b0908f..850444662 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,9 @@ go: go_import_path: github.com/harmony-one/harmony env: - TEST="make" - - TEST="./scripts/travis_checker.sh" - - TEST="./scripts/travis_node_checker.sh" + - TEST="bash ./scripts/travis_go_checker.sh" + - TEST="bash ./scripts/travis_rpc_checker.sh" + - TEST="bash ./scripts/travis_rosetta_checker.sh" install: # default working directory with source code is automatically set to # /home/travis/gopath/src/github.com/harmony-one/harmony diff --git a/Makefile b/Makefile index 46e4d9670..ebf000058 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,10 @@ help: @echo "distclean - remove node files & logs created by localnet, and all libs" @echo "test - run the entire test suite (go test & Node API test)" @echo "test-go - run the go test (with go lint, fmt, imports, mod, and generate checks)" - @echo "test-api - run the Node API test" - @echo "test-api-attach - attach onto the Node API testing docker container for inspection" + @echo "test-rpc - run the rpc tests" + @echo "test-rpc-attach - attach onto the rpc testing docker container for inspection" + @echo "test-rosetta - run the rosetta tests" + @echo "test-rosetta-attach - attach onto the rosetta testing docker container for inspection" @echo "linux_static - static build the harmony binary & bootnode along with the MCL & BLS libs (for linux)" @echo "arm_static - static build the harmony binary & bootnode on ARM64 platform" @echo "rpm - build a harmony RPM pacakge" @@ -82,11 +84,17 @@ test: test-go: bash ./test/go.sh -test-api: - bash ./test/api.sh run +test-rpc: + bash ./test/rpc.sh run -test-api-attach: - bash ./test/api.sh attach +test-rpc-attach: + bash ./test/rpc.sh attach + +test-rosetta: + bash ./test/rosetta.sh run + +test-rosetta-attach: + bash ./test/rosetta.sh attach linux_static: make -C $(TOP)/mcl -j8 diff --git a/README.md b/README.md index 9ede8b592..ea974e702 100644 --- a/README.md +++ b/README.md @@ -142,25 +142,36 @@ make debug-kill To keep things consistent, we have a docker image to run all tests. **These are the same tests ran on the pull request checks**. +Note that all testing docker container binds a couple of ports to the host machine for your convince. The ports are: +* `9500` - Shard 0 RPC for a validator +* `9501` - Shard 1 RPC for a validator +* `9599` - Shard 0 RPC for an explorer +* `9598` - Shard 1 RPC for an explorer +* `9799` - Shard 0 Rosetta (for an explorer) +* `9798` - Shard 1 Rosetta (for an explorer) +* `9899` - Shard 0 WS for an explorer +* `9898` - Shard 1 WS for an explorer +> This allows you to use curl, hmy CLI, postman, rosetta-cli, etc... on your host machine to play with or probe the localnet that was used for the test. + ### Go tests -To run this test do: +To run this test, do: ```bash make test-go ``` This test runs the go tests along with go lint, go fmt, go imports, go mod, and go generate checks. -### API tests -To run this test do: +### RPC tests +To run this test, do: ```bash -make test-api +make test-rpc ``` -This test starts a localnet (within the Docker container), **ensures it reaches consensus**, and runs a series of tests to ensure correct Node API behavior. +This test starts a localnet (within the Docker container), **ensures it reaches a consensus**, and runs a series of tests to ensure correct RPC behavior. This test also acts as a preliminary integration test (more through tests are done on the testnets). > The tests ran by this command can be found [here](https://github.com/harmony-one/harmony-test/tree/master/localnet). If you wish to debug further with the localnet after the tests are done, open a new shell and run: ```bash -make test-api-attach +make test-rpc-attach ``` > This will open a shell in the docker container that is running the Node API tests. > @@ -169,6 +180,20 @@ make test-api-attach > the current block height of localnet. Reference the documentation for the CLI [here](https://docs.harmony.one/home/wallets/harmony-cli) > for more details & commands. +### Rosetta tests +To run this test, do: +```bash +make test-rosetta +``` +This test starts a localnet (within the Docker container), **ensures it reaches a consensus**, and runs the Construction & Data API checks using the [rosetta-cli](https://github.com/coinbase/rosetta-cli). +This test also acts as a preliminary integration test (more through tests are done on the testnets). +> The config for this test can be found [here](https://github.com/harmony-one/harmony-test/blob/master/localnet/configs/localnet_rosetta_test_s0.json) & [here](https://github.com/harmony-one/harmony-test/blob/master/localnet/configs/localnet_rosetta_test_s1.json) + +Similar to the RPC tests, if you wish to debug further with the localnet after the tests are done, open a new shell and run: +```bash +make test-rosetta-attach +``` + ## License Harmony is licensed under the MIT License. See [`LICENSE`](LICENSE) file for diff --git a/cmd/harmony/config.go b/cmd/harmony/config.go index a68cb4ec1..8ac68238b 100644 --- a/cmd/harmony/config.go +++ b/cmd/harmony/config.go @@ -23,6 +23,7 @@ type harmonyConfig struct { P2P p2pConfig HTTP httpConfig WS wsConfig + RPCOpt rpcOptConfig BLSKeys blsConfig TxPool txPoolConfig Pprof pprofConfig @@ -117,6 +118,10 @@ type wsConfig struct { Port int } +type rpcOptConfig struct { + DebugEnabled bool // Enables PrivateDebugService APIs, including the EVM tracer +} + type devnetConfig struct { NumShards int ShardSize int diff --git a/cmd/harmony/default.go b/cmd/harmony/default.go index 37c91bcc9..b63ddf879 100644 --- a/cmd/harmony/default.go +++ b/cmd/harmony/default.go @@ -36,6 +36,9 @@ var defaultConfig = harmonyConfig{ IP: "127.0.0.1", Port: nodeconfig.DefaultWSPort, }, + RPCOpt: rpcOptConfig{ + DebugEnabled: false, + }, BLSKeys: blsConfig{ KeyDir: "./.hmy/blskeys", KeyFiles: []string{}, diff --git a/cmd/harmony/flags.go b/cmd/harmony/flags.go index cc147dbd6..5a063926e 100644 --- a/cmd/harmony/flags.go +++ b/cmd/harmony/flags.go @@ -59,6 +59,10 @@ var ( wsPortFlag, } + rpcOptFlags = []cli.Flag{ + rpcDebugEnabledFlag, + } + blsFlags = append(newBLSFlags, legacyBLSFlags...) newBLSFlags = []cli.Flag{ @@ -236,6 +240,7 @@ func getRootFlags() []cli.Flag { flags = append(flags, p2pFlags...) flags = append(flags, httpFlags...) flags = append(flags, wsFlags...) + flags = append(flags, rpcOptFlags...) flags = append(flags, blsFlags...) flags = append(flags, consensusFlags...) flags = append(flags, txPoolFlags...) @@ -503,6 +508,22 @@ func applyWSFlags(cmd *cobra.Command, config *harmonyConfig) { } } +// rpc opt flags +var ( + rpcDebugEnabledFlag = cli.BoolFlag{ + Name: "rpc.debug", + Usage: "enable private debug apis", + DefValue: defaultConfig.RPCOpt.DebugEnabled, + Hidden: true, + } +) + +func applyRPCOptFlags(cmd *cobra.Command, config *harmonyConfig) { + if cli.IsFlagChanged(cmd, rpcDebugEnabledFlag) { + config.RPCOpt.DebugEnabled = cli.GetBoolFlagValue(cmd, rpcDebugEnabledFlag) + } +} + // bls flags var ( blsDirFlag = cli.StringFlag{ diff --git a/cmd/harmony/flags_test.go b/cmd/harmony/flags_test.go index 0cb68e371..8194e8334 100644 --- a/cmd/harmony/flags_test.go +++ b/cmd/harmony/flags_test.go @@ -483,6 +483,31 @@ func TestWSFlags(t *testing.T) { } } +func TestRPCOptFlags(t *testing.T) { + tests := []struct { + args []string + expConfig rpcOptConfig + }{ + { + args: []string{"--rpc.debug"}, + expConfig: rpcOptConfig{ + DebugEnabled: true, + }, + }, + } + for i, test := range tests { + ts := newFlagTestSuite(t, rpcOptFlags, applyRPCOptFlags) + + hc, _ := ts.run(test.args) + + if !reflect.DeepEqual(hc.RPCOpt, test.expConfig) { + t.Errorf("Test %v: \n\t%+v\n\t%+v", i, hc.RPCOpt, test.expConfig) + } + + ts.tearDown() + } +} + func TestBLSFlags(t *testing.T) { tests := []struct { args []string diff --git a/cmd/harmony/main.go b/cmd/harmony/main.go index dd551ecc8..07971a601 100644 --- a/cmd/harmony/main.go +++ b/cmd/harmony/main.go @@ -191,6 +191,7 @@ func applyRootFlags(cmd *cobra.Command, config *harmonyConfig) { applyP2PFlags(cmd, config) applyHTTPFlags(cmd, config) applyWSFlags(cmd, config) + applyRPCOptFlags(cmd, config) applyBLSFlags(cmd, config) applyConsensusFlags(cmd, config) applyTxPoolFlags(cmd, config) @@ -306,12 +307,13 @@ func setupNodeAndRun(hc harmonyConfig) { // Parse RPC config nodeConfig.RPCServer = nodeconfig.RPCServerConfig{ - HTTPEnabled: hc.HTTP.Enabled, - HTTPIp: hc.HTTP.IP, - HTTPPort: hc.HTTP.Port, - WSEnabled: hc.WS.Enabled, - WSIp: hc.WS.IP, - WSPort: hc.WS.Port, + HTTPEnabled: hc.HTTP.Enabled, + HTTPIp: hc.HTTP.IP, + HTTPPort: hc.HTTP.Port, + WSEnabled: hc.WS.Enabled, + WSIp: hc.WS.IP, + WSPort: hc.WS.Port, + DebugEnabled: hc.RPCOpt.DebugEnabled, } if nodeConfig.ShardID != shard.BeaconChainShardID { utils.Logger().Info(). diff --git a/consensus/checks.go b/consensus/checks.go index 3c8e6d869..cc4c979b9 100644 --- a/consensus/checks.go +++ b/consensus/checks.go @@ -92,7 +92,7 @@ func (consensus *Consensus) onAnnounceSanityChecks(recvMsg *FBFTMessage) bool { "[OnAnnounce] Already in ViewChanging mode, conflicing announce, doing noop", ) } else { - consensus.startViewChange(consensus.GetCurBlockViewID() + 1) + consensus.startViewChange() } } consensus.getLogger().Debug(). diff --git a/consensus/consensus_service.go b/consensus/consensus_service.go index 153ca33e1..768fd99e8 100644 --- a/consensus/consensus_service.go +++ b/consensus/consensus_service.go @@ -81,7 +81,7 @@ func (consensus *Consensus) UpdatePublicKeys(pubKeys []bls_cosi.PublicKeyWrapper consensus.Decider.UpdateParticipants(pubKeys) utils.Logger().Info().Msg("My Committee updated") for i := range pubKeys { - utils.Logger().Debug(). + utils.Logger().Info(). Int("index", i). Str("BLSPubKey", pubKeys[i].Bytes.Hex()). Msg("Member") @@ -92,6 +92,9 @@ func (consensus *Consensus) UpdatePublicKeys(pubKeys []bls_cosi.PublicKeyWrapper consensus.LeaderPubKey = &allKeys[0] utils.Logger().Info(). Str("info", consensus.LeaderPubKey.Bytes.Hex()).Msg("My Leader") + } else { + utils.Logger().Error(). + Msg("[UpdatePublicKeys] Participants is empty") } consensus.pubKeyLock.Unlock() // reset states after update public keys @@ -551,6 +554,9 @@ func (consensus *Consensus) selfCommit(payload []byte) error { consensus.mutex.Lock() defer consensus.mutex.Unlock() + // Have to keep the block hash so the leader can finish the commit phase of prepared block + consensus.ResetState() + copy(consensus.blockHash[:], blockHash[:]) consensus.switchPhase("selfCommit", FBFTCommit) consensus.aggregatedPrepareSig = aggSig diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index 9b02635e7..a3d9642ba 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -142,7 +142,6 @@ func (consensus *Consensus) finalCommit() { return } - // if leader success finalize the block, send committed message to validators // TODO: once leader rotation is implemented, leader who is about to be switched out // needs to send the committed message immediately so the next leader can @@ -272,13 +271,11 @@ func (consensus *Consensus) Start( } if k != timeoutViewChange { consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops Consensus Timeout!!!") - viewID := consensus.GetCurBlockViewID() - consensus.startViewChange(viewID + 1) + consensus.startViewChange() break } else { consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops View Change Timeout!!!") - viewID := consensus.GetViewChangingID() - consensus.startViewChange(viewID + 1) + consensus.startViewChange() break } } diff --git a/consensus/quorum/quorum.go b/consensus/quorum/quorum.go index ddca4bcd9..115791605 100644 --- a/consensus/quorum/quorum.go +++ b/consensus/quorum/quorum.go @@ -2,7 +2,6 @@ package quorum import ( "fmt" - "github.com/harmony-one/harmony/internal/configs/sharding" "math/big" "github.com/harmony-one/harmony/crypto/bls" @@ -11,6 +10,7 @@ import ( bls_core "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/consensus/votepower" bls_cosi "github.com/harmony-one/harmony/crypto/bls" + shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" "github.com/harmony-one/harmony/multibls" "github.com/harmony-one/harmony/numeric" "github.com/harmony-one/harmony/shard" @@ -216,7 +216,29 @@ func (s *cIdentities) NthNext(pubKey *bls.PublicKeyWrapper, next int) (bool, *bl if idx != -1 { found = true } - idx = (idx + next) % int(s.ParticipantsCount()) + numNodes := int(s.ParticipantsCount()) + // sanity check to avoid out of bound access + if numNodes <= 0 || numNodes > len(s.publicKeys) { + numNodes = len(s.publicKeys) + } + idx = (idx + next) % numNodes + return found, &s.publicKeys[idx] +} + +// NthNextHmy return the Nth next pubkey of Harmony nodes, next can be negative number +func (s *cIdentities) NthNextHmy(instance shardingconfig.Instance, pubKey *bls.PublicKeyWrapper, next int) (bool, *bls.PublicKeyWrapper) { + found := false + + idx := s.IndexOf(pubKey.Bytes) + if idx != -1 { + found = true + } + numNodes := instance.NumHarmonyOperatedNodesPerShard() + // sanity check to avoid out of bound access + if numNodes <= 0 || numNodes > len(s.publicKeys) { + numNodes = len(s.publicKeys) + } + idx = (idx + next) % numNodes return found, &s.publicKeys[idx] } diff --git a/consensus/view_change.go b/consensus/view_change.go index 4c71d2197..63c8abfde 100644 --- a/consensus/view_change.go +++ b/consensus/view_change.go @@ -1,7 +1,6 @@ package consensus import ( - "github.com/harmony-one/harmony/shard" "math/big" "sync" "time" @@ -14,6 +13,7 @@ import ( nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/p2p" + "github.com/harmony-one/harmony/shard" "github.com/pkg/errors" ) @@ -219,21 +219,20 @@ func createTimeout() map[TimeoutType]*utils.Timeout { return timeouts } -// startViewChange send a new view change -// the viewID is the current viewID -func (consensus *Consensus) startViewChange(viewID uint64) { +// startViewChange start the view change process +func (consensus *Consensus) startViewChange() { if consensus.disableViewChange { return } consensus.consensusTimeout[timeoutConsensus].Stop() consensus.consensusTimeout[timeoutBootstrap].Stop() consensus.current.SetMode(ViewChanging) - consensus.SetViewChangingID(viewID) - consensus.LeaderPubKey = consensus.getNextLeaderKey(viewID) + nextViewID, duration := consensus.getNextViewID() + consensus.SetViewChangingID(nextViewID) + consensus.LeaderPubKey = consensus.getNextLeaderKey(nextViewID) - duration := consensus.current.GetViewChangeDuraion() consensus.getLogger().Warn(). - Uint64("viewID", viewID). + Uint64("nextViewID", nextViewID). Uint64("viewChangingID", consensus.GetViewChangingID()). Dur("timeoutDuration", duration). Str("NextLeader", consensus.LeaderPubKey.Bytes.Hex()). @@ -243,15 +242,15 @@ func (consensus *Consensus) startViewChange(viewID uint64) { defer consensus.consensusTimeout[timeoutViewChange].Start() // update the dictionary key if the viewID is first time received - consensus.vc.AddViewIDKeyIfNotExist(viewID, consensus.Decider.Participants()) + consensus.vc.AddViewIDKeyIfNotExist(nextViewID, consensus.Decider.Participants()) // init my own payload if err := consensus.vc.InitPayload( consensus.FBFTLog, - viewID, + nextViewID, consensus.blockNum, consensus.priKey); err != nil { - consensus.getLogger().Error().Err(err).Msg("Init Payload Error") + consensus.getLogger().Error().Err(err).Msg("[startViewChange] Init Payload Error") } // for view change, send separate view change per public key @@ -270,19 +269,27 @@ func (consensus *Consensus) startViewChange(viewID uint64) { true, ); err != nil { consensus.getLogger().Err(err). - Msg("could not send out the ViewChange message") + Msg("[startViewChange] could not send out the ViewChange message") } } } // startNewView stops the current view change -func (consensus *Consensus) startNewView(viewID uint64, newLeaderPriKey *bls.PrivateKeyWrapper) error { +func (consensus *Consensus) startNewView(viewID uint64, newLeaderPriKey *bls.PrivateKeyWrapper, reset bool) error { consensus.mutex.Lock() defer consensus.mutex.Unlock() + if !consensus.IsViewChangingMode() { + return errors.New("not in view changing mode anymore") + } + msgToSend := consensus.constructNewViewMessage( viewID, newLeaderPriKey, ) + if msgToSend == nil { + return errors.New("failed to construct NewView message") + } + if err := consensus.msgSender.SendWithRetry( consensus.blockNum, msg_pb.MessageType_NEWVIEW, @@ -298,6 +305,8 @@ func (consensus *Consensus) startNewView(viewID uint64, newLeaderPriKey *bls.Pri Hex("M1Payload", consensus.vc.GetM1Payload()). Msg("[startNewView] Sent NewView Messge") + consensus.msgSender.StopRetry(msg_pb.MessageType_VIEWCHANGE) + consensus.current.SetMode(Normal) consensus.consensusTimeout[timeoutViewChange].Stop() consensus.SetViewIDs(viewID) @@ -309,7 +318,10 @@ func (consensus *Consensus) startNewView(viewID uint64, newLeaderPriKey *bls.Pri Str("myKey", newLeaderPriKey.Pub.Bytes.Hex()). Msg("[startNewView] viewChange stopped. I am the New Leader") - consensus.ResetState() + // TODO: consider make ResetState unified and only called in one place like finalizeCommit() + if reset { + consensus.ResetState() + } consensus.LeaderPubKey = newLeaderPriKey.Pub return nil @@ -381,7 +393,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) { // no previous prepared message, go straight to normal mode // and start proposing new block if consensus.vc.IsM1PayloadEmpty() { - if err := consensus.startNewView(recvMsg.ViewID, newLeaderPriKey); err != nil { + if err := consensus.startNewView(recvMsg.ViewID, newLeaderPriKey, true); err != nil { consensus.getLogger().Error().Err(err).Msg("[onViewChange] startNewView failed") return } @@ -397,7 +409,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) { consensus.getLogger().Error().Err(err).Msg("[onViewChange] self commit failed") return } - if err := consensus.startNewView(recvMsg.ViewID, newLeaderPriKey); err != nil { + if err := consensus.startNewView(recvMsg.ViewID, newLeaderPriKey, false); err != nil { consensus.getLogger().Error().Err(err).Msg("[onViewChange] startNewView failed") return } @@ -508,6 +520,8 @@ func (consensus *Consensus) onNewView(msg *msg_pb.Message) { consensus.LeaderPubKey = senderKey consensus.ResetViewChangeState() + consensus.msgSender.StopRetry(msg_pb.MessageType_VIEWCHANGE) + // NewView message is verified, change state to normal consensus if preparedBlock != nil { consensus.sendCommitMessages(preparedBlock) diff --git a/consensus/view_change_msg.go b/consensus/view_change_msg.go index 40a366760..4848a6864 100644 --- a/consensus/view_change_msg.go +++ b/consensus/view_change_msg.go @@ -118,6 +118,9 @@ func (consensus *Consensus) constructNewViewMessage(viewID uint64, priKey *bls.P vcMsg.Payload, vcMsg.PreparedBlock = consensus.vc.GetPreparedBlock(consensus.FBFTLog, consensus.blockHash) vcMsg.M2Aggsigs, vcMsg.M2Bitmap = consensus.vc.GetM2Bitmap(viewID) vcMsg.M3Aggsigs, vcMsg.M3Bitmap = consensus.vc.GetM3Bitmap(viewID) + if vcMsg.M3Bitmap == nil || vcMsg.M3Aggsigs == nil { + return nil + } marshaledMessage, err := consensus.signAndMarshalConsensusMessage(message, priKey.Pri) if err != nil { diff --git a/consensus/view_change_test.go b/consensus/view_change_test.go index 2e0b6319b..50416be07 100644 --- a/consensus/view_change_test.go +++ b/consensus/view_change_test.go @@ -87,7 +87,7 @@ func TestGetNextLeaderKeyShouldFailForStandardGeneratedConsensus(t *testing.T) { // The below results in: "panic: runtime error: integer divide by zero" // This happens because there's no check for if there are any participants or not in https://github.com/harmony-one/harmony/blob/main/consensus/quorum/quorum.go#L188-L197 - assert.Panics(t, func() { consensus.GetNextLeaderKey(uint64(1)) }) + assert.Panics(t, func() { consensus.getNextLeaderKey(uint64(1)) }) } func TestGetNextLeaderKeyShouldSucceed(t *testing.T) { @@ -115,7 +115,7 @@ func TestGetNextLeaderKeyShouldSucceed(t *testing.T) { assert.Equal(t, keyCount, consensus.Decider.ParticipantsCount()) consensus.LeaderPubKey = &wrappedBLSKeys[0] - nextKey := consensus.GetNextLeaderKey(uint64(1)) + nextKey := consensus.getNextLeaderKey(uint64(1)) assert.Equal(t, nextKey, &wrappedBLSKeys[1]) } diff --git a/hmy/blockchain.go b/hmy/blockchain.go index 866504164..a64fb373b 100644 --- a/hmy/blockchain.go +++ b/hmy/blockchain.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/core" + "github.com/harmony-one/harmony/core/rawdb" "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/crypto/bls" @@ -19,6 +20,8 @@ import ( "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/shard" + "github.com/harmony-one/harmony/staking/availability" + stakingNetwork "github.com/harmony-one/harmony/staking/network" "github.com/pkg/errors" ) @@ -75,15 +78,10 @@ func (hmy *Harmony) GetBlockSigners( // DetailedBlockSignerInfo contains all of the block singing information type DetailedBlockSignerInfo struct { - // Signers is a map of addresses in the Signers for the block to - // all of the serialized BLS keys that signed said block. - Signers map[common.Address][]bls.SerializedPublicKey + // Signers are all the signers for the block + Signers shard.SlotList // Committee when the block was signed. Committee shard.SlotList - // TotalKeysSigned is the total number of bls keys that signed the block. - TotalKeysSigned uint - // Mask is the bitmap Mask for the block. - Mask *bls.Mask BlockHash common.Hash } @@ -91,31 +89,97 @@ type DetailedBlockSignerInfo struct { func (hmy *Harmony) GetDetailedBlockSignerInfo( ctx context.Context, blk *types.Block, ) (*DetailedBlockSignerInfo, error) { - slotList, mask, err := hmy.GetBlockSigners( - ctx, rpc.BlockNumber(blk.Number().Uint64()), + parentBlk, err := hmy.BlockByNumber(ctx, rpc.BlockNumber(blk.NumberU64()-1)) + if err != nil { + return nil, err + } + parentShardState, err := hmy.BlockChain.ReadShardState(parentBlk.Epoch()) + if err != nil { + return nil, err + } + committee, signers, _, err := availability.BallotResult( + parentBlk.Header(), blk.Header(), parentShardState, blk.ShardID(), ) + return &DetailedBlockSignerInfo{ + Signers: signers, + Committee: committee, + BlockHash: blk.Hash(), + }, nil +} + +// PreStakingBlockRewards are the rewards for a block in the pre-staking era (epoch < staking epoch). +type PreStakingBlockRewards map[common.Address]*big.Int + +// GetPreStakingBlockRewards for the given block number. +// Calculated rewards are done exactly like chain.AccumulateRewardsAndCountSigs. +func (hmy *Harmony) GetPreStakingBlockRewards( + ctx context.Context, blk *types.Block, +) (PreStakingBlockRewards, error) { + if hmy.IsStakingEpoch(blk.Epoch()) { + return nil, fmt.Errorf("block %v is in staking era", blk.Number()) + } + + if cachedReward, ok := hmy.preStakingBlockRewardsCache.Get(blk.Hash()); ok { + return cachedReward.(PreStakingBlockRewards), nil + } + rewards := PreStakingBlockRewards{} + + sigInfo, err := hmy.GetDetailedBlockSignerInfo(ctx, blk) if err != nil { return nil, err } + last := big.NewInt(0) + count := big.NewInt(int64(len(sigInfo.Signers))) + for i, slot := range sigInfo.Signers { + rewardsForThisAddr, ok := rewards[slot.EcdsaAddress] + if !ok { + rewardsForThisAddr = big.NewInt(0) + } + cur := big.NewInt(0) + cur.Mul(stakingNetwork.BlockReward, big.NewInt(int64(i+1))).Div(cur, count) + reward := big.NewInt(0).Sub(cur, last) + rewards[slot.EcdsaAddress] = new(big.Int).Add(reward, rewardsForThisAddr) + last = cur + } - totalSigners := uint(0) - sigInfos := map[common.Address][]bls.SerializedPublicKey{} - for _, slot := range slotList { - if _, ok := sigInfos[slot.EcdsaAddress]; !ok { - sigInfos[slot.EcdsaAddress] = []bls.SerializedPublicKey{} + // Report tx fees of the coinbase (== leader) + receipts, err := hmy.GetReceipts(ctx, blk.Hash()) + if err != nil { + return nil, err + } + txFees := big.NewInt(0) + for _, tx := range blk.Transactions() { + dbTx, _, _, receiptIndex := rawdb.ReadTransaction(hmy.ChainDb(), tx.Hash()) + if dbTx == nil { + return nil, fmt.Errorf("could not find receipt for tx: %v", tx.Hash().String()) } - if ok, err := mask.KeyEnabled(slot.BLSPublicKey); ok && err == nil { - sigInfos[slot.EcdsaAddress] = append(sigInfos[slot.EcdsaAddress], slot.BLSPublicKey) - totalSigners++ + if len(receipts) <= int(receiptIndex) { + return nil, fmt.Errorf("invalid receipt indext %v (>= num receipts: %v) for tx: %v", + receiptIndex, len(receipts), tx.Hash().String()) } + txFee := new(big.Int).Mul(tx.GasPrice(), big.NewInt(int64(receipts[receiptIndex].GasUsed))) + txFees = new(big.Int).Add(txFee, txFees) } - return &DetailedBlockSignerInfo{ - Signers: sigInfos, - Committee: slotList, - TotalKeysSigned: totalSigners, - Mask: mask, - BlockHash: blk.Hash(), - }, nil + for _, stx := range blk.StakingTransactions() { + dbsTx, _, _, receiptIndex := rawdb.ReadStakingTransaction(hmy.ChainDb(), stx.Hash()) + if dbsTx == nil { + return nil, fmt.Errorf("could not find receipt for tx: %v", stx.Hash().String()) + } + if len(receipts) <= int(receiptIndex) { + return nil, fmt.Errorf("invalid receipt indext %v (>= num receipts: %v) for tx: %v", + receiptIndex, len(receipts), stx.Hash().String()) + } + txFee := new(big.Int).Mul(stx.GasPrice(), big.NewInt(int64(receipts[receiptIndex].GasUsed))) + txFees = new(big.Int).Add(txFee, txFees) + } + if amt, ok := rewards[blk.Header().Coinbase()]; ok { + rewards[blk.Header().Coinbase()] = new(big.Int).Add(amt, txFees) + } else { + rewards[blk.Header().Coinbase()] = txFees + } + + hmy.preStakingBlockRewardsCache.Add(blk.Hash(), rewards) + return rewards, nil } // GetLatestChainHeaders .. diff --git a/hmy/hmy.go b/hmy/hmy.go index 0fd0e07c3..aa8b389df 100644 --- a/hmy/hmy.go +++ b/hmy/hmy.go @@ -29,10 +29,11 @@ import ( const ( // BloomBitsBlocks is the number of blocks a single bloom bit section vector // contains on the server side. - BloomBitsBlocks uint64 = 4096 - leaderCacheSize = 250 // Approx number of BLS keys in committee - undelegationPayoutsCacheSize = 500 // max number of epochs to store in cache - totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same + BloomBitsBlocks uint64 = 4096 + leaderCacheSize = 250 // Approx number of BLS keys in committee + undelegationPayoutsCacheSize = 500 // max number of epochs to store in cache + preStakingBlockRewardsCacheSize = 1024 // max number of block rewards to store in cache + totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same ) var ( @@ -67,6 +68,8 @@ type Harmony struct { leaderCache *lru.Cache // undelegationPayoutsCache to save on recomputation every epoch undelegationPayoutsCache *lru.Cache + // preStakingBlockRewardsCache to save on recomputation for commonly checked blocks in epoch < staking epoch + preStakingBlockRewardsCache *lru.Cache // totalStakeCache to save on recomputation for `totalStakeCacheDuration` blocks. totalStakeCache *totalStakeCache } @@ -111,25 +114,27 @@ func New( chainDb := nodeAPI.Blockchain().ChainDB() leaderCache, _ := lru.New(leaderCacheSize) undelegationPayoutsCache, _ := lru.New(undelegationPayoutsCacheSize) + preStakingBlockRewardsCache, _ := lru.New(preStakingBlockRewardsCacheSize) totalStakeCache := newTotalStakeCache(totalStakeCacheDuration) bloomIndexer := NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms) bloomIndexer.Start(nodeAPI.Blockchain()) return &Harmony{ - ShutdownChan: make(chan bool), - BloomRequests: make(chan chan *bloombits.Retrieval), - BloomIndexer: bloomIndexer, - BlockChain: nodeAPI.Blockchain(), - BeaconChain: nodeAPI.Beaconchain(), - TxPool: txPool, - CxPool: cxPool, - eventMux: new(event.TypeMux), - chainDb: chainDb, - NodeAPI: nodeAPI, - ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(), - ShardID: shardID, - leaderCache: leaderCache, - totalStakeCache: totalStakeCache, - undelegationPayoutsCache: undelegationPayoutsCache, + ShutdownChan: make(chan bool), + BloomRequests: make(chan chan *bloombits.Retrieval), + BloomIndexer: bloomIndexer, + BlockChain: nodeAPI.Blockchain(), + BeaconChain: nodeAPI.Beaconchain(), + TxPool: txPool, + CxPool: cxPool, + eventMux: new(event.TypeMux), + chainDb: chainDb, + NodeAPI: nodeAPI, + ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(), + ShardID: shardID, + leaderCache: leaderCache, + totalStakeCache: totalStakeCache, + undelegationPayoutsCache: undelegationPayoutsCache, + preStakingBlockRewardsCache: preStakingBlockRewardsCache, } } diff --git a/internal/configs/node/config.go b/internal/configs/node/config.go index df4f1a142..fef00ee9c 100644 --- a/internal/configs/node/config.go +++ b/internal/configs/node/config.go @@ -103,6 +103,8 @@ type RPCServerConfig struct { WSEnabled bool WSIp string WSPort int + + DebugEnabled bool } // RosettaServerConfig is the config for the rosetta server diff --git a/rosetta/services/block_special.go b/rosetta/services/block_special.go index 882590b1c..8b6dbe17d 100644 --- a/rosetta/services/block_special.go +++ b/rosetta/services/block_special.go @@ -155,31 +155,23 @@ func (s *BlockAPI) specialGenesisBlockTransaction( } // getPreStakingRewardTransactionIdentifiers is only used for the /block endpoint -// rewards for signing block n is paid out on block n+1 func (s *BlockAPI) getPreStakingRewardTransactionIdentifiers( ctx context.Context, currBlock *hmytypes.Block, ) ([]*types.TransactionIdentifier, *types.Error) { if currBlock.Number().Cmp(big.NewInt(1)) != 1 { return nil, nil } - blockNumToBeRewarded := currBlock.Number().Uint64() - 1 - rewardedBlock, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNumToBeRewarded).EthBlockNumber()) - if err != nil { - return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{ - "message": err.Error(), - }) - } - blockSigInfo, err := s.hmy.GetDetailedBlockSignerInfo(ctx, rewardedBlock) + rewards, err := s.hmy.GetPreStakingBlockRewards(ctx, currBlock) if err != nil { return nil, common.NewError(common.CatchAllError, map[string]interface{}{ "message": err.Error(), }) } txIDs := []*types.TransactionIdentifier{} - for acc, signedBlsKeys := range blockSigInfo.Signers { - if len(signedBlsKeys) > 0 { - txIDs = append(txIDs, getSpecialCaseTransactionIdentifier(currBlock.Hash(), acc, SpecialPreStakingRewardTxID)) - } + for addr := range rewards { + txIDs = append(txIDs, getSpecialCaseTransactionIdentifier( + currBlock.Hash(), addr, SpecialPreStakingRewardTxID, + )) } return txIDs, nil } @@ -221,13 +213,6 @@ func (s *BlockAPI) preStakingRewardBlockTransaction( if rosettaError != nil { return nil, rosettaError } - blockNumOfSigsForReward := blk.Number().Uint64() - 1 - signedBlock, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNumOfSigsForReward).EthBlockNumber()) - if err != nil { - return nil, common.NewError(common.BlockNotFoundError, map[string]interface{}{ - "message": err.Error(), - }) - } if blkHash.String() != blk.Hash().String() { return nil, common.NewError(common.SanityCheckError, map[string]interface{}{ "message": fmt.Sprintf( @@ -235,13 +220,13 @@ func (s *BlockAPI) preStakingRewardBlockTransaction( ), }) } - blockSignerInfo, err := s.hmy.GetDetailedBlockSignerInfo(ctx, signedBlock) + rewards, err := s.hmy.GetPreStakingBlockRewards(ctx, blk) if err != nil { return nil, common.NewError(common.CatchAllError, map[string]interface{}{ "message": err.Error(), }) } - transactions, rosettaError := FormatPreStakingRewardTransaction(txID, blockSignerInfo, address) + transactions, rosettaError := FormatPreStakingRewardTransaction(txID, rewards, address) if rosettaError != nil { return nil, rosettaError } diff --git a/rosetta/services/tx_format.go b/rosetta/services/tx_format.go index f23dc87ba..26b731d33 100644 --- a/rosetta/services/tx_format.go +++ b/rosetta/services/tx_format.go @@ -12,7 +12,6 @@ import ( "github.com/harmony-one/harmony/hmy" internalCommon "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/rosetta/common" - stakingNetwork "github.com/harmony-one/harmony/staking/network" stakingTypes "github.com/harmony-one/harmony/staking/types" ) @@ -176,44 +175,18 @@ func FormatGenesisTransaction( // FormatPreStakingRewardTransaction for block rewards in pre-staking era for a given Bech-32 address. func FormatPreStakingRewardTransaction( - txID *types.TransactionIdentifier, blockSigInfo *hmy.DetailedBlockSignerInfo, address ethcommon.Address, + txID *types.TransactionIdentifier, rewards hmy.PreStakingBlockRewards, address ethcommon.Address, ) (*types.Transaction, *types.Error) { - signatures, ok := blockSigInfo.Signers[address] - if !ok || len(signatures) == 0 { - return nil, &common.TransactionNotFoundError - } accID, rosettaError := newAccountIdentifier(address) if rosettaError != nil { return nil, rosettaError } - - // Calculate rewards exactly like `AccumulateRewardsAndCountSigs` but short circuit when possible. - // WARNING: must do calculation in the order of the committee to get accurate values. - i := 0 - last := big.NewInt(0) - rewardsForThisBlock := big.NewInt(0) - count := big.NewInt(int64(blockSigInfo.TotalKeysSigned)) - for _, slot := range blockSigInfo.Committee { - rewardsForThisAddr := big.NewInt(0) - if keys, ok := blockSigInfo.Signers[slot.EcdsaAddress]; ok { - for range keys { - cur := big.NewInt(0) - cur.Mul(stakingNetwork.BlockReward, big.NewInt(int64(i+1))).Div(cur, count) - reward := big.NewInt(0).Sub(cur, last) - rewardsForThisAddr = new(big.Int).Add(reward, rewardsForThisAddr) - last = cur - i++ - } - } - if slot.EcdsaAddress == address { - rewardsForThisBlock = rewardsForThisAddr - if !(rewardsForThisAddr.Cmp(big.NewInt(0)) > 0) { - return nil, common.NewError(common.SanityCheckError, map[string]interface{}{ - "message": "expected non-zero block reward in pre-staking era for block signer", - }) - } - break - } + value, ok := rewards[address] + if !ok { + return nil, common.NewError(common.TransactionNotFoundError, map[string]interface{}{ + "message": fmt.Sprintf("%v does not have any rewards for block", + internalCommon.MustAddressToBech32(address)), + }) } return &types.Transaction{ @@ -227,7 +200,7 @@ func FormatPreStakingRewardTransaction( Status: common.SuccessOperationStatus.Status, Account: accID, Amount: &types.Amount{ - Value: rewardsForThisBlock.String(), + Value: value.String(), Currency: &common.NativeCurrency, }, }, diff --git a/rosetta/services/tx_format_test.go b/rosetta/services/tx_format_test.go index 5a9db4c48..a3944d6f2 100644 --- a/rosetta/services/tx_format_test.go +++ b/rosetta/services/tx_format_test.go @@ -12,12 +12,9 @@ import ( "github.com/ethereum/go-ethereum/crypto" hmytypes "github.com/harmony-one/harmony/core/types" - "github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/hmy" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/rosetta/common" - "github.com/harmony-one/harmony/shard" - stakingNetwork "github.com/harmony-one/harmony/staking/network" stakingTypes "github.com/harmony-one/harmony/staking/types" "github.com/harmony-one/harmony/test/helpers" ) @@ -208,23 +205,12 @@ func TestFormatPreStakingRewardTransactionSuccess(t *testing.T) { t.Fatal(err) } testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - testBlockSigInfo := &hmy.DetailedBlockSignerInfo{ - Signers: map[ethcommon.Address][]bls.SerializedPublicKey{ - testAddr: { // Only care about length for this test - bls.SerializedPublicKey{}, - bls.SerializedPublicKey{}, - }, - }, - Committee: shard.SlotList{ - { - EcdsaAddress: testAddr, - }, - }, - TotalKeysSigned: 150, - BlockHash: ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238"), + testBlkHash := ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238") + testRewards := hmy.PreStakingBlockRewards{ + testAddr: big.NewInt(1), } - refTxID := getSpecialCaseTransactionIdentifier(testBlockSigInfo.BlockHash, testAddr, SpecialPreStakingRewardTxID) - tx, rosettaError := FormatPreStakingRewardTransaction(refTxID, testBlockSigInfo, testAddr) + refTxID := getSpecialCaseTransactionIdentifier(testBlkHash, testAddr, SpecialPreStakingRewardTxID) + tx, rosettaError := FormatPreStakingRewardTransaction(refTxID, testRewards, testAddr) if rosettaError != nil { t.Fatal(rosettaError) } @@ -247,39 +233,6 @@ func TestFormatPreStakingRewardTransactionSuccess(t *testing.T) { if tx.Operations[0].Status != common.SuccessOperationStatus.Status { t.Error("expected successful operation status") } - - // Expect: myNumberOfSigForBlock * (totalAmountOfRewardsPerBlock / numOfSigsForBlock) to be my block reward amount - refAmount := new(big.Int).Mul(new(big.Int).Quo(stakingNetwork.BlockReward, big.NewInt(150)), big.NewInt(2)) - fmtRefAmount := fmt.Sprintf("%v", refAmount) - if tx.Operations[0].Amount.Value != fmtRefAmount { - t.Errorf("expected operation amount to be %v not %v", fmtRefAmount, tx.Operations[0].Amount.Value) - } - - testBlockSigInfo = &hmy.DetailedBlockSignerInfo{ - Signers: map[ethcommon.Address][]bls.SerializedPublicKey{ - testAddr: { // Only care about length for this test - bls.SerializedPublicKey{}, - bls.SerializedPublicKey{}, - }, - }, - Committee: shard.SlotList{}, - TotalKeysSigned: 150, - BlockHash: ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238"), - } - tx, rosettaError = FormatPreStakingRewardTransaction(refTxID, testBlockSigInfo, testAddr) - if rosettaError != nil { - t.Fatal(rosettaError) - } - if len(tx.Operations) != 1 { - t.Fatal("expected exactly 1 operation") - } - amt, err := types.AmountValue(tx.Operations[0].Amount) - if err != nil { - t.Fatal(err) - } - if amt.Cmp(big.NewInt(0)) != 0 { - t.Error("expected amount to be 0") - } } func TestFormatPreStakingRewardTransactionFail(t *testing.T) { @@ -288,42 +241,16 @@ func TestFormatPreStakingRewardTransactionFail(t *testing.T) { t.Fatal(err) } testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - testBlockSigInfo := &hmy.DetailedBlockSignerInfo{ - Signers: map[ethcommon.Address][]bls.SerializedPublicKey{ - testAddr: {}, - }, - Committee: shard.SlotList{ - { - EcdsaAddress: testAddr, - }, - }, - TotalKeysSigned: 150, - BlockHash: ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238"), - } - testTxID := getSpecialCaseTransactionIdentifier(testBlockSigInfo.BlockHash, testAddr, SpecialPreStakingRewardTxID) - _, rosettaError := FormatPreStakingRewardTransaction(testTxID, testBlockSigInfo, testAddr) - if rosettaError == nil { - t.Fatal("expected rosetta error") - } - if !reflect.DeepEqual(&common.TransactionNotFoundError, rosettaError) { - t.Error("expected transaction not found error") - } - - testBlockSigInfo = &hmy.DetailedBlockSignerInfo{ - Signers: map[ethcommon.Address][]bls.SerializedPublicKey{}, - Committee: shard.SlotList{ - { - EcdsaAddress: testAddr, - }, - }, - TotalKeysSigned: 150, - BlockHash: ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238"), + testBlkHash := ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238") + testRewards := hmy.PreStakingBlockRewards{ + FormatDefaultSenderAddress: big.NewInt(1), } - _, rosettaError = FormatPreStakingRewardTransaction(testTxID, testBlockSigInfo, testAddr) + testTxID := getSpecialCaseTransactionIdentifier(testBlkHash, testAddr, SpecialPreStakingRewardTxID) + _, rosettaError := FormatPreStakingRewardTransaction(testTxID, testRewards, testAddr) if rosettaError == nil { t.Fatal("expected rosetta error") } - if !reflect.DeepEqual(&common.TransactionNotFoundError, rosettaError) { + if common.TransactionNotFoundError.Code != rosettaError.Code { t.Error("expected transaction not found error") } } diff --git a/rpc/rpc.go b/rpc/rpc.go index 232150870..4650616b5 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -64,7 +64,7 @@ func (n Version) Namespace() string { // StartServers starts the http & ws servers func StartServers(hmy *hmy.Harmony, apis []rpc.API, config nodeconfig.RPCServerConfig) error { - apis = append(apis, getAPIs(hmy)...) + apis = append(apis, getAPIs(hmy, config.DebugEnabled)...) if config.HTTPEnabled { httpEndpoint = fmt.Sprintf("%v:%v", config.HTTPIp, config.HTTPPort) @@ -115,8 +115,8 @@ func StopServers() error { } // getAPIs returns all the API methods for the RPC interface -func getAPIs(hmy *hmy.Harmony) []rpc.API { - return []rpc.API{ +func getAPIs(hmy *hmy.Harmony, debugEnable bool) []rpc.API { + publicAPIs := []rpc.API{ // Public methods NewPublicHarmonyAPI(hmy, V1), NewPublicHarmonyAPI(hmy, V2), @@ -130,13 +130,20 @@ func getAPIs(hmy *hmy.Harmony) []rpc.API { NewPublicPoolAPI(hmy, V2), NewPublicStakingAPI(hmy, V1), NewPublicStakingAPI(hmy, V2), - // Private methods - NewPrivateDebugAPI(hmy, V1), - NewPrivateDebugAPI(hmy, V2), // Legacy methods (subject to removal) v1.NewPublicLegacyAPI(hmy), v2.NewPublicLegacyAPI(hmy), } + + privateAPIs := []rpc.API{ + NewPrivateDebugAPI(hmy, V1), + NewPrivateDebugAPI(hmy, V2), + } + + if debugEnable { + return append(publicAPIs, privateAPIs...) + } + return publicAPIs } func startHTTP(apis []rpc.API) (err error) { diff --git a/scripts/node.sh b/scripts/node.sh index 3501f4ef1..1aa05d042 100755 --- a/scripts/node.sh +++ b/scripts/node.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -version="v2 20200811.1" +version="v2 20201023.0" unset -v progname progname="${0##*/}" @@ -56,7 +56,7 @@ function valid_ip() function myip() { # get ipv4 address only, right now only support ipv4 addresses - PUB_IP=$(dig -4 @resolver1.opendns.com ANY myip.opendns.com +short) + PUB_IP=$(dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | awk -F'"' '{ print $2}') if valid_ip $PUB_IP; then msg "public IP address autodetected: $PUB_IP" else diff --git a/scripts/travis_checker.sh b/scripts/travis_go_checker.sh similarity index 100% rename from scripts/travis_checker.sh rename to scripts/travis_go_checker.sh diff --git a/scripts/travis_rosetta_checker.sh b/scripts/travis_rosetta_checker.sh new file mode 100644 index 000000000..9d56835b4 --- /dev/null +++ b/scripts/travis_rosetta_checker.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -e +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +docker pull harmonyone/localnet-test +docker run -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test -r \ No newline at end of file diff --git a/scripts/travis_node_checker.sh b/scripts/travis_rpc_checker.sh similarity index 100% rename from scripts/travis_node_checker.sh rename to scripts/travis_rpc_checker.sh diff --git a/test/all.sh b/test/all.sh index cda933e3a..df55e8a76 100644 --- a/test/all.sh +++ b/test/all.sh @@ -2,4 +2,6 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" bash "$DIR/kill_node.sh" docker pull harmonyone/localnet-test -docker run -it --expose 9000-9999 -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test \ No newline at end of file +docker run -it \ + -p 9500:9500 -p 9501:9501 -p 9599:9599 -p 9598:9598 -p 9799:9799 -p 9798:9798 -p 9899:9899 -p 9898:9898 \ + -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test \ No newline at end of file diff --git a/test/deploy.sh b/test/deploy.sh index 6a9451151..b20d3d6e3 100755 --- a/test/deploy.sh +++ b/test/deploy.sh @@ -87,6 +87,9 @@ function launch_localnet() { echo "skip empty node" continue fi + if [[ $EXPOSEAPIS == "true" ]]; then + args=("${args[@]}" "--http.ip=0.0.0.0" "--ws.ip=0.0.0.0") + fi # Setup BLS key for i-th localnet node if [[ ! -e "$bls_key" ]]; then @@ -142,6 +145,7 @@ USAGE: $ME [OPTIONS] config_file_name [extra args to node] -N network network type (default: $NETWORK) -B don't build the binary -v verbosity in log (default: $VERBOSE) + -e expose WS & HTTP ip (default: $EXPOSEAPIS) This script will build all the binaries and start harmony and based on the configuration file. @@ -159,8 +163,9 @@ DRYRUN= NETWORK=localnet VERBOSE=false NOBUILD=false +EXPOSEAPIS=false -while getopts "hD:m:s:nBN:v" option; do +while getopts "hD:m:s:nBN:ve" option; do case ${option} in h) usage ;; D) DURATION=$OPTARG ;; @@ -170,6 +175,7 @@ while getopts "hD:m:s:nBN:v" option; do B) NOBUILD=true ;; N) NETWORK=$OPTARG ;; v) VERBOSE=true ;; + e) EXPOSEAPIS=true ;; *) usage ;; esac done diff --git a/test/go.sh b/test/go.sh index a37662308..8a7cdb756 100644 --- a/test/go.sh +++ b/test/go.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" docker pull harmonyone/localnet-test -docker run -it -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test -g +docker run -it \ + -p 9500:9500 -p 9501:9501 -p 9599:9599 -p 9598:9598 -p 9799:9799 -p 9798:9798 -p 9899:9899 -p 9898:9898 \ + -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test -g diff --git a/test/api.sh b/test/rosetta.sh old mode 100755 new mode 100644 similarity index 66% rename from test/api.sh rename to test/rosetta.sh index 1dd47dc20..22dc5c1ce --- a/test/api.sh +++ b/test/rosetta.sh @@ -8,7 +8,9 @@ case ${1} in run) docker pull harmonyone/localnet-test docker rm "$docker_name" - docker run -it --name "$docker_name" --expose 9000-9999 -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test -n -k + docker run -it --name "$docker_name" \ + -p 9500:9500 -p 9501:9501 -p 9599:9599 -p 9598:9598 -p 9799:9799 -p 9798:9798 -p 9899:9899 -p 9898:9898 \ + -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test -r -k ;; attach) docker exec -it "$docker_name" /bin/bash diff --git a/test/rpc.sh b/test/rpc.sh new file mode 100755 index 000000000..32ef8f4f4 --- /dev/null +++ b/test/rpc.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +bash "$DIR/kill_node.sh" + +docker_name="harmony-localnet-test" + +case ${1} in +run) + docker pull harmonyone/localnet-test + docker rm "$docker_name" + docker run -it --name "$docker_name" \ + -p 9500:9500 -p 9501:9501 -p 9599:9599 -p 9598:9598 -p 9799:9799 -p 9798:9798 -p 9899:9899 -p 9898:9898 \ + -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test -n -k + ;; +attach) + docker exec -it "$docker_name" /bin/bash + ;; +*) + echo " +Node API tests + +Param: Help: +run Run the Node API tests +attach Attach onto the Node API testing docker image for inspection +" + exit 0 + ;; +esac +