aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-mgr.go84
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/blockchain.go76
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go472
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go17
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/consensus.go162
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/watch-cat.go148
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go6
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/utils/round-event.go70
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/utils/utils.go6
-rw-r--r--vendor/github.com/onrik/ethrpc/LICENSE21
-rw-r--r--vendor/github.com/onrik/ethrpc/README.md103
-rw-r--r--vendor/github.com/onrik/ethrpc/ethrpc.go514
-rw-r--r--vendor/github.com/onrik/ethrpc/go.mod1
-rw-r--r--vendor/github.com/onrik/ethrpc/helpers.go40
-rw-r--r--vendor/github.com/onrik/ethrpc/interface.go50
-rw-r--r--vendor/github.com/onrik/ethrpc/options.go35
-rw-r--r--vendor/github.com/onrik/ethrpc/types.go322
17 files changed, 1744 insertions, 383 deletions
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-mgr.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-mgr.go
index 7b5effba8..0e39fa52a 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-mgr.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-mgr.go
@@ -90,8 +90,6 @@ func newAgreementMgrConfig(prev agreementMgrConfig, config *types.Config,
type baRoundSetting struct {
notarySet map[types.NodeID]struct{}
- agr *agreement
- recv *consensusBAReceiver
ticker Ticker
crs common.Hash
}
@@ -111,6 +109,7 @@ type agreementMgr struct {
initRound uint64
configs []agreementMgrConfig
baModule *agreement
+ recv *consensusBAReceiver
processedBAResult map[types.Position]struct{}
voteFilter *utils.VoteFilter
waitGroup sync.WaitGroup
@@ -136,15 +135,17 @@ func newAgreementMgr(con *Consensus, initRound uint64,
configs: []agreementMgrConfig{initConfig},
voteFilter: utils.NewVoteFilter(),
}
- recv := &consensusBAReceiver{
- consensus: con,
- restartNotary: make(chan types.Position, 1),
- roundValue: &atomic.Value{},
+ mgr.recv = &consensusBAReceiver{
+ consensus: con,
+ restartNotary: make(chan types.Position, 1),
+ roundValue: &atomic.Value{},
+ changeNotaryHeightValue: &atomic.Value{},
}
- recv.roundValue.Store(uint64(0))
+ mgr.recv.roundValue.Store(uint64(0))
+ mgr.recv.changeNotaryHeightValue.Store(uint64(0))
agr := newAgreement(
mgr.ID,
- recv,
+ mgr.recv,
newLeaderSelector(genValidLeader(mgr), mgr.logger),
mgr.signer,
mgr.logger)
@@ -156,7 +157,7 @@ func newAgreementMgr(con *Consensus, initRound uint64,
agr.notarySet = nodes.GetSubSet(
int(initConfig.notarySetSize), types.NewNotarySetTarget(initConfig.crs))
// Hacky way to make agreement module self contained.
- recv.agreementModule = agr
+ mgr.recv.agreementModule = agr
mgr.baModule = agr
return
}
@@ -188,15 +189,43 @@ func (mgr *agreementMgr) config(round uint64) *agreementMgrConfig {
return &mgr.configs[roundIndex]
}
-func (mgr *agreementMgr) appendConfig(
- round uint64, config *types.Config, crs common.Hash) (err error) {
+func (mgr *agreementMgr) notifyRoundEvents(evts []utils.RoundEventParam) error {
mgr.lock.Lock()
defer mgr.lock.Unlock()
- if round != uint64(len(mgr.configs))+mgr.initRound {
- return ErrRoundNotIncreasing
+ apply := func(e utils.RoundEventParam) error {
+ if len(mgr.configs) > 0 {
+ lastCfg := mgr.configs[len(mgr.configs)-1]
+ if e.BeginHeight != lastCfg.RoundEndHeight() {
+ return ErrInvalidBlockHeight
+ }
+ if lastCfg.RoundID() == e.Round {
+ mgr.configs[len(mgr.configs)-1].ExtendLength()
+ // It's not an atomic operation to update an atomic value based
+ // on another. However, it's the best way so far to extend
+ // length of round without refactoring.
+ if mgr.recv.round() == e.Round {
+ mgr.recv.changeNotaryHeightValue.Store(
+ mgr.configs[len(mgr.configs)-1].RoundEndHeight())
+ }
+ } else if lastCfg.RoundID()+1 == e.Round {
+ mgr.configs = append(mgr.configs, newAgreementMgrConfig(
+ lastCfg, e.Config, e.CRS))
+ } else {
+ return ErrInvalidRoundID
+ }
+ } else {
+ c := agreementMgrConfig{}
+ c.from(e.Round, e.Config, e.CRS)
+ c.SetRoundBeginHeight(e.BeginHeight)
+ mgr.configs = append(mgr.configs, c)
+ }
+ return nil
+ }
+ for _, e := range evts {
+ if err := apply(e); err != nil {
+ return err
+ }
}
- mgr.configs = append(mgr.configs, newAgreementMgrConfig(
- mgr.configs[len(mgr.configs)-1], config, crs))
return nil
}
@@ -252,7 +281,7 @@ func (mgr *agreementMgr) processAgreementResult(
}
} else if result.Position.Newer(aID) {
mgr.logger.Info("Fast syncing BA", "position", result.Position)
- nodes, err := mgr.cache.GetNodeSet(result.Position.Round)
+ nIDs, err := mgr.cache.GetNotarySet(result.Position.Round)
if err != nil {
return err
}
@@ -261,10 +290,6 @@ func (mgr *agreementMgr) processAgreementResult(
mgr.network.PullBlocks(common.Hashes{result.BlockHash})
mgr.logger.Debug("Calling Governance.CRS", "round", result.Position.Round)
crs := utils.GetCRSWithPanic(mgr.gov, result.Position.Round, mgr.logger)
- nIDs := nodes.GetSubSet(
- int(utils.GetConfigWithPanic(
- mgr.gov, result.Position.Round, mgr.logger).NotarySetSize),
- types.NewNotarySetTarget(crs))
for key := range result.Votes {
if err := mgr.baModule.processVote(&result.Votes[key]); err != nil {
return err
@@ -296,10 +321,7 @@ func (mgr *agreementMgr) runBA(initRound uint64) {
currentRound uint64
nextRound = initRound
curConfig = mgr.config(initRound)
- setting = baRoundSetting{
- agr: mgr.baModule,
- recv: mgr.baModule.data.recv.(*consensusBAReceiver),
- }
+ setting = baRoundSetting{}
tickDuration time.Duration
)
@@ -353,12 +375,12 @@ Loop:
break Loop
default:
}
- setting.recv.isNotary = checkRound()
+ mgr.recv.isNotary = checkRound()
// Run BA for this round.
- setting.recv.roundValue.Store(currentRound)
- setting.recv.changeNotaryHeight = curConfig.RoundEndHeight()
- setting.recv.restartNotary <- types.Position{
- Round: setting.recv.round(),
+ mgr.recv.roundValue.Store(currentRound)
+ mgr.recv.changeNotaryHeightValue.Store(curConfig.RoundEndHeight())
+ mgr.recv.restartNotary <- types.Position{
+ Round: mgr.recv.round(),
Height: math.MaxUint64,
}
mgr.voteFilter = utils.NewVoteFilter()
@@ -373,8 +395,8 @@ Loop:
func (mgr *agreementMgr) baRoutineForOneRound(
setting *baRoundSetting) (err error) {
- agr := setting.agr
- recv := setting.recv
+ agr := mgr.baModule
+ recv := mgr.recv
oldPos := agr.agreementID()
restart := func(restartPos types.Position) (breakLoop bool, err error) {
if !isStop(restartPos) {
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/blockchain.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/blockchain.go
index 19a580b4f..c5a22b628 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/blockchain.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/blockchain.go
@@ -41,7 +41,6 @@ var (
ErrNotFollowTipPosition = errors.New("not follow tip position")
ErrDuplicatedPendingBlock = errors.New("duplicated pending block")
ErrRetrySanityCheckLater = errors.New("retry sanity check later")
- ErrRoundNotIncreasing = errors.New("round not increasing")
ErrRoundNotSwitch = errors.New("round not switch")
ErrIncorrectBlockRandomnessResult = errors.New(
"incorrect block randomness result")
@@ -142,19 +141,8 @@ type blockChain struct {
}
func newBlockChain(nID types.NodeID, dMoment time.Time, initBlock *types.Block,
- initConfig blockChainConfig, app Application, vGetter tsigVerifierGetter,
- signer *utils.Signer, logger common.Logger) *blockChain {
- if initBlock != nil {
- if initConfig.RoundID() != initBlock.Position.Round {
- panic(fmt.Errorf("incompatible config/block %s %d",
- initBlock, initConfig.RoundID()))
- }
- } else {
- if initConfig.RoundID() != 0 {
- panic(fmt.Errorf("genesis config should from round 0 %d",
- initConfig.RoundID()))
- }
- }
+ app Application, vGetter tsigVerifierGetter, signer *utils.Signer,
+ logger common.Logger) *blockChain {
return &blockChain{
ID: nID,
lastConfirmed: initBlock,
@@ -163,23 +151,58 @@ func newBlockChain(nID types.NodeID, dMoment time.Time, initBlock *types.Block,
vGetter: vGetter,
app: app,
logger: logger,
- configs: []blockChainConfig{initConfig},
dMoment: dMoment,
pendingRandomnesses: make(
map[types.Position]*types.BlockRandomnessResult),
}
}
-func (bc *blockChain) appendConfig(round uint64, config *types.Config) error {
- expectedRound := uint64(len(bc.configs))
- if bc.lastConfirmed != nil {
- expectedRound += bc.lastConfirmed.Position.Round
+func (bc *blockChain) notifyRoundEvents(evts []utils.RoundEventParam) error {
+ bc.lock.Lock()
+ defer bc.lock.Unlock()
+ apply := func(e utils.RoundEventParam) error {
+ if len(bc.configs) > 0 {
+ lastCfg := bc.configs[len(bc.configs)-1]
+ if e.BeginHeight != lastCfg.RoundEndHeight() {
+ return ErrInvalidBlockHeight
+ }
+ if lastCfg.RoundID() == e.Round {
+ bc.configs[len(bc.configs)-1].ExtendLength()
+ } else if lastCfg.RoundID()+1 == e.Round {
+ bc.configs = append(bc.configs, newBlockChainConfig(
+ lastCfg, e.Config))
+ } else {
+ return ErrInvalidRoundID
+ }
+ } else {
+ c := blockChainConfig{}
+ c.fromConfig(e.Round, e.Config)
+ c.SetRoundBeginHeight(e.BeginHeight)
+ if bc.lastConfirmed == nil {
+ if c.RoundID() != 0 {
+ panic(fmt.Errorf("genesis config should from round 0 %d",
+ c.RoundID()))
+ }
+ } else {
+ if c.RoundID() != bc.lastConfirmed.Position.Round {
+ panic(fmt.Errorf("incompatible config/block %s %d",
+ bc.lastConfirmed, c.RoundID()))
+ }
+ if !c.Contains(bc.lastConfirmed.Position.Height) {
+ panic(fmt.Errorf(
+ "unmatched round-event with block %s %d %d %d",
+ bc.lastConfirmed, e.Round, e.Reset, e.BeginHeight))
+ }
+ }
+ bc.configs = append(bc.configs, c)
+ }
+ return nil
}
- if round != expectedRound {
- return ErrRoundNotIncreasing
+ for _, e := range evts {
+ if err := apply(e); err != nil {
+ return err
+ }
}
- bc.configs = append(bc.configs, newBlockChainConfig(
- bc.configs[len(bc.configs)-1], config))
return nil
}
@@ -558,8 +581,11 @@ func (bc *blockChain) prepareBlock(position types.Position,
}
if tipConfig.IsLastBlock(tip) {
if tip.Position.Round+1 != position.Round {
- b, err = nil, ErrRoundNotSwitch
- return
+ if !empty {
+ b, err = nil, ErrRoundNotSwitch
+ return
+ }
+ b.Position.Round = tip.Position.Round + 1
}
} else {
if tip.Position.Round != position.Round {
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
index 4201cbcc2..8529e4031 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
@@ -56,18 +56,22 @@ var (
// consensusBAReceiver implements agreementReceiver.
type consensusBAReceiver struct {
// TODO(mission): consensus would be replaced by blockChain and network.
- consensus *Consensus
- agreementModule *agreement
- changeNotaryHeight uint64
- roundValue *atomic.Value
- isNotary bool
- restartNotary chan types.Position
+ consensus *Consensus
+ agreementModule *agreement
+ changeNotaryHeightValue *atomic.Value
+ roundValue *atomic.Value
+ isNotary bool
+ restartNotary chan types.Position
}
func (recv *consensusBAReceiver) round() uint64 {
return recv.roundValue.Load().(uint64)
}
+func (recv *consensusBAReceiver) changeNotaryHeight() uint64 {
+ return recv.changeNotaryHeightValue.Load().(uint64)
+}
+
func (recv *consensusBAReceiver) ProposeVote(vote *types.Vote) {
if !recv.isNotary {
return
@@ -247,16 +251,17 @@ CleanChannelLoop:
}
}
newPos := block.Position
- if block.Position.Height+1 == recv.changeNotaryHeight {
+ if block.Position.Height+1 == recv.changeNotaryHeight() {
newPos.Round++
recv.roundValue.Store(newPos.Round)
}
currentRound := recv.round()
- if block.Position.Height > recv.changeNotaryHeight &&
+ changeNotaryHeight := recv.changeNotaryHeight()
+ if block.Position.Height > changeNotaryHeight &&
block.Position.Round <= currentRound {
panic(fmt.Errorf(
"round not switch when confirmig: %s, %d, should switch at %d",
- block, currentRound, recv.changeNotaryHeight))
+ block, currentRound, changeNotaryHeight))
}
recv.restartNotary <- newPos
}
@@ -396,11 +401,11 @@ type Consensus struct {
bcModule *blockChain
dMoment time.Time
nodeSetCache *utils.NodeSetCache
- roundForNewConfig uint64
lock sync.RWMutex
ctx context.Context
ctxCancel context.CancelFunc
event *common.Event
+ roundEvent *utils.RoundEvent
logger common.Logger
resetRandomnessTicker chan struct{}
resetDeliveryGuardTicker chan struct{}
@@ -453,6 +458,7 @@ func NewConsensusForSimulation(
func NewConsensusFromSyncer(
initBlock *types.Block,
initRoundBeginHeight uint64,
+ startWithEmpty bool,
dMoment time.Time,
app Application,
gov Governance,
@@ -495,6 +501,23 @@ func NewConsensusFromSyncer(
continue
}
}
+ if startWithEmpty {
+ pos := initBlock.Position
+ pos.Height++
+ block, err := con.bcModule.addEmptyBlock(pos)
+ if err != nil {
+ panic(err)
+ }
+ con.processBlockChan <- block
+ if pos.Round >= DKGDelayRound {
+ rand := &types.AgreementResult{
+ BlockHash: block.Hash,
+ Position: block.Position,
+ IsEmptyBlock: true,
+ }
+ go con.prepareRandomnessResult(rand)
+ }
+ }
return con, nil
}
@@ -522,8 +545,10 @@ func newConsensusForRound(
}
// Get configuration for bootstrap round.
initRound := uint64(0)
+ initBlockHeight := uint64(0)
if initBlock != nil {
initRound = initBlock.Position.Round
+ initBlockHeight = initBlock.Position.Height
}
initConfig := utils.GetConfigWithPanic(gov, initRound, logger)
initCRS := utils.GetCRSWithPanic(gov, initRound, logger)
@@ -548,10 +573,7 @@ func newConsensusForRound(
if usingNonBlocking {
appModule = newNonBlocking(app, debugApp)
}
- bcConfig := blockChainConfig{}
- bcConfig.fromConfig(initRound, initConfig)
- bcConfig.SetRoundBeginHeight(initRoundBeginHeight)
- bcModule := newBlockChain(ID, dMoment, initBlock, bcConfig, appModule,
+ bcModule := newBlockChain(ID, dMoment, initBlock, appModule,
NewTSigVerifierCache(gov, 7), signer, logger)
// Construct Consensus instance.
con := &Consensus{
@@ -576,6 +598,10 @@ func newConsensusForRound(
processBlockChan: make(chan *types.Block, 1024),
}
con.ctx, con.ctxCancel = context.WithCancel(context.Background())
+ if con.roundEvent, err = utils.NewRoundEvent(con.ctx, gov, logger, initRound,
+ initRoundBeginHeight, initBlockHeight, ConfigRoundShift); err != nil {
+ panic(err)
+ }
baConfig := agreementMgrConfig{}
baConfig.from(initRound, initConfig, initCRS)
baConfig.SetRoundBeginHeight(initRoundBeginHeight)
@@ -595,26 +621,139 @@ func newConsensusForRound(
// - the last finalized block
func (con *Consensus) prepare(
initRoundBeginHeight uint64, initBlock *types.Block) (err error) {
+ // Trigger the round validation method for the next round of the first
+ // round.
// The block past from full node should be delivered already or known by
// full node. We don't have to notify it.
initRound := uint64(0)
if initBlock != nil {
initRound = initBlock.Position.Round
}
- // Setup blockChain module.
- con.roundForNewConfig = initRound + 1
- initConfig := utils.GetConfigWithPanic(con.gov, initRound, con.logger)
- initPlusOneCfg := utils.GetConfigWithPanic(con.gov, initRound+1, con.logger)
- if err = con.bcModule.appendConfig(initRound+1, initPlusOneCfg); err != nil {
- return
- }
if initRound == 0 {
if DKGDelayRound == 0 {
panic("not implemented yet")
}
}
- // Register events.
- con.initialRound(initRoundBeginHeight, initRound, initConfig)
+ // Register round event handler to update BA and BC modules.
+ con.roundEvent.Register(func(evts []utils.RoundEventParam) {
+ // Always updates newer configs to the later modules first in the flow.
+ if err := con.bcModule.notifyRoundEvents(evts); err != nil {
+ panic(err)
+ }
+ // The init config is provided to baModule when construction.
+ if evts[len(evts)-1].BeginHeight != initRoundBeginHeight {
+ if err := con.baMgr.notifyRoundEvents(evts); err != nil {
+ panic(err)
+ }
+ }
+ })
+ // Register round event handler to propose new CRS.
+ con.roundEvent.Register(func(evts []utils.RoundEventParam) {
+ // We don't have to propose new CRS during DKG reset, the reset of DKG
+ // would be done by the DKG set in previous round.
+ e := evts[len(evts)-1]
+ if e.Reset != 0 || e.Round < DKGDelayRound {
+ return
+ }
+ if curDkgSet, err := con.nodeSetCache.GetDKGSet(e.Round); err != nil {
+ con.logger.Error("Error getting DKG set when proposing CRS",
+ "round", e.Round,
+ "error", err)
+ } else {
+ if _, exist := curDkgSet[con.ID]; !exist {
+ return
+ }
+ con.event.RegisterHeight(e.NextCRSProposingHeight(), func(uint64) {
+ con.logger.Debug(
+ "Calling Governance.CRS to check if already proposed",
+ "round", e.Round+1)
+ if (con.gov.CRS(e.Round+1) != common.Hash{}) {
+ con.logger.Debug("CRS already proposed", "round", e.Round+1)
+ return
+ }
+ con.runCRS(e.Round, e.CRS)
+ })
+ }
+ })
+ // Touch nodeSetCache for next round.
+ con.roundEvent.Register(func(evts []utils.RoundEventParam) {
+ e := evts[len(evts)-1]
+ if e.Reset != 0 {
+ return
+ }
+ con.event.RegisterHeight(e.NextTouchNodeSetCacheHeight(), func(uint64) {
+ if err := con.nodeSetCache.Touch(e.Round + 1); err != nil {
+ con.logger.Warn("Failed to update nodeSetCache",
+ "round", e.Round+1,
+ "error", err)
+ }
+ })
+ })
+ // checkCRS is a generator of checker to check if CRS for that round is
+ // ready or not.
+ checkCRS := func(round uint64) func() bool {
+ return func() bool {
+ nextCRS := con.gov.CRS(round)
+ if (nextCRS != common.Hash{}) {
+ return true
+ }
+ con.logger.Debug("CRS is not ready yet. Try again later...",
+ "nodeID", con.ID,
+ "round", round)
+ return false
+ }
+ }
+ // Trigger round validation method for next period.
+ con.roundEvent.Register(func(evts []utils.RoundEventParam) {
+ e := evts[len(evts)-1]
+ // Register a routine to trigger round events.
+ con.event.RegisterHeight(e.NextRoundValidationHeight(), func(
+ blockHeight uint64) {
+ con.roundEvent.ValidateNextRound(blockHeight)
+ })
+ // Register a routine to register next DKG.
+ con.event.RegisterHeight(e.NextDKGRegisterHeight(), func(uint64) {
+ nextRound := e.Round + 1
+ if nextRound < DKGDelayRound {
+ con.logger.Info("Skip runDKG for round", "round", nextRound)
+ return
+ }
+ // Normally, gov.CRS would return non-nil. Use this for in case of
+ // unexpected network fluctuation and ensure the robustness.
+ if !checkWithCancel(
+ con.ctx, 500*time.Millisecond, checkCRS(nextRound)) {
+ con.logger.Debug("unable to prepare CRS for DKG set",
+ "round", nextRound)
+ return
+ }
+ nextDkgSet, err := con.nodeSetCache.GetDKGSet(nextRound)
+ if err != nil {
+ con.logger.Error("Error getting DKG set for next round",
+ "round", nextRound,
+ "error", err)
+ return
+ }
+ if _, exist := nextDkgSet[con.ID]; !exist {
+ con.logger.Info("Not selected as DKG set", "round", nextRound)
+ return
+ }
+ con.logger.Info("Selected as DKG set", "round", nextRound)
+ nextConfig := utils.GetConfigWithPanic(con.gov, nextRound,
+ con.logger)
+ con.cfgModule.registerDKG(nextRound, utils.GetDKGThreshold(
+ nextConfig))
+ con.event.RegisterHeight(e.NextDKGPreparationHeight(),
+ func(uint64) {
+ func() {
+ con.dkgReady.L.Lock()
+ defer con.dkgReady.L.Unlock()
+ con.dkgRunning = 0
+ }()
+ con.runDKG(nextRound, nextConfig)
+ })
+ })
+ })
+ con.roundEvent.TriggerInitEvent()
return
}
@@ -686,27 +825,9 @@ func (con *Consensus) runDKG(round uint64, config *types.Config) {
}()
}
-func (con *Consensus) runCRS(round uint64) {
- for {
- con.logger.Debug("Calling Governance.CRS to check if already proposed",
- "round", round+1)
- if (con.gov.CRS(round+1) != common.Hash{}) {
- con.logger.Debug("CRS already proposed", "round", round+1)
- return
- }
- con.logger.Debug("Calling Governance.IsDKGFinal to check if ready to run CRS",
- "round", round)
- if con.cfgModule.isDKGFinal(round) {
- break
- }
- con.logger.Debug("DKG is not ready for running CRS. Retry later...",
- "round", round)
- time.Sleep(500 * time.Millisecond)
- }
+func (con *Consensus) runCRS(round uint64, hash common.Hash) {
// Start running next round CRS.
- con.logger.Debug("Calling Governance.CRS", "round", round)
- psig, err := con.cfgModule.preparePartialSignature(
- round, utils.GetCRSWithPanic(con.gov, round, con.logger))
+ psig, err := con.cfgModule.preparePartialSignature(round, hash)
if err != nil {
con.logger.Error("Failed to prepare partial signature", "error", err)
} else if err = con.signer.SignDKGPartialSignature(psig); err != nil {
@@ -733,136 +854,16 @@ func (con *Consensus) runCRS(round uint64) {
}
}
-func (con *Consensus) initialRound(
- startHeight uint64, round uint64, config *types.Config) {
- select {
- case <-con.ctx.Done():
- return
- default:
- }
- if round >= DKGDelayRound {
- curDkgSet, err := con.nodeSetCache.GetDKGSet(round)
- if err != nil {
- con.logger.Error("Error getting DKG set", "round", round, "error", err)
- curDkgSet = make(map[types.NodeID]struct{})
- }
- // Initiate CRS routine.
- if _, exist := curDkgSet[con.ID]; exist {
- con.event.RegisterHeight(
- startHeight+config.RoundLength/2,
- func(uint64) {
- go func() {
- con.runCRS(round)
- }()
- })
- }
- }
- // checkCRS is a generator of checker to check if CRS for that round is
- // ready or not.
- checkCRS := func(round uint64) func() bool {
- return func() bool {
- nextCRS := con.gov.CRS(round)
- if (nextCRS != common.Hash{}) {
- return true
- }
- con.logger.Debug("CRS is not ready yet. Try again later...",
- "nodeID", con.ID,
- "round", round)
- return false
- }
- }
- // Initiate BA modules.
- con.event.RegisterHeight(startHeight+config.RoundLength/2, func(uint64) {
- go func(nextRound uint64) {
- if !checkWithCancel(
- con.ctx, 500*time.Millisecond, checkCRS(nextRound)) {
- con.logger.Debug("unable to prepare CRS for baMgr",
- "round", nextRound)
- return
- }
- // Notify BA for new round.
- nextConfig := utils.GetConfigWithPanic(
- con.gov, nextRound, con.logger)
- nextCRS := utils.GetCRSWithPanic(
- con.gov, nextRound, con.logger)
- con.logger.Info("appendConfig for baMgr", "round", nextRound)
- if err := con.baMgr.appendConfig(
- nextRound, nextConfig, nextCRS); err != nil {
- panic(err)
- }
- }(round + 1)
- })
- // Initiate DKG for this round.
- con.event.RegisterHeight(startHeight+config.RoundLength/2, func(uint64) {
- go func(nextRound uint64) {
- if nextRound < DKGDelayRound {
- con.logger.Info("Skip runDKG for round", "round", nextRound)
- return
- }
- // Normally, gov.CRS would return non-nil. Use this for in case of
- // unexpected network fluctuation and ensure the robustness.
- if !checkWithCancel(
- con.ctx, 500*time.Millisecond, checkCRS(nextRound)) {
- con.logger.Debug("unable to prepare CRS for DKG set",
- "round", nextRound)
- return
- }
- nextDkgSet, err := con.nodeSetCache.GetDKGSet(nextRound)
- if err != nil {
- con.logger.Error("Error getting DKG set",
- "round", nextRound,
- "error", err)
- return
- }
- if _, exist := nextDkgSet[con.ID]; !exist {
- return
- }
- con.logger.Info("Selected as DKG set", "round", nextRound)
- con.cfgModule.registerDKG(nextRound, utils.GetDKGThreshold(config))
- con.event.RegisterHeight(startHeight+config.RoundLength*2/3,
- func(uint64) {
- func() {
- con.dkgReady.L.Lock()
- defer con.dkgReady.L.Unlock()
- con.dkgRunning = 0
- }()
- nextConfig := utils.GetConfigWithPanic(
- con.gov, nextRound, con.logger)
- con.runDKG(nextRound, nextConfig)
- })
- }(round + 1)
- })
- // Prepare blockChain module for next round and next "initialRound" routine.
- con.event.RegisterHeight(startHeight+config.RoundLength, func(uint64) {
- // Change round.
- // Get configuration for next round.
- nextRound := round + 1
- nextConfig := utils.GetConfigWithPanic(con.gov, nextRound, con.logger)
- con.initialRound(
- startHeight+config.RoundLength, nextRound, nextConfig)
- })
- // Touch nodeSetCache for next round.
- con.event.RegisterHeight(startHeight+config.RoundLength*9/10, func(uint64) {
- go func() {
- // TODO(jimmy): check DKGResetCount and do not touch if nextRound is reset.
- if err := con.nodeSetCache.Touch(round + 1); err != nil {
- con.logger.Warn("Failed to update nodeSetCache",
- "round", round+1, "error", err)
- }
- if _, _, err := con.bcModule.vGetter.UpdateAndGet(round + 1); err != nil {
- con.logger.Warn("Failed to update tsigVerifierCache",
- "round", round+1, "error", err)
- }
- }()
- })
-}
-
// Stop the Consensus core.
func (con *Consensus) Stop() {
con.ctxCancel()
con.baMgr.stop()
con.event.Reset()
con.waitGroup.Wait()
+ if nbApp, ok := con.app.(*nonBlocking); ok {
+ fmt.Println("Stopping nonBlocking App")
+ nbApp.wait()
+ }
}
func (con *Consensus) deliverNetworkMsg() {
@@ -1014,62 +1015,64 @@ func (con *Consensus) ProcessAgreementResult(
con.logger.Debug("Rebroadcast AgreementResult",
"result", rand)
con.network.BroadcastAgreementResult(rand)
+ go con.prepareRandomnessResult(rand)
+ return nil
+}
- go func() {
- dkgSet, err := con.nodeSetCache.GetDKGSet(rand.Position.Round)
- if err != nil {
- con.logger.Error("Failed to get dkg set",
- "round", rand.Position.Round, "error", err)
- return
- }
- if _, exist := dkgSet[con.ID]; !exist {
- return
- }
- psig, err := con.cfgModule.preparePartialSignature(rand.Position.Round, rand.BlockHash)
- if err != nil {
- con.logger.Error("Failed to prepare psig",
- "round", rand.Position.Round,
- "hash", rand.BlockHash.String()[:6],
- "error", err)
- return
- }
- if err = con.signer.SignDKGPartialSignature(psig); err != nil {
- con.logger.Error("Failed to sign psig",
+func (con *Consensus) prepareRandomnessResult(rand *types.AgreementResult) {
+ dkgSet, err := con.nodeSetCache.GetDKGSet(rand.Position.Round)
+ if err != nil {
+ con.logger.Error("Failed to get dkg set",
+ "round", rand.Position.Round, "error", err)
+ return
+ }
+ if _, exist := dkgSet[con.ID]; !exist {
+ return
+ }
+ con.logger.Debug("PrepareRandomness", "round", rand.Position.Round, "hash", rand.BlockHash)
+ psig, err := con.cfgModule.preparePartialSignature(rand.Position.Round, rand.BlockHash)
+ if err != nil {
+ con.logger.Error("Failed to prepare psig",
+ "round", rand.Position.Round,
+ "hash", rand.BlockHash.String()[:6],
+ "error", err)
+ return
+ }
+ if err = con.signer.SignDKGPartialSignature(psig); err != nil {
+ con.logger.Error("Failed to sign psig",
+ "hash", rand.BlockHash.String()[:6],
+ "error", err)
+ return
+ }
+ if err = con.cfgModule.processPartialSignature(psig); err != nil {
+ con.logger.Error("Failed process psig",
+ "hash", rand.BlockHash.String()[:6],
+ "error", err)
+ return
+ }
+ con.logger.Debug("Calling Network.BroadcastDKGPartialSignature",
+ "proposer", psig.ProposerID,
+ "round", psig.Round,
+ "hash", psig.Hash.String()[:6])
+ con.network.BroadcastDKGPartialSignature(psig)
+ tsig, err := con.cfgModule.runTSig(rand.Position.Round, rand.BlockHash)
+ if err != nil {
+ if err != ErrTSigAlreadyRunning {
+ con.logger.Error("Failed to run TSIG",
+ "position", rand.Position,
"hash", rand.BlockHash.String()[:6],
"error", err)
- return
}
- if err = con.cfgModule.processPartialSignature(psig); err != nil {
- con.logger.Error("Failed process psig",
- "hash", rand.BlockHash.String()[:6],
- "error", err)
- return
- }
- con.logger.Debug("Calling Network.BroadcastDKGPartialSignature",
- "proposer", psig.ProposerID,
- "round", psig.Round,
- "hash", psig.Hash.String()[:6])
- con.network.BroadcastDKGPartialSignature(psig)
- tsig, err := con.cfgModule.runTSig(rand.Position.Round, rand.BlockHash)
- if err != nil {
- if err != ErrTSigAlreadyRunning {
- con.logger.Error("Failed to run TSIG",
- "position", rand.Position,
- "hash", rand.BlockHash.String()[:6],
- "error", err)
- }
- return
- }
- result := &types.BlockRandomnessResult{
- BlockHash: rand.BlockHash,
- Position: rand.Position,
- Randomness: tsig.Signature,
- }
- // ProcessBlockRandomnessResult is not thread-safe so we put the result in
- // the message channnel to be processed in the main thread.
- con.msgChan <- result
- }()
- return nil
+ return
+ }
+ result := &types.BlockRandomnessResult{
+ BlockHash: rand.BlockHash,
+ Position: rand.Position,
+ Randomness: tsig.Signature,
+ }
+ // ProcessBlockRandomnessResult is not thread-safe so we put the result in
+ // the message channnel to be processed in the main thread.
+ con.msgChan <- result
}
// ProcessBlockRandomnessResult processes the randomness result.
@@ -1094,14 +1097,6 @@ func (con *Consensus) ProcessBlockRandomnessResult(
// preProcessBlock performs Byzantine Agreement on the block.
func (con *Consensus) preProcessBlock(b *types.Block) (err error) {
- var exist bool
- exist, err = con.nodeSetCache.Exists(b.Position.Round, b.ProposerID)
- if err != nil {
- return
- }
- if !exist {
- return ErrProposerNotInNodeSet
- }
err = con.baMgr.processBlock(b)
if err == nil && con.debugApp != nil {
con.debugApp.BlockReceived(b.Hash)
@@ -1137,7 +1132,10 @@ func (con *Consensus) deliveryGuard() {
defer con.waitGroup.Done()
time.Sleep(con.dMoment.Sub(time.Now()))
// Node takes time to start.
- time.Sleep(60 * time.Second)
+ select {
+ case <-con.ctx.Done():
+ case <-time.After(60 * time.Second):
+ }
for {
select {
case <-con.ctx.Done():
@@ -1176,24 +1174,6 @@ func (con *Consensus) deliverBlock(b *types.Block) {
con.cfgModule.untouchTSigHash(b.Hash)
con.logger.Debug("Calling Application.BlockDelivered", "block", b)
con.app.BlockDelivered(b.Hash, b.Position, b.Finalization.Clone())
- if b.Position.Round == con.roundForNewConfig {
- // Get configuration for the round next to next round. Configuration
- // for that round should be ready at this moment and is required for
- // blockChain module. This logic is related to:
- // - roundShift
- // - notifyGenesisRound
- futureRound := con.roundForNewConfig + 1
- futureConfig := utils.GetConfigWithPanic(con.gov, futureRound, con.logger)
- con.logger.Debug("Append Config", "round", futureRound)
- if err := con.bcModule.appendConfig(
- futureRound, futureConfig); err != nil {
- con.logger.Debug("Unable to append config",
- "round", futureRound,
- "error", err)
- panic(err)
- }
- con.roundForNewConfig++
- }
if con.debugApp != nil {
con.debugApp.BlockReady(b.Hash)
}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go
index 45a1fc7d5..ddd6c3bb9 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/interfaces.go
@@ -101,8 +101,12 @@ type Governance interface {
// Return the genesis configuration if round == 0.
Configuration(round uint64) *types.Config
- // CRS returns the CRS for a given round.
- // Return the genesis CRS if round == 0.
+ // CRS returns the CRS for a given round. Return the genesis CRS if
+ // round == 0.
+ //
+ // The CRS returned is the proposed or latest reseted one, it would be
+ // changed later if corresponding DKG set failed to generate group public
+ // key.
CRS(round uint64) common.Hash
// Propose a CRS of round.
@@ -162,3 +166,12 @@ type Ticker interface {
// Retart the ticker and clear all internal data.
Restart()
}
+
+// Recovery interface for interacting with recovery information.
+type Recovery interface {
+ // ProposeSkipBlock proposes a skip block.
+ ProposeSkipBlock(height uint64) error
+
+ // Votes gets the number of votes of given height.
+ Votes(height uint64) (uint64, error)
+}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/consensus.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/consensus.go
index 25911ce5f..f2f8f9e66 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/consensus.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/consensus.go
@@ -69,12 +69,14 @@ type Consensus struct {
configs []*types.Config
roundBeginHeights []uint64
agreementRoundCut uint64
+ heightEvt *common.Event
+ roundEvt *utils.RoundEvent
// lock for accessing all fields.
lock sync.RWMutex
duringBuffering bool
latestCRSRound uint64
- moduleWaitGroup sync.WaitGroup
+ waitGroup sync.WaitGroup
agreementWaitGroup sync.WaitGroup
pullChan chan common.Hash
receiveChan chan *types.Block
@@ -82,6 +84,7 @@ type Consensus struct {
ctxCancel context.CancelFunc
syncedLastBlock *types.Block
syncedConsensus *core.Consensus
+ syncedSkipNext bool
dummyCancel context.CancelFunc
dummyFinished <-chan struct{}
dummyMsgBuffer []interface{}
@@ -115,6 +118,7 @@ func NewConsensus(
receiveChan: make(chan *types.Block, 1000),
pullChan: make(chan common.Hash, 1000),
randomnessResults: make(map[common.Hash]*types.BlockRandomnessResult),
+ heightEvt: common.NewEvent(),
}
con.ctx, con.ctxCancel = context.WithCancel(context.Background())
_, con.initChainTipHeight = db.GetCompactionChainTipInfo()
@@ -142,9 +146,73 @@ func (con *Consensus) assureBuffering() {
return
}
con.duringBuffering = true
+ // Get latest block to prepare utils.RoundEvent.
+ var (
+ err error
+ blockHash, height = con.db.GetCompactionChainTipInfo()
+ )
+ if height == 0 {
+ con.roundEvt, err = utils.NewRoundEvent(con.ctx, con.gov, con.logger,
+ uint64(0), uint64(0), uint64(0), core.ConfigRoundShift)
+ } else {
+ var b types.Block
+ if b, err = con.db.GetBlock(blockHash); err == nil {
+ beginHeight := con.roundBeginHeights[b.Position.Round]
+ con.roundEvt, err = utils.NewRoundEvent(con.ctx, con.gov,
+ con.logger, b.Position.Round, beginHeight, beginHeight,
+ core.ConfigRoundShift)
+ }
+ }
+ if err != nil {
+ panic(err)
+ }
+ // Make sure con.roundEvt stopped before stopping con.agreementModule.
+ con.waitGroup.Add(1)
+ // Register a round event handler to notify CRS to agreementModule.
+ con.roundEvt.Register(func(evts []utils.RoundEventParam) {
+ con.waitGroup.Add(1)
+ go func() {
+ defer con.waitGroup.Done()
+ for _, e := range evts {
+ select {
+ case <-con.ctx.Done():
+ return
+ default:
+ }
+ for func() bool {
+ select {
+ case <-con.ctx.Done():
+ return false
+ case con.agreementModule.inputChan <- e.Round:
+ return false
+ case <-time.After(500 * time.Millisecond):
+ con.logger.Warn(
+ "agreement input channel is full when putting CRS",
+ "round", e.Round,
+ )
+ return true
+ }
+ }() {
+ }
+ }
+ }()
+ })
+ // Register a round event handler to validate next round.
+ con.roundEvt.Register(func(evts []utils.RoundEventParam) {
+ e := evts[len(evts)-1]
+ con.heightEvt.RegisterHeight(e.NextRoundValidationHeight(), func(
+ blockHeight uint64) {
+ select {
+ case <-con.ctx.Done():
+ return
+ default:
+ }
+ con.roundEvt.ValidateNextRound(blockHeight)
+ })
+ })
+ con.roundEvt.TriggerInitEvent()
con.startAgreement()
con.startNetwork()
- con.startCRSMonitor()
}
func (con *Consensus) checkIfSynced(blocks []*types.Block) (synced bool) {
@@ -180,6 +248,29 @@ func (con *Consensus) buildAllEmptyBlocks() {
}
}
+// ForceSync forces syncer to become synced.
+func (con *Consensus) ForceSync(skip bool) {
+ if con.syncedLastBlock != nil {
+ return
+ }
+ hash, _ := con.db.GetCompactionChainTipInfo()
+ var block types.Block
+ block, err := con.db.GetBlock(hash)
+ if err != nil {
+ panic(err)
+ }
+ con.logger.Info("Force Sync", "block", &block)
+ con.setupConfigsUntilRound(block.Position.Round + core.ConfigRoundShift - 1)
+ con.syncedLastBlock = &block
+ con.stopBuffering()
+ con.dummyCancel, con.dummyFinished = utils.LaunchDummyReceiver(
+ context.Background(), con.network.ReceiveChan(),
+ func(msg interface{}) {
+ con.dummyMsgBuffer = append(con.dummyMsgBuffer, msg)
+ })
+ con.syncedSkipNext = skip
+}
+
// SyncBlocks syncs blocks from compaction chain, latest is true if the caller
// regards the blocks are the latest ones. Notice that latest can be true for
// many times.
@@ -241,6 +332,7 @@ func (con *Consensus) SyncBlocks(
b.Hash, b.Finalization.Height); err != nil {
return
}
+ go con.heightEvt.NotifyHeight(b.Finalization.Height)
}
if latest {
con.assureBuffering()
@@ -279,6 +371,7 @@ func (con *Consensus) GetSyncedConsensus() (*core.Consensus, error) {
con.syncedConsensus, err = core.NewConsensusFromSyncer(
con.syncedLastBlock,
con.roundBeginHeights[con.syncedLastBlock.Position.Round],
+ con.syncedSkipNext,
con.dMoment,
con.app,
con.gov,
@@ -321,7 +414,10 @@ func (con *Consensus) stopBuffering() {
return
}
con.logger.Trace("stop syncer modules")
- con.moduleWaitGroup.Wait()
+ con.roundEvt.Stop()
+ con.waitGroup.Done()
+ // Wait for all routines depends on con.agreementModule stopped.
+ con.waitGroup.Wait()
// Since there is no one waiting for the receive channel of fullnode, we
// need to launch a dummy receiver right away.
con.dummyCancel, con.dummyFinished = utils.LaunchDummyReceiver(
@@ -467,9 +563,9 @@ func (con *Consensus) cacheRandomnessResult(r *types.BlockRandomnessResult) {
// startNetwork starts network for receiving blocks and agreement results.
func (con *Consensus) startNetwork() {
- con.moduleWaitGroup.Add(1)
+ con.waitGroup.Add(1)
go func() {
- defer con.moduleWaitGroup.Done()
+ defer con.waitGroup.Done()
loop:
for {
select {
@@ -497,62 +593,6 @@ func (con *Consensus) startNetwork() {
}()
}
-// startCRSMonitor is the dummiest way to verify if the CRS for one round
-// is ready or not.
-func (con *Consensus) startCRSMonitor() {
- var lastNotifiedRound uint64
- // Notify all agreements for new CRS.
- notifyNewCRS := func(round uint64) {
- con.setupConfigsUntilRound(round)
- if round == lastNotifiedRound {
- return
- }
- con.logger.Debug("CRS is ready", "round", round)
- lastNotifiedRound = round
- func() {
- con.lock.Lock()
- defer con.lock.Unlock()
- con.latestCRSRound = round
- }()
- for func() bool {
- select {
- case <-con.ctx.Done():
- return false
- case con.agreementModule.inputChan <- round:
- return false
- case <-time.After(500 * time.Millisecond):
- con.logger.Debug(
- "agreement input channel is full when putting CRS",
- "round", round,
- )
- return true
- }
- }() {
- }
- }
- con.moduleWaitGroup.Add(1)
- go func() {
- defer con.moduleWaitGroup.Done()
- for {
- select {
- case <-con.ctx.Done():
- return
- case <-time.After(500 * time.Millisecond):
- }
- // Notify agreement modules for the latest round that CRS is
- // available if the round is not notified yet.
- checked := lastNotifiedRound + 1
- for (con.gov.CRS(checked) != common.Hash{}) {
- checked++
- }
- checked--
- if checked > lastNotifiedRound {
- notifyNewCRS(checked)
- }
- }
- }()
-}
-
func (con *Consensus) stopAgreement() {
if con.agreementModule.inputChan != nil {
close(con.agreementModule.inputChan)
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/watch-cat.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/watch-cat.go
new file mode 100644
index 000000000..d08bff9e9
--- /dev/null
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/syncer/watch-cat.go
@@ -0,0 +1,148 @@
+// Copyright 2019 The dexon-consensus Authors
+// This file is part of the dexon-consensus-core library.
+//
+// The dexon-consensus-core library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus-core library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus-core library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package syncer
+
+import (
+ "context"
+ "time"
+
+ "github.com/dexon-foundation/dexon-consensus/common"
+ "github.com/dexon-foundation/dexon-consensus/core"
+ "github.com/dexon-foundation/dexon-consensus/core/types"
+ "github.com/dexon-foundation/dexon-consensus/core/utils"
+)
+
+type configReader interface {
+ Configuration(round uint64) *types.Config
+}
+
+// WatchCat is reponsible for signaling if syncer object should be terminated.
+type WatchCat struct {
+ recovery core.Recovery
+ timeout time.Duration
+ configReader configReader
+ feed chan types.Position
+ polling time.Duration
+ ctx context.Context
+ cancel context.CancelFunc
+ logger common.Logger
+}
+
+// NewWatchCat creats a new WatchCat 🐱 object.
+func NewWatchCat(
+ recovery core.Recovery,
+ configReader configReader,
+ polling time.Duration,
+ timeout time.Duration,
+ logger common.Logger) *WatchCat {
+ wc := &WatchCat{
+ recovery: recovery,
+ timeout: timeout,
+ configReader: configReader,
+ feed: make(chan types.Position),
+ polling: polling,
+ logger: logger,
+ }
+ return wc
+}
+
+// Feed the WatchCat so it won't produce the termination signal.
+func (wc *WatchCat) Feed(position types.Position) {
+ wc.feed <- position
+}
+
+// Start the WatchCat.
+func (wc *WatchCat) Start() {
+ wc.Stop()
+ wc.ctx, wc.cancel = context.WithCancel(context.Background())
+ go func() {
+ var lastPos types.Position
+ MonitorLoop:
+ for {
+ select {
+ case <-wc.ctx.Done():
+ return
+ default:
+ }
+ select {
+ case <-wc.ctx.Done():
+ return
+ case pos := <-wc.feed:
+ if !pos.Newer(lastPos) {
+ wc.logger.Warn("Feed with older height",
+ "pos", pos, "lastPos", lastPos)
+ continue
+ }
+ lastPos = pos
+ case <-time.After(wc.timeout):
+ break MonitorLoop
+ }
+ }
+ go func() {
+ for {
+ select {
+ case <-wc.ctx.Done():
+ return
+ case <-wc.feed:
+ }
+ }
+ }()
+ defer wc.cancel()
+ proposed := false
+ threshold := uint64(
+ utils.GetConfigWithPanic(wc.configReader, lastPos.Round, wc.logger).
+ NotarySetSize / 2)
+ wc.logger.Info("Threshold for recovery", "votes", threshold)
+ ResetLoop:
+ for {
+ if !proposed {
+ wc.logger.Info("Calling Recovery.ProposeSkipBlock",
+ "height", lastPos.Height)
+ if err := wc.recovery.ProposeSkipBlock(lastPos.Height); err != nil {
+ wc.logger.Warn("Failed to proposeSkipBlock", "height", lastPos.Height, "error", err)
+ } else {
+ proposed = true
+ }
+ }
+ votes, err := wc.recovery.Votes(lastPos.Height)
+ if err != nil {
+ wc.logger.Error("Failed to get recovery votes", "height", lastPos.Height, "error", err)
+ } else if votes > threshold {
+ wc.logger.Info("Threshold for recovery reached!")
+ break ResetLoop
+ }
+ select {
+ case <-wc.ctx.Done():
+ return
+ case <-time.After(wc.polling):
+ }
+ }
+ }()
+}
+
+// Stop the WatchCat.
+func (wc *WatchCat) Stop() {
+ if wc.cancel != nil {
+ wc.cancel()
+ }
+}
+
+// Meow return a closed channel if syncer should be terminated.
+func (wc *WatchCat) Meow() <-chan struct{} {
+ return wc.ctx.Done()
+}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
index f7dee757f..5742d113a 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
@@ -236,14 +236,14 @@ func checkWithCancel(parentCtx context.Context, interval time.Duration,
defer cancel()
Loop:
for {
+ if ret = checker(); ret {
+ return
+ }
select {
case <-ctx.Done():
break Loop
case <-time.After(interval):
}
- if ret = checker(); ret {
- return
- }
}
return
}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/round-event.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/round-event.go
index bab1d32d2..1ce877dda 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/round-event.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/round-event.go
@@ -28,15 +28,15 @@ import (
typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg"
)
-// ErrUnmatchedBlockHeightWithGov is for invalid parameters for NewRoundEvent.
-type ErrUnmatchedBlockHeightWithGov struct {
+// ErrUnmatchedBlockHeightWithConfig is for invalid parameters for NewRoundEvent.
+type ErrUnmatchedBlockHeightWithConfig struct {
round uint64
reset uint64
blockHeight uint64
}
-func (e ErrUnmatchedBlockHeightWithGov) Error() string {
- return fmt.Sprintf("unsynced block height and gov: round:%d reset:%d h:%d",
+func (e ErrUnmatchedBlockHeightWithConfig) Error() string {
+ return fmt.Sprintf("unsynced block height and cfg: round:%d reset:%d h:%d",
e.round, e.reset, e.blockHeight)
}
@@ -56,11 +56,43 @@ type RoundEventParam struct {
CRS common.Hash
}
-// NextRoundCheckpoint returns the height to check if the next round is ready.
-func (e RoundEventParam) NextRoundCheckpoint() uint64 {
+// NextRoundValidationHeight returns the height to check if the next round is
+// ready.
+func (e RoundEventParam) NextRoundValidationHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength*9/10
+}
+
+// NextCRSProposingHeight returns the height to propose CRS for next round.
+func (e RoundEventParam) NextCRSProposingHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength/2
+}
+
+// NextDKGPreparationHeight returns the height to prepare DKG set for next
+// round.
+func (e RoundEventParam) NextDKGPreparationHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength*2/3
+}
+
+// NextRoundHeight returns the height of the beginning of next round.
+func (e RoundEventParam) NextRoundHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength
+}
+
+// NextTouchNodeSetCacheHeight returns the height to touch the node set cache.
+func (e RoundEventParam) NextTouchNodeSetCacheHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength*9/10
+}
+
+// NextDKGResetHeight returns the height to reset DKG for next period.
+func (e RoundEventParam) NextDKGResetHeight() uint64 {
return e.BeginHeight + e.Config.RoundLength*8/10
}
+// NextDKGRegisterHeight returns the height to register DKG.
+func (e RoundEventParam) NextDKGRegisterHeight() uint64 {
+ return e.BeginHeight + e.Config.RoundLength/2
+}
+
// roundEventFn defines the fingerprint of handlers of round events.
type roundEventFn func([]RoundEventParam)
@@ -131,7 +163,7 @@ func NewRoundEvent(parentCtx context.Context, gov governanceAccessor,
e.config.ExtendLength()
}
if !e.config.Contains(initBlockHeight) {
- return nil, ErrUnmatchedBlockHeightWithGov{
+ return nil, ErrUnmatchedBlockHeightWithConfig{
round: initRound,
reset: resetCount,
blockHeight: initBlockHeight,
@@ -149,6 +181,22 @@ func (e *RoundEvent) Register(h roundEventFn) {
e.handlers = append(e.handlers, h)
}
+// TriggerInitEvent triggers event from the initial setting.
+func (e *RoundEvent) TriggerInitEvent() {
+ e.lock.Lock()
+ defer e.lock.Unlock()
+ events := []RoundEventParam{RoundEventParam{
+ Round: e.lastTriggeredRound,
+ Reset: e.lastTriggeredResetCount,
+ BeginHeight: e.config.LastPeriodBeginHeight(),
+ CRS: GetCRSWithPanic(e.gov, e.lastTriggeredRound, e.logger),
+ Config: GetConfigWithPanic(e.gov, e.lastTriggeredRound, e.logger),
+ }}
+ for _, h := range e.handlers {
+ h(events)
+ }
+}
+
// ValidateNextRound validate if the DKG set for next round is ready to go or
// failed to setup, all registered handlers would be called once some decision
// is made on chain.
@@ -225,14 +273,6 @@ func (e *RoundEvent) check(blockHeight, startRound uint64, lastDKGCheck bool) (
"crs", param.CRS.String()[:6],
)
}()
- // Make sure current last config covers the blockHeight.
- if !e.config.Contains(blockHeight) {
- panic(ErrUnmatchedBlockHeightWithGov{
- round: e.lastTriggeredRound,
- reset: e.lastTriggeredResetCount,
- blockHeight: blockHeight,
- })
- }
nextRound := e.lastTriggeredRound + 1
if nextRound >= startRound+e.roundShift {
// Avoid access configuration newer than last confirmed one over
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/utils.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/utils.go
index b8bd95ec4..14687d6ac 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/utils.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/utils.go
@@ -138,3 +138,9 @@ func LaunchDummyReceiver(
func GetDKGThreshold(config *types.Config) int {
return int(config.DKGSetSize/3) + 1
}
+
+// GetNextRoundValidationHeight returns the block height to check if the next
+// round is ready.
+func GetNextRoundValidationHeight(begin, length uint64) uint64 {
+ return begin + length*9/10
+}
diff --git a/vendor/github.com/onrik/ethrpc/LICENSE b/vendor/github.com/onrik/ethrpc/LICENSE
new file mode 100644
index 000000000..c8162bd91
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Andrey
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/onrik/ethrpc/README.md b/vendor/github.com/onrik/ethrpc/README.md
new file mode 100644
index 000000000..c273e8931
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/README.md
@@ -0,0 +1,103 @@
+# Ethrpc
+[![Build Status](https://travis-ci.org/onrik/ethrpc.svg?branch=master)](https://travis-ci.org/onrik/ethrpc)
+[![Coverage Status](https://coveralls.io/repos/github/onrik/ethrpc/badge.svg?branch=master)](https://coveralls.io/github/onrik/ethrpc?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/onrik/ethrpc)](https://goreportcard.com/report/github.com/onrik/ethrpc)
+[![GoDoc](https://godoc.org/github.com/onrik/ethrpc?status.svg)](https://godoc.org/github.com/onrik/ethrpc)
+[![Donate with Ethereum](https://en.cryptobadges.io/badge/micro/0xf4144308d6D67A1F00a61A596c0eB7B08411344a)](https://en.cryptobadges.io/donate/0xf4144308d6D67A1F00a61A596c0eB7B08411344a)
+
+Golang client for ethereum [JSON RPC API](https://github.com/ethereum/wiki/wiki/JSON-RPC).
+
+- [x] web3_clientVersion
+- [x] web3_sha3
+- [x] net_version
+- [x] net_peerCount
+- [x] net_listening
+- [x] eth_protocolVersion
+- [x] eth_syncing
+- [x] eth_coinbase
+- [x] eth_mining
+- [x] eth_hashrate
+- [x] eth_gasPrice
+- [x] eth_accounts
+- [x] eth_blockNumber
+- [x] eth_getBalance
+- [x] eth_getStorageAt
+- [x] eth_getTransactionCount
+- [x] eth_getBlockTransactionCountByHash
+- [x] eth_getBlockTransactionCountByNumber
+- [x] eth_getUncleCountByBlockHash
+- [x] eth_getUncleCountByBlockNumber
+- [x] eth_getCode
+- [x] eth_sign
+- [x] eth_sendTransaction
+- [x] eth_sendRawTransaction
+- [x] eth_call
+- [x] eth_estimateGas
+- [x] eth_getBlockByHash
+- [x] eth_getBlockByNumber
+- [x] eth_getTransactionByHash
+- [x] eth_getTransactionByBlockHashAndIndex
+- [x] eth_getTransactionByBlockNumberAndIndex
+- [x] eth_getTransactionReceipt
+- [ ] eth_getUncleByBlockHashAndIndex
+- [ ] eth_getUncleByBlockNumberAndIndex
+- [x] eth_getCompilers
+- [ ] eth_compileLLL
+- [ ] eth_compileSolidity
+- [ ] eth_compileSerpent
+- [x] eth_newFilter
+- [x] eth_newBlockFilter
+- [x] eth_newPendingTransactionFilter
+- [x] eth_uninstallFilter
+- [x] eth_getFilterChanges
+- [x] eth_getFilterLogs
+- [x] eth_getLogs
+- [ ] eth_getWork
+- [ ] eth_submitWork
+- [ ] eth_submitHashrate
+- [ ] shh_post
+- [ ] shh_version
+- [ ] shh_newIdentity
+- [ ] shh_hasIdentity
+- [ ] shh_newGroup
+- [ ] shh_addToGroup
+- [ ] shh_newFilter
+- [ ] shh_uninstallFilter
+- [ ] shh_getFilterChanges
+- [ ] shh_getMessages
+
+##### Usage:
+```go
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/onrik/ethrpc"
+)
+
+func main() {
+ client := ethrpc.New("http://127.0.0.1:8545")
+
+ version, err := client.Web3ClientVersion()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(version)
+
+ // Send 1 eth
+ txid, err := client.EthSendTransaction(ethrpc.T{
+ From: "0x6247cf0412c6462da2a51d05139e2a3c6c630f0a",
+ To: "0xcfa202c4268749fbb5136f2b68f7402984ed444b",
+ Value: ethrpc.Eth1(),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(txid)
+}
+```
+
+[![Donate with Ethereum](https://en.cryptobadges.io/badge/big/0xf4144308d6D67A1F00a61A596c0eB7B08411344a?showBalance=true)](https://en.cryptobadges.io/donate/0xf4144308d6D67A1F00a61A596c0eB7B08411344a)
+
diff --git a/vendor/github.com/onrik/ethrpc/ethrpc.go b/vendor/github.com/onrik/ethrpc/ethrpc.go
new file mode 100644
index 000000000..5118b425d
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/ethrpc.go
@@ -0,0 +1,514 @@
+package ethrpc
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "math/big"
+ "net/http"
+ "os"
+)
+
+// EthError - ethereum error
+type EthError struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+}
+
+func (err EthError) Error() string {
+ return fmt.Sprintf("Error %d (%s)", err.Code, err.Message)
+}
+
+type ethResponse struct {
+ ID int `json:"id"`
+ JSONRPC string `json:"jsonrpc"`
+ Result json.RawMessage `json:"result"`
+ Error *EthError `json:"error"`
+}
+
+type ethRequest struct {
+ ID int `json:"id"`
+ JSONRPC string `json:"jsonrpc"`
+ Method string `json:"method"`
+ Params []interface{} `json:"params"`
+}
+
+// EthRPC - Ethereum rpc client
+type EthRPC struct {
+ url string
+ client httpClient
+ log logger
+ Debug bool
+}
+
+// New create new rpc client with given url
+func New(url string, options ...func(rpc *EthRPC)) *EthRPC {
+ rpc := &EthRPC{
+ url: url,
+ client: http.DefaultClient,
+ log: log.New(os.Stderr, "", log.LstdFlags),
+ }
+ for _, option := range options {
+ option(rpc)
+ }
+
+ return rpc
+}
+
+// NewEthRPC create new rpc client with given url
+func NewEthRPC(url string, options ...func(rpc *EthRPC)) *EthRPC {
+ return New(url, options...)
+}
+
+func (rpc *EthRPC) call(method string, target interface{}, params ...interface{}) error {
+ result, err := rpc.Call(method, params...)
+ if err != nil {
+ return err
+ }
+
+ if target == nil {
+ return nil
+ }
+
+ return json.Unmarshal(result, target)
+}
+
+// URL returns client url
+func (rpc *EthRPC) URL() string {
+ return rpc.url
+}
+
+// Call returns raw response of method call
+func (rpc *EthRPC) Call(method string, params ...interface{}) (json.RawMessage, error) {
+ request := ethRequest{
+ ID: 1,
+ JSONRPC: "2.0",
+ Method: method,
+ Params: params,
+ }
+
+ body, err := json.Marshal(request)
+ if err != nil {
+ return nil, err
+ }
+
+ response, err := rpc.client.Post(rpc.url, "application/json", bytes.NewBuffer(body))
+ if response != nil {
+ defer response.Body.Close()
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ if rpc.Debug {
+ rpc.log.Println(fmt.Sprintf("%s\nRequest: %s\nResponse: %s\n", method, body, data))
+ }
+
+ resp := new(ethResponse)
+ if err := json.Unmarshal(data, resp); err != nil {
+ return nil, err
+ }
+
+ if resp.Error != nil {
+ return nil, *resp.Error
+ }
+
+ return resp.Result, nil
+
+}
+
+// RawCall returns raw response of method call (Deprecated)
+func (rpc *EthRPC) RawCall(method string, params ...interface{}) (json.RawMessage, error) {
+ return rpc.Call(method, params...)
+}
+
+// Web3ClientVersion returns the current client version.
+func (rpc *EthRPC) Web3ClientVersion() (string, error) {
+ var clientVersion string
+
+ err := rpc.call("web3_clientVersion", &clientVersion)
+ return clientVersion, err
+}
+
+// Web3Sha3 returns Keccak-256 (not the standardized SHA3-256) of the given data.
+func (rpc *EthRPC) Web3Sha3(data []byte) (string, error) {
+ var hash string
+
+ err := rpc.call("web3_sha3", &hash, fmt.Sprintf("0x%x", data))
+ return hash, err
+}
+
+// NetVersion returns the current network protocol version.
+func (rpc *EthRPC) NetVersion() (string, error) {
+ var version string
+
+ err := rpc.call("net_version", &version)
+ return version, err
+}
+
+// NetListening returns true if client is actively listening for network connections.
+func (rpc *EthRPC) NetListening() (bool, error) {
+ var listening bool
+
+ err := rpc.call("net_listening", &listening)
+ return listening, err
+}
+
+// NetPeerCount returns number of peers currently connected to the client.
+func (rpc *EthRPC) NetPeerCount() (int, error) {
+ var response string
+ if err := rpc.call("net_peerCount", &response); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthProtocolVersion returns the current ethereum protocol version.
+func (rpc *EthRPC) EthProtocolVersion() (string, error) {
+ var protocolVersion string
+
+ err := rpc.call("eth_protocolVersion", &protocolVersion)
+ return protocolVersion, err
+}
+
+// EthSyncing returns an object with data about the sync status or false.
+func (rpc *EthRPC) EthSyncing() (*Syncing, error) {
+ result, err := rpc.RawCall("eth_syncing")
+ if err != nil {
+ return nil, err
+ }
+ syncing := new(Syncing)
+ if bytes.Equal(result, []byte("false")) {
+ return syncing, nil
+ }
+ err = json.Unmarshal(result, syncing)
+ return syncing, err
+}
+
+// EthCoinbase returns the client coinbase address
+func (rpc *EthRPC) EthCoinbase() (string, error) {
+ var address string
+
+ err := rpc.call("eth_coinbase", &address)
+ return address, err
+}
+
+// EthMining returns true if client is actively mining new blocks.
+func (rpc *EthRPC) EthMining() (bool, error) {
+ var mining bool
+
+ err := rpc.call("eth_mining", &mining)
+ return mining, err
+}
+
+// EthHashrate returns the number of hashes per second that the node is mining with.
+func (rpc *EthRPC) EthHashrate() (int, error) {
+ var response string
+
+ if err := rpc.call("eth_hashrate", &response); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthGasPrice returns the current price per gas in wei.
+func (rpc *EthRPC) EthGasPrice() (big.Int, error) {
+ var response string
+ if err := rpc.call("eth_gasPrice", &response); err != nil {
+ return big.Int{}, err
+ }
+
+ return ParseBigInt(response)
+}
+
+// EthAccounts returns a list of addresses owned by client.
+func (rpc *EthRPC) EthAccounts() ([]string, error) {
+ accounts := []string{}
+
+ err := rpc.call("eth_accounts", &accounts)
+ return accounts, err
+}
+
+// EthBlockNumber returns the number of most recent block.
+func (rpc *EthRPC) EthBlockNumber() (int, error) {
+ var response string
+ if err := rpc.call("eth_blockNumber", &response); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthGetBalance returns the balance of the account of given address in wei.
+func (rpc *EthRPC) EthGetBalance(address, block string) (big.Int, error) {
+ var response string
+ if err := rpc.call("eth_getBalance", &response, address, block); err != nil {
+ return big.Int{}, err
+ }
+
+ return ParseBigInt(response)
+}
+
+// EthGetStorageAt returns the value from a storage position at a given address.
+func (rpc *EthRPC) EthGetStorageAt(data string, position int, tag string) (string, error) {
+ var result string
+
+ err := rpc.call("eth_getStorageAt", &result, data, IntToHex(position), tag)
+ return result, err
+}
+
+// EthGetTransactionCount returns the number of transactions sent from an address.
+func (rpc *EthRPC) EthGetTransactionCount(address, block string) (int, error) {
+ var response string
+
+ if err := rpc.call("eth_getTransactionCount", &response, address, block); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthGetBlockTransactionCountByHash returns the number of transactions in a block from a block matching the given block hash.
+func (rpc *EthRPC) EthGetBlockTransactionCountByHash(hash string) (int, error) {
+ var response string
+
+ if err := rpc.call("eth_getBlockTransactionCountByHash", &response, hash); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthGetBlockTransactionCountByNumber returns the number of transactions in a block from a block matching the given block
+func (rpc *EthRPC) EthGetBlockTransactionCountByNumber(number int) (int, error) {
+ var response string
+
+ if err := rpc.call("eth_getBlockTransactionCountByNumber", &response, IntToHex(number)); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthGetUncleCountByBlockHash returns the number of uncles in a block from a block matching the given block hash.
+func (rpc *EthRPC) EthGetUncleCountByBlockHash(hash string) (int, error) {
+ var response string
+
+ if err := rpc.call("eth_getUncleCountByBlockHash", &response, hash); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthGetUncleCountByBlockNumber returns the number of uncles in a block from a block matching the given block number.
+func (rpc *EthRPC) EthGetUncleCountByBlockNumber(number int) (int, error) {
+ var response string
+
+ if err := rpc.call("eth_getUncleCountByBlockNumber", &response, IntToHex(number)); err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+// EthGetCode returns code at a given address.
+func (rpc *EthRPC) EthGetCode(address, block string) (string, error) {
+ var code string
+
+ err := rpc.call("eth_getCode", &code, address, block)
+ return code, err
+}
+
+// EthSign signs data with a given address.
+// Calculates an Ethereum specific signature with: sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))
+func (rpc *EthRPC) EthSign(address, data string) (string, error) {
+ var signature string
+
+ err := rpc.call("eth_sign", &signature, address, data)
+ return signature, err
+}
+
+// EthSendTransaction creates new message call transaction or a contract creation, if the data field contains code.
+func (rpc *EthRPC) EthSendTransaction(transaction T) (string, error) {
+ var hash string
+
+ err := rpc.call("eth_sendTransaction", &hash, transaction)
+ return hash, err
+}
+
+// EthSendRawTransaction creates new message call transaction or a contract creation for signed transactions.
+func (rpc *EthRPC) EthSendRawTransaction(data string) (string, error) {
+ var hash string
+
+ err := rpc.call("eth_sendRawTransaction", &hash, data)
+ return hash, err
+}
+
+// EthCall executes a new message call immediately without creating a transaction on the block chain.
+func (rpc *EthRPC) EthCall(transaction T, tag string) (string, error) {
+ var data string
+
+ err := rpc.call("eth_call", &data, transaction, tag)
+ return data, err
+}
+
+// EthEstimateGas makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas.
+func (rpc *EthRPC) EthEstimateGas(transaction T) (int, error) {
+ var response string
+
+ err := rpc.call("eth_estimateGas", &response, transaction)
+ if err != nil {
+ return 0, err
+ }
+
+ return ParseInt(response)
+}
+
+func (rpc *EthRPC) getBlock(method string, withTransactions bool, params ...interface{}) (*Block, error) {
+ result, err := rpc.RawCall(method, params...)
+ if err != nil {
+ return nil, err
+ }
+ if bytes.Equal(result, []byte("null")) {
+ return nil, nil
+ }
+
+ var response proxyBlock
+ if withTransactions {
+ response = new(proxyBlockWithTransactions)
+ } else {
+ response = new(proxyBlockWithoutTransactions)
+ }
+
+ err = json.Unmarshal(result, response)
+ if err != nil {
+ return nil, err
+ }
+
+ block := response.toBlock()
+ return &block, nil
+}
+
+// EthGetBlockByHash returns information about a block by hash.
+func (rpc *EthRPC) EthGetBlockByHash(hash string, withTransactions bool) (*Block, error) {
+ return rpc.getBlock("eth_getBlockByHash", withTransactions, hash, withTransactions)
+}
+
+// EthGetBlockByNumber returns information about a block by block number.
+func (rpc *EthRPC) EthGetBlockByNumber(number int, withTransactions bool) (*Block, error) {
+ return rpc.getBlock("eth_getBlockByNumber", withTransactions, IntToHex(number), withTransactions)
+}
+
+func (rpc *EthRPC) getTransaction(method string, params ...interface{}) (*Transaction, error) {
+ transaction := new(Transaction)
+
+ err := rpc.call(method, transaction, params...)
+ return transaction, err
+}
+
+// EthGetTransactionByHash returns the information about a transaction requested by transaction hash.
+func (rpc *EthRPC) EthGetTransactionByHash(hash string) (*Transaction, error) {
+ return rpc.getTransaction("eth_getTransactionByHash", hash)
+}
+
+// EthGetTransactionByBlockHashAndIndex returns information about a transaction by block hash and transaction index position.
+func (rpc *EthRPC) EthGetTransactionByBlockHashAndIndex(blockHash string, transactionIndex int) (*Transaction, error) {
+ return rpc.getTransaction("eth_getTransactionByBlockHashAndIndex", blockHash, IntToHex(transactionIndex))
+}
+
+// EthGetTransactionByBlockNumberAndIndex returns information about a transaction by block number and transaction index position.
+func (rpc *EthRPC) EthGetTransactionByBlockNumberAndIndex(blockNumber, transactionIndex int) (*Transaction, error) {
+ return rpc.getTransaction("eth_getTransactionByBlockNumberAndIndex", IntToHex(blockNumber), IntToHex(transactionIndex))
+}
+
+// EthGetTransactionReceipt returns the receipt of a transaction by transaction hash.
+// Note That the receipt is not available for pending transactions.
+func (rpc *EthRPC) EthGetTransactionReceipt(hash string) (*TransactionReceipt, error) {
+ transactionReceipt := new(TransactionReceipt)
+
+ err := rpc.call("eth_getTransactionReceipt", transactionReceipt, hash)
+ if err != nil {
+ return nil, err
+ }
+
+ return transactionReceipt, nil
+}
+
+// EthGetCompilers returns a list of available compilers in the client.
+func (rpc *EthRPC) EthGetCompilers() ([]string, error) {
+ compilers := []string{}
+
+ err := rpc.call("eth_getCompilers", &compilers)
+ return compilers, err
+}
+
+// EthNewFilter creates a new filter object.
+func (rpc *EthRPC) EthNewFilter(params FilterParams) (string, error) {
+ var filterID string
+ err := rpc.call("eth_newFilter", &filterID, params)
+ return filterID, err
+}
+
+// EthNewBlockFilter creates a filter in the node, to notify when a new block arrives.
+// To check if the state has changed, call EthGetFilterChanges.
+func (rpc *EthRPC) EthNewBlockFilter() (string, error) {
+ var filterID string
+ err := rpc.call("eth_newBlockFilter", &filterID)
+ return filterID, err
+}
+
+// EthNewPendingTransactionFilter creates a filter in the node, to notify when new pending transactions arrive.
+// To check if the state has changed, call EthGetFilterChanges.
+func (rpc *EthRPC) EthNewPendingTransactionFilter() (string, error) {
+ var filterID string
+ err := rpc.call("eth_newPendingTransactionFilter", &filterID)
+ return filterID, err
+}
+
+// EthUninstallFilter uninstalls a filter with given id.
+func (rpc *EthRPC) EthUninstallFilter(filterID string) (bool, error) {
+ var res bool
+ err := rpc.call("eth_uninstallFilter", &res, filterID)
+ return res, err
+}
+
+// EthGetFilterChanges polling method for a filter, which returns an array of logs which occurred since last poll.
+func (rpc *EthRPC) EthGetFilterChanges(filterID string) ([]Log, error) {
+ var logs = []Log{}
+ err := rpc.call("eth_getFilterChanges", &logs, filterID)
+ return logs, err
+}
+
+// EthGetFilterLogs returns an array of all logs matching filter with given id.
+func (rpc *EthRPC) EthGetFilterLogs(filterID string) ([]Log, error) {
+ var logs = []Log{}
+ err := rpc.call("eth_getFilterLogs", &logs, filterID)
+ return logs, err
+}
+
+// EthGetLogs returns an array of all logs matching a given filter object.
+func (rpc *EthRPC) EthGetLogs(params FilterParams) ([]Log, error) {
+ var logs = []Log{}
+ err := rpc.call("eth_getLogs", &logs, params)
+ return logs, err
+}
+
+// Eth1 returns 1 ethereum value (10^18 wei)
+func (rpc *EthRPC) Eth1() *big.Int {
+ return Eth1()
+}
+
+// Eth1 returns 1 ethereum value (10^18 wei)
+func Eth1() *big.Int {
+ return big.NewInt(1000000000000000000)
+}
diff --git a/vendor/github.com/onrik/ethrpc/go.mod b/vendor/github.com/onrik/ethrpc/go.mod
new file mode 100644
index 000000000..8f047b1d3
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/go.mod
@@ -0,0 +1 @@
+module github.com/onrik/ethrpc
diff --git a/vendor/github.com/onrik/ethrpc/helpers.go b/vendor/github.com/onrik/ethrpc/helpers.go
new file mode 100644
index 000000000..e98030055
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/helpers.go
@@ -0,0 +1,40 @@
+package ethrpc
+
+import (
+ "fmt"
+ "math/big"
+ "strconv"
+ "strings"
+)
+
+// ParseInt parse hex string value to int
+func ParseInt(value string) (int, error) {
+ i, err := strconv.ParseInt(strings.TrimPrefix(value, "0x"), 16, 64)
+ if err != nil {
+ return 0, err
+ }
+
+ return int(i), nil
+}
+
+// ParseBigInt parse hex string value to big.Int
+func ParseBigInt(value string) (big.Int, error) {
+ i := big.Int{}
+ _, err := fmt.Sscan(value, &i)
+
+ return i, err
+}
+
+// IntToHex convert int to hexadecimal representation
+func IntToHex(i int) string {
+ return fmt.Sprintf("0x%x", i)
+}
+
+// BigToHex covert big.Int to hexadecimal representation
+func BigToHex(bigInt big.Int) string {
+ if bigInt.BitLen() == 0 {
+ return "0x0"
+ }
+
+ return "0x" + strings.TrimPrefix(fmt.Sprintf("%x", bigInt.Bytes()), "0")
+}
diff --git a/vendor/github.com/onrik/ethrpc/interface.go b/vendor/github.com/onrik/ethrpc/interface.go
new file mode 100644
index 000000000..2e3021d1b
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/interface.go
@@ -0,0 +1,50 @@
+package ethrpc
+
+import (
+ "math/big"
+)
+
+type EthereumAPI interface {
+ Web3ClientVersion() (string, error)
+ Web3Sha3(data []byte) (string, error)
+ NetVersion() (string, error)
+ NetListening() (bool, error)
+ NetPeerCount() (int, error)
+ EthProtocolVersion() (string, error)
+ EthSyncing() (*Syncing, error)
+ EthCoinbase() (string, error)
+ EthMining() (bool, error)
+ EthHashrate() (int, error)
+ EthGasPrice() (big.Int, error)
+ EthAccounts() ([]string, error)
+ EthBlockNumber() (int, error)
+ EthGetBalance(address, block string) (big.Int, error)
+ EthGetStorageAt(data string, position int, tag string) (string, error)
+ EthGetTransactionCount(address, block string) (int, error)
+ EthGetBlockTransactionCountByHash(hash string) (int, error)
+ EthGetBlockTransactionCountByNumber(number int) (int, error)
+ EthGetUncleCountByBlockHash(hash string) (int, error)
+ EthGetUncleCountByBlockNumber(number int) (int, error)
+ EthGetCode(address, block string) (string, error)
+ EthSign(address, data string) (string, error)
+ EthSendTransaction(transaction T) (string, error)
+ EthSendRawTransaction(data string) (string, error)
+ EthCall(transaction T, tag string) (string, error)
+ EthEstimateGas(transaction T) (int, error)
+ EthGetBlockByHash(hash string, withTransactions bool) (*Block, error)
+ EthGetBlockByNumber(number int, withTransactions bool) (*Block, error)
+ EthGetTransactionByHash(hash string) (*Transaction, error)
+ EthGetTransactionByBlockHashAndIndex(blockHash string, transactionIndex int) (*Transaction, error)
+ EthGetTransactionByBlockNumberAndIndex(blockNumber, transactionIndex int) (*Transaction, error)
+ EthGetTransactionReceipt(hash string) (*TransactionReceipt, error)
+ EthGetCompilers() ([]string, error)
+ EthNewFilter(params FilterParams) (string, error)
+ EthNewBlockFilter() (string, error)
+ EthNewPendingTransactionFilter() (string, error)
+ EthUninstallFilter(filterID string) (bool, error)
+ EthGetFilterChanges(filterID string) ([]Log, error)
+ EthGetFilterLogs(filterID string) ([]Log, error)
+ EthGetLogs(params FilterParams) ([]Log, error)
+}
+
+var _ EthereumAPI = (*EthRPC)(nil)
diff --git a/vendor/github.com/onrik/ethrpc/options.go b/vendor/github.com/onrik/ethrpc/options.go
new file mode 100644
index 000000000..72ab39879
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/options.go
@@ -0,0 +1,35 @@
+package ethrpc
+
+import (
+ "io"
+ "net/http"
+)
+
+type httpClient interface {
+ Post(url string, contentType string, body io.Reader) (*http.Response, error)
+}
+
+type logger interface {
+ Println(v ...interface{})
+}
+
+// WithHttpClient set custom http client
+func WithHttpClient(client httpClient) func(rpc *EthRPC) {
+ return func(rpc *EthRPC) {
+ rpc.client = client
+ }
+}
+
+// WithLogger set custom logger
+func WithLogger(l logger) func(rpc *EthRPC) {
+ return func(rpc *EthRPC) {
+ rpc.log = l
+ }
+}
+
+// WithDebug set debug flag
+func WithDebug(enabled bool) func(rpc *EthRPC) {
+ return func(rpc *EthRPC) {
+ rpc.Debug = enabled
+ }
+}
diff --git a/vendor/github.com/onrik/ethrpc/types.go b/vendor/github.com/onrik/ethrpc/types.go
new file mode 100644
index 000000000..b90baeef0
--- /dev/null
+++ b/vendor/github.com/onrik/ethrpc/types.go
@@ -0,0 +1,322 @@
+package ethrpc
+
+import (
+ "bytes"
+ "encoding/json"
+ "math/big"
+ "unsafe"
+)
+
+// Syncing - object with syncing data info
+type Syncing struct {
+ IsSyncing bool
+ StartingBlock int
+ CurrentBlock int
+ HighestBlock int
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (s *Syncing) UnmarshalJSON(data []byte) error {
+ proxy := new(proxySyncing)
+ if err := json.Unmarshal(data, proxy); err != nil {
+ return err
+ }
+
+ proxy.IsSyncing = true
+ *s = *(*Syncing)(unsafe.Pointer(proxy))
+
+ return nil
+}
+
+// T - input transaction object
+type T struct {
+ From string
+ To string
+ Gas int
+ GasPrice *big.Int
+ Value *big.Int
+ Data string
+ Nonce int
+}
+
+// MarshalJSON implements the json.Unmarshaler interface.
+func (t T) MarshalJSON() ([]byte, error) {
+ params := map[string]interface{}{
+ "from": t.From,
+ }
+ if t.To != "" {
+ params["to"] = t.To
+ }
+ if t.Gas > 0 {
+ params["gas"] = IntToHex(t.Gas)
+ }
+ if t.GasPrice != nil {
+ params["gasPrice"] = BigToHex(*t.GasPrice)
+ }
+ if t.Value != nil {
+ params["value"] = BigToHex(*t.Value)
+ }
+ if t.Data != "" {
+ params["data"] = t.Data
+ }
+ if t.Nonce > 0 {
+ params["nonce"] = IntToHex(t.Nonce)
+ }
+
+ return json.Marshal(params)
+}
+
+// Transaction - transaction object
+type Transaction struct {
+ Hash string
+ Nonce int
+ BlockHash string
+ BlockNumber *int
+ TransactionIndex *int
+ From string
+ To string
+ Value big.Int
+ Gas int
+ GasPrice big.Int
+ Input string
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (t *Transaction) UnmarshalJSON(data []byte) error {
+ proxy := new(proxyTransaction)
+ if err := json.Unmarshal(data, proxy); err != nil {
+ return err
+ }
+
+ *t = *(*Transaction)(unsafe.Pointer(proxy))
+
+ return nil
+}
+
+// Log - log object
+type Log struct {
+ Removed bool
+ LogIndex int
+ TransactionIndex int
+ TransactionHash string
+ BlockNumber int
+ BlockHash string
+ Address string
+ Data string
+ Topics []string
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (log *Log) UnmarshalJSON(data []byte) error {
+ proxy := new(proxyLog)
+ if err := json.Unmarshal(data, proxy); err != nil {
+ return err
+ }
+
+ *log = *(*Log)(unsafe.Pointer(proxy))
+
+ return nil
+}
+
+// FilterParams - Filter parameters object
+type FilterParams struct {
+ FromBlock string `json:"fromBlock,omitempty"`
+ ToBlock string `json:"toBlock,omitempty"`
+ Address []string `json:"address,omitempty"`
+ Topics [][]string `json:"topics,omitempty"`
+}
+
+// TransactionReceipt - transaction receipt object
+type TransactionReceipt struct {
+ TransactionHash string
+ TransactionIndex int
+ BlockHash string
+ BlockNumber int
+ CumulativeGasUsed int
+ GasUsed int
+ ContractAddress string
+ Logs []Log
+ LogsBloom string
+ Root string
+ Status string
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (t *TransactionReceipt) UnmarshalJSON(data []byte) error {
+ proxy := new(proxyTransactionReceipt)
+ if err := json.Unmarshal(data, proxy); err != nil {
+ return err
+ }
+
+ *t = *(*TransactionReceipt)(unsafe.Pointer(proxy))
+
+ return nil
+}
+
+// Block - block object
+type Block struct {
+ Number int
+ Hash string
+ ParentHash string
+ Nonce string
+ Sha3Uncles string
+ LogsBloom string
+ TransactionsRoot string
+ StateRoot string
+ Miner string
+ Difficulty big.Int
+ TotalDifficulty big.Int
+ ExtraData string
+ Size int
+ GasLimit int
+ GasUsed int
+ Timestamp int
+ Uncles []string
+ Transactions []Transaction
+}
+
+type proxySyncing struct {
+ IsSyncing bool `json:"-"`
+ StartingBlock hexInt `json:"startingBlock"`
+ CurrentBlock hexInt `json:"currentBlock"`
+ HighestBlock hexInt `json:"highestBlock"`
+}
+
+type proxyTransaction struct {
+ Hash string `json:"hash"`
+ Nonce hexInt `json:"nonce"`
+ BlockHash string `json:"blockHash"`
+ BlockNumber *hexInt `json:"blockNumber"`
+ TransactionIndex *hexInt `json:"transactionIndex"`
+ From string `json:"from"`
+ To string `json:"to"`
+ Value hexBig `json:"value"`
+ Gas hexInt `json:"gas"`
+ GasPrice hexBig `json:"gasPrice"`
+ Input string `json:"input"`
+}
+
+type proxyLog struct {
+ Removed bool `json:"removed"`
+ LogIndex hexInt `json:"logIndex"`
+ TransactionIndex hexInt `json:"transactionIndex"`
+ TransactionHash string `json:"transactionHash"`
+ BlockNumber hexInt `json:"blockNumber"`
+ BlockHash string `json:"blockHash"`
+ Address string `json:"address"`
+ Data string `json:"data"`
+ Topics []string `json:"topics"`
+}
+
+type proxyTransactionReceipt struct {
+ TransactionHash string `json:"transactionHash"`
+ TransactionIndex hexInt `json:"transactionIndex"`
+ BlockHash string `json:"blockHash"`
+ BlockNumber hexInt `json:"blockNumber"`
+ CumulativeGasUsed hexInt `json:"cumulativeGasUsed"`
+ GasUsed hexInt `json:"gasUsed"`
+ ContractAddress string `json:"contractAddress,omitempty"`
+ Logs []Log `json:"logs"`
+ LogsBloom string `json:"logsBloom"`
+ Root string `json:"root"`
+ Status string `json:"status,omitempty"`
+}
+
+type hexInt int
+
+func (i *hexInt) UnmarshalJSON(data []byte) error {
+ result, err := ParseInt(string(bytes.Trim(data, `"`)))
+ *i = hexInt(result)
+
+ return err
+}
+
+type hexBig big.Int
+
+func (i *hexBig) UnmarshalJSON(data []byte) error {
+ result, err := ParseBigInt(string(bytes.Trim(data, `"`)))
+ *i = hexBig(result)
+
+ return err
+}
+
+type proxyBlock interface {
+ toBlock() Block
+}
+
+type proxyBlockWithTransactions struct {
+ Number hexInt `json:"number"`
+ Hash string `json:"hash"`
+ ParentHash string `json:"parentHash"`
+ Nonce string `json:"nonce"`
+ Sha3Uncles string `json:"sha3Uncles"`
+ LogsBloom string `json:"logsBloom"`
+ TransactionsRoot string `json:"transactionsRoot"`
+ StateRoot string `json:"stateRoot"`
+ Miner string `json:"miner"`
+ Difficulty hexBig `json:"difficulty"`
+ TotalDifficulty hexBig `json:"totalDifficulty"`
+ ExtraData string `json:"extraData"`
+ Size hexInt `json:"size"`
+ GasLimit hexInt `json:"gasLimit"`
+ GasUsed hexInt `json:"gasUsed"`
+ Timestamp hexInt `json:"timestamp"`
+ Uncles []string `json:"uncles"`
+ Transactions []proxyTransaction `json:"transactions"`
+}
+
+func (proxy *proxyBlockWithTransactions) toBlock() Block {
+ return *(*Block)(unsafe.Pointer(proxy))
+}
+
+type proxyBlockWithoutTransactions struct {
+ Number hexInt `json:"number"`
+ Hash string `json:"hash"`
+ ParentHash string `json:"parentHash"`
+ Nonce string `json:"nonce"`
+ Sha3Uncles string `json:"sha3Uncles"`
+ LogsBloom string `json:"logsBloom"`
+ TransactionsRoot string `json:"transactionsRoot"`
+ StateRoot string `json:"stateRoot"`
+ Miner string `json:"miner"`
+ Difficulty hexBig `json:"difficulty"`
+ TotalDifficulty hexBig `json:"totalDifficulty"`
+ ExtraData string `json:"extraData"`
+ Size hexInt `json:"size"`
+ GasLimit hexInt `json:"gasLimit"`
+ GasUsed hexInt `json:"gasUsed"`
+ Timestamp hexInt `json:"timestamp"`
+ Uncles []string `json:"uncles"`
+ Transactions []string `json:"transactions"`
+}
+
+func (proxy *proxyBlockWithoutTransactions) toBlock() Block {
+ block := Block{
+ Number: int(proxy.Number),
+ Hash: proxy.Hash,
+ ParentHash: proxy.ParentHash,
+ Nonce: proxy.Nonce,
+ Sha3Uncles: proxy.Sha3Uncles,
+ LogsBloom: proxy.LogsBloom,
+ TransactionsRoot: proxy.TransactionsRoot,
+ StateRoot: proxy.StateRoot,
+ Miner: proxy.Miner,
+ Difficulty: big.Int(proxy.Difficulty),
+ TotalDifficulty: big.Int(proxy.TotalDifficulty),
+ ExtraData: proxy.ExtraData,
+ Size: int(proxy.Size),
+ GasLimit: int(proxy.GasLimit),
+ GasUsed: int(proxy.GasUsed),
+ Timestamp: int(proxy.Timestamp),
+ Uncles: proxy.Uncles,
+ }
+
+ block.Transactions = make([]Transaction, len(proxy.Transactions))
+ for i := range proxy.Transactions {
+ block.Transactions[i] = Transaction{
+ Hash: proxy.Transactions[i],
+ }
+ }
+
+ return block
+}