mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-01 15:57:39 +00:00
sharding: merge with master
Former-commit-id: e48146fdad0808ff828bf53a039ee9cffba0b9a4 [formerly 4c6b0a3d19a582949b1542837c6132768df86da7] Former-commit-id: 4a83a6533243b5d7afa24767777164e18defd7af
This commit is contained in:
commit
9f791f20a1
File diff suppressed because one or more lines are too long
@ -1,253 +1,224 @@
|
||||
pragma solidity ^0.4.19;
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
|
||||
contract SMC {
|
||||
event TxToShard(address indexed to, int indexed shardId, int receiptId);
|
||||
event CollationAdded(int indexed shardId, uint expectedPeriodNumber,
|
||||
bytes32 periodStartPrevHash, bytes32 parentHash,
|
||||
bytes32 transactionRoot, address coinbase,
|
||||
bytes32 stateRoot, bytes32 receiptRoot,
|
||||
int number, bool isNewHead, int score);
|
||||
event Deposit(address collator, int index);
|
||||
event Withdraw(int index);
|
||||
event HeaderAdded(uint indexed shardId, bytes32 chunkRoot, int128 period, address proposerAddress);
|
||||
event NotaryRegistered(address notary, uint poolIndex);
|
||||
event NotaryDeregistered(address notary, uint poolIndex, uint deregisteredPeriod);
|
||||
event NotaryReleased(address notary, uint poolIndex);
|
||||
|
||||
struct Collator {
|
||||
// Amount of wei the collator holds
|
||||
uint deposit;
|
||||
// The collator's address
|
||||
address addr;
|
||||
}
|
||||
struct Notary {
|
||||
uint deregisteredPeriod;
|
||||
uint poolIndex;
|
||||
bool deposited;
|
||||
}
|
||||
|
||||
struct CollationHeader {
|
||||
bytes32 parentHash;
|
||||
int score;
|
||||
}
|
||||
|
||||
struct Receipt {
|
||||
int shardId;
|
||||
uint txStartgas;
|
||||
uint txGasprice;
|
||||
uint value;
|
||||
bytes32 data;
|
||||
address sender;
|
||||
address to;
|
||||
}
|
||||
struct CollationHeader {
|
||||
uint shardId; // Number of the shard ID
|
||||
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 collatorAddr;
|
||||
bool isNewHead;
|
||||
}
|
||||
|
||||
// collatorId => Collators
|
||||
mapping (int => Collator) public collators;
|
||||
// shardId => (headerHash => CollationHeader)
|
||||
mapping (int => mapping (bytes32 => CollationHeader)) public collationHeaders;
|
||||
// receiptId => Receipt
|
||||
mapping (int => Receipt) public receipts;
|
||||
// shardId => headerHash
|
||||
mapping (int => bytes32) shardHead;
|
||||
|
||||
// Number of collators
|
||||
int public numCollators;
|
||||
// Number of receipts
|
||||
int numReceipts;
|
||||
// Indexs of empty slots caused by the function `withdraw`
|
||||
mapping (int => int) emptySlotsStack;
|
||||
// The top index of the stack in empty_slots_stack
|
||||
int emptySlotsStackTop;
|
||||
// Has the collator deposited before?
|
||||
mapping (address => bool) public isCollatorDeposited;
|
||||
|
||||
// Constant values
|
||||
uint constant periodLength = 5;
|
||||
int constant public shardCount = 100;
|
||||
// The exact deposit size which you have to deposit to become a collator
|
||||
uint constant depositSize = 1000 ether;
|
||||
// Number of periods ahead of current period, which the contract
|
||||
// is able to return the collator of that period
|
||||
uint constant lookAheadPeriods = 4;
|
||||
|
||||
// Log the latest period number of the shard
|
||||
mapping (int => int) public periodHead;
|
||||
|
||||
function SMC() public {
|
||||
}
|
||||
|
||||
// Returns the gas limit that collations can currently have (by default make
|
||||
// this function always answer 10 million).
|
||||
function getCollationGasLimit() public pure returns(uint) {
|
||||
return 10000000;
|
||||
}
|
||||
|
||||
// Uses a block hash as a seed to pseudorandomly select a signer from the collator pool.
|
||||
// [TODO] Chance of being selected should be proportional to the collator's deposit.
|
||||
// Should be able to return a value for the current period or any future period up to.
|
||||
function getEligibleCollator(int _shardId, uint _period) public view returns(address) {
|
||||
require(_period >= lookAheadPeriods);
|
||||
require((_period - lookAheadPeriods) * periodLength < block.number);
|
||||
require(numCollators > 0);
|
||||
// [TODO] Should check further if this safe or not
|
||||
return collators[
|
||||
int(
|
||||
uint(
|
||||
keccak256(
|
||||
uint(block.blockhash((_period - lookAheadPeriods) * periodLength)),
|
||||
_shardId
|
||||
)
|
||||
) %
|
||||
uint(getCollatorsMaxIndex())
|
||||
)
|
||||
].addr;
|
||||
}
|
||||
|
||||
function deposit() public payable returns(int) {
|
||||
require(!isCollatorDeposited[msg.sender]);
|
||||
require(msg.value == depositSize);
|
||||
// Find the empty slot index in collators pool
|
||||
int index;
|
||||
if (!isStackEmpty())
|
||||
index = stackPop();
|
||||
else
|
||||
index = int(numCollators);
|
||||
|
||||
collators[index] = Collator({
|
||||
deposit: msg.value,
|
||||
addr: msg.sender
|
||||
});
|
||||
++numCollators;
|
||||
isCollatorDeposited[msg.sender] = true;
|
||||
|
||||
Deposit(msg.sender, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
// Removes the collator from the collator pool and refunds the deposited ether
|
||||
function withdraw(int _collatorIndex) public {
|
||||
require(msg.sender == collators[_collatorIndex].addr);
|
||||
// [FIXME] Should consider calling the collator's contract, might be useful
|
||||
// when the collator is a contract.
|
||||
collators[_collatorIndex].addr.transfer(collators[_collatorIndex].deposit);
|
||||
isCollatorDeposited[collators[_collatorIndex].addr] = false;
|
||||
delete collators[_collatorIndex];
|
||||
stackPush(_collatorIndex);
|
||||
--numCollators;
|
||||
Withdraw(_collatorIndex);
|
||||
}
|
||||
|
||||
// Attempts to process a collation header, returns true on success, reverts on failure.
|
||||
function addHeader(int _shardId, uint _expectedPeriodNumber, bytes32 _periodStartPrevHash,
|
||||
bytes32 _parentHash, bytes32 _transactionRoot,
|
||||
address _coinbase, bytes32 _stateRoot, bytes32 _receiptRoot,
|
||||
int _number) public returns(bool) {
|
||||
HeaderVars memory headerVars;
|
||||
|
||||
// Check if the header is valid
|
||||
require((_shardId >= 0) && (_shardId < shardCount));
|
||||
require(block.number >= periodLength);
|
||||
require(_expectedPeriodNumber == block.number / periodLength);
|
||||
require(_periodStartPrevHash == block.blockhash(_expectedPeriodNumber * periodLength - 1));
|
||||
|
||||
// Check if this header already exists
|
||||
headerVars.entireHeaderHash = keccak256(_shardId, _expectedPeriodNumber, _periodStartPrevHash,
|
||||
_parentHash, _transactionRoot, bytes32(_coinbase),
|
||||
_stateRoot, _receiptRoot, _number);
|
||||
assert(collationHeaders[_shardId][headerVars.entireHeaderHash].score == 0);
|
||||
// Check whether the parent exists.
|
||||
// if (parent_collation_hash == 0), i.e., is the genesis,
|
||||
// then there is no need to check.
|
||||
if (_parentHash != 0x0)
|
||||
assert(collationHeaders[_shardId][_parentHash].score > 0);
|
||||
// Check if only one collation in one period
|
||||
assert(periodHead[_shardId] < int(_expectedPeriodNumber));
|
||||
|
||||
// Check the signature with validation_code_addr
|
||||
headerVars.collatorAddr = getEligibleCollator(_shardId, block.number/periodLength);
|
||||
require(headerVars.collatorAddr != 0x0);
|
||||
require(msg.sender == headerVars.collatorAddr);
|
||||
|
||||
// Check score == collationNumber
|
||||
headerVars.score = collationHeaders[_shardId][_parentHash].score + 1;
|
||||
require(_number == headerVars.score);
|
||||
|
||||
// Add the header
|
||||
collationHeaders[_shardId][headerVars.entireHeaderHash] = CollationHeader({
|
||||
parentHash: _parentHash,
|
||||
score: headerVars.score
|
||||
});
|
||||
|
||||
// Update the latest period number
|
||||
periodHead[_shardId] = int(_expectedPeriodNumber);
|
||||
|
||||
// Determine the head
|
||||
if (headerVars.score > collationHeaders[_shardId][shardHead[_shardId]].score) {
|
||||
shardHead[_shardId] = headerVars.entireHeaderHash;
|
||||
headerVars.isNewHead = true;
|
||||
struct HeaderVars {
|
||||
bytes32 entireHeaderHash;
|
||||
int score;
|
||||
address notaryAddr;
|
||||
bool isNewHead;
|
||||
}
|
||||
|
||||
CollationAdded(_shardId, _expectedPeriodNumber, _periodStartPrevHash,
|
||||
_parentHash, _transactionRoot, _coinbase, _stateRoot,
|
||||
_receiptRoot, _number, headerVars.isNewHead, headerVars.score);
|
||||
address[] public notaryPool;
|
||||
|
||||
return true;
|
||||
}
|
||||
// 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;
|
||||
|
||||
// Records a request to deposit msg.value ETH to address to in shard shard_id
|
||||
// during a future collation. Saves a `receipt ID` for this request,
|
||||
// also saving `msg.sender`, `msg.value`, `to`, `shard_id`, `startgas`,
|
||||
// `gasprice`, and `data`.
|
||||
function txToShard(address _to, int _shardId, uint _txStartgas, uint _txGasprice,
|
||||
bytes12 _data) public payable returns(int) {
|
||||
receipts[numReceipts] = Receipt({
|
||||
shardId: _shardId,
|
||||
txStartgas: _txStartgas,
|
||||
txGasprice: _txGasprice,
|
||||
value: msg.value,
|
||||
sender: msg.sender,
|
||||
to: _to,
|
||||
data: _data
|
||||
});
|
||||
var receiptId = numReceipts;
|
||||
++numReceipts;
|
||||
// Number of notaries
|
||||
uint public notaryPoolLength;
|
||||
// Stack of empty notary slot indicies
|
||||
uint[] emptySlotsStack;
|
||||
// Top index of the stack
|
||||
uint emptySlotsStackTop;
|
||||
|
||||
TxToShard(_to, _shardId, receiptId);
|
||||
return receiptId;
|
||||
}
|
||||
// Notary sample size at current period and next period
|
||||
uint currentPeriodNotarySampleSize;
|
||||
uint nextPeriodNotarySampleSize;
|
||||
uint sampleSizeLastUpdatedPeriod;
|
||||
|
||||
function updateGasPrice(int _receiptId, uint _txGasprice) public payable returns(bool) {
|
||||
require(receipts[_receiptId].sender == msg.sender);
|
||||
receipts[_receiptId].txGasprice = _txGasprice;
|
||||
return true;
|
||||
}
|
||||
// Constant values
|
||||
uint constant PERIOD_LENGTH = 5;
|
||||
// Number of shards
|
||||
uint constant SHARD_COUNT = 100;
|
||||
// The minimum deposit size for a notary
|
||||
uint constant NOTARY_DEPOSIT = 1000 ether;
|
||||
// The reward for notary on voting for a collation
|
||||
uint constant NOTARY_REWARD = 0.001 ether;
|
||||
// Time the ether is locked by notaries
|
||||
uint constant NOTARY_LOCKUP_LENGTH = 16128;
|
||||
// Number of periods ahead of current period, which the contract
|
||||
// is able to return the notary of that period
|
||||
uint constant LOOKAHEAD_LENGTH = 4;
|
||||
// Number of notaries to select from notary pool for each shard in each period
|
||||
uint constant COMMITTEE_SIZE = 135;
|
||||
// Threshold(number of notaries in committee) for a proposal to be accepted
|
||||
uint constant QUORUM_SIZE = 90;
|
||||
|
||||
function isStackEmpty() internal view returns(bool) {
|
||||
return emptySlotsStackTop == 0;
|
||||
}
|
||||
// Log the latest period number of the shard
|
||||
mapping (int => int) public periodHead;
|
||||
|
||||
function stackPush(int index) internal {
|
||||
emptySlotsStack[emptySlotsStackTop] = index;
|
||||
++emptySlotsStackTop;
|
||||
}
|
||||
/// 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) {
|
||||
uint period = block.number / PERIOD_LENGTH;
|
||||
|
||||
function stackPop() internal returns(int) {
|
||||
if (isStackEmpty())
|
||||
return -1;
|
||||
--emptySlotsStackTop;
|
||||
return emptySlotsStack[emptySlotsStackTop];
|
||||
}
|
||||
// Determine notary pool length based on notary sample size
|
||||
uint sampleSize;
|
||||
if (period > sampleSizeLastUpdatedPeriod) {
|
||||
sampleSize = nextPeriodNotarySampleSize;
|
||||
} else {
|
||||
sampleSize = currentPeriodNotarySampleSize;
|
||||
}
|
||||
|
||||
function getCollatorsMaxIndex() internal view returns(int) {
|
||||
int activateCollatorNum = 0;
|
||||
int allCollatorSlotsNum = numCollators + emptySlotsStackTop;
|
||||
// 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;
|
||||
|
||||
// TODO: any better way to iterate the mapping?
|
||||
for (int i = 0; i < 1024; ++i) {
|
||||
if (i >= allCollatorSlotsNum)
|
||||
break;
|
||||
if (collators[i].addr != 0x0)
|
||||
activateCollatorNum += 1;
|
||||
return notaryPool[index];
|
||||
}
|
||||
return activateCollatorNum + emptySlotsStackTop;
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers notary to notatery registry, locks in the notary deposit,
|
||||
/// and returns true on success
|
||||
function registerNotary() public payable {
|
||||
address notaryAddress = msg.sender;
|
||||
require(!notaryRegistry[notaryAddress].deposited);
|
||||
require(msg.value == NOTARY_DEPOSIT);
|
||||
|
||||
// Track the numbers of participating notaries in between periods
|
||||
updateNotarySampleSize();
|
||||
|
||||
uint index;
|
||||
if (emptyStack()) {
|
||||
index = notaryPoolLength;
|
||||
notaryPool.push(notaryAddress);
|
||||
} else {
|
||||
index = stackPop();
|
||||
notaryPool[index] = notaryAddress;
|
||||
}
|
||||
++notaryPoolLength;
|
||||
|
||||
notaryRegistry[notaryAddress] = Notary({
|
||||
deregisteredPeriod: 0,
|
||||
poolIndex: index,
|
||||
deposited: true
|
||||
});
|
||||
|
||||
// if current index is greater than notary sample size, increase notary sample size for next period
|
||||
if (index >= nextPeriodNotarySampleSize) {
|
||||
nextPeriodNotarySampleSize = index + 1;
|
||||
}
|
||||
|
||||
emit NotaryRegistered(notaryAddress, index);
|
||||
}
|
||||
|
||||
/// Deregisters notary from notatery registry, lock up period countdowns down,
|
||||
/// notary may call releaseNotary after lock up period finishses to withdraw deposit,
|
||||
/// and returns true on success
|
||||
function deregisterNotary() public {
|
||||
address notaryAddress = msg.sender;
|
||||
uint index = notaryRegistry[notaryAddress].poolIndex;
|
||||
require(notaryRegistry[notaryAddress].deposited);
|
||||
require(notaryPool[index] == notaryAddress);
|
||||
|
||||
// Track the numbers of participating notaries in between periods
|
||||
updateNotarySampleSize();
|
||||
|
||||
uint deregisteredPeriod = block.number / PERIOD_LENGTH;
|
||||
notaryRegistry[notaryAddress].deregisteredPeriod = deregisteredPeriod;
|
||||
|
||||
stackPush(index);
|
||||
delete notaryPool[index];
|
||||
--notaryPoolLength;
|
||||
emit NotaryDeregistered(notaryAddress, index, deregisteredPeriod);
|
||||
}
|
||||
|
||||
/// Removes an entry from notary registry, returns deposit back to the notary,
|
||||
/// and returns true on success.
|
||||
function releaseNotary() public {
|
||||
address notaryAddress = msg.sender;
|
||||
uint index = notaryRegistry[notaryAddress].poolIndex;
|
||||
require(notaryRegistry[notaryAddress].deposited == true);
|
||||
require(notaryRegistry[notaryAddress].deregisteredPeriod != 0);
|
||||
require((block.number / PERIOD_LENGTH) > (notaryRegistry[notaryAddress].deregisteredPeriod + NOTARY_LOCKUP_LENGTH));
|
||||
|
||||
delete notaryRegistry[notaryAddress];
|
||||
notaryAddress.transfer(NOTARY_DEPOSIT);
|
||||
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
|
||||
) 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
|
||||
*/
|
||||
}
|
||||
|
||||
/// To keep track of notary size in between periods, we call updateNotarySampleSize
|
||||
/// before notary registration/deregistration so correct size can be applied next period
|
||||
function updateNotarySampleSize() internal {
|
||||
uint currentPeriod = block.number / PERIOD_LENGTH;
|
||||
if (currentPeriod < sampleSizeLastUpdatedPeriod) {
|
||||
return;
|
||||
}
|
||||
currentPeriodNotarySampleSize = nextPeriodNotarySampleSize;
|
||||
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];
|
||||
}
|
||||
}
|
@ -1,25 +1,40 @@
|
||||
package contracts
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/sharding"
|
||||
)
|
||||
|
||||
type SMCConfig struct {
|
||||
notaryLockupLenght *big.Int
|
||||
proposerLockupLength *big.Int
|
||||
}
|
||||
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
accountBalance1001Eth, _ = new(big.Int).SetString("1001000000000000000000", 10)
|
||||
collatorDeposit, _ = new(big.Int).SetString("1000000000000000000000", 10)
|
||||
mainKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
accountBalance2000Eth, _ = new(big.Int).SetString("2000000000000000000000", 10)
|
||||
notaryDepositInsufficient, _ = new(big.Int).SetString("999000000000000000000", 10)
|
||||
notaryDeposit, _ = new(big.Int).SetString("1000000000000000000000", 10)
|
||||
FastForward100Blocks = 100
|
||||
smcConfig = SMCConfig{
|
||||
notaryLockupLenght: new(big.Int).SetInt64(1),
|
||||
proposerLockupLength: new(big.Int).SetInt64(1),
|
||||
}
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
func deploySMCContract(backend *backends.SimulatedBackend) (common.Address, *types.Transaction, *SMC, error) {
|
||||
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)
|
||||
@ -27,104 +42,523 @@ func deploySMCContract(backend *backends.SimulatedBackend) (common.Address, *typ
|
||||
|
||||
// Test creating the SMC contract
|
||||
func TestContractCreation(t *testing.T) {
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance1001Eth}})
|
||||
_, _, _, err := deploySMCContract(backend)
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
_, _, _, err := deploySMCContract(backend, mainKey)
|
||||
backend.Commit()
|
||||
if err != nil {
|
||||
t.Fatalf("can't deploy SMC: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test getting the collation gas limit
|
||||
func TestGetCollationGasLimit(t *testing.T) {
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance1001Eth}})
|
||||
_, _, smc, _ := deploySMCContract(backend)
|
||||
gasLimit, err := smc.GetCollationGasLimit(&bind.CallOpts{})
|
||||
func TestNotaryRegister(t *testing.T) {
|
||||
const notaryCount = 3
|
||||
var notaryPoolAddr [notaryCount]common.Address
|
||||
var notaryPoolPrivKeys [notaryCount]*ecdsa.PrivateKey
|
||||
var txOpts [notaryCount]*bind.TransactOpts
|
||||
genesis := make(core.GenesisAlloc)
|
||||
|
||||
// initializes back end with 3 accounts and each with 2000 eth balances
|
||||
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])
|
||||
|
||||
// Notary 0 has not registered
|
||||
notary, err := smc.NotaryRegistry(&bind.CallOpts{}, notaryPoolAddr[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting collationGasLimit: %v", err)
|
||||
t.Fatalf("Can't get notary registry info: %v", err)
|
||||
}
|
||||
if gasLimit.Cmp(big.NewInt(10000000)) != 0 {
|
||||
t.Fatalf("collation gas limit should be 10000000 gas")
|
||||
|
||||
if notary.Deposited {
|
||||
t.Errorf("Notary has not registered. Got deposited flag: %v", notary.Deposited)
|
||||
}
|
||||
}
|
||||
|
||||
// Test collator deposit
|
||||
func TestCollatorDeposit(t *testing.T) {
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance1001Eth}})
|
||||
transactOpts := bind.NewKeyedTransactor(key)
|
||||
_, _, smc, _ := deploySMCContract(backend)
|
||||
|
||||
// Test deposit() function
|
||||
// Deposit 100 Eth
|
||||
transactOpts.Value = collatorDeposit
|
||||
|
||||
if _, err := smc.Deposit(transactOpts); err != nil {
|
||||
t.Fatalf("Collator cannot deposit: %v", err)
|
||||
// Test notary 0 has registered
|
||||
if _, err := smc.RegisterNotary(txOpts[0]); err != nil {
|
||||
t.Fatalf("Registering notary has failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Check updated number of collators
|
||||
numCollators, err := smc.NumCollators(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get number of collators: %v", err)
|
||||
}
|
||||
if numCollators.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Fatalf("Failed to update number of collators")
|
||||
notary, err = smc.NotaryRegistry(&bind.CallOpts{}, notaryPoolAddr[0])
|
||||
|
||||
if !notary.Deposited ||
|
||||
notary.PoolIndex.Cmp(big.NewInt(0)) != 0 ||
|
||||
notary.DeregisteredPeriod.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect notary registry. Want - deposited:true, index:0, period:0"+
|
||||
"Got - deposited:%v, index:%v, period:%v ", notary.Deposited, notary.PoolIndex, notary.DeregisteredPeriod)
|
||||
}
|
||||
|
||||
// Check collator structure
|
||||
collatorStruct, err := smc.Collators(&bind.CallOpts{}, big.NewInt(0))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get collator structure: %v", err)
|
||||
}
|
||||
if collatorStruct.Addr != addr {
|
||||
t.Fatalf("Wrong collator address, %v should be %v", collatorStruct.Addr, addr)
|
||||
}
|
||||
if collatorStruct.Deposit.Cmp(collatorDeposit) != 0 {
|
||||
t.Fatalf("Wrong collator deposit, %v should be %v", collatorStruct.Deposit, collatorDeposit)
|
||||
}
|
||||
|
||||
// Check for the Deposit event
|
||||
depositsEventsIterator, err := smc.FilterDeposit(&bind.FilterOpts{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get Deposit event: %v", err)
|
||||
}
|
||||
if !depositsEventsIterator.Next() {
|
||||
t.Fatal("No Deposit event found")
|
||||
}
|
||||
if depositsEventsIterator.Event.Collator != addr {
|
||||
t.Fatalf("Collator address mismatch: %x should be %x", depositsEventsIterator.Event.Collator, addr)
|
||||
}
|
||||
if depositsEventsIterator.Event.Index.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Fatalf("Collator index mismatch: %d should be 0", depositsEventsIterator.Event.Index)
|
||||
}
|
||||
}
|
||||
|
||||
// Test collator withdraw
|
||||
func TestCollatorWithdraw(t *testing.T) {
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance1001Eth}})
|
||||
transactOpts := bind.NewKeyedTransactor(key)
|
||||
_, _, smc, _ := deploySMCContract(backend)
|
||||
|
||||
transactOpts.Value = collatorDeposit
|
||||
smc.Deposit(transactOpts)
|
||||
|
||||
transactOpts.Value = big.NewInt(0)
|
||||
_, err := smc.Withdraw(transactOpts, big.NewInt(0))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to withdraw: %v", err)
|
||||
// Test notary 1 and 2 have registered
|
||||
if _, err := smc.RegisterNotary(txOpts[1]); err != nil {
|
||||
t.Fatalf("Registering notary has failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Check for the Withdraw event
|
||||
withdrawsEventsIterator, err := smc.FilterWithdraw(&bind.FilterOpts{Start: 0})
|
||||
if _, err := smc.RegisterNotary(txOpts[2]); err != nil {
|
||||
t.Fatalf("Registering notary has failed: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
notary, err = smc.NotaryRegistry(&bind.CallOpts{}, notaryPoolAddr[1])
|
||||
|
||||
if !notary.Deposited ||
|
||||
notary.PoolIndex.Cmp(big.NewInt(1)) != 0 ||
|
||||
notary.DeregisteredPeriod.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect notary registry. Want - deposited:true, index:1, period:0"+
|
||||
"Got - deposited:%v, index:%v, period:%v ", notary.Deposited, notary.PoolIndex, notary.DeregisteredPeriod)
|
||||
}
|
||||
|
||||
notary, err = smc.NotaryRegistry(&bind.CallOpts{}, notaryPoolAddr[2])
|
||||
|
||||
if !notary.Deposited ||
|
||||
notary.PoolIndex.Cmp(big.NewInt(2)) != 0 ||
|
||||
notary.DeregisteredPeriod.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect notary registry. Want - deposited:true, index:2, period:0"+
|
||||
"Got - deposited:%v, index:%v, period:%v ", notary.Deposited, notary.PoolIndex, notary.DeregisteredPeriod)
|
||||
}
|
||||
|
||||
// Check total numbers of notaries in pool
|
||||
numNotaries, err := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get withdraw event: %v", err)
|
||||
t.Fatalf("Failed to get notary pool length: %v", err)
|
||||
}
|
||||
if !withdrawsEventsIterator.Next() {
|
||||
t.Fatal("No withdraw event found")
|
||||
}
|
||||
if withdrawsEventsIterator.Event.Index.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Fatalf("Collator index mismatch: %d should be 0", withdrawsEventsIterator.Event.Index)
|
||||
if numNotaries.Cmp(big.NewInt(3)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 3, Got: %v", numNotaries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotaryRegisterInsufficientEther(t *testing.T) {
|
||||
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
|
||||
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
|
||||
txOpts := bind.NewKeyedTransactor(mainKey)
|
||||
txOpts.Value = notaryDepositInsufficient
|
||||
_, _, smc, _ := deploySMCContract(backend, mainKey)
|
||||
|
||||
_, err := smc.RegisterNotary(txOpts)
|
||||
if err == nil {
|
||||
t.Errorf("Notary register should have failed with insufficient deposit")
|
||||
}
|
||||
|
||||
notary, _ := smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
|
||||
if notary.Deposited {
|
||||
t.Errorf("Notary deposited with insufficient fund")
|
||||
}
|
||||
|
||||
if numNotaries.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNotaryDoubleRegisters(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()
|
||||
|
||||
notary, _ := smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
|
||||
if !notary.Deposited {
|
||||
t.Errorf("Notary has not registered. Got deposited flag: %v", notary.Deposited)
|
||||
}
|
||||
|
||||
if numNotaries.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Notary 0 registers again
|
||||
_, err := smc.RegisterNotary(txOpts)
|
||||
if err == nil {
|
||||
t.Errorf("Notary register should have failed with double registers")
|
||||
}
|
||||
if numNotaries.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNotaryDeregister(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()
|
||||
|
||||
notary, _ := smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
|
||||
if !notary.Deposited {
|
||||
t.Errorf("Notary has not registered. Got deposited flag: %v", notary.Deposited)
|
||||
}
|
||||
|
||||
if numNotaries.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Fast forward 100 blocks to check notary's deregistered period field is set correctly
|
||||
for i := 0; i < FastForward100Blocks; i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Notary 0 deregisters
|
||||
txOpts = bind.NewKeyedTransactor(mainKey)
|
||||
_, err := smc.DeregisterNotary(txOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deregister notary: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Verify notary has saved the deregistered period as: current block number / period length
|
||||
notary, _ = smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
currentPeriod := big.NewInt(int64(FastForward100Blocks) / sharding.PeriodLength)
|
||||
if currentPeriod.Cmp(notary.DeregisteredPeriod) != 0 {
|
||||
t.Errorf("Incorrect notary degister period. Want: %v, Got: %v ", currentPeriod, notary.DeregisteredPeriod)
|
||||
}
|
||||
|
||||
numNotaries, _ = smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if numNotaries.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotaryDeregisterThenRegister(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()
|
||||
|
||||
notary, _ := smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
|
||||
if !notary.Deposited {
|
||||
t.Errorf("Notary has not registered. Got deposited flag: %v", notary.Deposited)
|
||||
}
|
||||
|
||||
if numNotaries.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Notary 0 deregisters
|
||||
txOpts = bind.NewKeyedTransactor(mainKey)
|
||||
_, err := smc.DeregisterNotary(txOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deregister notary: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
numNotaries, _ = smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if numNotaries.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Notary 0 re-registers again
|
||||
smc.RegisterNotary(txOpts)
|
||||
backend.Commit()
|
||||
|
||||
numNotaries, _ = smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if numNotaries.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotaryRelease(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()
|
||||
|
||||
notary, _ := smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
|
||||
if !notary.Deposited {
|
||||
t.Errorf("Notary has not registered. Got deposited flag: %v", notary.Deposited)
|
||||
}
|
||||
|
||||
if numNotaries.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Fast forward to the next period to deregister
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Notary 0 deregisters
|
||||
txOpts = bind.NewKeyedTransactor(mainKey)
|
||||
_, err := smc.DeregisterNotary(txOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deregister notary: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
numNotaries, _ = smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if numNotaries.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Fast forward until lockup ends
|
||||
for i := 0; i < int(sharding.NotaryLockupLength*sharding.PeriodLength+1); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Notary 0 releases
|
||||
_, err = smc.ReleaseNotary(txOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to release notary: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
notary, err = smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't get notary registry info: %v", err)
|
||||
}
|
||||
|
||||
if notary.Deposited {
|
||||
t.Errorf("Notary deposit flag should be false after released")
|
||||
}
|
||||
|
||||
balance, err := backend.BalanceAt(ctx, addr, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Can't get account balance, err: %s", err)
|
||||
}
|
||||
|
||||
if balance.Cmp(notaryDeposit) < 0 {
|
||||
t.Errorf("Notary did not receive deposit after lock up ends")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotaryInstantRelease(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()
|
||||
|
||||
notary, _ := smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
numNotaries, _ := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
|
||||
if !notary.Deposited {
|
||||
t.Errorf("Notary has not registered. Got deposited flag: %v", notary.Deposited)
|
||||
}
|
||||
|
||||
if numNotaries.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Fast forward to the next period to deregister
|
||||
for i := 0; i < int(sharding.PeriodLength); i++ {
|
||||
backend.Commit()
|
||||
}
|
||||
|
||||
// Notary 0 deregisters
|
||||
txOpts = bind.NewKeyedTransactor(mainKey)
|
||||
_, err := smc.DeregisterNotary(txOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deregister notary: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
numNotaries, _ = smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if numNotaries.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Fatalf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// Notary 0 tries to release before lockup ends
|
||||
_, err = smc.ReleaseNotary(txOpts)
|
||||
backend.Commit()
|
||||
|
||||
notary, err = smc.NotaryRegistry(&bind.CallOpts{}, addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't get notary registry info: %v", err)
|
||||
}
|
||||
|
||||
if !notary.Deposited {
|
||||
t.Errorf("Notary deposit flag should be true before released")
|
||||
}
|
||||
|
||||
balance, err := backend.BalanceAt(ctx, addr, nil)
|
||||
|
||||
if balance.Cmp(notaryDeposit) > 0 {
|
||||
t.Errorf("Notary received deposit before lockup ends")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitteeListsAreDifferent(t *testing.T) {
|
||||
const notaryCount = 10000
|
||||
var notaryPoolAddr [notaryCount]common.Address
|
||||
var notaryPoolPrivKeys [notaryCount]*ecdsa.PrivateKey
|
||||
var txOpts [notaryCount]*bind.TransactOpts
|
||||
genesis := make(core.GenesisAlloc)
|
||||
|
||||
// initializes back end with 10000 accounts and each with 2000 eth balances
|
||||
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 10000 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 {
|
||||
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() {
|
||||
t.Errorf("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)
|
||||
|
||||
// initialize back end with 11 accounts and each with 2000 eth balances
|
||||
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, 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 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() {
|
||||
t.Errorf("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)
|
||||
|
||||
// initialize back end with 10 accounts and each with 2000 eth balances
|
||||
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.Errorf("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.Errorf("Incorrect count from notary pool. Want: 9, Got: %v", numNotaries)
|
||||
}
|
||||
|
||||
// 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() {
|
||||
t.Errorf("Account %s is not a notary", notaryPoolAddr[0].String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func checkSMCForNotary(c client.Client, head *types.Header) error {
|
||||
period := big.NewInt(0).Div(head.Number, big.NewInt(sharding.PeriodLength))
|
||||
for s := int64(0); s < sharding.ShardCount; s++ {
|
||||
// Checks if we are an eligible notary according to the SMC
|
||||
addr, err := c.SMCCaller().GetEligibleNotary(&bind.CallOpts{}, big.NewInt(s), period)
|
||||
addr, err := c.SMCCaller().GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(s), period)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@ -70,7 +70,16 @@ func checkSMCForNotary(c client.Client, head *types.Header) error {
|
||||
log.Info(fmt.Sprintf("Selected as notary on shard: %d", s))
|
||||
err := submitCollation(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not add collation. %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// If the account is selected as collator, submit collation
|
||||
if addr == c.Account().Address {
|
||||
log.Info(fmt.Sprintf("Selected as collator on shard: %d", s))
|
||||
err := submitCollation(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not add collation. %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,11 +94,11 @@ func checkSMCForNotary(c client.Client, head *types.Header) error {
|
||||
func isAccountInNotaryPool(c client.Client) (bool, error) {
|
||||
account := c.Account()
|
||||
// Checks if our deposit has gone through according to the SMC
|
||||
b, err := c.SMCCaller().IsNotaryDeposited(&bind.CallOpts{}, account.Address)
|
||||
if !b && err != nil {
|
||||
n, err := c.SMCCaller().NotaryRegistry(&bind.CallOpts{}, account.Address)
|
||||
if !n.Deposited && err != nil {
|
||||
log.Warn(fmt.Sprintf("Account %s not in notary pool.", account.Address.String()))
|
||||
}
|
||||
return b, err
|
||||
return n.Deposited, err
|
||||
}
|
||||
|
||||
// submitCollation interacts with the SMC directly to add a collation header
|
||||
@ -132,7 +141,7 @@ func joinNotaryPool(c client.Client) error {
|
||||
return fmt.Errorf("unable to intiate the deposit transaction: %v", err)
|
||||
}
|
||||
|
||||
tx, err := c.SMCTransactor().Deposit(txOps)
|
||||
tx, err := c.SMCTransactor().RegisterNotary(txOps)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to deposit eth and become a notary: %v", err)
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ func TestIsAccountInNotaryPool(t *testing.T) {
|
||||
txOpts := transactOpts()
|
||||
// deposit in notary pool, then it should return true
|
||||
txOpts.Value = sharding.NotaryDeposit
|
||||
if _, err := smc.Deposit(txOpts); err != nil {
|
||||
if _, err := smc.RegisterNotary(txOpts); err != nil {
|
||||
t.Fatalf("Failed to deposit: %v", err)
|
||||
}
|
||||
backend.Commit()
|
||||
@ -105,7 +105,7 @@ func TestJoinNotaryPool(t *testing.T) {
|
||||
client := &mockClient{smc, t}
|
||||
|
||||
// There should be no notary initially
|
||||
numNotaries, err := smc.NumNotaries(&bind.CallOpts{})
|
||||
numNotaries, err := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -120,7 +120,7 @@ func TestJoinNotaryPool(t *testing.T) {
|
||||
backend.Commit()
|
||||
|
||||
// Now there should be one notary
|
||||
numNotaries, err = smc.NumNotaries(&bind.CallOpts{})
|
||||
numNotaries, err = smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user