aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/consensus-timestamp.go23
-rw-r--r--core/consensus-timestamp_test.go10
-rw-r--r--core/consensus.go2
-rw-r--r--core/lattice.go48
-rw-r--r--core/lattice_test.go21
-rw-r--r--core/test/governance.go1
-rw-r--r--core/total-ordering.go41
-rw-r--r--core/total-ordering_test.go27
-rw-r--r--core/types/config.go4
-rw-r--r--integration_test/node.go2
-rw-r--r--simulation/governance.go1
11 files changed, 147 insertions, 33 deletions
diff --git a/core/consensus-timestamp.go b/core/consensus-timestamp.go
index c43ca82..9188c02 100644
--- a/core/consensus-timestamp.go
+++ b/core/consensus-timestamp.go
@@ -27,6 +27,10 @@ import (
// consensusTimestamp is for Concensus Timestamp Algorithm.
type consensusTimestamp struct {
chainTimestamps []time.Time
+
+ // This part keeps configs for each round.
+ numChainsForRounds []uint32
+ minRound uint64
}
var (
@@ -36,8 +40,23 @@ var (
)
// newConsensusTimestamp create timestamper object.
-func newConsensusTimestamp() *consensusTimestamp {
- return &consensusTimestamp{}
+func newConsensusTimestamp(round uint64, numChains uint32) *consensusTimestamp {
+ return &consensusTimestamp{
+ numChainsForRounds: []uint32{numChains},
+ minRound: round,
+ }
+}
+
+// appendConfig appends a configuration for upcoming round. When you append
+// a config for round R, next time you can only append the config for round R+1.
+func (ct *consensusTimestamp) appendConfig(
+ round uint64, config *types.Config) error {
+
+ if round != ct.minRound+uint64(len(ct.numChainsForRounds)) {
+ return ErrRoundNotIncreasing
+ }
+ ct.numChainsForRounds = append(ct.numChainsForRounds, config.NumChains)
+ return nil
}
// ProcessBlocks is the entry function.
diff --git a/core/consensus-timestamp_test.go b/core/consensus-timestamp_test.go
index be90f41..ea85c38 100644
--- a/core/consensus-timestamp_test.go
+++ b/core/consensus-timestamp_test.go
@@ -90,11 +90,12 @@ func (s *ConsensusTimestampTest) extractTimestamps(
// TestTimestampPartition verifies that processing segments of compatction chain
// should have the same result as processing the whole chain at once.
func (s *ConsensusTimestampTest) TestTimestampPartition() {
+ var round uint64
blockNums := []int{50, 100, 30}
chainNum := 19
sigma := 100 * time.Millisecond
totalTimestamps := make([]time.Time, 0)
- ct := newConsensusTimestamp()
+ ct := newConsensusTimestamp(round, uint32(chainNum))
totalBlockNum := 0
for _, blockNum := range blockNums {
totalBlockNum += blockNum
@@ -110,7 +111,7 @@ func (s *ConsensusTimestampTest) TestTimestampPartition() {
totalChain = append(totalChain, chain...)
totalTimestamps = append(totalTimestamps, timestamps...)
}
- ct2 := newConsensusTimestamp()
+ ct2 := newConsensusTimestamp(round, uint32(chainNum))
err := ct2.processBlocks(totalChain)
s.Require().NoError(err)
timestamps2 := s.extractTimestamps(totalChain)
@@ -118,9 +119,10 @@ func (s *ConsensusTimestampTest) TestTimestampPartition() {
}
func (s *ConsensusTimestampTest) TestTimestampIncrease() {
+ var round uint64
chainNum := 19
sigma := 100 * time.Millisecond
- ct := newConsensusTimestamp()
+ ct := newConsensusTimestamp(round, uint32(chainNum))
chain := s.generateBlocksWithTimestamp(1000, chainNum, time.Second, sigma)
err := ct.processBlocks(chain)
s.Require().NoError(err)
@@ -129,7 +131,7 @@ func (s *ConsensusTimestampTest) TestTimestampIncrease() {
s.False(timestamps[i].Before(timestamps[i-1]))
}
// Test if the processBlocks is stable.
- ct2 := newConsensusTimestamp()
+ ct2 := newConsensusTimestamp(round, uint32(chainNum))
ct2.processBlocks(chain)
s.Require().NoError(err)
timestamps2 := s.extractTimestamps(chain)
diff --git a/core/consensus.go b/core/consensus.go
index 51c4e35..4bcc116 100644
--- a/core/consensus.go
+++ b/core/consensus.go
@@ -216,7 +216,7 @@ func NewConsensus(
// Setup nonblocking module.
nbModule := newNonBlocking(app, debugApp)
// Init lattice.
- lattice := NewLattice(config, authModule, nbModule, nbModule, db)
+ lattice := NewLattice(round, config, authModule, nbModule, nbModule, db)
// Init configuration chain.
ID := types.NewNodeID(prv.PublicKey())
cfgModule := newConfigurationChain(
diff --git a/core/lattice.go b/core/lattice.go
index 2da32ba..8906c74 100644
--- a/core/lattice.go
+++ b/core/lattice.go
@@ -18,6 +18,7 @@
package core
import (
+ "errors"
"fmt"
"sync"
"time"
@@ -45,6 +46,11 @@ var (
ErrIncorrectBlockTime = fmt.Errorf("block timestampe is incorrect")
)
+// Errors for method usage
+var (
+ ErrRoundNotIncreasing = errors.New("round not increasing")
+)
+
// latticeData is a module for storing lattice.
type latticeData struct {
// chains stores chains' blocks and other info.
@@ -57,10 +63,15 @@ type latticeData struct {
// parent/child blocks.
minBlockTimeInterval time.Duration
maxBlockTimeInterval time.Duration
+
+ // This stores configuration for each round.
+ numChainsForRounds []uint32
+ minRound uint64
}
// newLatticeData creates a new latticeData struct.
func newLatticeData(
+ round uint64,
chainNum uint32,
minBlockTimeInterval time.Duration,
maxBlockTimeInterval time.Duration) (data *latticeData) {
@@ -69,6 +80,8 @@ func newLatticeData(
blockByHash: make(map[common.Hash]*types.Block),
minBlockTimeInterval: minBlockTimeInterval,
maxBlockTimeInterval: maxBlockTimeInterval,
+ numChainsForRounds: []uint32{chainNum},
+ minRound: round,
}
for i := range data.chains {
data.chains[i] = &chainStatus{
@@ -289,6 +302,18 @@ func (data *latticeData) nextPosition(chainID uint32) types.Position {
return data.chains[chainID].nextPosition()
}
+// appendConfig appends a configuration for upcoming round. When you append
+// a config for round R, next time you can only append the config for round R+1.
+func (data *latticeData) appendConfig(
+ round uint64, config *types.Config) (err error) {
+
+ if round != data.minRound+uint64(len(data.numChainsForRounds)) {
+ return ErrRoundNotIncreasing
+ }
+ data.numChainsForRounds = append(data.numChainsForRounds, config.NumChains)
+ return nil
+}
+
type chainStatus struct {
// ID keeps the chainID of this chain status.
ID uint32
@@ -400,12 +425,14 @@ type Lattice struct {
// NewLattice constructs an Lattice instance.
func NewLattice(
+ round uint64,
cfg *types.Config,
authModule *Authenticator,
app Application,
debug Debug,
db blockdb.BlockDatabase) (s *Lattice) {
data := newLatticeData(
+ round,
cfg.NumChains,
cfg.MinBlockInterval,
cfg.MaxBlockInterval)
@@ -418,10 +445,11 @@ func NewLattice(
pool: newBlockPool(cfg.NumChains),
data: data,
toModule: newTotalOrdering(
+ round,
uint64(cfg.K),
uint64(float32(cfg.NumChains-1)*cfg.PhiRatio+1),
cfg.NumChains),
- ctModule: newConsensusTimestamp(),
+ ctModule: newConsensusTimestamp(round, cfg.NumChains),
}
return
}
@@ -553,3 +581,21 @@ func (s *Lattice) NextPosition(chainID uint32) types.Position {
return s.data.nextPosition(chainID)
}
+
+// AppendConfig add new configs for upcoming rounds. If you add a config for
+// round R, next time you can only add the config for round R+1.
+func (s *Lattice) AppendConfig(round uint64, config *types.Config) (err error) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ if err = s.data.appendConfig(round, config); err != nil {
+ return
+ }
+ if err = s.toModule.appendConfig(round, config); err != nil {
+ return
+ }
+ if err = s.ctModule.appendConfig(round, config); err != nil {
+ return
+ }
+ return
+}
diff --git a/core/lattice_test.go b/core/lattice_test.go
index 2da17e4..1df4f4e 100644
--- a/core/lattice_test.go
+++ b/core/lattice_test.go
@@ -93,7 +93,10 @@ type LatticeTestSuite struct {
func (s *LatticeTestSuite) newTestLatticeMgr(
cfg *types.Config) *testLatticeMgr {
- var req = s.Require()
+ var (
+ req = s.Require()
+ round uint64
+ )
// Setup private key.
prvKey, err := ecdsa.NewPrivateKey()
req.Nil(err)
@@ -108,6 +111,7 @@ func (s *LatticeTestSuite) newTestLatticeMgr(
app: app,
db: db,
lattice: NewLattice(
+ round,
cfg,
NewAuthenticator(prvKey),
app,
@@ -150,6 +154,7 @@ func (s *LatticeTestSuite) prepareGenesisBlock(
func (s *LatticeTestSuite) genTestCase1() (data *latticeData) {
// Create new reliableBroadcast instance with 4 validators
var (
+ round uint64
b *types.Block
delivered []*types.Block
h common.Hash
@@ -158,7 +163,7 @@ func (s *LatticeTestSuite) genTestCase1() (data *latticeData) {
err error
)
- data = newLatticeData(chainNum, 2*time.Nanosecond, 1000*time.Second)
+ data = newLatticeData(round, chainNum, 2*time.Nanosecond, 1000*time.Second)
// Add genesis blocks.
for i := uint32(0); i < chainNum; i++ {
b = s.prepareGenesisBlock(i)
@@ -464,8 +469,9 @@ func (s *LatticeTestSuite) TestAreAllAcksInLattice() {
func (s *LatticeTestSuite) TestRandomIntensiveAcking() {
var (
+ round uint64
chainNum uint32 = 19
- data = newLatticeData(chainNum, 0, 1000*time.Second)
+ data = newLatticeData(round, chainNum, 0, 1000*time.Second)
req = s.Require()
delivered []*types.Block
extracted []*types.Block
@@ -503,6 +509,7 @@ func (s *LatticeTestSuite) TestRandomIntensiveAcking() {
func (s *LatticeTestSuite) TestRandomlyGeneratedBlocks() {
var (
+ round uint64
chainNum uint32 = 19
blockNum = 50
repeat = 20
@@ -528,7 +535,7 @@ func (s *LatticeTestSuite) TestRandomlyGeneratedBlocks() {
revealedHashesAsString := map[string]struct{}{}
deliveredHashesAsString := map[string]struct{}{}
for i := 0; i < repeat; i++ {
- data := newLatticeData(chainNum, 0, 1000*time.Second)
+ data := newLatticeData(round, chainNum, 0, 1000*time.Second)
deliveredHashes := common.Hashes{}
revealedHashes := common.Hashes{}
revealer.Reset()
@@ -604,12 +611,14 @@ func (s *LatticeTestSuite) TestRandomlyGeneratedBlocks() {
func (s *LatticeTestSuite) TestPrepareBlock() {
var (
+ round uint64
chainNum uint32 = 4
req = s.Require()
- data = newLatticeData(chainNum, 0, 3000*time.Second)
minInterval = 50 * time.Millisecond
delivered []*types.Block
err error
+ data = newLatticeData(
+ round, chainNum, 0, 3000*time.Second)
)
// Setup genesis blocks.
b00 := s.prepareGenesisBlock(0)
@@ -737,7 +746,7 @@ func (s *LatticeTestSuite) TestNextPosition() {
s.Equal(data.nextPosition(0), types.Position{ChainID: 0, Height: 4})
// Test 'NextPosition' method when lattice is empty.
- data = newLatticeData(4, 0, 1000*time.Second)
+ data = newLatticeData(0, 4, 0, 1000*time.Second)
s.Equal(data.nextPosition(0), types.Position{ChainID: 0, Height: 0})
}
diff --git a/core/test/governance.go b/core/test/governance.go
index 5c7ae02..95cd6f7 100644
--- a/core/test/governance.go
+++ b/core/test/governance.go
@@ -90,7 +90,6 @@ func (g *Governance) NodeSet(_ uint64) (
// Configuration returns the configuration at a given block height.
func (g *Governance) Configuration(_ uint64) *types.Config {
return &types.Config{
- NumShards: 1,
NumChains: uint32(len(g.privateKeys)),
LambdaBA: g.lambdaBA,
LambdaDKG: g.lambdaDKG,
diff --git a/core/total-ordering.go b/core/total-ordering.go
index 8bf9ad7..dd2d99c 100644
--- a/core/total-ordering.go
+++ b/core/total-ordering.go
@@ -39,7 +39,14 @@ var (
ErrChainIDNotRecognized = fmt.Errorf("chain ID not recognized")
)
-// totalOrderinWinRecord caches which chains this candidate
+// totalOrderingConfig is the configuration for total ordering.
+type totalOrderingConfig struct {
+ k uint64
+ phi uint64
+ numChains uint32
+}
+
+// totalOrderingWinRecord caches which chains this candidate
// wins another one based on their height vector.
type totalOrderingWinRecord struct {
wins []int8
@@ -579,10 +586,14 @@ type totalOrdering struct {
// candidateChainIDs records chain ID of all candidates.
candidateChainIDs []uint32
+
+ // configs keeps configuration for each round in continuous way.
+ configs []*totalOrderingConfig
+ minRound uint64
}
-func newTotalOrdering(k, phi uint64, chainNum uint32) *totalOrdering {
- return &totalOrdering{
+func newTotalOrdering(round, k, phi uint64, chainNum uint32) *totalOrdering {
+ to := &totalOrdering{
pendings: make(map[common.Hash]*types.Block),
k: k,
phi: phi,
@@ -595,6 +606,30 @@ func newTotalOrdering(k, phi uint64, chainNum uint32) *totalOrdering {
candidates: make([]*totalOrderingCandidateInfo, chainNum),
candidateChainIDs: make([]uint32, 0, chainNum),
}
+ to.configs = []*totalOrderingConfig{
+ &totalOrderingConfig{
+ k: k,
+ phi: phi,
+ numChains: chainNum,
+ }}
+ to.minRound = round
+ return to
+}
+
+// appendConfig add new configs for upcoming rounds. If you add a config for
+// round R, next time you can only add the config for round R+1.
+func (to *totalOrdering) appendConfig(
+ round uint64, config *types.Config) error {
+
+ if round != to.minRound+uint64(len(to.configs)) {
+ return ErrRoundNotIncreasing
+ }
+ to.configs = append(to.configs, &totalOrderingConfig{
+ numChains: config.NumChains,
+ k: uint64(config.K),
+ phi: uint64(float32(config.NumChains-1)*config.PhiRatio + 1),
+ })
+ return nil
}
// buildBlockRelation populates the acked according their acking relationships.
diff --git a/core/total-ordering_test.go b/core/total-ordering_test.go
index 478697b..347ef22 100644
--- a/core/total-ordering_test.go
+++ b/core/total-ordering_test.go
@@ -77,6 +77,7 @@ func (s *TotalOrderingTestSuite) TestBlockRelation() {
//
// The DAG used below is:
// A <- B <- C
+ var round uint64
nodes := test.GenerateRandomNodeIDs(5)
vID := nodes[0]
blockA := s.genGenesisBlock(nodes, 0, common.Hashes{})
@@ -101,7 +102,7 @@ func (s *TotalOrderingTestSuite) TestBlockRelation() {
Acks: common.NewSortedHashes(common.Hashes{blockB.Hash}),
}
- to := newTotalOrdering(1, 3, uint32(len(nodes)))
+ to := newTotalOrdering(round, 1, 3, uint32(len(nodes)))
s.checkNotDeliver(to, blockA)
s.checkNotDeliver(to, blockB)
s.checkNotDeliver(to, blockC)
@@ -241,6 +242,7 @@ func (s *TotalOrderingTestSuite) TestGrade() {
func (s *TotalOrderingTestSuite) TestCycleDetection() {
// Make sure we don't get hang by cycle from
// block's acks.
+ var round uint64
nodes := test.GenerateRandomNodeIDs(5)
// create blocks with cycles in acking relation.
@@ -282,7 +284,7 @@ func (s *TotalOrderingTestSuite) TestCycleDetection() {
b10.Acks = append(b10.Acks, b10.Hash)
// Make sure we won't hang when cycle exists.
- to := newTotalOrdering(1, 3, uint32(len(nodes)))
+ to := newTotalOrdering(round, 1, 3, uint32(len(nodes)))
s.checkNotDeliver(to, b00)
s.checkNotDeliver(to, b01)
s.checkNotDeliver(to, b02)
@@ -294,8 +296,9 @@ func (s *TotalOrderingTestSuite) TestCycleDetection() {
}
func (s *TotalOrderingTestSuite) TestNotValidDAGDetection() {
+ var round uint64
nodes := test.GenerateRandomNodeIDs(5)
- to := newTotalOrdering(1, 3, uint32(len(nodes)))
+ to := newTotalOrdering(round, 1, 3, uint32(len(nodes)))
b00 := s.genGenesisBlock(nodes, 0, common.Hashes{})
b01 := &types.Block{
@@ -326,8 +329,9 @@ func (s *TotalOrderingTestSuite) TestEarlyDeliver() {
// A B
// Even when B is not received, A should
// be able to be delivered.
+ var round uint64
nodes := test.GenerateRandomNodeIDs(5)
- to := newTotalOrdering(2, 3, uint32(len(nodes)))
+ to := newTotalOrdering(round, 2, 3, uint32(len(nodes)))
genNextBlock := func(b *types.Block) *types.Block {
return &types.Block{
ProposerID: b.ProposerID,
@@ -431,8 +435,9 @@ func (s *TotalOrderingTestSuite) TestEarlyDeliver() {
func (s *TotalOrderingTestSuite) TestBasicCaseForK2() {
// It's a handcrafted test case.
+ var round uint64
nodes := test.GenerateRandomNodeIDs(5)
- to := newTotalOrdering(2, 3, uint32(len(nodes)))
+ to := newTotalOrdering(round, 2, 3, uint32(len(nodes)))
// Setup blocks.
b00 := s.genGenesisBlock(nodes, 0, common.Hashes{})
b10 := s.genGenesisBlock(nodes, 1, common.Hashes{})
@@ -766,9 +771,10 @@ func (s *TotalOrderingTestSuite) TestBasicCaseForK0() {
// v v v v
// o o o <- o Height: 0
var (
+ round uint64
req = s.Require()
nodes = test.GenerateRandomNodeIDs(5)
- to = newTotalOrdering(0, 3, uint32(len(nodes)))
+ to = newTotalOrdering(round, 0, 3, uint32(len(nodes)))
)
// Setup blocks.
b00 := s.genGenesisBlock(nodes, 0, common.Hashes{})
@@ -938,6 +944,7 @@ func (s *TotalOrderingTestSuite) baseTestRandomlyGeneratedBlocks(
func (s *TotalOrderingTestSuite) TestRandomlyGeneratedBlocks() {
var (
+ round uint64
chainNum = uint32(13)
blockNum = 50
phi uint64 = 10
@@ -954,25 +961,25 @@ func (s *TotalOrderingTestSuite) TestRandomlyGeneratedBlocks() {
for _, gen := range ackingCountGenerators {
// Test for K=0.
constructor := func(chainNum uint32) *totalOrdering {
- return newTotalOrdering(0, phi, chainNum)
+ return newTotalOrdering(round, 0, phi, chainNum)
}
s.baseTestRandomlyGeneratedBlocks(
constructor, chainNum, blockNum, gen, repeat)
// Test for K=1,
constructor = func(chainNum uint32) *totalOrdering {
- return newTotalOrdering(1, phi, chainNum)
+ return newTotalOrdering(round, 1, phi, chainNum)
}
s.baseTestRandomlyGeneratedBlocks(
constructor, chainNum, blockNum, gen, repeat)
// Test for K=2,
constructor = func(chainNum uint32) *totalOrdering {
- return newTotalOrdering(2, phi, chainNum)
+ return newTotalOrdering(round, 2, phi, chainNum)
}
s.baseTestRandomlyGeneratedBlocks(
constructor, chainNum, blockNum, gen, repeat)
// Test for K=3,
constructor = func(chainNum uint32) *totalOrdering {
- return newTotalOrdering(3, phi, chainNum)
+ return newTotalOrdering(round, 3, phi, chainNum)
}
s.baseTestRandomlyGeneratedBlocks(
constructor, chainNum, blockNum, gen, repeat)
diff --git a/core/types/config.go b/core/types/config.go
index 0b85363..384041d 100644
--- a/core/types/config.go
+++ b/core/types/config.go
@@ -26,7 +26,6 @@ import (
// Config stands for Current Configuration Parameters.
type Config struct {
// Network related.
- NumShards uint32
NumChains uint32
// Lambda related.
@@ -50,8 +49,6 @@ type Config struct {
// Bytes returns []byte representation of Config.
func (c *Config) Bytes() []byte {
- binaryNumShards := make([]byte, 4)
- binary.LittleEndian.PutUint32(binaryNumShards, c.NumShards)
binaryNumChains := make([]byte, 4)
binary.LittleEndian.PutUint32(binaryNumChains, c.NumChains)
@@ -85,7 +82,6 @@ func (c *Config) Bytes() []byte {
uint64(c.MaxBlockInterval.Nanoseconds()))
enc := make([]byte, 0, 40)
- enc = append(enc, binaryNumShards...)
enc = append(enc, binaryNumChains...)
enc = append(enc, binaryLambdaBA...)
enc = append(enc, binaryLambdaDKG...)
diff --git a/integration_test/node.go b/integration_test/node.go
index 62901d6..c0c4fa1 100644
--- a/integration_test/node.go
+++ b/integration_test/node.go
@@ -85,6 +85,7 @@ func NewNode(
proposingLatency test.LatencyModel) *Node {
var (
+ round uint64
chainID = uint32(math.MaxUint32)
governanceConfig = gov.Configuration(0)
nodeSetKeys = gov.NodeSet(0)
@@ -115,6 +116,7 @@ func NewNode(
app: app,
db: db,
lattice: core.NewLattice(
+ round,
governanceConfig,
core.NewAuthenticator(privateKey),
app,
diff --git a/simulation/governance.go b/simulation/governance.go
index 8a83feb..dd4ef37 100644
--- a/simulation/governance.go
+++ b/simulation/governance.go
@@ -92,7 +92,6 @@ func (g *simGovernance) NodeSet(round uint64) (ret []crypto.PublicKey) {
// Configuration returns the configuration at a given round.
func (g *simGovernance) Configuration(round uint64) *types.Config {
return &types.Config{
- NumShards: 1,
NumChains: g.chainNum,
LambdaBA: g.lambdaBA,
LambdaDKG: g.lambdaDKG,