pull/3405/head
Rongjian Lan 4 years ago
commit bb4caa2078
  1. 5
      .travis.yml
  2. 20
      Makefile
  3. 37
      README.md
  4. 5
      cmd/harmony/config.go
  5. 3
      cmd/harmony/default.go
  6. 21
      cmd/harmony/flags.go
  7. 25
      cmd/harmony/flags_test.go
  8. 2
      cmd/harmony/main.go
  9. 2
      consensus/checks.go
  10. 8
      consensus/consensus_service.go
  11. 7
      consensus/consensus_v2.go
  12. 26
      consensus/quorum/quorum.go
  13. 44
      consensus/view_change.go
  14. 3
      consensus/view_change_msg.go
  15. 4
      consensus/view_change_test.go
  16. 112
      hmy/blockchain.go
  17. 5
      hmy/hmy.go
  18. 2
      internal/configs/node/config.go
  19. 29
      rosetta/services/block_special.go
  20. 41
      rosetta/services/tx_format.go
  21. 95
      rosetta/services/tx_format_test.go
  22. 19
      rpc/rpc.go
  23. 4
      scripts/node.sh
  24. 0
      scripts/travis_go_checker.sh
  25. 5
      scripts/travis_rosetta_checker.sh
  26. 0
      scripts/travis_rpc_checker.sh
  27. 4
      test/all.sh
  28. 8
      test/deploy.sh
  29. 4
      test/go.sh
  30. 4
      test/rosetta.sh
  31. 29
      test/rpc.sh

@ -6,8 +6,9 @@ go:
go_import_path: github.com/harmony-one/harmony go_import_path: github.com/harmony-one/harmony
env: env:
- TEST="make" - TEST="make"
- TEST="./scripts/travis_checker.sh" - TEST="bash ./scripts/travis_go_checker.sh"
- TEST="./scripts/travis_node_checker.sh" - TEST="bash ./scripts/travis_rpc_checker.sh"
- TEST="bash ./scripts/travis_rosetta_checker.sh"
install: install:
# default working directory with source code is automatically set to # default working directory with source code is automatically set to
# /home/travis/gopath/src/github.com/harmony-one/harmony # /home/travis/gopath/src/github.com/harmony-one/harmony

@ -29,8 +29,10 @@ help:
@echo "distclean - remove node files & logs created by localnet, and all libs" @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 - 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-go - run the go test (with go lint, fmt, imports, mod, and generate checks)"
@echo "test-api - run the Node API test" @echo "test-rpc - run the rpc tests"
@echo "test-api-attach - attach onto the Node API testing docker container for inspection" @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 "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 "arm_static - static build the harmony binary & bootnode on ARM64 platform"
@echo "rpm - build a harmony RPM pacakge" @echo "rpm - build a harmony RPM pacakge"
@ -82,11 +84,17 @@ test:
test-go: test-go:
bash ./test/go.sh bash ./test/go.sh
test-api: test-rpc:
bash ./test/api.sh run bash ./test/rpc.sh run
test-api-attach: test-rpc-attach:
bash ./test/api.sh attach bash ./test/rpc.sh attach
test-rosetta:
bash ./test/rosetta.sh run
test-rosetta-attach:
bash ./test/rosetta.sh attach
linux_static: linux_static:
make -C $(TOP)/mcl -j8 make -C $(TOP)/mcl -j8

@ -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**. 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 ### Go tests
To run this test do: To run this test, do:
```bash ```bash
make test-go make test-go
``` ```
This test runs the go tests along with go lint, go fmt, go imports, go mod, and go generate checks. This test runs the go tests along with go lint, go fmt, go imports, go mod, and go generate checks.
### API tests ### RPC tests
To run this test do: To run this test, do:
```bash ```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). 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). > 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: If you wish to debug further with the localnet after the tests are done, open a new shell and run:
```bash ```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. > 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) > 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. > 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 ## License
Harmony is licensed under the MIT License. See [`LICENSE`](LICENSE) file for Harmony is licensed under the MIT License. See [`LICENSE`](LICENSE) file for

