2018-05-10 16:33:18 +00:00
package proposer
2018-05-11 21:40:23 +00:00
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
2018-05-25 16:15:13 +00:00
"github.com/ethereum/go-ethereum/core/types"
2018-05-11 21:40:23 +00:00
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/sharding"
2018-06-07 06:03:54 +00:00
"github.com/ethereum/go-ethereum/sharding/mainchain"
2018-05-11 21:40:23 +00:00
)
2018-05-25 16:15:13 +00:00
// 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.
2018-06-12 23:03:20 +00:00
func createCollation ( client mainchain . Client , shardID * big . Int , period * big . Int , txs [ ] * types . Transaction ) ( * sharding . Collation , error ) {
2018-05-28 15:06:32 +00:00
// shardId has to be within range
2018-06-12 23:03:20 +00:00
shardCount , err := client . GetShardCount ( )
if err != nil {
return nil , fmt . Errorf ( "can't get shard count from smc: %v" , err )
}
if shardID . Cmp ( big . NewInt ( 0 ) ) < 0 || shardID . Cmp ( big . NewInt ( shardCount ) ) > 0 {
return nil , fmt . Errorf ( "can't create collation for shard %v. Must be between 0 and %v" , shardID , shardCount )
2018-05-28 15:06:32 +00:00
}
2018-05-25 16:15:13 +00:00
2018-06-01 23:04:42 +00:00
// check with SMC to see if we can add the header.
2018-06-12 23:03:20 +00:00
if a , _ := checkHeaderAdded ( client , shardID , period ) ; ! a {
2018-06-01 23:04:42 +00:00
return nil , fmt . Errorf ( "can't create collation, collation with same period has already been added" )
2018-05-28 15:06:32 +00:00
}
// serialized tx to blob for collation body.
2018-05-25 16:15:13 +00:00
blobs , err := sharding . SerializeTxToBlob ( txs )
if err != nil {
2018-05-28 15:06:32 +00:00
return nil , fmt . Errorf ( "can't create collation, serialization to blob failed: %v" , err )
2018-05-25 16:15:13 +00:00
}
// construct the header, leave chunkRoot and signature fields empty, to be filled later.
2018-06-07 06:03:54 +00:00
addr := client . Account ( ) . Address
2018-06-12 23:03:20 +00:00
header := sharding . NewCollationHeader ( shardID , nil , period , & addr , nil )
2018-05-25 16:15:13 +00:00
// construct the body with header, blobs(serialized txs) and txs.
collation := sharding . NewCollation ( header , blobs , txs )
collation . CalculateChunkRoot ( )
2018-06-07 06:03:54 +00:00
sig , err := client . Sign ( collation . Header ( ) . Hash ( ) )
2018-05-25 16:15:13 +00:00
if err != nil {
2018-05-28 15:06:32 +00:00
return nil , fmt . Errorf ( "can't create collation, sign collationHeader failed: %v" , err )
2018-05-25 16:15:13 +00:00
}
2018-05-28 15:06:32 +00:00
// add proposer signature to collation header.
2018-05-25 16:15:13 +00:00
collation . Header ( ) . AddSig ( sig )
2018-06-07 22:16:34 +00:00
log . Info ( fmt . Sprintf ( "Collation %v created for shardID %v period %v" , collation . Header ( ) . Hash ( ) . Hex ( ) , collation . Header ( ) . ShardID ( ) , collation . Header ( ) . Period ( ) ) )
2018-05-25 16:15:13 +00:00
return collation , nil
}
2018-05-11 21:40:23 +00:00
// 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.
2018-06-07 22:16:34 +00:00
func addHeader ( client mainchain . Client , collation * sharding . Collation ) error {
2018-05-11 21:40:23 +00:00
log . Info ( "Adding header to SMC" )
2018-06-07 06:03:54 +00:00
txOps , err := client . CreateTXOpts ( big . NewInt ( 0 ) )
2018-05-11 21:40:23 +00:00
if err != nil {
return fmt . Errorf ( "unable to initiate add header transaction: %v" , err )
}
2018-06-07 22:16:34 +00:00
// TODO: Copy is inefficient here. Let's research how to best convert hash to [32]byte.
2018-06-01 23:04:42 +00:00
var chunkRoot [ 32 ] byte
copy ( chunkRoot [ : ] , collation . Header ( ) . ChunkRoot ( ) . Bytes ( ) )
2018-05-21 02:28:37 +00:00
2018-06-07 06:03:54 +00:00
tx , err := client . SMCTransactor ( ) . AddHeader ( txOps , collation . Header ( ) . ShardID ( ) , collation . Header ( ) . Period ( ) , chunkRoot )
2018-05-11 21:40:23 +00:00
if err != nil {
return fmt . Errorf ( "unable to add header to SMC: %v" , err )
}
2018-06-07 22:16:34 +00:00
log . Info ( fmt . Sprintf ( "Add header transaction hash: %v" , tx . Hash ( ) . Hex ( ) ) )
2018-05-11 21:40:23 +00:00
return nil
}
2018-06-07 22:16:34 +00:00
// checkHeaderAdded checks if a collation header has already
2018-06-03 06:21:42 +00:00
// submitted to the main chain. There can only be one header per shard
// per period, proposer should check if a header's already submitted,
2018-06-07 22:16:34 +00:00
// checkHeaderAdded returns true if it's available, false if it's unavailable.
2018-06-12 23:03:20 +00:00
func checkHeaderAdded ( client mainchain . Client , shardID * big . Int , period * big . Int ) ( bool , error ) {
2018-05-11 21:40:23 +00:00
// Get the period of the last header.
2018-06-12 23:03:20 +00:00
lastPeriod , err := client . SMCCaller ( ) . LastSubmittedCollation ( & bind . CallOpts { } , shardID )
2018-05-11 21:40:23 +00:00
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
}