2018-01-28 21:57:20 +00:00
|
|
|
package sharding
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2018-02-01 19:30:24 +00:00
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
2018-01-28 21:57:20 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
// subscribeBlockHeaders checks incoming block headers and determines if
|
|
|
|
// we are an eligible proposer 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 VMC to create a collation
|
|
|
|
func subscribeBlockHeaders(c *Client) error {
|
|
|
|
headerChan := make(chan *types.Header, 16)
|
|
|
|
|
|
|
|
_, err := c.client.SubscribeNewHead(context.Background(), headerChan)
|
|
|
|
if err != nil {
|
2018-02-05 18:00:29 +00:00
|
|
|
return fmt.Errorf("unable to subscribe to incoming headers. %v", err)
|
2018-01-28 21:57:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Info("listening for new headers...")
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case head := <-headerChan:
|
|
|
|
// Query the current state to see if we are an eligible proposer
|
|
|
|
log.Info(fmt.Sprintf("received new header %v", head.Number.String()))
|
2018-02-01 04:33:38 +00:00
|
|
|
// TODO: Only run this code on certain periods?
|
2018-02-01 19:30:24 +00:00
|
|
|
err := watchShards(c, head)
|
|
|
|
if err != nil {
|
2018-02-05 18:00:29 +00:00
|
|
|
return fmt.Errorf("unable to watch shards. %v", err)
|
2018-02-01 19:30:24 +00:00
|
|
|
}
|
2018-01-28 21:57:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-01 04:33:38 +00:00
|
|
|
|
|
|
|
// watchShards checks if we are an eligible proposer for collation for
|
|
|
|
// the available shards in the VMC. The function calls getEligibleProposer from
|
|
|
|
// the VMC and proposes a collation if conditions are met
|
2018-02-01 04:41:11 +00:00
|
|
|
func watchShards(c *Client, head *types.Header) error {
|
2018-02-01 04:33:38 +00:00
|
|
|
|
2018-02-01 04:41:11 +00:00
|
|
|
accounts := c.keystore.Accounts()
|
|
|
|
if len(accounts) == 0 {
|
|
|
|
return fmt.Errorf("no accounts found")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := c.unlockAccount(accounts[0]); err != nil {
|
2018-02-05 18:00:29 +00:00
|
|
|
return fmt.Errorf("cannot unlock account. %v", err)
|
2018-02-01 04:41:11 +00:00
|
|
|
}
|
|
|
|
|
2018-02-01 19:30:24 +00:00
|
|
|
ops := bind.CallOpts{}
|
|
|
|
count, err := c.vmc.VMCCaller.ShardCount(&ops)
|
2018-02-01 04:41:11 +00:00
|
|
|
if err != nil {
|
2018-02-05 18:00:29 +00:00
|
|
|
return fmt.Errorf("unable to fetch shard count. %v", err)
|
2018-02-01 19:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s := 0
|
|
|
|
for s < int(count.Int64()) {
|
|
|
|
// Checks if we are an eligible proposer according to the VMC
|
2018-02-05 17:54:33 +00:00
|
|
|
addr, err := c.vmc.VMCCaller.GetEligibleProposer(&ops, big.NewInt(s))
|
2018-02-02 00:01:21 +00:00
|
|
|
if err != nil {
|
2018-02-05 18:00:29 +00:00
|
|
|
return fmt.Errorf("cannot fetch eligible collation proposer. %v", err)
|
2018-02-02 00:01:21 +00:00
|
|
|
}
|
|
|
|
// if the address is the coinbase addr (current node running the sharding
|
|
|
|
// clint, then we propose a new collation)
|
|
|
|
if addr == accounts[0].Address {
|
2018-02-05 18:00:29 +00:00
|
|
|
err := proposeCollation()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not propose collation. %v", err)
|
|
|
|
}
|
2018-02-02 00:01:21 +00:00
|
|
|
}
|
2018-02-01 19:30:24 +00:00
|
|
|
s++
|
2018-02-01 04:41:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2018-02-01 04:33:38 +00:00
|
|
|
}
|
2018-02-02 00:01:21 +00:00
|
|
|
|
2018-02-06 00:50:32 +00:00
|
|
|
// proposeCollation interacts with the VMC directly to add a collation header
|
|
|
|
func proposeCollation(shardID int) error {
|
|
|
|
// TODO: Adds a collation header to the VMC with the following fields:
|
|
|
|
// [
|
|
|
|
// shard_id: uint256,
|
|
|
|
// expected_period_number: uint256,
|
|
|
|
// period_start_prevhash: bytes32,
|
|
|
|
// parent_hash: bytes32,
|
|
|
|
// transactions_root: bytes32,
|
|
|
|
// coinbase: address,
|
|
|
|
// state_root: bytes32,
|
|
|
|
// receipts_root: bytes32,
|
|
|
|
// number: uint256,
|
|
|
|
// sig: bytes
|
|
|
|
// ]
|
|
|
|
//
|
|
|
|
// Before calling this, we would need to have access to the state of
|
|
|
|
// the period_start_prevhash. Refer to the comments in:
|
|
|
|
// https://github.com/ethereum/py-evm/issues/258#issuecomment-359879350
|
|
|
|
//
|
|
|
|
// This function will call FetchCandidateHead() of the VMC to obtain
|
|
|
|
// more necessary information.
|
|
|
|
//
|
|
|
|
// This functions will fetch the transactions in the txpool and and apply
|
|
|
|
// them to finish up the collation. It will then need to broadcast the
|
|
|
|
// collation to the main chain using JSON-RPC.
|
|
|
|
|
2018-02-02 00:01:21 +00:00
|
|
|
return nil
|
|
|
|
}
|