@ -23,6 +23,7 @@ type harmonyConfig struct {
P2P p2pConfig P2P p2pConfig
HTTP httpConfig HTTP httpConfig
WS wsConfig WS wsConfig
RPCOpt rpcOptConfig
BLSKeys blsConfig BLSKeys blsConfig
TxPool txPoolConfig TxPool txPoolConfig
Pprof pprofConfig Pprof pprofConfig
@ -117,6 +118,10 @@ type wsConfig struct {
Port int Port int
} }
type rpcOptConfig struct {
DebugEnabled bool // Enables PrivateDebugService APIs, including the EVM tracer
}
type devnetConfig struct { type devnetConfig struct {
NumShards int NumShards int
ShardSize int ShardSize int

@ -36,6 +36,9 @@ var defaultConfig = harmonyConfig{
IP: "127.0.0.1", IP: "127.0.0.1",
Port: nodeconfig.DefaultWSPort, Port: nodeconfig.DefaultWSPort,
}, },
RPCOpt: rpcOptConfig{
DebugEnabled: false,
},
BLSKeys: blsConfig{ BLSKeys: blsConfig{
KeyDir: "./.hmy/blskeys", KeyDir: "./.hmy/blskeys",
KeyFiles: []string{}, KeyFiles: []string{},

@ -59,6 +59,10 @@ var (
wsPortFlag, wsPortFlag,
} }
rpcOptFlags = []cli.Flag{
rpcDebugEnabledFlag,
}
blsFlags = append(newBLSFlags, legacyBLSFlags...) blsFlags = append(newBLSFlags, legacyBLSFlags...)
newBLSFlags = []cli.Flag{ newBLSFlags = []cli.Flag{
@ -236,6 +240,7 @@ func getRootFlags() []cli.Flag {
flags = append(flags, p2pFlags...) flags = append(flags, p2pFlags...)
flags = append(flags, httpFlags...) flags = append(flags, httpFlags...)
flags = append(flags, wsFlags...) flags = append(flags, wsFlags...)
flags = append(flags, rpcOptFlags...)
flags = append(flags, blsFlags...) flags = append(flags, blsFlags...)
flags = append(flags, consensusFlags...) flags = append(flags, consensusFlags...)
flags = append(flags, txPoolFlags...) 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 // bls flags
var ( var (
blsDirFlag = cli.StringFlag{ blsDirFlag = cli.StringFlag{

@ -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) { func TestBLSFlags(t *testing.T) {
tests := []struct { tests := []struct {
args []string args []string

@ -191,6 +191,7 @@ func applyRootFlags(cmd *cobra.Command, config *harmonyConfig) {
applyP2PFlags(cmd, config) applyP2PFlags(cmd, config)
applyHTTPFlags(cmd, config) applyHTTPFlags(cmd, config)
applyWSFlags(cmd, config) applyWSFlags(cmd, config)
applyRPCOptFlags(cmd, config)
applyBLSFlags(cmd, config) applyBLSFlags(cmd, config)
applyConsensusFlags(cmd, config) applyConsensusFlags(cmd, config)
applyTxPoolFlags(cmd, config) applyTxPoolFlags(cmd, config)
@ -312,6 +313,7 @@ func setupNodeAndRun(hc harmonyConfig) {
WSEnabled: hc.WS.Enabled, WSEnabled: hc.WS.Enabled,
WSIp: hc.WS.IP, WSIp: hc.WS.IP,
WSPort: hc.WS.Port, WSPort: hc.WS.Port,
DebugEnabled: hc.RPCOpt.DebugEnabled,
} }
if nodeConfig.ShardID != shard.BeaconChainShardID { if nodeConfig.ShardID != shard.BeaconChainShardID {
utils.Logger().Info(). utils.Logger().Info().

@ -92,7 +92,7 @@ func (consensus *Consensus) onAnnounceSanityChecks(recvMsg *FBFTMessage) bool {
"[OnAnnounce] Already in ViewChanging mode, conflicing announce, doing noop", "[OnAnnounce] Already in ViewChanging mode, conflicing announce, doing noop",
) )
} else { } else {
consensus.startViewChange(consensus.GetCurBlockViewID() + 1) consensus.startViewChange()
} }
} }
consensus.getLogger().Debug(). consensus.getLogger().Debug().

@ -81,7 +81,7 @@ func (consensus *Consensus) UpdatePublicKeys(pubKeys []bls_cosi.PublicKeyWrapper
consensus.Decider.UpdateParticipants(pubKeys) consensus.Decider.UpdateParticipants(pubKeys)
utils.Logger().Info().Msg("My Committee updated") utils.Logger().Info().Msg("My Committee updated")
for i := range pubKeys { for i := range pubKeys {
utils.Logger().Debug(). utils.Logger().Info().
Int("index", i). Int("index", i).
Str("BLSPubKey", pubKeys[i].Bytes.Hex()). Str("BLSPubKey", pubKeys[i].Bytes.Hex()).
Msg("Member") Msg("Member")
@ -92,6 +92,9 @@ func (consensus *Consensus) UpdatePublicKeys(pubKeys []bls_cosi.PublicKeyWrapper
consensus.LeaderPubKey = &allKeys[0] consensus.LeaderPubKey = &allKeys[0]
utils.Logger().Info(). utils.Logger().Info().
Str("info", consensus.LeaderPubKey.Bytes.Hex()).Msg("My Leader") Str("info", consensus.LeaderPubKey.Bytes.Hex()).Msg("My Leader")
} else {
utils.Logger().Error().
Msg("[UpdatePublicKeys] Participants is empty")
} }
consensus.pubKeyLock.Unlock() consensus.pubKeyLock.Unlock()
// reset states after update public keys // reset states after update public keys
@ -551,6 +554,9 @@ func (consensus *Consensus) selfCommit(payload []byte) error {
consensus.mutex.Lock() consensus.mutex.Lock()
defer consensus.mutex.Unlock() 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[:]) copy(consensus.blockHash[:], blockHash[:])
consensus.switchPhase("selfCommit", FBFTCommit) consensus.switchPhase("selfCommit", FBFTCommit)
consensus.aggregatedPrepareSig = aggSig consensus.aggregatedPrepareSig = aggSig

@ -142,7 +142,6 @@ func (consensus *Consensus) finalCommit() {
return return
} }
// if leader success finalize the block, send committed message to validators // 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 // 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 // needs to send the committed message immediately so the next leader can
@ -272,13 +271,11 @@ func (consensus *Consensus) Start(
} }
if k != timeoutViewChange { if k != timeoutViewChange {
consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops Consensus Timeout!!!") consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops Consensus Timeout!!!")
viewID := consensus.GetCurBlockViewID() consensus.startViewChange()
consensus.startViewChange(viewID + 1)
break break
} else { } else {
consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops View Change Timeout!!!") consensus.getLogger().Warn().Msg("[ConsensusMainLoop] Ops View Change Timeout!!!")
viewID := consensus.GetViewChangingID() consensus.startViewChange()
consensus.startViewChange(viewID + 1)
break break
} }
} }

@ -2,7 +2,6 @@ package quorum
import ( import (
"fmt" "fmt"
"github.com/harmony-one/harmony/internal/configs/sharding"
"math/big" "math/big"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
@ -11,6 +10,7 @@ import (
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/votepower" "github.com/harmony-one/harmony/consensus/votepower"
bls_cosi "github.com/harmony-one/harmony/crypto/bls" 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/multibls"
"github.com/harmony-one/harmony/numeric" "github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard" "github.com/harmony-one/harmony/shard"
@ -216,7 +216,29 @@ func (s *cIdentities) NthNext(pubKey *bls.PublicKeyWrapper, next int) (bool, *bl
if idx != -1 { if idx != -1 {
found = true 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] return found, &s.publicKeys[idx]
} }

@ -1,7 +1,6 @@
package consensus package consensus
import ( import (
"github.com/harmony-one/harmony/shard"
"math/big" "math/big"
"sync" "sync"
"time" "time"
@ -14,6 +13,7 @@ import (
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p" "github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/shard"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -219,21 +219,20 @@ func createTimeout() map[TimeoutType]*utils.Timeout {
return timeouts return timeouts
} }
// startViewChange send a new view change // startViewChange start the view change process
// the viewID is the current viewID func (consensus *Consensus) startViewChange() {
func (consensus *Consensus) startViewChange(viewID uint64) {
if consensus.disableViewChange { if consensus.disableViewChange {
return return
} }
consensus.consensusTimeout[timeoutConsensus].Stop() consensus.consensusTimeout[timeoutConsensus].Stop()
consensus.consensusTimeout[timeoutBootstrap].Stop() consensus.consensusTimeout[timeoutBootstrap].Stop()
consensus.current.SetMode(ViewChanging) consensus.current.SetMode(ViewChanging)
consensus.SetViewChangingID(viewID) nextViewID, duration := consensus.getNextViewID()
consensus.LeaderPubKey = consensus.getNextLeaderKey(viewID) consensus.SetViewChangingID(nextViewID)
consensus.LeaderPubKey = consensus.getNextLeaderKey(nextViewID)
duration := consensus.current.GetViewChangeDuraion()
consensus.getLogger().Warn(). consensus.getLogger().Warn().
Uint64("viewID", viewID). Uint64("nextViewID", nextViewID).
Uint64("viewChangingID", consensus.GetViewChangingID()). Uint64("viewChangingID", consensus.GetViewChangingID()).
Dur("timeoutDuration", duration). Dur("timeoutDuration", duration).
Str("NextLeader", consensus.LeaderPubKey.Bytes.Hex()). Str("NextLeader", consensus.LeaderPubKey.Bytes.Hex()).
@ -243,15 +242,15 @@ func (consensus *Consensus) startViewChange(viewID uint64) {
defer consensus.consensusTimeout[timeoutViewChange].Start() defer consensus.consensusTimeout[timeoutViewChange].Start()
// update the dictionary key if the viewID is first time received // 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 // init my own payload
if err := consensus.vc.InitPayload( if err := consensus.vc.InitPayload(
consensus.FBFTLog, consensus.FBFTLog,
viewID, nextViewID,
consensus.blockNum, consensus.blockNum,
consensus.priKey); err != nil { 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 // for view change, send separate view change per public key
@ -270,19 +269,27 @@ func (consensus *Consensus) startViewChange(viewID uint64) {
true, true,
); err != nil { ); err != nil {
consensus.getLogger().Err(err). 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 // 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() consensus.mutex.Lock()
defer consensus.mutex.Unlock() defer consensus.mutex.Unlock()
if !consensus.IsViewChangingMode() {
return errors.New("not in view changing mode anymore")
}
msgToSend := consensus.constructNewViewMessage( msgToSend := consensus.constructNewViewMessage(
viewID, newLeaderPriKey, viewID, newLeaderPriKey,
) )
if msgToSend == nil {
return errors.New("failed to construct NewView message")
}
if err := consensus.msgSender.SendWithRetry( if err := consensus.msgSender.SendWithRetry(
consensus.blockNum, consensus.blockNum,
msg_pb.MessageType_NEWVIEW, msg_pb.MessageType_NEWVIEW,
@ -298,6 +305,8 @@ func (consensus *Consensus) startNewView(viewID uint64, newLeaderPriKey *bls.Pri
Hex("M1Payload", consensus.vc.GetM1Payload()). Hex("M1Payload", consensus.vc.GetM1Payload()).
Msg("[startNewView] Sent NewView Messge") Msg("[startNewView] Sent NewView Messge")
consensus.msgSender.StopRetry(msg_pb.MessageType_VIEWCHANGE)
consensus.current.SetMode(Normal) consensus.current.SetMode(Normal)
consensus.consensusTimeout[timeoutViewChange].Stop() consensus.consensusTimeout[timeoutViewChange].Stop()
consensus.SetViewIDs(viewID) consensus.SetViewIDs(viewID)
@ -309,7 +318,10 @@ func (consensus *Consensus) startNewView(viewID uint64, newLeaderPriKey *bls.Pri
Str("myKey", newLeaderPriKey.Pub.Bytes.Hex()). Str("myKey", newLeaderPriKey.Pub.Bytes.Hex()).
Msg("[startNewView] viewChange stopped. I am the New Leader") Msg("[startNewView] viewChange stopped. I am the New Leader")
// TODO: consider make ResetState unified and only called in one place like finalizeCommit()
if reset {
consensus.ResetState() consensus.ResetState()
}
consensus.LeaderPubKey = newLeaderPriKey.Pub consensus.LeaderPubKey = newLeaderPriKey.Pub
return nil return nil
@ -381,7 +393,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
// no previous prepared message, go straight to normal mode // no previous prepared message, go straight to normal mode
// and start proposing new block // and start proposing new block
if consensus.vc.IsM1PayloadEmpty() { 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") consensus.getLogger().Error().Err(err).Msg("[onViewChange] startNewView failed")
return return
} }
@ -397,7 +409,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
consensus.getLogger().Error().Err(err).Msg("[onViewChange] self commit failed") consensus.getLogger().Error().Err(err).Msg("[onViewChange] self commit failed")
return 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") consensus.getLogger().Error().Err(err).Msg("[onViewChange] startNewView failed")
return return
} }
@ -508,6 +520,8 @@ func (consensus *Consensus) onNewView(msg *msg_pb.Message) {
consensus.LeaderPubKey = senderKey consensus.LeaderPubKey = senderKey
consensus.ResetViewChangeState() consensus.ResetViewChangeState()
consensus.msgSender.StopRetry(msg_pb.MessageType_VIEWCHANGE)
// NewView message is verified, change state to normal consensus // NewView message is verified, change state to normal consensus
if preparedBlock != nil { if preparedBlock != nil {
consensus.sendCommitMessages(preparedBlock) consensus.sendCommitMessages(preparedBlock)

@ -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.Payload, vcMsg.PreparedBlock = consensus.vc.GetPreparedBlock(consensus.FBFTLog, consensus.blockHash)
vcMsg.M2Aggsigs, vcMsg.M2Bitmap = consensus.vc.GetM2Bitmap(viewID) vcMsg.M2Aggsigs, vcMsg.M2Bitmap = consensus.vc.GetM2Bitmap(viewID)
vcMsg.M3Aggsigs, vcMsg.M3Bitmap = consensus.vc.GetM3Bitmap(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) marshaledMessage, err := consensus.signAndMarshalConsensusMessage(message, priKey.Pri)
if err != nil { if err != nil {

@ -87,7 +87,7 @@ func TestGetNextLeaderKeyShouldFailForStandardGeneratedConsensus(t *testing.T) {
// The below results in: "panic: runtime error: integer divide by zero" // 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 // 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) { func TestGetNextLeaderKeyShouldSucceed(t *testing.T) {
@ -115,7 +115,7 @@ func TestGetNextLeaderKeyShouldSucceed(t *testing.T) {
assert.Equal(t, keyCount, consensus.Decider.ParticipantsCount()) assert.Equal(t, keyCount, consensus.Decider.ParticipantsCount())
consensus.LeaderPubKey = &wrappedBLSKeys[0] consensus.LeaderPubKey = &wrappedBLSKeys[0]
nextKey := consensus.GetNextLeaderKey(uint64(1)) nextKey := consensus.getNextLeaderKey(uint64(1))
assert.Equal(t, nextKey, &wrappedBLSKeys[1]) assert.Equal(t, nextKey, &wrappedBLSKeys[1])
} }

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/core" "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/state"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto/bls" "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/params"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard" "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" "github.com/pkg/errors"
) )
@ -75,15 +78,10 @@ func (hmy *Harmony) GetBlockSigners(
// DetailedBlockSignerInfo contains all of the block singing information // DetailedBlockSignerInfo contains all of the block singing information
type DetailedBlockSignerInfo struct { type DetailedBlockSignerInfo struct {
// Signers is a map of addresses in the Signers for the block to // Signers are all the signers for the block
// all of the serialized BLS keys that signed said block. Signers shard.SlotList
Signers map[common.Address][]bls.SerializedPublicKey
// Committee when the block was signed. // Committee when the block was signed.
Committee shard.SlotList 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 BlockHash common.Hash
} }
@ -91,31 +89,97 @@ type DetailedBlockSignerInfo struct {
func (hmy *Harmony) GetDetailedBlockSignerInfo( func (hmy *Harmony) GetDetailedBlockSignerInfo(
ctx context.Context, blk *types.Block, ctx context.Context, blk *types.Block,
) (*DetailedBlockSignerInfo, error) { ) (*DetailedBlockSignerInfo, error) {
slotList, mask, err := hmy.GetBlockSigners( parentBlk, err := hmy.BlockByNumber(ctx, rpc.BlockNumber(blk.NumberU64()-1))
ctx, rpc.BlockNumber(blk.Number().Uint64()), 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 { if err != nil {
return nil, err 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) // Report tx fees of the coinbase (== leader)
sigInfos := map[common.Address][]bls.SerializedPublicKey{} receipts, err := hmy.GetReceipts(ctx, blk.Hash())
for _, slot := range slotList { if err != nil {
if _, ok := sigInfos[slot.EcdsaAddress]; !ok { return nil, err
sigInfos[slot.EcdsaAddress] = []bls.SerializedPublicKey{}
} }
if ok, err := mask.KeyEnabled(slot.BLSPublicKey); ok && err == nil { txFees := big.NewInt(0)
sigInfos[slot.EcdsaAddress] = append(sigInfos[slot.EcdsaAddress], slot.BLSPublicKey) for _, tx := range blk.Transactions() {
totalSigners++ 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 len(receipts) <= int(receiptIndex) {
return nil, fmt.Errorf("invalid receipt indext %v (>= num receipts: %v) for tx: %v",
receiptIndex, len(receipts), tx.Hash().String())
} }
return &DetailedBlockSignerInfo{ txFee := new(big.Int).Mul(tx.GasPrice(), big.NewInt(int64(receipts[receiptIndex].GasUsed)))
Signers: sigInfos, txFees = new(big.Int).Add(txFee, txFees)
Committee: slotList, }
TotalKeysSigned: totalSigners, for _, stx := range blk.StakingTransactions() {
Mask: mask, dbsTx, _, _, receiptIndex := rawdb.ReadStakingTransaction(hmy.ChainDb(), stx.Hash())
BlockHash: blk.Hash(), if dbsTx == nil {
}, 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 .. // GetLatestChainHeaders ..

@ -32,6 +32,7 @@ const (
BloomBitsBlocks uint64 = 4096 BloomBitsBlocks uint64 = 4096
leaderCacheSize = 250 // Approx number of BLS keys in committee leaderCacheSize = 250 // Approx number of BLS keys in committee
undelegationPayoutsCacheSize = 500 // max number of epochs to store in cache 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 totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same
) )
@ -67,6 +68,8 @@ type Harmony struct {
leaderCache *lru.Cache leaderCache *lru.Cache
// undelegationPayoutsCache to save on recomputation every epoch // undelegationPayoutsCache to save on recomputation every epoch
undelegationPayoutsCache *lru.Cache 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 to save on recomputation for `totalStakeCacheDuration` blocks.
totalStakeCache *totalStakeCache totalStakeCache *totalStakeCache
} }
@ -111,6 +114,7 @@ func New(
chainDb := nodeAPI.Blockchain().ChainDB() chainDb := nodeAPI.Blockchain().ChainDB()
leaderCache, _ := lru.New(leaderCacheSize) leaderCache, _ := lru.New(leaderCacheSize)
undelegationPayoutsCache, _ := lru.New(undelegationPayoutsCacheSize) undelegationPayoutsCache, _ := lru.New(undelegationPayoutsCacheSize)
preStakingBlockRewardsCache, _ := lru.New(preStakingBlockRewardsCacheSize)
totalStakeCache := newTotalStakeCache(totalStakeCacheDuration) totalStakeCache := newTotalStakeCache(totalStakeCacheDuration)
bloomIndexer := NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms) bloomIndexer := NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms)
bloomIndexer.Start(nodeAPI.Blockchain()) bloomIndexer.Start(nodeAPI.Blockchain())
@ -130,6 +134,7 @@ func New(
leaderCache: leaderCache, leaderCache: leaderCache,
totalStakeCache: totalStakeCache, totalStakeCache: totalStakeCache,
undelegationPayoutsCache: undelegationPayoutsCache, undelegationPayoutsCache: undelegationPayoutsCache,
preStakingBlockRewardsCache: preStakingBlockRewardsCache,
} }
} }

@ -103,6 +103,8 @@ type RPCServerConfig struct {
WSEnabled bool WSEnabled bool
WSIp string WSIp string
WSPort int WSPort int
DebugEnabled bool
} }
// RosettaServerConfig is the config for the rosetta server // RosettaServerConfig is the config for the rosetta server

@ -155,31 +155,23 @@ func (s *BlockAPI) specialGenesisBlockTransaction(
} }
// getPreStakingRewardTransactionIdentifiers is only used for the /block endpoint // getPreStakingRewardTransactionIdentifiers is only used for the /block endpoint
// rewards for signing block n is paid out on block n+1
func (s *BlockAPI) getPreStakingRewardTransactionIdentifiers( func (s *BlockAPI) getPreStakingRewardTransactionIdentifiers(
ctx context.Context, currBlock *hmytypes.Block, ctx context.Context, currBlock *hmytypes.Block,
) ([]*types.TransactionIdentifier, *types.Error) { ) ([]*types.TransactionIdentifier, *types.Error) {
if currBlock.Number().Cmp(big.NewInt(1)) != 1 { if currBlock.Number().Cmp(big.NewInt(1)) != 1 {
return nil, nil return nil, nil
} }
blockNumToBeRewarded := currBlock.Number().Uint64() - 1 rewards, err := s.hmy.GetPreStakingBlockRewards(ctx, currBlock)
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)
if err != nil { if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{ return nil, common.NewError(common.CatchAllError, map[string]interface{}{
"message": err.Error(), "message": err.Error(),
}) })
} }
txIDs := []*types.TransactionIdentifier{} txIDs := []*types.TransactionIdentifier{}
for acc, signedBlsKeys := range blockSigInfo.Signers { for addr := range rewards {
if len(signedBlsKeys) > 0 { txIDs = append(txIDs, getSpecialCaseTransactionIdentifier(
txIDs = append(txIDs, getSpecialCaseTransactionIdentifier(currBlock.Hash(), acc, SpecialPreStakingRewardTxID)) currBlock.Hash(), addr, SpecialPreStakingRewardTxID,
} ))
} }
return txIDs, nil return txIDs, nil
} }
@ -221,13 +213,6 @@ func (s *BlockAPI) preStakingRewardBlockTransaction(
if rosettaError != nil { if rosettaError != nil {
return nil, rosettaError 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() { if blkHash.String() != blk.Hash().String() {
return nil, common.NewError(common.SanityCheckError, map[string]interface{}{ return nil, common.NewError(common.SanityCheckError, map[string]interface{}{
"message": fmt.Sprintf( "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 { if err != nil {
return nil, common.NewError(common.CatchAllError, map[string]interface{}{ return nil, common.NewError(common.CatchAllError, map[string]interface{}{
"message": err.Error(), "message": err.Error(),
}) })
} }
transactions, rosettaError := FormatPreStakingRewardTransaction(txID, blockSignerInfo, address) transactions, rosettaError := FormatPreStakingRewardTransaction(txID, rewards, address)
if rosettaError != nil { if rosettaError != nil {
return nil, rosettaError return nil, rosettaError
} }

@ -12,7 +12,6 @@ import (
"github.com/harmony-one/harmony/hmy" "github.com/harmony-one/harmony/hmy"
internalCommon "github.com/harmony-one/harmony/internal/common" internalCommon "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/rosetta/common" "github.com/harmony-one/harmony/rosetta/common"
stakingNetwork "github.com/harmony-one/harmony/staking/network"
stakingTypes "github.com/harmony-one/harmony/staking/types" stakingTypes "github.com/harmony-one/harmony/staking/types"
) )
@ -176,45 +175,19 @@ func FormatGenesisTransaction(
// FormatPreStakingRewardTransaction for block rewards in pre-staking era for a given Bech-32 address. // FormatPreStakingRewardTransaction for block rewards in pre-staking era for a given Bech-32 address.
func FormatPreStakingRewardTransaction( func FormatPreStakingRewardTransaction(
txID *types.TransactionIdentifier, blockSigInfo *hmy.DetailedBlockSignerInfo, address ethcommon.Address, txID *types.TransactionIdentifier, rewards hmy.PreStakingBlockRewards, address ethcommon.Address,
) (*types.Transaction, *types.Error) { ) (*types.Transaction, *types.Error) {
signatures, ok := blockSigInfo.Signers[address]
if !ok || len(signatures) == 0 {
return nil, &common.TransactionNotFoundError
}
accID, rosettaError := newAccountIdentifier(address) accID, rosettaError := newAccountIdentifier(address)
if rosettaError != nil { if rosettaError != nil {
return nil, rosettaError return nil, rosettaError
} }
value, ok := rewards[address]
// Calculate rewards exactly like `AccumulateRewardsAndCountSigs` but short circuit when possible. if !ok {
// WARNING: must do calculation in the order of the committee to get accurate values. return nil, common.NewError(common.TransactionNotFoundError, map[string]interface{}{
i := 0 "message": fmt.Sprintf("%v does not have any rewards for block",
last := big.NewInt(0) internalCommon.MustAddressToBech32(address)),
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
}
}
return &types.Transaction{ return &types.Transaction{
TransactionIdentifier: txID, TransactionIdentifier: txID,
@ -227,7 +200,7 @@ func FormatPreStakingRewardTransaction(
Status: common.SuccessOperationStatus.Status, Status: common.SuccessOperationStatus.Status,
Account: accID, Account: accID,
Amount: &types.Amount{ Amount: &types.Amount{
Value: rewardsForThisBlock.String(), Value: value.String(),
Currency: &common.NativeCurrency, Currency: &common.NativeCurrency,
}, },
}, },

@ -12,12 +12,9 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
hmytypes "github.com/harmony-one/harmony/core/types" 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/hmy"
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/rosetta/common" "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" stakingTypes "github.com/harmony-one/harmony/staking/types"
"github.com/harmony-one/harmony/test/helpers" "github.com/harmony-one/harmony/test/helpers"
) )
@ -208,23 +205,12 @@ func TestFormatPreStakingRewardTransactionSuccess(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
testBlockSigInfo := &hmy.DetailedBlockSignerInfo{ testBlkHash := ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238")
Signers: map[ethcommon.Address][]bls.SerializedPublicKey{ testRewards := hmy.PreStakingBlockRewards{
testAddr: { // Only care about length for this test testAddr: big.NewInt(1),
bls.SerializedPublicKey{},
bls.SerializedPublicKey{},
},
},
Committee: shard.SlotList{
{
EcdsaAddress: testAddr,
},
},
TotalKeysSigned: 150,
BlockHash: ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238"),
} }
refTxID := getSpecialCaseTransactionIdentifier(testBlockSigInfo.BlockHash, testAddr, SpecialPreStakingRewardTxID) refTxID := getSpecialCaseTransactionIdentifier(testBlkHash, testAddr, SpecialPreStakingRewardTxID)
tx, rosettaError := FormatPreStakingRewardTransaction(refTxID, testBlockSigInfo, testAddr) tx, rosettaError := FormatPreStakingRewardTransaction(refTxID, testRewards, testAddr)
if rosettaError != nil { if rosettaError != nil {
t.Fatal(rosettaError) t.Fatal(rosettaError)
} }
@ -247,39 +233,6 @@ func TestFormatPreStakingRewardTransactionSuccess(t *testing.T) {
if tx.Operations[0].Status != common.SuccessOperationStatus.Status { if tx.Operations[0].Status != common.SuccessOperationStatus.Status {
t.Error("expected successful operation 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) { func TestFormatPreStakingRewardTransactionFail(t *testing.T) {
@ -288,42 +241,16 @@ func TestFormatPreStakingRewardTransactionFail(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
testBlockSigInfo := &hmy.DetailedBlockSignerInfo{ testBlkHash := ethcommon.HexToHash("0x1a06b0378d63bf589282c032f0c85b32827e3a2317c2f992f45d8f07d0caa238")
Signers: map[ethcommon.Address][]bls.SerializedPublicKey{ testRewards := hmy.PreStakingBlockRewards{
testAddr: {}, FormatDefaultSenderAddress: big.NewInt(1),
},
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"),
} }
_, rosettaError = FormatPreStakingRewardTransaction(testTxID, testBlockSigInfo, testAddr) testTxID := getSpecialCaseTransactionIdentifier(testBlkHash, testAddr, SpecialPreStakingRewardTxID)
_, rosettaError := FormatPreStakingRewardTransaction(testTxID, testRewards, testAddr)
if rosettaError == nil { if rosettaError == nil {
t.Fatal("expected rosetta error") t.Fatal("expected rosetta error")
} }
if !reflect.DeepEqual(&common.TransactionNotFoundError, rosettaError) { if common.TransactionNotFoundError.Code != rosettaError.Code {
t.Error("expected transaction not found error") t.Error("expected transaction not found error")
} }
} }

@ -64,7 +64,7 @@ func (n Version) Namespace() string {
// StartServers starts the http & ws servers // StartServers starts the http & ws servers
func StartServers(hmy *hmy.Harmony, apis []rpc.API, config nodeconfig.RPCServerConfig) error { 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 { if config.HTTPEnabled {
httpEndpoint = fmt.Sprintf("%v:%v", config.HTTPIp, config.HTTPPort) 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 // getAPIs returns all the API methods for the RPC interface
func getAPIs(hmy *hmy.Harmony) []rpc.API { func getAPIs(hmy *hmy.Harmony, debugEnable bool) []rpc.API {
return []rpc.API{ publicAPIs := []rpc.API{
// Public methods // Public methods
NewPublicHarmonyAPI(hmy, V1), NewPublicHarmonyAPI(hmy, V1),
NewPublicHarmonyAPI(hmy, V2), NewPublicHarmonyAPI(hmy, V2),
@ -130,13 +130,20 @@ func getAPIs(hmy *hmy.Harmony) []rpc.API {
NewPublicPoolAPI(hmy, V2), NewPublicPoolAPI(hmy, V2),
NewPublicStakingAPI(hmy, V1), NewPublicStakingAPI(hmy, V1),
NewPublicStakingAPI(hmy, V2), NewPublicStakingAPI(hmy, V2),
// Private methods
NewPrivateDebugAPI(hmy, V1),
NewPrivateDebugAPI(hmy, V2),
// Legacy methods (subject to removal) // Legacy methods (subject to removal)
v1.NewPublicLegacyAPI(hmy), v1.NewPublicLegacyAPI(hmy),
v2.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) { func startHTTP(apis []rpc.API) (err error) {

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
version="v2 20200811.1" version="v2 20201023.0"
unset -v progname unset -v progname
progname="${0##*/}" progname="${0##*/}"
@ -56,7 +56,7 @@ function valid_ip()
function myip() { function myip() {
# get ipv4 address only, right now only support ipv4 addresses # 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 if valid_ip $PUB_IP; then
msg "public IP address autodetected: $PUB_IP" msg "public IP address autodetected: $PUB_IP"
else else

@ -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

@ -2,4 +2,6 @@
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
bash "$DIR/kill_node.sh" bash "$DIR/kill_node.sh"
docker pull harmonyone/localnet-test docker pull harmonyone/localnet-test
docker run -it --expose 9000-9999 -v "$DIR/../:/go/src/github.com/harmony-one/harmony" harmonyone/localnet-test 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

@ -87,6 +87,9 @@ function launch_localnet() {
echo "skip empty node" echo "skip empty node"
continue continue
fi 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 # Setup BLS key for i-th localnet node
if [[ ! -e "$bls_key" ]]; then 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) -N network network type (default: $NETWORK)
-B don't build the binary -B don't build the binary
-v verbosity in log (default: $VERBOSE) -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. This script will build all the binaries and start harmony and based on the configuration file.
@ -159,8 +163,9 @@ DRYRUN=
NETWORK=localnet NETWORK=localnet
VERBOSE=false VERBOSE=false
NOBUILD=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 case ${option} in
h) usage ;; h) usage ;;
D) DURATION=$OPTARG ;; D) DURATION=$OPTARG ;;
@ -170,6 +175,7 @@ while getopts "hD:m:s:nBN:v" option; do
B) NOBUILD=true ;; B) NOBUILD=true ;;
N) NETWORK=$OPTARG ;; N) NETWORK=$OPTARG ;;
v) VERBOSE=true ;; v) VERBOSE=true ;;
e) EXPOSEAPIS=true ;;
*) usage ;; *) usage ;;
esac esac
done done

@ -1,4 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
docker pull harmonyone/localnet-test 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

@ -8,7 +8,9 @@ case ${1} in
run) run)
docker pull harmonyone/localnet-test docker pull harmonyone/localnet-test
docker rm "$docker_name" 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) attach)
docker exec -it "$docker_name" /bin/bash docker exec -it "$docker_name" /bin/bash

@ -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
Loading…
Cancel
Save