aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2018-11-20 13:51:27 +0800
committerWei-Ning Huang <w@dexon.org>2019-03-12 12:19:09 +0800
commit9d29febd3e4958960ed28e98dcfc1c8b2713052a (patch)
tree6092247b4074709ef8197bbd06eb9ac7a94575b8
parentf982aba1c9a0b0c4db00b9032ae9751ffb3333ac (diff)
downloaddexon-9d29febd3e4958960ed28e98dcfc1c8b2713052a.tar.gz
dexon-9d29febd3e4958960ed28e98dcfc1c8b2713052a.tar.zst
dexon-9d29febd3e4958960ed28e98dcfc1c8b2713052a.zip
vendor: sync to latest core (#37)
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/LICENSE165
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go18
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go10
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go4
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go71
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go39
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go140
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go6
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go49
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go3
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go4
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go2
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go8
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go3
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go3
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go9
-rw-r--r--vendor/github.com/dexon-foundation/dexon-consensus/core/utils/nodeset-cache.go (renamed from vendor/github.com/dexon-foundation/dexon-consensus/core/nodeset-cache.go)4
-rw-r--r--vendor/vendor.json46
18 files changed, 427 insertions, 157 deletions
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/LICENSE b/vendor/github.com/dexon-foundation/dexon-consensus/LICENSE
new file mode 100644
index 000000000..0a041280b
--- /dev/null
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go
index 9023799a3..77f293376 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement-state.go
@@ -40,6 +40,7 @@ const (
stateCommit
stateForward
statePullVote
+ stateSleep
)
var nullBlockHash = common.Hash{}
@@ -156,3 +157,20 @@ func (s *pullVoteState) clocks() int { return 4 }
func (s *pullVoteState) nextState() (agreementState, error) {
return s, nil
}
+
+// ----- SleepState -----
+// sleepState is a special state after BA has output and waits for restart.
+type sleepState struct {
+ a *agreementData
+}
+
+func newSleepState(a *agreementData) *sleepState {
+ return &sleepState{a: a}
+}
+
+func (s *sleepState) state() agreementStateType { return stateSleep }
+func (s *sleepState) clocks() int { return 65536 }
+
+func (s *sleepState) nextState() (agreementState, error) {
+ return s, nil
+}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go
index f31b7efba..ff1c71a7c 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/agreement.go
@@ -232,6 +232,8 @@ func isStop(aID types.Position) bool {
// clocks returns how many time this state is required.
func (a *agreement) clocks() int {
+ a.data.lock.RLock()
+ defer a.data.lock.RUnlock()
return a.state.clocks()
}
@@ -250,6 +252,14 @@ func (a *agreement) agreementID() types.Position {
// nextState is called at the specific clock time.
func (a *agreement) nextState() (err error) {
+ if func() bool {
+ a.lock.RLock()
+ defer a.lock.RUnlock()
+ return a.hasOutput
+ }() {
+ a.state = newSleepState(a.data)
+ return
+ }
a.state, err = a.state.nextState()
return
}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go
index 760646e10..b45af229b 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/blockdb/memory.go
@@ -124,6 +124,10 @@ func (m *MemBackedBlockDB) Put(block types.Block) error {
// Update updates a block in the database.
func (m *MemBackedBlockDB) Update(block types.Block) error {
+ if !m.Has(block.Hash) {
+ return ErrBlockDoesNotExist
+ }
+
m.blocksMutex.Lock()
defer m.blocksMutex.Unlock()
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go
index 4a5ba3637..f6bc0149d 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/compaction-chain.go
@@ -33,6 +33,10 @@ var (
"block not registered")
ErrNotInitiazlied = fmt.Errorf(
"not initialized")
+ ErrTSigNotReady = fmt.Errorf(
+ "tsig not ready")
+ ErrIncorrectBlockRandomnessResult = fmt.Errorf(
+ "incorrect block randomness result")
)
type finalizedBlockHeap = types.ByFinalizationHeight
@@ -154,6 +158,24 @@ func (cc *compactionChain) extractBlocks() []*types.Block {
return deliveringBlocks
}
+func (cc *compactionChain) verifyRandomness(
+ blockHash common.Hash, round uint64, randomness []byte) (bool, error) {
+ if round == 0 {
+ return len(randomness) == 0, nil
+ }
+ // Randomness is not available at round 0.
+ v, ok, err := cc.tsigVerifier.UpdateAndGet(round)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, ErrTSigNotReady
+ }
+ return v.VerifySignature(blockHash, crypto.Signature{
+ Type: "bls",
+ Signature: randomness}), nil
+}
+
func (cc *compactionChain) processFinalizedBlock(block *types.Block) {
if block.Finalization.Height <= cc.lastBlock().Finalization.Height {
return
@@ -166,6 +188,19 @@ func (cc *compactionChain) processFinalizedBlock(block *types.Block) {
cc.lock.Lock()
defer cc.lock.Unlock()
+ // The randomness result is missed previously.
+ if cc.blockRegisteredNoLock(block.Hash) {
+ ok, err := cc.verifyRandomness(
+ block.Hash, block.Position.Round, block.Finalization.Randomness)
+ if err != nil {
+ panic(err)
+ }
+ if ok {
+ cc.blockRandomness[block.Hash] = block.Finalization.Randomness
+ }
+ return
+ }
+
heap.Push(cc.pendingFinalizedBlocks, block)
return
@@ -206,22 +241,14 @@ func (cc *compactionChain) extractFinalizedBlocks() []*types.Block {
b.Finalization.Height == prevBlock.Finalization.Height {
continue
}
- round := b.Position.Round
- if round != 0 {
- // Randomness is not available at round 0.
- v, ok, err := cc.tsigVerifier.UpdateAndGet(round)
- if err != nil {
- continue
- }
- if !ok {
- toPending = append(toPending, b)
- continue
- }
- if ok := v.VerifySignature(b.Hash, crypto.Signature{
- Type: "bls",
- Signature: b.Finalization.Randomness}); !ok {
- continue
- }
+ ok, err := cc.verifyRandomness(
+ b.Hash, b.Position.Round, b.Finalization.Randomness)
+ if err != nil {
+ toPending = append(toPending, b)
+ continue
+ }
+ if !ok {
+ continue
}
// Fork resolution: choose block with smaller hash.
if prevBlock.Finalization.Height == b.Finalization.Height {
@@ -261,11 +288,19 @@ func (cc *compactionChain) processBlockRandomnessResult(
rand *types.BlockRandomnessResult) error {
cc.lock.Lock()
defer cc.lock.Unlock()
- // TODO(jimmy-dexon): the result should not be discarded here. Blocks may
- // be registered later.
if !cc.blockRegisteredNoLock(rand.BlockHash) {
+ // If the randomness result is discarded here, it'll later be processed by
+ //finalized block
return ErrBlockNotRegistered
}
+ ok, err := cc.verifyRandomness(
+ rand.BlockHash, rand.Position.Round, rand.Randomness)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return ErrIncorrectBlockRandomnessResult
+ }
cc.blockRandomness[rand.BlockHash] = rand.Randomness
return nil
}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go
index bda2fdf62..fdfcd13d0 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/configuration-chain.go
@@ -26,6 +26,7 @@ import (
"github.com/dexon-foundation/dexon-consensus/core/crypto"
"github.com/dexon-foundation/dexon-consensus/core/types"
typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg"
+ "github.com/dexon-foundation/dexon-consensus/core/utils"
)
// Errors for configuration chain..
@@ -51,7 +52,7 @@ type configurationChain struct {
tsig map[common.Hash]*tsigProtocol
tsigTouched map[common.Hash]struct{}
tsigReady *sync.Cond
- cache *NodeSetCache
+ cache *utils.NodeSetCache
dkgSet map[types.NodeID]struct{}
mpkReady bool
pendingPrvShare map[types.NodeID]*typesDKG.PrivateShare
@@ -64,7 +65,7 @@ func newConfigurationChain(
ID types.NodeID,
recv dkgReceiver,
gov Governance,
- cache *NodeSetCache,
+ cache *utils.NodeSetCache,
logger common.Logger) *configurationChain {
return &configurationChain{
ID: ID,
@@ -106,6 +107,10 @@ func (cc *configurationChain) runDKG(round uint64) error {
cc.dkgLock.Lock()
defer cc.dkgLock.Unlock()
if cc.dkg == nil || cc.dkg.round != round {
+ if cc.dkg != nil && cc.dkg.round > round {
+ cc.logger.Warn("DKG canceled", "round", round)
+ return nil
+ }
return ErrDKGNotRegistered
}
if func() bool {
@@ -116,6 +121,11 @@ func (cc *configurationChain) runDKG(round uint64) error {
}() {
return nil
}
+ cc.logger.Debug("Calling Governance.IsDKGFinal", "round", round)
+ if cc.gov.IsDKGFinal(round) {
+ cc.logger.Warn("DKG already final", "round", round)
+ return nil
+ }
ticker := newTicker(cc.gov, round, TickerDKG)
cc.dkgLock.Unlock()
@@ -182,10 +192,6 @@ func (cc *configurationChain) runDKG(round uint64) error {
if err != nil {
return err
}
- signer, err := cc.dkg.recoverShareSecret(gpk.qualifyIDs)
- if err != nil {
- return err
- }
qualifies := ""
for nID := range gpk.qualifyNodeIDs {
qualifies += fmt.Sprintf("%s ", nID.String()[:6])
@@ -195,6 +201,14 @@ func (cc *configurationChain) runDKG(round uint64) error {
"round", round,
"count", len(gpk.qualifyIDs),
"qualifies", qualifies)
+ if _, exist := gpk.qualifyNodeIDs[cc.ID]; !exist {
+ cc.logger.Warn("Self is not in Qualify Nodes")
+ return nil
+ }
+ signer, err := cc.dkg.recoverShareSecret(gpk.qualifyIDs)
+ if err != nil {
+ return err
+ }
cc.dkgResult.Lock()
defer cc.dkgResult.Unlock()
cc.dkgSigner[round] = signer
@@ -264,13 +278,24 @@ func (cc *configurationChain) runTSig(
}
}
}()
+ timeout := make(chan struct{}, 1)
+ go func() {
+ // TODO(jimmy-dexon): make timeout configurable.
+ time.Sleep(5 * time.Second)
+ timeout <- struct{}{}
+ cc.tsigReady.Broadcast()
+ }()
var signature crypto.Signature
var err error
for func() bool {
signature, err = cc.tsig[hash].signature()
+ select {
+ case <-timeout:
+ return false
+ default:
+ }
return err == ErrNotEnoughtPartialSignatures
}() {
- // TODO(jimmy-dexon): add a timeout here.
cc.tsigReady.Wait()
}
delete(cc.tsig, hash)
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 3d46c5c8b..e09ee2579 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/consensus.go
@@ -29,6 +29,7 @@ import (
"github.com/dexon-foundation/dexon-consensus/core/crypto"
"github.com/dexon-foundation/dexon-consensus/core/types"
typesDKG "github.com/dexon-foundation/dexon-consensus/core/types/dkg"
+ "github.com/dexon-foundation/dexon-consensus/core/utils"
)
// Errors for consensus core.
@@ -55,8 +56,6 @@ var (
"incorrect vote position")
ErrIncorrectVoteProposer = fmt.Errorf(
"incorrect vote proposer")
- ErrIncorrectBlockRandomnessResult = fmt.Errorf(
- "incorrect block randomness result")
)
// consensusBAReceiver implements agreementReceiver.
@@ -135,6 +134,8 @@ func (recv *consensusBAReceiver) ConfirmBlock(
"hash", hash,
"chainID", recv.chainID)
recv.agreementModule.addCandidateBlock(block)
+ recv.agreementModule.lock.Lock()
+ defer recv.agreementModule.lock.Unlock()
recv.ConfirmBlock(block.Hash, votes)
}()
return
@@ -187,7 +188,7 @@ type consensusDKGReceiver struct {
ID types.NodeID
gov Governance
authModule *Authenticator
- nodeSetCache *NodeSetCache
+ nodeSetCache *utils.NodeSetCache
cfgModule *configurationChain
network Network
logger common.Logger
@@ -284,6 +285,7 @@ type Consensus struct {
// Dexon consensus v1's modules.
lattice *Lattice
ccModule *compactionChain
+ toSyncer *totalOrderingSyncer
// Interfaces.
db blockdb.BlockDatabase
@@ -294,7 +296,7 @@ type Consensus struct {
// Misc.
dMoment time.Time
- nodeSetCache *NodeSetCache
+ nodeSetCache *utils.NodeSetCache
round uint64
roundToNotify uint64
lock sync.RWMutex
@@ -318,7 +320,7 @@ func NewConsensus(
var round uint64
logger.Debug("Calling Governance.Configuration", "round", round)
config := gov.Configuration(round)
- nodeSetCache := NewNodeSetCache(gov)
+ nodeSetCache := utils.NewNodeSetCache(gov)
logger.Debug("Calling Governance.CRS", "round", round)
// Setup auth module.
authModule := NewAuthenticator(prv)
@@ -441,7 +443,7 @@ func (con *Consensus) Run(initBlock *types.Block) {
con.cfgModule.registerDKG(initRound, int(initConfig.DKGSetSize)/3+1)
con.event.RegisterTime(con.dMoment.Add(initConfig.RoundInterval/4),
func(time.Time) {
- con.runDKGTSIG(initRound, initConfig)
+ con.runDKG(initRound, initConfig)
})
}
con.initialRound(con.dMoment, initRound, initConfig)
@@ -492,6 +494,12 @@ BALoop:
select {
case newNotary := <-recv.restartNotary:
if newNotary {
+ con.logger.Debug("Calling Governance.CRS", "round", recv.round)
+ crs = con.gov.CRS(recv.round)
+ if (crs == common.Hash{}) {
+ // Governance is out-of-sync.
+ continue BALoop
+ }
configForNewRound := con.gov.Configuration(recv.round)
recv.changeNotaryTime =
recv.changeNotaryTime.Add(configForNewRound.RoundInterval)
@@ -499,8 +507,6 @@ BALoop:
if err != nil {
panic(err)
}
- con.logger.Debug("Calling Governance.CRS", "round", recv.round)
- crs = con.gov.CRS(recv.round)
con.logger.Debug("Calling Governance.Configuration",
"round", recv.round)
nIDs = nodes.GetSubSet(
@@ -541,8 +547,8 @@ BALoop:
}
}
-// runDKGTSIG starts running DKG+TSIG protocol.
-func (con *Consensus) runDKGTSIG(round uint64, config *types.Config) {
+// runDKG starts running DKG protocol.
+func (con *Consensus) runDKG(round uint64, config *types.Config) {
con.dkgReady.L.Lock()
defer con.dkgReady.L.Unlock()
if con.dkgRunning != 0 {
@@ -564,41 +570,18 @@ func (con *Consensus) runDKGTSIG(round uint64, config *types.Config) {
}
}()
if err := con.cfgModule.runDKG(round); err != nil {
- panic(err)
- }
- nodes, err := con.nodeSetCache.GetNodeSet(round)
- if err != nil {
- panic(err)
- }
- con.logger.Debug("Calling Governance.Configuration", "round", round)
- hash := HashConfigurationBlock(
- nodes.IDs,
- con.gov.Configuration(round),
- common.Hash{},
- con.cfgModule.prevHash)
- psig, err := con.cfgModule.preparePartialSignature(
- round, hash)
- if err != nil {
- panic(err)
- }
- if err = con.authModule.SignDKGPartialSignature(psig); err != nil {
- panic(err)
- }
- if err = con.cfgModule.processPartialSignature(psig); err != nil {
- panic(err)
- }
- con.logger.Debug("Calling Network.BroadcastDKGPartialSignature",
- "proposer", psig.ProposerID,
- "round", psig.Round,
- "hash", psig.Hash)
- con.network.BroadcastDKGPartialSignature(psig)
- if _, err = con.cfgModule.runBlockTSig(round, hash); err != nil {
- panic(err)
+ con.logger.Error("Failed to runDKG", "error", err)
}
}()
}
func (con *Consensus) runCRS(round uint64) {
+ con.logger.Debug("Calling Governance.CRS to check if already proposed",
+ "round", round+1)
+ if (con.gov.CRS(round+1) != common.Hash{}) {
+ con.logger.Info("CRS already proposed", "round", round+1)
+ return
+ }
// Start running next round CRS.
con.logger.Debug("Calling Governance.CRS", "round", round)
psig, err := con.cfgModule.preparePartialSignature(round, con.gov.CRS(round))
@@ -683,7 +666,7 @@ func (con *Consensus) initialRound(
con.logger.Debug("Calling Governance.Configuration",
"round", nextRound)
nextConfig := con.gov.Configuration(nextRound)
- con.runDKGTSIG(nextRound, nextConfig)
+ con.runDKG(nextRound, nextConfig)
})
}(round + 1)
})
@@ -695,23 +678,6 @@ func (con *Consensus) initialRound(
con.logger.Debug("Calling Governance.Configuration",
"round", nextRound)
nextConfig := con.gov.Configuration(nextRound)
- // Get configuration for the round next to next round. Configuration
- // for that round should be ready at this moment and is required for
- // lattice module. This logic is related to:
- // - roundShift
- // - notifyGenesisRound
- futureRound := nextRound + 1
- con.logger.Debug("Calling Governance.Configuration",
- "round", futureRound)
- futureConfig := con.gov.Configuration(futureRound)
- con.logger.Debug("Append Config", "round", futureRound)
- if err := con.lattice.AppendConfig(
- futureRound, futureConfig); err != nil {
- con.logger.Debug("Unable to append config",
- "round", futureRound,
- "error", err)
- panic(err)
- }
con.initialRound(
startTime.Add(config.RoundInterval), nextRound, nextConfig)
})
@@ -833,6 +799,12 @@ func (con *Consensus) proposeEmptyBlock(
// ProcessVote is the entry point to submit ont vote to a Consensus instance.
func (con *Consensus) ProcessVote(vote *types.Vote) (err error) {
+ if vote.Position.ChainID >= uint32(len(con.baModules)) {
+ return nil
+ }
+ if isStop(con.baModules[vote.Position.ChainID].agreementID()) {
+ return nil
+ }
v := vote.Clone()
err = con.baModules[v.Position.ChainID].processVote(v)
return err
@@ -877,6 +849,9 @@ func (con *Consensus) ProcessAgreementResult(
// Syncing BA Module.
agreement := con.baModules[rand.Position.ChainID]
aID := agreement.agreementID()
+ if isStop(aID) {
+ return nil
+ }
if rand.Position.Newer(&aID) {
con.logger.Info("Syncing BA", "position", &rand.Position)
nodes, err := con.nodeSetCache.GetNodeSet(rand.Position.Round)
@@ -959,31 +934,17 @@ func (con *Consensus) ProcessBlockRandomnessResult(
if rand.Position.Round == 0 {
return nil
}
- if !con.ccModule.blockRegistered(rand.BlockHash) {
- return nil
- }
- round := rand.Position.Round
- v, ok, err := con.ccModule.tsigVerifier.UpdateAndGet(round)
- if err != nil {
+ if err := con.ccModule.processBlockRandomnessResult(rand); err != nil {
+ if err == ErrBlockNotRegistered {
+ err = nil
+ }
return err
}
- if !ok {
- return nil
- }
- if !v.VerifySignature(
- rand.BlockHash, crypto.Signature{Signature: rand.Randomness}) {
- return ErrIncorrectBlockRandomnessResult
- }
con.logger.Debug("Calling Network.BroadcastRandomnessResult",
"hash", rand.BlockHash,
"position", &rand.Position,
"randomness", hex.EncodeToString(rand.Randomness))
con.network.BroadcastRandomnessResult(rand)
- if err := con.ccModule.processBlockRandomnessResult(rand); err != nil {
- if err != ErrBlockNotRegistered {
- return err
- }
- }
return nil
}
@@ -1000,6 +961,23 @@ func (con *Consensus) deliverBlock(b *types.Block) {
con.logger.Debug("Calling Application.BlockDelivered", "block", b)
con.app.BlockDelivered(b.Hash, b.Position, b.Finalization.Clone())
if b.Position.Round == con.roundToNotify {
+ // Get configuration for the round next to next round. Configuration
+ // for that round should be ready at this moment and is required for
+ // lattice module. This logic is related to:
+ // - roundShift
+ // - notifyGenesisRound
+ futureRound := con.roundToNotify + 1
+ con.logger.Debug("Calling Governance.Configuration",
+ "round", con.roundToNotify)
+ futureConfig := con.gov.Configuration(futureRound)
+ con.logger.Debug("Append Config", "round", futureRound)
+ if err := con.lattice.AppendConfig(
+ futureRound, futureConfig); err != nil {
+ con.logger.Debug("Unable to append config",
+ "round", futureRound,
+ "error", err)
+ panic(err)
+ }
// Only the first block delivered of that round would
// trigger this noitification.
con.logger.Debug("Calling Governance.NotifyRoundHeight",
@@ -1048,7 +1026,10 @@ func (con *Consensus) processBlock(block *types.Block) (err error) {
// processFinalizedBlock is the entry point for syncing blocks.
func (con *Consensus) processFinalizedBlock(block *types.Block) (err error) {
if err = con.lattice.SanityCheck(block); err != nil {
- return
+ if err != ErrRetrySanityCheckLater {
+ return
+ }
+ err = nil
}
con.ccModule.processFinalizedBlock(block)
for {
@@ -1066,6 +1047,11 @@ func (con *Consensus) processFinalizedBlock(block *types.Block) (err error) {
}
err = nil
}
+ con.lattice.ProcessFinalizedBlock(b)
+ // TODO(jimmy): BlockConfirmed and DeliverBlock may not be removed if
+ // application implements state snapshot.
+ con.logger.Debug("Calling Application.BlockConfirmed", "block", b)
+ con.app.BlockConfirmed(*b.Clone())
con.deliverBlock(b)
}
}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go
index ce6d32ba3..6fe810ac0 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice-data.go
@@ -26,12 +26,12 @@ import (
"github.com/dexon-foundation/dexon-consensus/common"
"github.com/dexon-foundation/dexon-consensus/core/blockdb"
"github.com/dexon-foundation/dexon-consensus/core/types"
+ "github.com/dexon-foundation/dexon-consensus/core/utils"
)
// Errors for sanity check error.
var (
ErrDuplicatedAckOnOneChain = fmt.Errorf("duplicated ack on one chain")
- ErrInvalidChainID = fmt.Errorf("invalid chain id")
ErrInvalidProposerID = fmt.Errorf("invalid proposer id")
ErrInvalidWitness = fmt.Errorf("invalid witness data")
ErrInvalidBlock = fmt.Errorf("invalid block")
@@ -181,7 +181,7 @@ func (data *latticeData) sanityCheck(b *types.Block) error {
}
// Check if the chain id is valid.
if b.Position.ChainID >= config.numChains {
- return ErrInvalidChainID
+ return utils.ErrInvalidChainID
}
// Make sure parent block is arrived.
chain := data.chains[b.Position.ChainID]
@@ -382,7 +382,7 @@ func (data *latticeData) prepareBlock(b *types.Block) error {
}
// If chainID is illegal in this round, reject it.
if b.Position.ChainID >= config.numChains {
- return ErrInvalidChainID
+ return utils.ErrInvalidChainID
}
// Reset fields to make sure we got these information from parent block.
b.Position.Height = 0
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go
index 108f2887b..7b66bd557 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/lattice.go
@@ -41,6 +41,7 @@ type Lattice struct {
pool blockPool
retryAdd bool
data *latticeData
+ toSyncer *totalOrderingSyncer
toModule *totalOrdering
ctModule *consensusTimestamp
logger common.Logger
@@ -64,6 +65,7 @@ func NewLattice(
debug: debug,
pool: newBlockPool(cfg.NumChains),
data: newLatticeData(db, dMoment, round, cfg),
+ toSyncer: newTotalOrderingSyncer(cfg.NumChains),
toModule: newTotalOrdering(dMoment, cfg),
ctModule: newConsensusTimestamp(dMoment, round, cfg.NumChains),
logger: logger,
@@ -144,7 +146,6 @@ func (l *Lattice) SanityCheck(b *types.Block) (err error) {
if _, ok := err.(*ErrAckingBlockNotExists); ok {
err = ErrRetrySanityCheckLater
}
- l.logger.Error("Sanity Check failed", "error", err)
return
}
return
@@ -236,28 +237,31 @@ func (l *Lattice) ProcessBlock(
return
}
- // Perform total ordering for each block added to lattice.
- for _, b = range inLattice {
- toDelivered, deliveredMode, err = l.toModule.processBlock(b)
- if err != nil {
- // All errors from total ordering is serious, should panic.
- panic(err)
- }
- if len(toDelivered) == 0 {
- continue
- }
- hashes := make(common.Hashes, len(toDelivered))
- for idx := range toDelivered {
- hashes[idx] = toDelivered[idx].Hash
- }
- if l.debug != nil {
- l.debug.TotalOrderingDelivered(hashes, deliveredMode)
- }
- // Perform consensus timestamp module.
- if err = l.ctModule.processBlocks(toDelivered); err != nil {
- return
+ for _, blockToSyncer := range inLattice {
+ toTotalOrdering := l.toSyncer.processBlock(blockToSyncer)
+ // Perform total ordering for each block added to lattice.
+ for _, b = range toTotalOrdering {
+ toDelivered, deliveredMode, err = l.toModule.processBlock(b)
+ if err != nil {
+ // All errors from total ordering is serious, should panic.
+ panic(err)
+ }
+ if len(toDelivered) == 0 {
+ continue
+ }
+ hashes := make(common.Hashes, len(toDelivered))
+ for idx := range toDelivered {
+ hashes[idx] = toDelivered[idx].Hash
+ }
+ if l.debug != nil {
+ l.debug.TotalOrderingDelivered(hashes, deliveredMode)
+ }
+ // Perform consensus timestamp module.
+ if err = l.ctModule.processBlocks(toDelivered); err != nil {
+ return
+ }
+ delivered = append(delivered, toDelivered...)
}
- delivered = append(delivered, toDelivered...)
}
return
}
@@ -304,4 +308,5 @@ func (l *Lattice) ProcessFinalizedBlock(b *types.Block) {
panic(err)
}
l.pool.purgeBlocks(b.Position.ChainID, b.Position.Height)
+ l.toSyncer.processFinalizedBlock(b)
}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go
index aa90a1ded..1360611f7 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/total-ordering-syncer.go
@@ -156,6 +156,9 @@ func (tos *totalOrderingSyncer) processBlock(
// The finalized block should be passed by the order of consensus height.
func (tos *totalOrderingSyncer) processFinalizedBlock(block *types.Block) {
+ if tos.synced() {
+ return
+ }
tos.lock.Lock()
defer tos.lock.Unlock()
if len(tos.pendingDeliveryBlocks) > 0 {
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go
index 1eaa3e398..6df245b08 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block-randomness.go
@@ -31,8 +31,8 @@ type AgreementResult struct {
}
func (r *AgreementResult) String() string {
- return fmt.Sprintf(
- "agreementResult[%s:%s]", r.BlockHash, &r.Position)
+ return fmt.Sprintf("agreementResult{Hash:%s %s}",
+ r.BlockHash.String()[:6], &r.Position)
}
// BlockRandomnessResult describes a block randomness result
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go
index b24e1f715..f42b70267 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/block.go
@@ -221,7 +221,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
}
func (b *Block) String() string {
- return fmt.Sprintf("Block(%v:%s)", b.Hash.String()[:6], &b.Position)
+ return fmt.Sprintf("Block{Hash:%v %s}", b.Hash.String()[:6], &b.Position)
}
// Clone returns a deep copy of a block.
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go
index 4053c5a28..cecc4f16c 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/dkg/dkg.go
@@ -61,7 +61,7 @@ type MasterPublicKey struct {
}
func (d *MasterPublicKey) String() string {
- return fmt.Sprintf("MasterPublicKey[%s:%d]",
+ return fmt.Sprintf("MasterPublicKey{KP:%s Round:%d}",
d.ProposerID.String()[:6],
d.Round)
}
@@ -141,11 +141,11 @@ type Complaint struct {
func (c *Complaint) String() string {
if c.IsNack() {
- return fmt.Sprintf("DKGNackComplaint[%s:%d]%s",
+ return fmt.Sprintf("DKGNackComplaint{CP:%s Round:%d PSP:%s}",
c.ProposerID.String()[:6], c.Round,
c.PrivateShare.ProposerID.String()[:6])
}
- return fmt.Sprintf("Complaint[%s:%d]%v",
+ return fmt.Sprintf("DKGComplaint{CP:%s Round:%d PrivateShare:%v}",
c.ProposerID.String()[:6], c.Round, c.PrivateShare)
}
@@ -175,7 +175,7 @@ type Finalize struct {
}
func (final *Finalize) String() string {
- return fmt.Sprintf("DKGFinal[%s:%d]",
+ return fmt.Sprintf("DKGFinal{FP:%s Round:%d}",
final.ProposerID.String()[:6],
final.Round)
}
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go
index 1fc7e6bf4..8822f6ea9 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/position.go
@@ -29,7 +29,8 @@ type Position struct {
}
func (pos *Position) String() string {
- return fmt.Sprintf("[%d:%d:%d]", pos.Round, pos.ChainID, pos.Height)
+ return fmt.Sprintf("Position{Round:%d Chain:%d Height:%d}",
+ pos.Round, pos.ChainID, pos.Height)
}
// Equal checks if two positions are equal, it panics when their chainIDs
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go
index 12c3af892..7601542ae 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/types/vote.go
@@ -52,7 +52,8 @@ type Vote struct {
}
func (v *Vote) String() string {
- return fmt.Sprintf("Vote[%s:%s](%d:%d):%s", v.ProposerID.String()[:6],
+ return fmt.Sprintf("Vote{BP:%s %s Period:%d Type:%d Hash:%s}",
+ v.ProposerID.String()[:6],
&v.Position, v.Period, v.Type, v.BlockHash.String()[:6])
}
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 6b9ce634f..f6be46130 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils.go
@@ -27,8 +27,17 @@ import (
"github.com/dexon-foundation/dexon-consensus/common"
"github.com/dexon-foundation/dexon-consensus/core/crypto"
"github.com/dexon-foundation/dexon-consensus/core/types"
+ "github.com/dexon-foundation/dexon-consensus/core/utils"
)
+// NodeSetCache is type alias to avoid fullnode compile error when moving
+// it to core/utils package.
+type NodeSetCache = utils.NodeSetCache
+
+// NewNodeSetCache is function alias to avoid fullnode compile error when moving
+// it to core/utils package.
+var NewNodeSetCache = utils.NewNodeSetCache
+
var (
debug = false
// ErrEmptyTimestamps would be reported if Block.timestamps is empty.
diff --git a/vendor/github.com/dexon-foundation/dexon-consensus/core/nodeset-cache.go b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/nodeset-cache.go
index 26e3d55f9..a8f8fe58f 100644
--- a/vendor/github.com/dexon-foundation/dexon-consensus/core/nodeset-cache.go
+++ b/vendor/github.com/dexon-foundation/dexon-consensus/core/utils/nodeset-cache.go
@@ -15,7 +15,7 @@
// along with the dexon-consensus library. If not, see
// <http://www.gnu.org/licenses/>.
-package core
+package utils
import (
"errors"
@@ -29,6 +29,8 @@ import (
var (
// ErrRoundNotReady means we got nil config.
ErrRoundNotReady = errors.New("round is not ready")
+ // ErrInvalidChainID means the chain ID is unexpected.
+ ErrInvalidChainID = errors.New("invalid chain id")
)
type sets struct {
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 711396aec..258268f01 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -105,50 +105,56 @@
{
"checksumSHA1": "ev84RyegNbt2Pr/sK26LK9LoQNI=",
"path": "github.com/dexon-foundation/dexon-consensus/common",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
- "checksumSHA1": "CDbhowufKnHipqNsFhQymXdlAyY=",
+ "checksumSHA1": "AnU2LZzOJNt2DrSiZHBu+ZGoI6I=",
"path": "github.com/dexon-foundation/dexon-consensus/core",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
- "checksumSHA1": "vNsaBvsrXJF+W6K5DCLpgy1rUZY=",
+ "checksumSHA1": "v4fKR7uhoyufi6hAVO44cFEb+tY=",
"path": "github.com/dexon-foundation/dexon-consensus/core/blockdb",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
"checksumSHA1": "tQSbYCu5P00lUhKsx3IbBZCuSLY=",
"path": "github.com/dexon-foundation/dexon-consensus/core/crypto",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
"checksumSHA1": "p2jOAulavUU2xyj018pYPHlj8XA=",
"path": "github.com/dexon-foundation/dexon-consensus/core/crypto/dkg",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
"checksumSHA1": "6Pf6caC8LTNCI7IflFmglKYnxYo=",
"path": "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
- "checksumSHA1": "Kzw8b6kQLSApzVHO2WvmxrBfixA=",
+ "checksumSHA1": "A4SIILI1wPAoyUTaUhs6iycDxbk=",
"path": "github.com/dexon-foundation/dexon-consensus/core/types",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
- "checksumSHA1": "ovChyW9OfDGnk/7CDAR+A5vJymc=",
+ "checksumSHA1": "Sn3PAYsblIXmr7gVKDzxnoBPku4=",
"path": "github.com/dexon-foundation/dexon-consensus/core/types/dkg",
- "revision": "01642721a7768218e7f9a5be8f0829eb8ae7c7b1",
- "revisionTime": "2018-11-13T08:28:24Z"
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
+ },
+ {
+ "checksumSHA1": "pE0L1qyJ7Jyir1SQ6jEsj8U+83U=",
+ "path": "github.com/dexon-foundation/dexon-consensus/core/utils",
+ "revision": "e5891f7ca08737c3f3bc37fd523537cb243f8b0d",
+ "revisionTime": "2018-11-20T02:47:06Z"
},
{
"checksumSHA1": "TAkwduKZqLyimyTPPWIllZWYFuE=",