Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Proposers Submitting Collations onto SMC #111

Merged
merged 19 commits into from Jun 10, 2018
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -70,6 +70,14 @@ func (h *CollationHeader) Hash() (hash common.Hash) {
return hash
}

// AddSig adds the signature of proposer after collationHeader gets signed.
func (h *CollationHeader) AddSig(sig []byte) {
h.data.ProposerSignature = sig
}

// Signature of the collation corresponds to.
func (h *CollationHeader) Sig() []byte { return h.data.ProposerSignature }

// ShardID the collation corresponds to.
func (h *CollationHeader) ShardID() *big.Int { return h.data.ShardID }

@@ -42,6 +42,7 @@ type Client interface {
DepositFlag() bool
SetDepositFlag(deposit bool)
DataDirPath() string
Sign(hash common.Hash) ([]byte, error)
}

// SMCClient defines a struct that interacts with a
@@ -217,3 +218,10 @@ func (s *SMCClient) unlockAccount(account accounts.Account) error {

return s.keystore.Unlock(account, pass)
}

// Sign signs the hash of collationHeader contents by
// using default account on keystore and returns signed signature.
func (s *SMCClient) Sign(hash common.Hash) ([]byte, error) {
account := s.Account()
return s.keystore.SignHash(*account, hash.Bytes())
}
@@ -144,7 +144,7 @@ func joinNotaryPool(client mainchain.Client) error {
log.Info("Joining notary pool")
txOps, err := client.CreateTXOpts(sharding.NotaryDeposit)
if err != nil {
return fmt.Errorf("unable to intiate the deposit transaction: %v", err)
return fmt.Errorf("unable to initiate the deposit transaction: %v", err)
}

tx, err := client.SMCTransactor().RegisterNotary(txOps)
@@ -75,6 +75,20 @@ func (s *smcClient) SetDepositFlag(deposit bool) {
s.depositFlag = deposit
}

func (m *smcClient) Sign(hash common.Hash) ([]byte, error) {
return nil, nil
}

// Unused mockClient methods.
func (m *smcClient) Start() error {
m.t.Fatal("Start called")
return nil
}

func (m *smcClient) Close() {
m.t.Fatal("Close called")
}

func (s *smcClient) DataDirPath() string {
return "/tmp/datadir"
}
@@ -0,0 +1,89 @@
package proposer

import (
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/sharding"
"github.com/ethereum/go-ethereum/sharding/mainchain"
)

// createCollation creates collation base struct with header
// and body. Header consists of shardID, ChunkRoot, period,
// proposer addr and signatures. Body contains serialized blob
// of a collations transactions.
func createCollation(client mainchain.Client, shardId *big.Int, period *big.Int, txs []*types.Transaction) (*sharding.Collation, error) {
// shardId has to be within range
if shardId.Cmp(big.NewInt(0)) < 0 || shardId.Cmp(big.NewInt(sharding.ShardCount)) > 0 {
return nil, fmt.Errorf("can't create collation for shard %v. Must be between 0 and %v", shardId, sharding.ShardCount)
}

// check with SMC to see if we can add the header.
if a, _ := checkHeaderAdded(client, shardId, period); !a {
return nil, fmt.Errorf("can't create collation, collation with same period has already been added")
}

// serialized tx to blob for collation body.
blobs, err := sharding.SerializeTxToBlob(txs)
if err != nil {
return nil, fmt.Errorf("can't create collation, serialization to blob failed: %v", err)
}

// construct the header, leave chunkRoot and signature fields empty, to be filled later.
addr := client.Account().Address
header := sharding.NewCollationHeader(shardId, nil, period, &addr, nil)

// construct the body with header, blobs(serialized txs) and txs.
collation := sharding.NewCollation(header, blobs, txs)
collation.CalculateChunkRoot()
sig, err := client.Sign(collation.Header().Hash())
if err != nil {
return nil, fmt.Errorf("can't create collation, sign collationHeader failed: %v", err)
}

// add proposer signature to collation header.
collation.Header().AddSig(sig)
log.Info(fmt.Sprintf("Collation %v created for shardID %v period %v", collation.Header().Hash().Hex(), collation.Header().ShardID(), collation.Header().Period()))
return collation, nil
}

// addHeader adds the collation header to the main chain by sending
// an addHeader transaction to the sharding manager contract.
// There can only exist one header per period per shard, it's proposer's
// responsibility to check if a header has been added.
func addHeader(client mainchain.Client, collation *sharding.Collation) error {
log.Info("Adding header to SMC")

txOps, err := client.CreateTXOpts(big.NewInt(0))
if err != nil {
return fmt.Errorf("unable to initiate add header transaction: %v", err)
}

// TODO: Copy is inefficient here. Let's research how to best convert hash to [32]byte.
var chunkRoot [32]byte
copy(chunkRoot[:], collation.Header().ChunkRoot().Bytes())

tx, err := client.SMCTransactor().AddHeader(txOps, collation.Header().ShardID(), collation.Header().Period(), chunkRoot)
if err != nil {
return fmt.Errorf("unable to add header to SMC: %v", err)
}
log.Info(fmt.Sprintf("Add header transaction hash: %v", tx.Hash().Hex()))
return nil
}

// checkHeaderAdded checks if a collation header has already
// submitted to the main chain. There can only be one header per shard
// per period, proposer should check if a header's already submitted,
// checkHeaderAdded returns true if it's available, false if it's unavailable.
func checkHeaderAdded(client mainchain.Client, shardId *big.Int, period *big.Int) (bool, error) {
// Get the period of the last header.
lastPeriod, err := client.SMCCaller().LastSubmittedCollation(&bind.CallOpts{}, shardId)
if err != nil {
return false, fmt.Errorf("unable to get the period of last submitted collation: %v", err)
}
// True if current period is greater than last added period.
return period.Cmp(lastPeriod) > 0, nil
}
@@ -3,6 +3,12 @@
package proposer

import (
"context"
"crypto/rand"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/sharding"
@@ -13,7 +19,7 @@ import (
// in a sharded system. Must satisfy the Service interface defined in
// sharding/service.go.
type Proposer struct {
client mainchain.Client
client *mainchain.SMCClient
shardp2p sharding.ShardP2P
txpool sharding.TXPool
shardChainDb ethdb.Database
@@ -22,15 +28,49 @@ type Proposer struct {
// NewProposer creates a struct instance of a proposer service.
// It will have access to a mainchain client, a shardp2p network,
// and a shard transaction pool.
func NewProposer(client mainchain.Client, shardp2p sharding.ShardP2P, txpool sharding.TXPool, shardChainDb ethdb.Database) (*Proposer, error) {
func NewProposer(client *mainchain.SMCClient, shardp2p sharding.ShardP2P, txpool sharding.TXPool, shardChainDb ethdb.Database) (*Proposer, error) {
// Initializes a directory persistent db.
return &Proposer{client, shardp2p, txpool, shardChainDb}, nil
}

// Start the main loop for proposing collations.
func (p *Proposer) Start() error {
log.Info("Starting proposer service")
// TODO: Propose collations.

// TODO: Receive TXs from shard TX generator or TXpool (Github Issues 153 and 161)
var txs []*types.Transaction
for i := 0; i < 10; i++ {
data := make([]byte, 1024)
rand.Read(data)
txs = append(txs, types.NewTransaction(0, common.HexToAddress("0x0"),
nil, 0, nil, data))
}

// TODO: Create and use CLI flag for shardID
shardID := big.NewInt(0)

// Get current block number.
blockNumber, err := p.client.ChainReader().BlockByNumber(context.Background(), nil)
if err != nil {
return err
}
period := new(big.Int).Div(blockNumber.Number(), big.NewInt(sharding.PeriodLength))

// Create collation.
collation, err := createCollation(p.client, shardID, period, txs)
if err != nil {
return err
}

// Check SMC if we can submit header before addHeader
canAdd, err := checkHeaderAdded(p.client, shardID, period)
if err != nil {
return err
}
if canAdd {
addHeader(p.client, collation)
}

return nil
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.