2018-04-26 18:10:31 +00:00
package notary
2018-01-28 21:57:20 +00:00
import (
"context"
"fmt"
2018-02-08 02:00:20 +00:00
"math/big"
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"
2018-04-01 19:46:02 +00:00
"github.com/ethereum/go-ethereum/params"
2018-03-31 04:07:42 +00:00
"github.com/ethereum/go-ethereum/sharding"
2018-03-31 18:25:39 +00:00
"github.com/ethereum/go-ethereum/sharding/client"
2018-01-28 21:57:20 +00:00
)
2018-02-07 01:06:47 +00:00
// SubscribeBlockHeaders checks incoming block headers and determines if
2018-04-26 18:10:31 +00:00
// we are an eligible notary for collations. Then, it finds the pending tx's
2018-01-28 21:57:20 +00:00
// 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
2018-05-03 01:16:07 +00:00
// to the SMC to create a collation.
2018-03-31 18:25:39 +00:00
func subscribeBlockHeaders ( c client . Client ) error {
2018-01-28 21:57:20 +00:00
headerChan := make ( chan * types . Header , 16 )
2018-02-08 02:00:20 +00:00
_ , err := c . ChainReader ( ) . SubscribeNewHead ( context . Background ( ) , headerChan )
2018-01-28 21:57:20 +00:00
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
}
2018-02-06 20:04:45 +00:00
log . Info ( "Listening for new headers..." )
2018-01-28 21:57:20 +00:00
for {
2018-05-03 01:16:07 +00:00
// TODO: Error handling for getting disconnected from the client.
2018-02-10 00:07:51 +00:00
head := <- headerChan
2018-05-03 01:16:07 +00:00
// Query the current state to see if we are an eligible notary.
2018-02-10 00:07:51 +00:00
log . Info ( fmt . Sprintf ( "Received new header: %v" , head . Number . String ( ) ) )
2018-02-26 22:48:42 +00:00
2018-05-03 01:16:07 +00:00
// Check if we are in the notary pool before checking if we are an eligible notary.
2018-04-26 18:10:31 +00:00
v , err := isAccountInNotaryPool ( c )
2018-02-26 22:48:42 +00:00
if err != nil {
2018-04-26 18:10:31 +00:00
return fmt . Errorf ( "unable to verify client in notary pool. %v" , err )
2018-02-26 22:48:42 +00:00
}
2018-02-27 02:19:36 +00:00
if v {
2018-04-26 18:10:31 +00:00
if err := checkSMCForNotary ( c , head ) ; err != nil {
2018-02-26 22:48:42 +00:00
return fmt . Errorf ( "unable to watch shards. %v" , err )
}
} else {
2018-01-28 21:57:20 +00:00
}
2018-02-10 00:07:51 +00:00
2018-01-28 21:57:20 +00:00
}
}
2018-02-01 04:33:38 +00:00
2018-04-26 18:10:31 +00:00
// checkSMCForNotary checks if we are an eligible notary for
2018-03-08 17:34:15 +00:00
// collation for the available shards in the SMC. The function calls
2018-04-26 18:10:31 +00:00
// getEligibleNotary from the SMC and notary a collation if
2018-05-03 01:16:07 +00:00
// conditions are met.
2018-04-26 18:10:31 +00:00
func checkSMCForNotary ( c client . Client , head * types . Header ) error {
log . Info ( "Checking if we are an eligible collation notary for a shard..." )
2018-03-31 04:07:42 +00:00
period := big . NewInt ( 0 ) . Div ( head . Number , big . NewInt ( sharding . PeriodLength ) )
for s := int64 ( 0 ) ; s < sharding . ShardCount ; s ++ {
2018-05-03 01:16:07 +00:00
// Checks if we are an eligible notary according to the SMC.
2018-04-28 00:23:42 +00:00
addr , err := c . SMCCaller ( ) . GetNotaryInCommittee ( & bind . CallOpts { } , big . NewInt ( s ) , period )
2018-02-23 19:57:37 +00:00
2018-02-09 23:47:36 +00:00
if err != nil {
return err
}
2018-02-06 04:41:40 +00:00
2018-05-03 01:16:07 +00:00
// If output is non-empty and the addr == coinbase.
2018-03-31 18:25:39 +00:00
if addr == c . Account ( ) . Address {
2018-04-26 18:10:31 +00:00
log . Info ( fmt . Sprintf ( "Selected as notary on shard: %d" , s ) )
2018-03-08 17:34:15 +00:00
err := submitCollation ( s )
2018-02-05 18:00:29 +00:00
if err != nil {
2018-04-24 16:01:16 +00:00
return err
}
2018-05-03 01:16:07 +00:00
// If the account is selected as collator, submit collation.
2018-04-24 16:01:16 +00:00
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 )
}
2018-02-05 18:00:29 +00:00
}
2018-02-02 00:01:21 +00:00
}
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-04-26 18:10:31 +00:00
// isAccountInNotaryPool checks if the client is in the notary pool because
2018-02-27 01:31:00 +00:00
// we can't guarantee our tx for deposit will be in the next block header we receive.
2018-04-26 18:10:31 +00:00
// The function calls IsNotaryDeposited from the SMC and returns true if
2018-05-03 01:16:07 +00:00
// the client is in the notary pool.
2018-04-26 18:10:31 +00:00
func isAccountInNotaryPool ( c client . Client ) ( bool , error ) {
2018-02-27 17:14:00 +00:00
account := c . Account ( )
2018-05-03 01:16:07 +00:00
// Checks if our deposit has gone through according to the SMC.
2018-04-28 00:23:42 +00:00
n , err := c . SMCCaller ( ) . NotaryRegistry ( & bind . CallOpts { } , account . Address )
if ! n . Deposited && err != nil {
2018-04-26 18:10:31 +00:00
log . Warn ( fmt . Sprintf ( "Account %s not in notary pool." , account . Address . String ( ) ) )
2018-03-31 18:25:39 +00:00
}
2018-04-28 00:23:42 +00:00
return n . Deposited , err
2018-02-26 22:48:42 +00:00
}
2018-05-03 01:16:07 +00:00
// submitCollation interacts with the SMC directly to add a collation header.
2018-03-08 17:34:15 +00:00
func submitCollation ( shardID int64 ) error {
// TODO: Adds a collation header to the SMC with the following fields:
2018-02-06 00:50:32 +00:00
// [
// 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
//
2018-03-08 17:34:15 +00:00
// This function will call FetchCandidateHead() of the SMC to obtain
2018-02-06 00:50:32 +00:00
// more necessary information.
//
2018-03-08 17:40:59 +00:00
// This functions will fetch the transactions in the proposer tx pool and and apply
2018-02-06 00:50:32 +00:00
// them to finish up the collation. It will then need to broadcast the
// collation to the main chain using JSON-RPC.
2018-03-08 17:34:15 +00:00
log . Info ( "Submit collation function called" )
2018-02-02 00:01:21 +00:00
return nil
}
2018-04-01 19:46:02 +00:00
2018-04-26 18:10:31 +00:00
// joinNotaryPool checks if the account is a notary in the SMC. If
2018-04-03 02:25:26 +00:00
// the account is not in the set, it will deposit ETH into contract.
2018-04-26 18:10:31 +00:00
func joinNotaryPool ( c client . Client ) error {
2018-04-01 19:46:02 +00:00
2018-04-26 18:10:31 +00:00
log . Info ( "Joining notary pool" )
2018-04-23 18:42:08 +00:00
txOps , err := c . CreateTXOpts ( sharding . NotaryDeposit )
2018-04-01 19:46:02 +00:00
if err != nil {
return fmt . Errorf ( "unable to intiate the deposit transaction: %v" , err )
}
2018-04-24 16:01:16 +00:00
tx , err := c . SMCTransactor ( ) . RegisterNotary ( txOps )
2018-04-01 19:46:02 +00:00
if err != nil {
2018-04-26 18:10:31 +00:00
return fmt . Errorf ( "unable to deposit eth and become a notary: %v" , err )
2018-04-01 19:46:02 +00:00
}
2018-04-23 18:42:08 +00:00
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 ( ) ) )
2018-04-01 19:46:02 +00:00
return nil
}