From f32a80a911965c9fd1506d5e393a424677ef08fc Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Mon, 23 Apr 2018 22:02:37 -0700 Subject: [PATCH] sharding: added tests for sampling committees Former-commit-id: abdaf09b36f79ea6dc90d2a1d6390b20fb1f2420 [formerly cd0aac8794f2c51e19bd839c0c04bdf4103731a4] Former-commit-id: 9a53826157411039ec2fad9adefd8623147b6725 --- sharding/contracts/sharding_manager.sol | 14 +- sharding/contracts/sharding_manager_test.go | 148 +++++++++++++++++++- 2 files changed, 149 insertions(+), 13 deletions(-) diff --git a/sharding/contracts/sharding_manager.sol b/sharding/contracts/sharding_manager.sol index 5d976ac99..c39037064 100644 --- a/sharding/contracts/sharding_manager.sol +++ b/sharding/contracts/sharding_manager.sol @@ -74,8 +74,8 @@ contract SMC { /// Checks if a notary with given shard id and period has been chosen as /// a committee member to vote for header added on to the main chain - function isNotaryInCommittee(uint shardId, uint period) public view returns(bool) { - uint currentPeriod = block.number / PERIOD_LENGTH; + function getNotaryInCommittee(uint shardId, uint _index) public view returns(address) { + uint period = block.number / PERIOD_LENGTH; // Determine notary pool length based on notary sample size uint sampleSize; @@ -87,14 +87,9 @@ contract SMC { // Get the most recent block number before the period started uint latestBlock = period * PERIOD_LENGTH - 1; + uint index = uint(keccak256(block.blockhash(latestBlock), _index, shardId)) % sampleSize; - for (uint i = 0; i < QUORUM_SIZE; i++) { - uint index = uint(keccak256(block.blockhash(latestBlock), index)) % sampleSize; - if (notaryPool[index] == msg.sender) { - return true; - } - } - return false; + return notaryPool[index]; } /// Registers notary to notatery registry, locks in the notary deposit, @@ -148,6 +143,7 @@ contract SMC { notaryRegistry[notaryAddress].deregisteredPeriod = deregisteredPeriod; stackPush(index); + delete notaryPool[index]; --notaryPoolLength; emit NotaryDeregistered(notaryAddress, index, deregisteredPeriod); return true; diff --git a/sharding/contracts/sharding_manager_test.go b/sharding/contracts/sharding_manager_test.go index 4848941e4..9dd735474 100644 --- a/sharding/contracts/sharding_manager_test.go +++ b/sharding/contracts/sharding_manager_test.go @@ -49,7 +49,6 @@ func TestContractCreation(t *testing.T) { } } -// Test register notary func TestNotaryRegister(t *testing.T) { const notaryCount = 3 var notaryPoolAddr [notaryCount]common.Address @@ -327,7 +326,7 @@ func TestNotaryRelease(t *testing.T) { } // Fast forward until lockup ends - for i := 0; i < int(sharding.CollatorLockupLength*sharding.PeriodLength+1); i++ { + for i := 0; i < int(sharding.NotaryLockupLength*sharding.PeriodLength+1); i++ { backend.Commit() } @@ -394,7 +393,6 @@ func TestNotaryInstantRelease(t *testing.T) { t.Fatalf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries) } - // Notary 0 releases before lockup ends _, err = smc.ReleaseNotary(txOpts) backend.Commit() @@ -413,4 +411,146 @@ func TestNotaryInstantRelease(t *testing.T) { if balance.Cmp(notaryDeposit) > 0 { t.Fatalf("Notary received deposit before lockup ends") } -} \ No newline at end of file +} + +func TestCommitteeListsAreDifferent(t *testing.T) { + const notaryCount = 1000 + var notaryPoolAddr [notaryCount]common.Address + var notaryPoolPrivKeys [notaryCount]*ecdsa.PrivateKey + var txOpts [notaryCount]*bind.TransactOpts + genesis := make(core.GenesisAlloc) + + for i := 0; i < notaryCount; i++ { + key, _ := crypto.GenerateKey() + notaryPoolPrivKeys[i] = key + notaryPoolAddr[i] = crypto.PubkeyToAddress(key.PublicKey) + txOpts[i] = bind.NewKeyedTransactor(key) + txOpts[i].Value = notaryDeposit + genesis[notaryPoolAddr[i]] = core.GenesisAccount{ + Balance: accountBalance2000Eth, + } + } + + backend := backends.NewSimulatedBackend(genesis) + _, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0]) + + // register 1000 notaries to SMC + for i := 0; i < notaryCount; i++ { + smc.RegisterNotary(txOpts[i]) + backend.Commit() + } + + numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{}) + if numNotaries.Cmp(big.NewInt(1000)) != 0 { + t.Fatalf("Incorrect count from notary pool. Want: 1000, Got: %v", numNotaries) + } + + // get a list of sampled notaries from shard 0 + var shard0CommitteeList []string + for i := 0; i < int(sharding.NotaryCommitSize); i++ { + addr, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(0), big.NewInt(int64(i))) + shard0CommitteeList = append(shard0CommitteeList, addr.String()) + } + + // get a list of sampled notaries from shard 1, verify it's not identical to shard 0 + for i := 0; i < int(sharding.NotaryCommitSize); i++ { + addr, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(1), big.NewInt(int64(i))) + if shard0CommitteeList[i] == addr.String() { + t.Log(i) + t.Fatalf("Shard 0 committee list is identical to shard 1's committee list") + } + } +} + +func TestGetCommitteeWithNonMember(t *testing.T) { + const notaryCount = 11 + var notaryPoolAddr [notaryCount]common.Address + var notaryPoolPrivKeys [notaryCount]*ecdsa.PrivateKey + var txOpts [notaryCount]*bind.TransactOpts + genesis := make(core.GenesisAlloc) + + for i := 0; i < notaryCount; i++ { + key, _ := crypto.GenerateKey() + notaryPoolPrivKeys[i] = key + notaryPoolAddr[i] = crypto.PubkeyToAddress(key.PublicKey) + txOpts[i] = bind.NewKeyedTransactor(key) + txOpts[i].Value = notaryDeposit + genesis[notaryPoolAddr[i]] = core.GenesisAccount{ + Balance: accountBalance2000Eth, + } + } + + backend := backends.NewSimulatedBackend(genesis) + _, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0]) + + // registers 10 notaries to SMC, leave 1 address free + for i := 0; i < 10; i++ { + smc.RegisterNotary(txOpts[i]) + backend.Commit() + } + + numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{}) + if numNotaries.Cmp(big.NewInt(10)) != 0 { + t.Fatalf("Incorrect count from notary pool. Want: 135, Got: %v", numNotaries) + } + + // verify the free address can't be sampled from the committee list + for i := 0; i < 10; i++ { + addr, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(0), big.NewInt(int64(i))) + if notaryPoolAddr[10].String() == addr.String() { + t.Fatalf("Account %s is not a notary", notaryPoolAddr[10].String()) + } + } + +} + +func TestGetCommitteeAfterDeregisters(t *testing.T) { + const notaryCount = 10 + var notaryPoolAddr [notaryCount]common.Address + var notaryPoolPrivKeys [notaryCount]*ecdsa.PrivateKey + var txOpts [notaryCount]*bind.TransactOpts + genesis := make(core.GenesisAlloc) + + for i := 0; i < notaryCount; i++ { + key, _ := crypto.GenerateKey() + notaryPoolPrivKeys[i] = key + notaryPoolAddr[i] = crypto.PubkeyToAddress(key.PublicKey) + txOpts[i] = bind.NewKeyedTransactor(key) + txOpts[i].Value = notaryDeposit + genesis[notaryPoolAddr[i]] = core.GenesisAccount{ + Balance: accountBalance2000Eth, + } + } + + backend := backends.NewSimulatedBackend(genesis) + _, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0]) + + // register 10 notaries to SMC + for i := 0; i < 10; i++ { + smc.RegisterNotary(txOpts[i]) + backend.Commit() + } + + numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{}) + if numNotaries.Cmp(big.NewInt(10)) != 0 { + t.Fatalf("Incorrect count from notary pool. Want: 10, Got: %v", numNotaries) + } + + // deregister notary 0 from SMC + txOpts[0].Value = big.NewInt(0) + smc.DeregisterNotary(txOpts[0]) + backend.Commit() + + numNotaries, _ = smc.NotaryPoolLength(&bind.CallOpts{}) + if numNotaries.Cmp(big.NewInt(9)) != 0 { + t.Fatalf("Incorrect count from notary pool. Want: 9, Got: %v", numNotaries) + } + + // verify degistered notary 0 can't be sampled + for i := 0; i < 10; i++ { + addr, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(0), big.NewInt(int64(i))) + if notaryPoolAddr[0].String() == addr.String() { + t.Fatalf("Account %s is not a notary", notaryPoolAddr[0].String()) + } + } +}