mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-01 15:57:39 +00:00
Merge pull request #98 from rauljordan/formatting
Fixing Comment Formatting According to Best Linter Practices Former-commit-id: 9cea91282e4f4abb12452789170508c6948513c1 [formerly 07a948d06466be9ae694cf10b06fc5e1edff07f9] Former-commit-id: a779adf197484b50af077ecc2217c0af4e8de62b
This commit is contained in:
commit
e51ef0be87
@ -168,7 +168,7 @@ func init() {
|
||||
attachCommand,
|
||||
javascriptCommand,
|
||||
// See shardingcmd.go:
|
||||
collatorClientCommand,
|
||||
notaryClientCommand,
|
||||
proposerClientCommand,
|
||||
// See misccmd.go:
|
||||
makecacheCommand,
|
||||
|
@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/sharding/collator"
|
||||
"github.com/ethereum/go-ethereum/sharding/notary"
|
||||
"github.com/ethereum/go-ethereum/sharding/proposer"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
@ -9,16 +9,16 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
collatorClientCommand = cli.Command{
|
||||
Action: utils.MigrateFlags(collatorClient),
|
||||
Name: "sharding-collator",
|
||||
Aliases: []string{"shard-collator"},
|
||||
Usage: "Start a sharding collator client",
|
||||
notaryClientCommand = cli.Command{
|
||||
Action: utils.MigrateFlags(notaryClient),
|
||||
Name: "sharding-notary",
|
||||
Aliases: []string{"shard-notary"},
|
||||
Usage: "Start a sharding notary client",
|
||||
ArgsUsage: "[endpoint]",
|
||||
Flags: []cli.Flag{utils.DataDirFlag, utils.PasswordFileFlag, utils.NetworkIdFlag, utils.IPCPathFlag, utils.DepositFlag},
|
||||
Category: "SHARDING COMMANDS",
|
||||
Description: `
|
||||
Launches a sharding collator client that connects to a running geth node and submit collations to a Sharding Manager Contract. This feature is a work in progress.
|
||||
Launches a sharding notary client that connects to a running geth node and submit collations to a Sharding Manager Contract. This feature is a work in progress.
|
||||
`,
|
||||
}
|
||||
proposerClientCommand = cli.Command{
|
||||
@ -30,13 +30,13 @@ Launches a sharding collator client that connects to a running geth node and sub
|
||||
Flags: []cli.Flag{utils.DataDirFlag, utils.PasswordFileFlag, utils.NetworkIdFlag, utils.IPCPathFlag},
|
||||
Category: "SHARDING COMMANDS",
|
||||
Description: `
|
||||
Launches a sharding proposer client that connects to a running geth node and proposes collations to collator node. This feature is a work in progress.
|
||||
Launches a sharding proposer client that connects to a running geth node and proposes collations to notary node. This feature is a work in progress.
|
||||
`,
|
||||
}
|
||||
)
|
||||
|
||||
func collatorClient(ctx *cli.Context) error {
|
||||
c := collator.NewCollator(ctx)
|
||||
func notaryClient(ctx *cli.Context) error {
|
||||
c := notary.NewNotary(ctx)
|
||||
return c.Start()
|
||||
}
|
||||
|
||||
|
@ -536,7 +536,7 @@ var (
|
||||
//Sharding Settings
|
||||
DepositFlag = cli.BoolFlag{
|
||||
Name: "deposit",
|
||||
Usage: "To become a collator with your sharding client, 100 ETH will be deposited from user's account into SMC ",
|
||||
Usage: "To become a notary with your sharding client, 100 ETH will be deposited from user's account into SMC ",
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Package client provides an interface for interacting with a running ethereum full node.
|
||||
// As part of the initial phases of sharding, actors will need access to the sharding management
|
||||
// contract on the main PoW chain.
|
||||
// contract on the main PoW chain
|
||||
package client
|
||||
|
||||
import (
|
||||
@ -33,8 +33,7 @@ const (
|
||||
clientIdentifier = "geth" // Used to determine the ipc name.
|
||||
)
|
||||
|
||||
// General client for Collator/Proposer. Communicates to Geth node via JSON RPC.
|
||||
|
||||
// General client for Notary/Proposer - Communicates to Geth node via JSON RPC
|
||||
type shardingClient struct {
|
||||
endpoint string // Endpoint to JSON RPC
|
||||
client *ethclient.Client // Ethereum RPC client.
|
||||
@ -44,6 +43,7 @@ type shardingClient struct {
|
||||
rpcClient *rpc.Client // The RPC client connection to the main geth node
|
||||
}
|
||||
|
||||
// Client methods that must be implemented to run a sharded system
|
||||
type Client interface {
|
||||
Start() error
|
||||
Close()
|
||||
@ -54,7 +54,8 @@ type Client interface {
|
||||
SMCTransactor() *contracts.SMCTransactor
|
||||
}
|
||||
|
||||
func NewClient(ctx *cli.Context) *shardingClient {
|
||||
// NewClient forms a new struct instance
|
||||
func NewClient(ctx *cli.Context) Client {
|
||||
path := node.DefaultDataDir()
|
||||
if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
|
||||
path = ctx.GlobalString(utils.DataDirFlag.Name)
|
||||
@ -85,9 +86,9 @@ func NewClient(ctx *cli.Context) *shardingClient {
|
||||
}
|
||||
}
|
||||
|
||||
// Start the sharding client.
|
||||
// * Connects to Geth node.
|
||||
// * Verifies or deploys the sharding manager contract.
|
||||
// Start the sharding client
|
||||
// Connects to Geth node
|
||||
// Verifies or deploys the sharding manager contract
|
||||
func (c *shardingClient) Start() error {
|
||||
rpcClient, err := dialRPC(c.endpoint)
|
||||
if err != nil {
|
||||
@ -96,7 +97,7 @@ func (c *shardingClient) Start() error {
|
||||
c.rpcClient = rpcClient
|
||||
c.client = ethclient.NewClient(rpcClient)
|
||||
|
||||
// Check account existence and unlock account before starting collator client
|
||||
// Check account existence and unlock account before starting notary client
|
||||
accounts := c.keystore.Accounts()
|
||||
if len(accounts) == 0 {
|
||||
return fmt.Errorf("no accounts found")
|
||||
@ -184,6 +185,7 @@ func (c *shardingClient) SMCCaller() *contracts.SMCCaller {
|
||||
return &c.smc.SMCCaller
|
||||
}
|
||||
|
||||
// SMCTransactor allows us to send tx's to the SMC programmatically
|
||||
func (c *shardingClient) SMCTransactor() *contracts.SMCTransactor {
|
||||
return &c.smc.SMCTransactor
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
package sharding
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// Collation base struct
|
||||
type Collation struct {
|
||||
header *CollationHeader
|
||||
transactions []*types.Transaction
|
||||
}
|
||||
|
||||
// CollationHeader base struct
|
||||
type CollationHeader struct {
|
||||
shardID *big.Int //the shard ID of the shard
|
||||
chunkRoot *common.Hash //the root of the chunk tree which identifies collation body
|
||||
@ -21,27 +22,26 @@ type CollationHeader struct {
|
||||
proposerSignature []byte //the proposer's signature for calculating collation hash
|
||||
}
|
||||
|
||||
func (c *Collation) Header() *CollationHeader { return c.header }
|
||||
func (c *Collation) Transactions() []*types.Transaction { return c.transactions }
|
||||
func (c *Collation) ShardID() *big.Int { return c.header.shardID }
|
||||
func (c *Collation) Period() *big.Int { return c.header.period }
|
||||
func (c *Collation) ProposerAddress() *common.Address { return c.header.proposerAddress }
|
||||
// Header returns the collation's header
|
||||
func (c *Collation) Header() *CollationHeader { return c.header }
|
||||
|
||||
// Transactions returns an array of tx's in the collation
|
||||
func (c *Collation) Transactions() []*types.Transaction { return c.transactions }
|
||||
|
||||
// ShardID is the identifier for a shard
|
||||
func (c *Collation) ShardID() *big.Int { return c.header.shardID }
|
||||
|
||||
// Period the collation corresponds to
|
||||
func (c *Collation) Period() *big.Int { return c.header.period }
|
||||
|
||||
// ProposerAddress is the coinbase addr of the creator for the collation
|
||||
func (c *Collation) ProposerAddress() *common.Address { return c.header.proposerAddress }
|
||||
|
||||
// SetHeader updates the collation's header
|
||||
func (c *Collation) SetHeader(h *CollationHeader) { c.header = h }
|
||||
|
||||
// AddTransaction adds to the collation's body of tx blobs
|
||||
func (c *Collation) AddTransaction(tx *types.Transaction) {
|
||||
// TODO: Check transaction does not exceed gas limit
|
||||
// TODO: Include blob serialization instead
|
||||
c.transactions = append(c.transactions, tx)
|
||||
}
|
||||
|
||||
func (c *Collation) GasUsed() *big.Int {
|
||||
g := uint64(0)
|
||||
for _, tx := range c.transactions {
|
||||
if g > math.MaxUint64-(g+tx.Gas()) {
|
||||
g = math.MaxUint64
|
||||
break
|
||||
}
|
||||
g += tx.Gas()
|
||||
}
|
||||
return big.NewInt(0).SetUint64(g)
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
// Package collator holds all of the functionality to run as a collator in a sharded system.
|
||||
package collator
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/sharding/client"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
// Collator runnable client.
|
||||
type Collator interface {
|
||||
// Start the main routine for a collator.
|
||||
Start() error
|
||||
}
|
||||
|
||||
type collator struct {
|
||||
client client.Client
|
||||
}
|
||||
|
||||
// NewCollator creates a new collator instance.
|
||||
func NewCollator(ctx *cli.Context) Collator {
|
||||
return &collator{
|
||||
client: client.NewClient(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// Start the main routine for a collator.
|
||||
func (c *collator) Start() error {
|
||||
log.Info("Starting collator client")
|
||||
err := c.client.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.client.Close()
|
||||
|
||||
if err := joinCollatorPool(c.client); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return subscribeBlockHeaders(c.client)
|
||||
}
|
@ -7,32 +7,33 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// Number of network shards
|
||||
// ShardCount is the number of network shards
|
||||
ShardCount = int64(100)
|
||||
// Address of the sharding manager contract
|
||||
// ShardingManagerAddress is the address of the sharding manager contract
|
||||
ShardingManagerAddress = common.HexToAddress("0x0") // TODO
|
||||
// Gas limit for verifying signatures
|
||||
// SigGasLimit for verifying signatures
|
||||
SigGasLimit = 40000
|
||||
// Number of blocks in a period
|
||||
// PeriodLength is num of blocks in period
|
||||
PeriodLength = int64(5)
|
||||
// Number of periods ahead of current period which the contract is able to return the notary of that period.
|
||||
// LookaheadPeriods is the umber of periods ahead of current period
|
||||
// which the contract is able to return the notary of that period
|
||||
LookaheadPeriods = 4
|
||||
// Required deposit size in wei for notary
|
||||
// NotaryDeposit is a required deposit size in wei
|
||||
NotaryDeposit = new(big.Int).Exp(big.NewInt(10), big.NewInt(21), nil) // 1000 ETH
|
||||
// Required deposit size in wei for proposer
|
||||
// ProposerDeposit is a required deposit size in wei
|
||||
ProposerDeposit = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) // 1 ETH
|
||||
// Minimum Balance of proposer where bids are deducted
|
||||
// MinProposerBalance of proposer where bids are deducted
|
||||
MinProposerBalance = new(big.Int).Exp(big.NewInt(10), big.NewInt(17), nil) // 0.1 ETH
|
||||
// Gas limit to create contract
|
||||
// ContractGasLimit to create contract
|
||||
ContractGasLimit = uint64(4700000) // Max is 4712388
|
||||
// Number of periods to lockup notary deposit from time of deregistration
|
||||
// NotaryLockupLength to lockup notary deposit from time of deregistration
|
||||
NotaryLockupLength = int64(16128)
|
||||
// Number of periods to lockup proposer deposit from time of deregistration
|
||||
// ProposerLockupLength to lockup proposer deposit from time of deregistration
|
||||
ProposerLockupLength = int64(48)
|
||||
// Number of ETH awarded to notary after collation included in canonical chain
|
||||
// NotarySubsidy is ETH awarded to notary after collation included in canonical chain
|
||||
NotarySubsidy = new(big.Int).Exp(big.NewInt(10), big.NewInt(15), nil) // 0.001 ETH
|
||||
// Number of notaries sampled per block from the notaries pool per period per shard
|
||||
// NotaryCommitSize sampled per block from the notaries pool per period per shard
|
||||
NotaryCommitSize = int64(135)
|
||||
// Number of notary votes the collation needs to get accepted to the canonical chain
|
||||
// NotaryQuorumSize votes the collation needs to get accepted to the canonical chain
|
||||
NotaryQuorumSize = int64(90)
|
||||
)
|
||||
|
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package collator
|
||||
package notary
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// SubscribeBlockHeaders checks incoming block headers and determines if
|
||||
// we are an eligible collator for collations. Then, it finds the pending tx's
|
||||
// we are an eligible notary for collations. Then, it finds the pending tx's
|
||||
// from the running geth node and sorts them by descending order of gas price,
|
||||
// eliminates those that ask for too much gas, and routes them over
|
||||
// to the SMC to create a collation
|
||||
@ -31,17 +31,17 @@ func subscribeBlockHeaders(c client.Client) error {
|
||||
for {
|
||||
// TODO: Error handling for getting disconnected from the client
|
||||
head := <-headerChan
|
||||
// Query the current state to see if we are an eligible collator
|
||||
// Query the current state to see if we are an eligible notary
|
||||
log.Info(fmt.Sprintf("Received new header: %v", head.Number.String()))
|
||||
|
||||
// Check if we are in the collator pool before checking if we are an eligible collator
|
||||
v, err := isAccountInCollatorPool(c)
|
||||
// Check if we are in the notary pool before checking if we are an eligible notary
|
||||
v, err := isAccountInNotaryPool(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to verify client in collator pool. %v", err)
|
||||
return fmt.Errorf("unable to verify client in notary pool. %v", err)
|
||||
}
|
||||
|
||||
if v {
|
||||
if err := checkSMCForCollator(c, head); err != nil {
|
||||
if err := checkSMCForNotary(c, head); err != nil {
|
||||
return fmt.Errorf("unable to watch shards. %v", err)
|
||||
}
|
||||
} else {
|
||||
@ -50,16 +50,16 @@ func subscribeBlockHeaders(c client.Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
// checkSMCForCollator checks if we are an eligible collator for
|
||||
// checkSMCForNotary checks if we are an eligible notary for
|
||||
// collation for the available shards in the SMC. The function calls
|
||||
// getEligibleCollator from the SMC and collator a collation if
|
||||
// getEligibleNotary from the SMC and notary a collation if
|
||||
// conditions are met
|
||||
func checkSMCForCollator(c client.Client, head *types.Header) error {
|
||||
log.Info("Checking if we are an eligible collation collator for a shard...")
|
||||
func checkSMCForNotary(c client.Client, head *types.Header) error {
|
||||
log.Info("Checking if we are an eligible collation notary for a shard...")
|
||||
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 collator according to the SMC
|
||||
addr, err := c.SMCCaller().GetEligibleCollator(&bind.CallOpts{}, big.NewInt(s), period)
|
||||
// Checks if we are an eligible notary according to the SMC
|
||||
addr, err := c.SMCCaller().GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(s), period)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@ -67,10 +67,19 @@ func checkSMCForCollator(c client.Client, head *types.Header) error {
|
||||
|
||||
// If output is non-empty and the addr == coinbase
|
||||
if addr == c.Account().Address {
|
||||
log.Info(fmt.Sprintf("Selected as collator on shard: %d", s))
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,18 +87,18 @@ func checkSMCForCollator(c client.Client, head *types.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isAccountInCollatorPool checks if the client is in the collator pool because
|
||||
// isAccountInNotaryPool checks if the client is in the notary pool because
|
||||
// we can't guarantee our tx for deposit will be in the next block header we receive.
|
||||
// The function calls IsCollatorDeposited from the SMC and returns true if
|
||||
// the client is in the collator pool
|
||||
func isAccountInCollatorPool(c client.Client) (bool, error) {
|
||||
// The function calls IsNotaryDeposited from the SMC and returns true if
|
||||
// the client is in the notary pool
|
||||
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().IsCollatorDeposited(&bind.CallOpts{}, account.Address)
|
||||
if !b && err != nil {
|
||||
log.Warn(fmt.Sprintf("Account %s not in collator pool.", account.Address.String()))
|
||||
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
|
||||
@ -122,19 +131,19 @@ func submitCollation(shardID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// joinCollatorPool checks if the account is a collator in the SMC. If
|
||||
// joinNotaryPool checks if the account is a notary in the SMC. If
|
||||
// the account is not in the set, it will deposit ETH into contract.
|
||||
func joinCollatorPool(c client.Client) error {
|
||||
func joinNotaryPool(c client.Client) error {
|
||||
|
||||
log.Info("Joining collator pool")
|
||||
log.Info("Joining notary pool")
|
||||
txOps, err := c.CreateTXOpts(sharding.NotaryDeposit)
|
||||
if err != nil {
|
||||
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 collator: %v", err)
|
||||
return fmt.Errorf("unable to deposit eth and become a notary: %v", err)
|
||||
}
|
||||
log.Info(fmt.Sprintf("Deposited %dETH into contract with transaction hash: %s", new(big.Int).Div(sharding.NotaryDeposit, big.NewInt(params.Ether)), tx.Hash().String()))
|
||||
|
40
sharding/notary/notary_client.go
Normal file
40
sharding/notary/notary_client.go
Normal file
@ -0,0 +1,40 @@
|
||||
package notary
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/sharding/client"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
// Notary runnable client
|
||||
type Notary interface {
|
||||
// Start the main routine for a notary
|
||||
Start() error
|
||||
}
|
||||
|
||||
type notary struct {
|
||||
client client.Client
|
||||
}
|
||||
|
||||
// NewNotary creates a new notary instance
|
||||
func NewNotary(ctx *cli.Context) Notary {
|
||||
return ¬ary{
|
||||
client: client.NewClient(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// Start the main routine for a notary
|
||||
func (c *notary) Start() error {
|
||||
log.Info("Starting notary client")
|
||||
err := c.client.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.client.Close()
|
||||
|
||||
if err := joinNotaryPool(c.client); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return subscribeBlockHeaders(c.client)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package collator
|
||||
package notary
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
@ -20,7 +20,7 @@ var (
|
||||
accountBalance1001Eth, _ = new(big.Int).SetString("1001000000000000000000", 10)
|
||||
)
|
||||
|
||||
// Mock client for testing collator. Should this go into sharding/client/testing?
|
||||
// Mock client for testing notary. Should this go into sharding/client/testing?
|
||||
type mockClient struct {
|
||||
smc *contracts.SMC
|
||||
t *testing.T
|
||||
@ -59,7 +59,7 @@ func (m *mockClient) Close() {
|
||||
}
|
||||
|
||||
// Helper/setup methods
|
||||
// TODO: consider moving these to common sharding testing package as the collator and smc tests
|
||||
// TODO: consider moving these to common sharding testing package as the notary and smc tests
|
||||
// use them.
|
||||
func transactOpts() *bind.TransactOpts {
|
||||
return bind.NewKeyedTransactor(key)
|
||||
@ -71,60 +71,60 @@ func setup() (*backends.SimulatedBackend, *contracts.SMC) {
|
||||
return backend, smc
|
||||
}
|
||||
|
||||
func TestIsAccountInCollatorPool(t *testing.T) {
|
||||
func TestIsAccountInNotaryPool(t *testing.T) {
|
||||
backend, smc := setup()
|
||||
client := &mockClient{smc: smc, t: t}
|
||||
|
||||
// address should not be in pool initially
|
||||
b, err := isAccountInCollatorPool(client)
|
||||
b, err := isAccountInNotaryPool(client)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("Account unexpectedly in collator pool")
|
||||
t.Fatal("Account unexpectedly in notary pool")
|
||||
}
|
||||
|
||||
txOpts := transactOpts()
|
||||
// deposit in collator pool, then it should return true
|
||||
// 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()
|
||||
b, err = isAccountInCollatorPool(client)
|
||||
b, err = isAccountInNotaryPool(client)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Fatal("Account not in collator pool when expected to be")
|
||||
t.Fatal("Account not in notary pool when expected to be")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinCollatorPool(t *testing.T) {
|
||||
func TestJoinNotaryPool(t *testing.T) {
|
||||
backend, smc := setup()
|
||||
client := &mockClient{smc, t}
|
||||
|
||||
// There should be no collators initially
|
||||
numCollators, err := smc.NumCollators(&bind.CallOpts{})
|
||||
// There should be no notary initially
|
||||
numNotaries, err := smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if big.NewInt(0).Cmp(numCollators) != 0 {
|
||||
t.Fatalf("Unexpected number of collators. Got %d, wanted 0.", numCollators)
|
||||
if big.NewInt(0).Cmp(numNotaries) != 0 {
|
||||
t.Fatalf("Unexpected number of notaries. Got %d, wanted 0.", numNotaries)
|
||||
}
|
||||
|
||||
err = joinCollatorPool(client)
|
||||
err = joinNotaryPool(client)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
// Now there should be one collator
|
||||
numCollators, err = smc.NumCollators(&bind.CallOpts{})
|
||||
// Now there should be one notary
|
||||
numNotaries, err = smc.NotaryPoolLength(&bind.CallOpts{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if big.NewInt(1).Cmp(numCollators) != 0 {
|
||||
t.Fatalf("Unexpected number of collators. Got %d, wanted 1.", numCollators)
|
||||
if big.NewInt(1).Cmp(numNotaries) != 0 {
|
||||
t.Fatalf("Unexpected number of notaries. Got %d, wanted 1.", numNotaries)
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
// Package proposer holds all of the functionality to run as a collation proposer in a sharded
|
||||
// system.
|
||||
package proposer
|
||||
|
||||
import (
|
||||
@ -8,6 +6,8 @@ import (
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
// Proposer holds functionality required to run a collation proposer
|
||||
// in a sharded system
|
||||
type Proposer interface {
|
||||
Start() error
|
||||
}
|
||||
@ -16,12 +16,14 @@ type proposer struct {
|
||||
client client.Client
|
||||
}
|
||||
|
||||
// NewProposer creates a struct instance
|
||||
func NewProposer(ctx *cli.Context) Proposer {
|
||||
return &proposer{
|
||||
client: client.NewClient(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
// Start the main entry point for proposing collations
|
||||
func (p *proposer) Start() error {
|
||||
log.Info("Starting proposer client")
|
||||
err := p.client.Start()
|
||||
|
Loading…
Reference in New Issue
Block a user