mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-04 08:44:28 +00:00
Merge pull request #113 from prestonvanloon/fix/flag
Add sharding flags option to usage. Former-commit-id: 172a2e8e071fc49557211c264695cc877a3cb537 [formerly dbe63e90a3b1eec1042f5bfe37dec22e6531f562] Former-commit-id: 71aaf4a610d475f3e1aab9218fcc6339b41c96f9
This commit is contained in:
commit
5c7ce3af5e
@ -83,6 +83,12 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
utils.LightKDFFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "SHARDING",
|
||||
Flags: []cli.Flag{
|
||||
utils.DepositFlag,
|
||||
},
|
||||
},
|
||||
{Name: "DEVELOPER CHAIN",
|
||||
Flags: []cli.Flag{
|
||||
utils.DeveloperFlag,
|
||||
|
@ -55,6 +55,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/sharding"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
@ -536,7 +537,7 @@ var (
|
||||
//Sharding Settings
|
||||
DepositFlag = cli.BoolFlag{
|
||||
Name: "deposit",
|
||||
Usage: "To become a notary with your sharding client, 100 ETH will be deposited from user's account into SMC ",
|
||||
Usage: "To become a notary with your sharding client, " + new(big.Int).Div(sharding.NotaryDeposit, new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)).String() + " ETH will be deposited into SMC",
|
||||
}
|
||||
)
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -2,10 +2,11 @@ pragma solidity ^0.4.23;
|
||||
|
||||
|
||||
contract SMC {
|
||||
event HeaderAdded(uint indexed shardId, bytes32 chunkRoot, int128 period, address proposerAddress);
|
||||
event HeaderAdded(uint indexed shardId, bytes32 chunkRoot, uint period, address proposerAddress);
|
||||
event NotaryRegistered(address notary, uint poolIndex);
|
||||
event NotaryDeregistered(address notary, uint poolIndex, uint deregisteredPeriod);
|
||||
event NotaryReleased(address notary, uint poolIndex);
|
||||
event VoteSubmitted(uint indexed shardId, bytes32 chunkRoot, uint period, address notaryAddress);
|
||||
|
||||
struct Notary {
|
||||
uint deregisteredPeriod;
|
||||
@ -13,39 +14,36 @@ contract SMC {
|
||||
bool deposited;
|
||||
}
|
||||
|
||||
struct CollationHeader {
|
||||
uint shardId; // Number of the shard ID
|
||||
struct CollationRecord {
|
||||
bytes32 chunkRoot; // Root hash of the collation body
|
||||
uint period; // Period which header should be included
|
||||
address proposerAddress;
|
||||
}
|
||||
|
||||
// Packed variables to be used in addHeader
|
||||
struct HeaderVars {
|
||||
bytes32 entireHeaderHash;
|
||||
int score;
|
||||
address notaryAddr;
|
||||
bool isNewHead;
|
||||
address proposer; // Address of the proposer
|
||||
bool isElected; // True if the collation has reached quorum size
|
||||
}
|
||||
|
||||
// Notary state variables
|
||||
address[] public notaryPool;
|
||||
|
||||
// notaryAddress => notaryStruct
|
||||
mapping (address => Notary) public notaryRegistry;
|
||||
// shardId => (headerHash => treeRoot)
|
||||
mapping (uint => mapping (bytes32 => bytes32)) public collationTrees;
|
||||
// shardId => (headerHash => collationHeader)
|
||||
mapping (int => mapping (bytes32 => CollationHeader)) public collationHeaders;
|
||||
// shardId => headerHash
|
||||
mapping (int => bytes32) shardHead;
|
||||
|
||||
// Number of notaries
|
||||
// number of notaries
|
||||
uint public notaryPoolLength;
|
||||
// current vote count of each shard
|
||||
// first 31 bytes are bitfield of notary's vote
|
||||
// last 1 byte is the number of the total votes
|
||||
mapping (uint => bytes32) public currentVote;
|
||||
|
||||
// Collation state variables
|
||||
// shardId => (period => CollationHeader), collation records been appended by proposer
|
||||
mapping (uint => mapping (uint => CollationRecord)) public collationRecords;
|
||||
// shardId => period, last period of the submitted collation header
|
||||
mapping (uint => uint) public lastSubmittedCollation;
|
||||
// shardId => period, last period of the approved collation header
|
||||
mapping (uint => uint) public lastApprovedCollation;
|
||||
|
||||
// Internal help functions variables
|
||||
// Stack of empty notary slot indicies
|
||||
uint[] emptySlotsStack;
|
||||
// Top index of the stack
|
||||
uint emptySlotsStackTop;
|
||||
|
||||
// Notary sample size at current period and next period
|
||||
uint currentPeriodNotarySampleSize;
|
||||
uint nextPeriodNotarySampleSize;
|
||||
@ -69,12 +67,9 @@ contract SMC {
|
||||
// Threshold(number of notaries in committee) for a proposal to be accepted
|
||||
uint constant QUORUM_SIZE = 90;
|
||||
|
||||
// Log the latest period number of the shard
|
||||
mapping (int => int) public periodHead;
|
||||
|
||||
/// 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 getNotaryInCommittee(uint shardId, uint _index) public view returns(address) {
|
||||
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
|
||||
@ -88,7 +83,7 @@ contract SMC {
|
||||
// Get the most recent block number before the period started
|
||||
uint latestBlock = period * PERIOD_LENGTH - 1;
|
||||
uint latestBlockHash = uint(block.blockhash(latestBlock));
|
||||
uint index = uint(keccak256(latestBlockHash, _index, shardId)) % sampleSize;
|
||||
uint index = uint(keccak256(latestBlockHash, _index, _shardId)) % sampleSize;
|
||||
|
||||
return notaryPool[index];
|
||||
}
|
||||
@ -100,7 +95,6 @@ contract SMC {
|
||||
require(!notaryRegistry[notaryAddress].deposited);
|
||||
require(msg.value == NOTARY_DEPOSIT);
|
||||
|
||||
// Track the numbers of participating notaries in between periods
|
||||
updateNotarySampleSize();
|
||||
|
||||
uint index;
|
||||
@ -136,7 +130,6 @@ contract SMC {
|
||||
require(notaryRegistry[notaryAddress].deposited);
|
||||
require(notaryPool[index] == notaryAddress);
|
||||
|
||||
// Track the numbers of participating notaries in between periods
|
||||
updateNotarySampleSize();
|
||||
|
||||
uint deregisteredPeriod = block.number / PERIOD_LENGTH;
|
||||
@ -162,31 +155,88 @@ contract SMC {
|
||||
emit NotaryReleased(notaryAddress, index);
|
||||
}
|
||||
|
||||
/// Calcuates the hash of the header from the input parameters
|
||||
function computeHeaderHash(
|
||||
uint256 shardId,
|
||||
bytes32 parentHash,
|
||||
bytes32 chunkRoot,
|
||||
uint256 period,
|
||||
address proposerAddress
|
||||
) public returns(bytes32) {
|
||||
/*
|
||||
TODO: Calculate the hash of the collation header from the input parameters
|
||||
*/
|
||||
}
|
||||
|
||||
/// Add collation header to the main chain, anyone can call this function. It emits a log
|
||||
function addHeader(
|
||||
uint _shardId,
|
||||
uint period,
|
||||
bytes32 chunkRoot,
|
||||
address proposerAddress
|
||||
uint _period,
|
||||
bytes32 _chunkRoot
|
||||
) public {
|
||||
/*
|
||||
TODO: Anyone can call this at any time. The first header
|
||||
to get included for a given shard in a given period gets in,
|
||||
all others don’t. This function just emits a log
|
||||
*/
|
||||
require((_shardId >= 0) && (_shardId < SHARD_COUNT));
|
||||
require(_period == block.number / PERIOD_LENGTH);
|
||||
require(_period > lastSubmittedCollation[_shardId]);
|
||||
|
||||
updateNotarySampleSize();
|
||||
|
||||
collationRecords[_shardId][_period] = CollationRecord({
|
||||
chunkRoot: _chunkRoot,
|
||||
proposer: msg.sender,
|
||||
isElected: false
|
||||
});
|
||||
|
||||
lastSubmittedCollation[_shardId] = block.number / PERIOD_LENGTH;
|
||||
delete currentVote[_shardId];
|
||||
|
||||
emit HeaderAdded(_shardId, _chunkRoot, _period, msg.sender);
|
||||
}
|
||||
|
||||
/// Sampled notary can call the following funtion to submit vote,
|
||||
/// a vote log will be emitted for client to monitor
|
||||
function submitVote(
|
||||
uint _shardId,
|
||||
uint _period,
|
||||
uint _index,
|
||||
bytes32 _chunkRoot
|
||||
) public {
|
||||
require((_shardId >= 0) && (_shardId < SHARD_COUNT));
|
||||
require(_period == block.number / PERIOD_LENGTH);
|
||||
require(_period == lastSubmittedCollation[_shardId]);
|
||||
require(_index < COMMITTEE_SIZE);
|
||||
require(_chunkRoot == collationRecords[_shardId][_period].chunkRoot);
|
||||
require(notaryRegistry[msg.sender].deposited);
|
||||
require(!hasVoted(_shardId, _index));
|
||||
require(getNotaryInCommittee(_shardId, _index) == msg.sender);
|
||||
|
||||
castVote(_shardId, _index);
|
||||
uint voteCount = getVoteCount(_shardId);
|
||||
if (voteCount >= QUORUM_SIZE) {
|
||||
lastApprovedCollation[_shardId] = _period;
|
||||
collationRecords[_shardId][_period].isElected = true;
|
||||
}
|
||||
emit VoteSubmitted(_shardId, _chunkRoot, _period, msg.sender);
|
||||
}
|
||||
|
||||
/// Returns total vote count of currentVote
|
||||
/// the vote count is stored in the last byte of currentVote
|
||||
function getVoteCount(uint _shardId) public view returns (uint) {
|
||||
uint votes = uint(currentVote[_shardId]);
|
||||
// Extra the last byte of currentVote
|
||||
return votes % 256;
|
||||
}
|
||||
|
||||
/// Check if a bit is set, this function is used to check
|
||||
/// if a notary has casted the vote. Right shift currentVote by index
|
||||
/// and AND with 1, return true if voted, false if not
|
||||
function hasVoted(uint _shardId, uint _index) public view returns (bool) {
|
||||
uint votes = uint(currentVote[_shardId]);
|
||||
// Shift currentVote to right by given index
|
||||
votes = votes >> (255 - _index);
|
||||
// AND 1 to neglect everything but bit 0, then compare to 1
|
||||
return votes & 1 == 1;
|
||||
}
|
||||
|
||||
/// Check if the empty slots stack is empty
|
||||
function emptyStack() internal view returns(bool) {
|
||||
return emptySlotsStackTop == 0;
|
||||
}
|
||||
|
||||
/// Save one uint into the empty slots stack for notary to use later
|
||||
function stackPush(uint _index) internal {
|
||||
if (emptySlotsStack.length == emptySlotsStackTop)
|
||||
emptySlotsStack.push(_index);
|
||||
else
|
||||
emptySlotsStack[emptySlotsStackTop] = _index;
|
||||
|
||||
++emptySlotsStackTop;
|
||||
}
|
||||
|
||||
/// To keep track of notary size in between periods, we call updateNotarySampleSize
|
||||
@ -200,25 +250,24 @@ contract SMC {
|
||||
sampleSizeLastUpdatedPeriod = currentPeriod;
|
||||
}
|
||||
|
||||
/// Check if the empty slots stack is empty
|
||||
function emptyStack() internal view returns(bool) {
|
||||
return emptySlotsStackTop == 0;
|
||||
}
|
||||
|
||||
/// Save one uint into the empty slots stack for notary to use later
|
||||
function stackPush(uint index) internal {
|
||||
if (emptySlotsStack.length == emptySlotsStackTop)
|
||||
emptySlotsStack.push(index);
|
||||
else
|
||||
emptySlotsStack[emptySlotsStackTop] = index;
|
||||
|
||||
++emptySlotsStackTop;
|
||||
}
|
||||
|
||||
/// Get one uint out of the empty slots stack for notary index
|
||||
function stackPop() internal returns(uint) {
|
||||
require(emptySlotsStackTop > 1);
|
||||
--emptySlotsStackTop;
|
||||
return emptySlotsStack[emptySlotsStackTop];
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the index bit to one, notary uses this function to cast its vote,
|
||||
/// after the notary casts its vote, we increase currentVote's count by 1
|
||||
function castVote(uint _shardId, uint _index) internal {
|
||||
uint votes = uint(currentVote[_shardId]);
|
||||
// Get the bitfield by shifting 1 to the index
|
||||
uint indexToFlag = 2 ** (255 - _index);
|
||||
// OR with currentVote to cast notary index to 1
|
||||
votes = votes | indexToFlag;
|
||||
// Update vote count
|
||||
votes++;
|
||||
currentVote[_shardId] = bytes32(votes);
|
||||
}
|
||||
|
||||
}
|
@ -34,13 +34,14 @@ var (
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
// deploySMCContract is a helper function for deploying SMC.
|
||||
func deploySMCContract(backend *backends.SimulatedBackend, key *ecdsa.PrivateKey) (common.Address, *types.Transaction, *SMC, error) {
|
||||
transactOpts := bind.NewKeyedTransactor(key)
|
||||
defer backend.Commit()
|
||||
return DeploySMC(transactOpts, backend)
|
||||
}
|
||||
|
||||
// Test creating the SMC contract.
|
||||
// TestContractCreation tests SMC smart contract can successfully be deployed.
|
||||
func TestContractCreation(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
@ -51,6 +52,7 @@ func TestContractCreation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNotaryRegister tests notary registers in a normal condition.
|
||||
func TestNotaryRegister(t *testing.T) {
|
||||
const notaryCount = 3
|
||||
var notaryPoolAddr [notaryCount]common.Address
|
||||
@ -58,7 +60,7 @@ func TestNotaryRegister(t *testing.T) {
|
||||
var txOpts [notaryCount]*bind.TransactOpts
|
||||
genesis := make(core.GenesisAlloc)
|
||||
|
||||
// initializes back end with 3 accounts and each with 2000 eth balances.
|
||||
// Initializes back end with 3 accounts and each with 2000 eth balances.
|
||||
for i := 0; i < notaryCount; i++ {
|
||||
key, _ := crypto.GenerateKey()
|
||||
notaryPoolPrivKeys[i] = key
|
||||
@ -137,6 +139,7 @@ func TestNotaryRegister(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNotaryRegisterInsufficientEther tests notary registers with insufficient deposit.
|
||||
func TestNotaryRegisterInsufficientEther(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
@ -162,6 +165,7 @@ func TestNotaryRegisterInsufficientEther(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
// TestNotaryDoubleRegisters tests notary registers twice.
|
||||
func TestNotaryDoubleRegisters(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
@ -195,6 +199,7 @@ func TestNotaryDoubleRegisters(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
// TestNotaryDeregister tests notary deregisters in a normal condition.
|
||||
func TestNotaryDeregister(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
@ -243,6 +248,7 @@ func TestNotaryDeregister(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNotaryDeregisterThenRegister tests notary deregisters then registers before lock up ends.
|
||||
func TestNotaryDeregisterThenRegister(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
@ -288,6 +294,7 @@ func TestNotaryDeregisterThenRegister(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNotaryRelease tests notary releases in a normal condition.
|
||||
func TestNotaryRelease(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
@ -359,6 +366,7 @@ func TestNotaryRelease(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNotaryInstantRelease tests notary releases before lockup ends.
|
||||
func TestNotaryInstantRelease(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
@ -419,8 +427,9 @@ func TestNotaryInstantRelease(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestCommitteeListsAreDifferent tests different shards have different notary committee.
|
||||
func TestCommitteeListsAreDifferent(t *testing.T) {
|
||||
const notaryCount = 10000
|
||||
const notaryCount = 1000
|
||||
var notaryPoolAddr [notaryCount]common.Address
|
||||
var notaryPoolPrivKeys [notaryCount]*ecdsa.PrivateKey
|
||||
var txOpts [notaryCount]*bind.TransactOpts
|
||||
@ -441,33 +450,29 @@ func TestCommitteeListsAreDifferent(t *testing.T) {
|
||||
backend := backends.NewSimulatedBackend(genesis)
|
||||
_, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0])
|
||||
|
||||
// register 10000 notaries to SMC.
|
||||
// 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(10000)) != 0 {
|
||||
if numNotaries.Cmp(big.NewInt(notaryCount)) != 0 {
|
||||
t.Errorf("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() {
|
||||
// Compare sampled first 5 notaries of shard 0 to shard 1, they should not be identical.
|
||||
for i := 0; i < 5; i++ {
|
||||
addr0, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(0), big.NewInt(int64(i)))
|
||||
addr1, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(1), big.NewInt(int64(i)))
|
||||
if addr0 == addr1 {
|
||||
t.Errorf("Shard 0 committee list is identical to shard 1's committee list")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestGetCommitteeWithNonMember tests unregistered notary tries to be in the committee.
|
||||
func TestGetCommitteeWithNonMember(t *testing.T) {
|
||||
const notaryCount = 11
|
||||
var notaryPoolAddr [notaryCount]common.Address
|
||||
@ -475,7 +480,7 @@ func TestGetCommitteeWithNonMember(t *testing.T) {
|
||||
var txOpts [notaryCount]*bind.TransactOpts
|
||||
genesis := make(core.GenesisAlloc)
|
||||
|
||||
// initialize back end with 11 accounts and each with 2000 eth balances.
|
||||
// Initialize back end with 11 accounts and each with 2000 eth balances.
|
||||
for i := 0; i < notaryCount; i++ {
|
||||
key, _ := crypto.GenerateKey()
|
||||
notaryPoolPrivKeys[i] = key
|
||||
@ -490,7 +495,7 @@ func TestGetCommitteeWithNonMember(t *testing.T) {
|
||||
backend := backends.NewSimulatedBackend(genesis)
|
||||
_, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0])
|
||||
|
||||
// register 10 notaries to SMC, leave 1 address free.
|
||||
// Register 10 notaries to SMC, leave 1 address free.
|
||||
for i := 0; i < 10; i++ {
|
||||
smc.RegisterNotary(txOpts[i])
|
||||
backend.Commit()
|
||||
@ -501,7 +506,7 @@ func TestGetCommitteeWithNonMember(t *testing.T) {
|
||||
t.Fatalf("Incorrect count from notary pool. Want: 135, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// verify the unregistered account is not in the notary pool list.
|
||||
// Verify the unregistered account is not in the notary pool 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() {
|
||||
@ -511,6 +516,7 @@ func TestGetCommitteeWithNonMember(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
// TestGetCommitteeAfterDeregisters tests notary tries to be in committee after deregistered.
|
||||
func TestGetCommitteeAfterDeregisters(t *testing.T) {
|
||||
const notaryCount = 10
|
||||
var notaryPoolAddr [notaryCount]common.Address
|
||||
@ -518,7 +524,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
|
||||
var txOpts [notaryCount]*bind.TransactOpts
|
||||
genesis := make(core.GenesisAlloc)
|
||||
|
||||
// initialize back end with 10 accounts and each with 2000 eth balances.
|
||||
// Initialize back end with 10 accounts and each with 2000 eth balances.
|
||||
for i := 0; i < notaryCount; i++ {
|
||||
key, _ := crypto.GenerateKey()
|
||||
notaryPoolPrivKeys[i] = key
|
||||
@ -533,7 +539,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
|
||||
backend := backends.NewSimulatedBackend(genesis)
|
||||
_, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0])
|
||||
|
||||
// register 10 notaries to SMC.
|
||||
// Register 10 notaries to SMC.
|
||||
for i := 0; i < 10; i++ {
|
||||
smc.RegisterNotary(txOpts[i])
|
||||
backend.Commit()
|
||||
@ -544,7 +550,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 10, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// deregister notary 0 from SMC.
|
||||
// Deregister notary 0 from SMC.
|
||||
txOpts[0].Value = big.NewInt(0)
|
||||
smc.DeregisterNotary(txOpts[0])
|
||||
backend.Commit()
|
||||
@ -554,7 +560,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 9, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// verify degistered notary 0 is not in the notary pool list.
|
||||
// Verify degistered notary 0 is not in the notary pool list.
|
||||
for i := 0; i < 10; i++ {
|
||||
addr, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(0), big.NewInt(int64(i)))
|
||||
if notaryPoolAddr[0].String() == addr.String() {
|
||||
@ -562,3 +568,419 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNormalAddHeader tests proposer add header in normal condition.
|
||||
func TestNormalAddHeader(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
shard0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
_, err := smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
p, err := smc.LastSubmittedCollation(&bind.CallOpts{}, shard0)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't get last submitted collation's period number: %v", err)
|
||||
}
|
||||
if p.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect last period, when header was added. Got: %v", p)
|
||||
}
|
||||
|
||||
cr, err := smc.CollationRecords(&bind.CallOpts{}, shard0, period1)
|
||||
if cr.ChunkRoot != chunkRoot {
|
||||
t.Errorf("Chunkroot mismatched. Want: %v, Got: %v", chunkRoot, cr)
|
||||
}
|
||||
|
||||
// Fast forward to the next period. Period 2.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 2 and chunkroot 0xB.
|
||||
period2 := big.NewInt(2)
|
||||
chunkRoot = [32]byte{'B'}
|
||||
_, err = smc.AddHeader(txOpts, shard0, period2, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
p, err = smc.LastSubmittedCollation(&bind.CallOpts{}, shard0)
|
||||
if p.Cmp(big.NewInt(2)) != 0 {
|
||||
t.Errorf("Incorrect last period, when header was added. Got: %v", p)
|
||||
}
|
||||
|
||||
cr, err = smc.CollationRecords(&bind.CallOpts{}, shard0, period2)
|
||||
if cr.ChunkRoot != chunkRoot {
|
||||
t.Errorf("Chunkroot mismatched. Want: %v, Got: %v", chunkRoot, cr)
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 1, period 2 and chunkroot 0xC.
|
||||
shard1 := big.NewInt(1)
|
||||
chunkRoot = [32]byte{'C'}
|
||||
_, err = smc.AddHeader(txOpts, shard1, period2, chunkRoot)
|
||||
backend.Commit()
|
||||
|
||||
p, err = smc.LastSubmittedCollation(&bind.CallOpts{}, shard1)
|
||||
if p.Cmp(big.NewInt(2)) != 0 {
|
||||
t.Errorf("Incorrect last period, when header was added. Got: %v", p)
|
||||
}
|
||||
|
||||
cr, err = smc.CollationRecords(&bind.CallOpts{}, shard1, period2)
|
||||
if cr.ChunkRoot != chunkRoot {
|
||||
t.Errorf("Chunkroot mismatched. Want: %v, Got: %v", cr, chunkRoot)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddTwoHeadersAtSamePeriod tests we can't add two headers within the same period.
|
||||
func TestAddTwoHeadersAtSamePeriod(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
shard0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
_, err := smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
p, err := smc.LastSubmittedCollation(&bind.CallOpts{}, shard0)
|
||||
if p.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect last period, when header was added. Got: %v", p)
|
||||
}
|
||||
cr, err := smc.CollationRecords(&bind.CallOpts{}, shard0, period1)
|
||||
if cr.ChunkRoot != chunkRoot {
|
||||
t.Errorf("Chunkroot mismatched. Want: %v, Got: %v", chunkRoot, cr)
|
||||
}
|
||||
|
||||
// Proposer attempts to add another header chunkroot 0xB on the same period for the same shard.
|
||||
chunkRoot = [32]byte{'B'}
|
||||
_, err = smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err == nil {
|
||||
t.Errorf("Proposer is not allowed to add 2 headers within same period")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddHeadersAtWrongPeriod tests proposer adds header in the wrong period.
|
||||
func TestAddHeadersAtWrongPeriod(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header at wrong period, shard 0, period 0 and chunkroot 0xA.
|
||||
period0 := big.NewInt(0)
|
||||
shard0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
chunkRootEmpty := [32]byte{}
|
||||
_, err := smc.AddHeader(txOpts, shard0, period0, chunkRoot)
|
||||
if err == nil {
|
||||
t.Errorf("Proposer adds header at wrong period should have failed")
|
||||
}
|
||||
|
||||
cr, err := smc.CollationRecords(&bind.CallOpts{}, shard0, period0)
|
||||
if cr.ChunkRoot != chunkRootEmpty {
|
||||
t.Errorf("Chunkroot mismatched. Want: %v, Got: %v", chunkRoot, cr)
|
||||
}
|
||||
|
||||
// Proposer adds header at wrong period, shard 0, period 2 and chunkroot 0xA.
|
||||
period2 := big.NewInt(2)
|
||||
_, err = smc.AddHeader(txOpts, shard0, period2, chunkRoot)
|
||||
if err == nil {
|
||||
t.Errorf("Proposer adds header at wrong period should have failed")
|
||||
}
|
||||
|
||||
cr, err = smc.CollationRecords(&bind.CallOpts{}, shard0, period2)
|
||||
if cr.ChunkRoot != chunkRootEmpty {
|
||||
t.Errorf("Chunkroot mismatched. Want: %v, Got: %v", chunkRoot, cr)
|
||||
}
|
||||
|
||||
// Proposer adds header at correct period, shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
_, err = smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
|
||||
cr, err = smc.CollationRecords(&bind.CallOpts{}, shard0, period1)
|
||||
if cr.ChunkRoot != chunkRootEmpty {
|
||||
t.Errorf("Chunkroot mismatched. Want: %v, Got: %v", chunkRoot, cr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubmitVote tests notary submit votes in normal condition.
|
||||
func TestSubmitVote(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
txOpts.Value = notaryDeposit
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Notary 0 registers.
|
||||
smc.RegisterNotary(txOpts)
|
||||
backend.Commit()
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
shard0 := big.NewInt(0)
|
||||
index0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
txOpts.Value = big.NewInt(0)
|
||||
_, err := smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Notary 0 votes on header.
|
||||
c, err := smc.GetVoteCount(&bind.CallOpts{}, shard0)
|
||||
if err != nil {
|
||||
t.Fatalf("Get notary vote count failed: %v", err)
|
||||
}
|
||||
if c.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect notary vote count, want: 0, got: %v", c)
|
||||
}
|
||||
|
||||
_, err = smc.SubmitVote(txOpts, shard0, period1, index0, chunkRoot)
|
||||
backend.Commit()
|
||||
if err != nil {
|
||||
t.Fatalf("Notary submits vote failed: %v", err)
|
||||
}
|
||||
|
||||
// Check notary 0's vote is correctly casted.
|
||||
v, err := smc.HasVoted(&bind.CallOpts{}, shard0, index0)
|
||||
if err != nil {
|
||||
t.Fatalf("Check notary's vote failed: %v", err)
|
||||
}
|
||||
if !v {
|
||||
t.Errorf("Notary's indexd bit did not cast to 1 in index %v", index0)
|
||||
}
|
||||
c, err = smc.GetVoteCount(&bind.CallOpts{}, shard0)
|
||||
if c.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect notary vote count, want: 1, got: %v", c)
|
||||
}
|
||||
|
||||
// Check header's submitted with the current period, should be period 1.
|
||||
p, err := smc.LastSubmittedCollation(&bind.CallOpts{}, shard0)
|
||||
if err != nil {
|
||||
t.Fatalf("Get period of last submitted header failed: %v", err)
|
||||
}
|
||||
if p.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect period submitted, want: 1, got: %v", p)
|
||||
}
|
||||
|
||||
// Check header's approved with the current period, should be period 0.
|
||||
p, err = smc.LastApprovedCollation(&bind.CallOpts{}, shard0)
|
||||
if err != nil {
|
||||
t.Fatalf("Get period of last approved header failed: %v", err)
|
||||
}
|
||||
if p.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect period submitted, want: 0, got: %v", p)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestSubmitVoteTwice tests notary tries to submit same vote twice.
|
||||
func TestSubmitVoteTwice(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
txOpts.Value = notaryDeposit
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Notary 0 registers.
|
||||
smc.RegisterNotary(txOpts)
|
||||
backend.Commit()
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
shard0 := big.NewInt(0)
|
||||
index0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
txOpts.Value = big.NewInt(0)
|
||||
_, err := smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Notary 0 votes on header.
|
||||
smc.SubmitVote(txOpts, shard0, period1, index0, chunkRoot)
|
||||
backend.Commit()
|
||||
|
||||
// Check notary 0's vote is correctly casted.
|
||||
c, _ := smc.GetVoteCount(&bind.CallOpts{}, shard0)
|
||||
if c.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect notary vote count, want: 1, got: %v", c)
|
||||
}
|
||||
|
||||
// Notary 0 votes on header again, it should fail.
|
||||
_, err = smc.SubmitVote(txOpts, shard0, period1, index0, chunkRoot)
|
||||
if err == nil {
|
||||
t.Errorf("notary voting twice should have failed")
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Check notary 0's vote is correctly casted.
|
||||
c, _ = smc.GetVoteCount(&bind.CallOpts{}, shard0)
|
||||
if c.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect notary vote count, want: 1, got: %v", c)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubmitVoteByNonEligibleNotary tests a non-eligible notary tries to submit vote.
|
||||
func TestSubmitVoteByNonEligibleNotary(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
txOpts.Value = notaryDeposit
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
shard0 := big.NewInt(0)
|
||||
index0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
txOpts.Value = big.NewInt(0)
|
||||
_, err := smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Unregistered Notary 0 votes on header, it should fail.
|
||||
_, err = smc.SubmitVote(txOpts, shard0, period1, index0, chunkRoot)
|
||||
backend.Commit()
|
||||
if err == nil {
|
||||
t.Errorf("Non registered notary submits vote should have failed")
|
||||
}
|
||||
c, _ := smc.GetVoteCount(&bind.CallOpts{}, shard0)
|
||||
if c.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect notary vote count, want: 0, got: %v", c)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubmitVoteWithOutAHeader tests a notary tries to submit vote before header gets added.
|
||||
func TestSubmitVoteWithOutAHeader(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
txOpts.Value = notaryDeposit
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Notary 0 registers.
|
||||
smc.RegisterNotary(txOpts)
|
||||
backend.Commit()
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
shard0 := big.NewInt(0)
|
||||
index0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
txOpts.Value = big.NewInt(0)
|
||||
|
||||
// Notary 0 votes on header, it should fail because no header has added.
|
||||
_, err := smc.SubmitVote(txOpts, shard0, period1, index0, chunkRoot)
|
||||
if err == nil {
|
||||
t.Errorf("Notary votes should have failed due to missing header")
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Check notary 0's vote is correctly casted.
|
||||
c, _ := smc.GetVoteCount(&bind.CallOpts{}, shard0)
|
||||
if c.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect notary vote count, want: 1, got: %v", c)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubmitVoteWithInvalidArgs tests notary submits vote using wrong chunkroot and period.
|
||||
func TestSubmitVoteWithInvalidArgs(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
txOpts.Value = notaryDeposit
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
// Notary 0 registers
|
||||
smc.RegisterNotary(txOpts)
|
||||
backend.Commit()
|
||||
|
||||
// Fast forward to the next period to submit header. Period 1.
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Proposer adds header consists shard 0, period 1 and chunkroot 0xA.
|
||||
period1 := big.NewInt(1)
|
||||
shard0 := big.NewInt(0)
|
||||
index0 := big.NewInt(0)
|
||||
chunkRoot := [32]byte{'A'}
|
||||
txOpts.Value = big.NewInt(0)
|
||||
_, err := smc.AddHeader(txOpts, shard0, period1, chunkRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Proposer adds header failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Notary voting with incorrect period.
|
||||
period2 := big.NewInt(2)
|
||||
_, err = smc.SubmitVote(txOpts, shard0, period2, index0, chunkRoot)
|
||||
if err == nil {
|
||||
t.Errorf("Notary votes should have failed due to incorrect period")
|
||||
}
|
||||
|
||||
// Notary voting with incorrect chunk root.
|
||||
chunkRootWrong := [32]byte{'B'}
|
||||
_, err = smc.SubmitVote(txOpts, shard0, period1, index0, chunkRootWrong)
|
||||
if err == nil {
|
||||
t.Errorf("Notary votes should have failed due to incorrect chunk root")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user