mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Merge pull request #3 from prestonvanloon/sharding
geth sharding entrypoint and contract deployment Former-commit-id: ba96e81a08e862ab78d7aaf96eba5709875d9704 [formerly b3969a9fc197dacc351f944e5abcacc4e1a41381] Former-commit-id: 4b8e449ce897cf8576678535811b95727982e2a1
This commit is contained in:
@@ -164,6 +164,8 @@ func init() {
|
||||
consoleCommand,
|
||||
attachCommand,
|
||||
javascriptCommand,
|
||||
// See shardingcmd.go:
|
||||
shardingClientCommand,
|
||||
// See misccmd.go:
|
||||
makecacheCommand,
|
||||
makedagCommand,
|
||||
|
||||
32
cmd/geth/shardingcmd.go
Normal file
32
cmd/geth/shardingcmd.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/sharding"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
shardingClientCommand = cli.Command{
|
||||
Action: utils.MigrateFlags(shardingClient),
|
||||
Name: "sharding",
|
||||
Aliases: []string{"shard"},
|
||||
Usage: "Start a sharding client",
|
||||
ArgsUsage: "[endpoint]",
|
||||
Flags: []cli.Flag{utils.DataDirFlag, utils.PasswordFileFlag, utils.NetworkIdFlag},
|
||||
Category: "SHARDING COMMANDS",
|
||||
Description: `
|
||||
The Geth sharding client connects to a running geth node in sharding mode. This feature is a work in progress.
|
||||
`,
|
||||
}
|
||||
)
|
||||
|
||||
func shardingClient(ctx *cli.Context) error {
|
||||
c := sharding.MakeShardingClient(ctx)
|
||||
if err := c.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.Wait()
|
||||
return nil
|
||||
}
|
||||
1
password.txt
Normal file
1
password.txt
Normal file
@@ -0,0 +1 @@
|
||||
123456
|
||||
105
sharding/client.go
Normal file
105
sharding/client.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package sharding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
clientIdentifier = "geth" // Used to determine the ipc name.
|
||||
)
|
||||
|
||||
// Client for sharding. Communicates to geth node via JSON RPC.
|
||||
type Client struct {
|
||||
endpoint string // Endpoint to JSON RPC
|
||||
client *ethclient.Client // Ethereum RPC client.
|
||||
keystore *keystore.KeyStore // Keystore containing the single signer
|
||||
ctx *cli.Context // Command line context
|
||||
}
|
||||
|
||||
// MakeShardingClient for interfacing with geth full node.
|
||||
func MakeShardingClient(ctx *cli.Context) *Client {
|
||||
path := node.DefaultDataDir()
|
||||
if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
|
||||
path = ctx.GlobalString(utils.DataDirFlag.Name)
|
||||
}
|
||||
|
||||
endpoint := ctx.Args().First()
|
||||
if endpoint == "" {
|
||||
endpoint = fmt.Sprintf("%s/%s.ipc", path, clientIdentifier)
|
||||
}
|
||||
|
||||
config := &node.Config{
|
||||
DataDir: path,
|
||||
}
|
||||
scryptN, scryptP, keydir, err := config.AccountConfig()
|
||||
if err != nil {
|
||||
panic(err) // TODO(prestonvanloon): handle this
|
||||
}
|
||||
ks := keystore.NewKeyStore(keydir, scryptN, scryptP)
|
||||
|
||||
return &Client{
|
||||
endpoint: endpoint,
|
||||
keystore: ks,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Start the sharding client.
|
||||
// * Connects to node.
|
||||
// * Verifies or deploys the validator management contract.
|
||||
func (c *Client) Start() error {
|
||||
log.Info("Starting sharding client")
|
||||
rpcClient, err := dialRPC(c.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.client = ethclient.NewClient(rpcClient)
|
||||
defer rpcClient.Close()
|
||||
if err := c.verifyVMC(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Wait to be selected as collator in goroutine?
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait until sharding client is shutdown.
|
||||
func (c *Client) Wait() {
|
||||
// TODO: Blocking lock.
|
||||
}
|
||||
|
||||
// dialRPC endpoint to node.
|
||||
func dialRPC(endpoint string) (*rpc.Client, error) {
|
||||
if endpoint == "" {
|
||||
endpoint = node.DefaultIPCEndpoint(clientIdentifier)
|
||||
}
|
||||
return rpc.Dial(endpoint)
|
||||
}
|
||||
|
||||
// UnlockAccount will unlock the specified account using utils.PasswordFileFlag or empty string if unset.
|
||||
func (c *Client) unlockAccount(account accounts.Account) error {
|
||||
pass := ""
|
||||
|
||||
if c.ctx.GlobalIsSet(utils.PasswordFileFlag.Name) {
|
||||
blob, err := ioutil.ReadFile(c.ctx.GlobalString(utils.PasswordFileFlag.Name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read account password contents in file %s. %v", utils.PasswordFileFlag.Value, err)
|
||||
}
|
||||
// TODO: Use bufio.Scanner or other reader that doesn't include a trailing newline character.
|
||||
pass = strings.Trim(string(blob), "\n") // Some text files end in new line, remove with strings.Trim.
|
||||
}
|
||||
|
||||
return c.keystore.Unlock(account, pass)
|
||||
}
|
||||
132
sharding/client_test.go
Normal file
132
sharding/client_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package sharding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
// FakeEthService based on implementation of internal/ethapi.Client
|
||||
type FakeEthService struct {
|
||||
mu sync.Mutex
|
||||
|
||||
getCodeResp hexutil.Bytes
|
||||
getCodeErr error
|
||||
}
|
||||
|
||||
// eth_getCode
|
||||
func (s *FakeEthService) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.getCodeResp, s.getCodeErr
|
||||
}
|
||||
|
||||
// Set return values for eth_getCode
|
||||
func (s *FakeEthService) SetGetCode(resp hexutil.Bytes, err error) {
|
||||
s.mu.Lock()
|
||||
s.getCodeResp = resp
|
||||
s.getCodeErr = err
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *FakeEthService) GasPrice(ctx context.Context) (*big.Int, error) {
|
||||
return big.NewInt(10000), nil
|
||||
}
|
||||
|
||||
func (s *FakeEthService) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *FakeEthService) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) {
|
||||
return common.Hash{}, nil
|
||||
}
|
||||
|
||||
func (s *FakeEthService) GetTransactionReceipt(hash common.Hash) (*types.Receipt, error) {
|
||||
return &types.Receipt{
|
||||
ContractAddress: common.StringToAddress("0x1"),
|
||||
Logs: []*types.Log{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *FakeEthService) GetTransactionByHash(hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
type FakeNetworkService struct{}
|
||||
|
||||
func (s *FakeNetworkService) Version() (string, error) {
|
||||
return "100", nil
|
||||
}
|
||||
|
||||
func newTestServer(endpoint string) (*rpc.Server, error) {
|
||||
// Create datadir.
|
||||
if err := os.Mkdir(endpoint, 0777); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a default account without password.
|
||||
scryptN, scryptP, keydir, err := (&node.Config{DataDir: endpoint}).AccountConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := keystore.StoreKey(keydir, "" /*password*/, scryptN, scryptP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create server and register eth service with FakeEthService
|
||||
server := rpc.NewServer()
|
||||
if err := server.RegisterName("eth", new(FakeEthService)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := server.RegisterName("net", new(FakeNetworkService)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := rpc.CreateIPCListener(endpoint + "/geth.ipc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go server.ServeListener(l)
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func createContext() *cli.Context {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(utils.DataDirFlag.Name, "", "")
|
||||
return cli.NewContext(nil, set, nil)
|
||||
}
|
||||
|
||||
func TestShardingClient(t *testing.T) {
|
||||
endpoint := fmt.Sprintf("%s/go-ethereum-test-ipc-%d-%d", os.TempDir(), os.Getpid(), rand.Int63())
|
||||
server, err := newTestServer(endpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a test server: %v", err)
|
||||
}
|
||||
defer server.Stop()
|
||||
|
||||
ctx := createContext()
|
||||
if err := ctx.GlobalSet(utils.DataDirFlag.Name, endpoint); err != nil {
|
||||
t.Fatalf("Failed to set global variable for flag %s. Error: %v", utils.DataDirFlag.Name, err)
|
||||
}
|
||||
|
||||
c := MakeShardingClient(ctx)
|
||||
|
||||
if err := c.Start(); err != nil {
|
||||
t.Errorf("Failed to start server: %v", err)
|
||||
}
|
||||
}
|
||||
24
sharding/config.go
Normal file
24
sharding/config.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package sharding
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
// Number of network shards
|
||||
shardCount = 100
|
||||
// Address of the validator management contract
|
||||
validatorManagerAddress = common.HexToAddress("0x0") // TODO
|
||||
// Gas limit for verifying signatures
|
||||
sigGasLimit = 40000
|
||||
// Number of blocks in a period
|
||||
periodLength = 5
|
||||
// Number of periods ahead of current period which the contract is able to return the collator of that period.
|
||||
lookaheadPeriods = 4
|
||||
// Required deposit size in wei
|
||||
depositSize = new(big.Int).Exp(big.NewInt(10), big.NewInt(20), nil) // 100 ETH
|
||||
// Gas limit to create contract
|
||||
contractGasLimit = uint64(4700000) // Max is 4712388
|
||||
)
|
||||
16
sharding/config_test.go
Normal file
16
sharding/config_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package sharding
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDepositSize(t *testing.T) {
|
||||
want, err := new(big.Int).SetString("100000000000000000000", 10) // 100 ETH
|
||||
if !err {
|
||||
t.Fatalf("Failed to setup test")
|
||||
}
|
||||
if depositSize.Cmp(want) != 0 {
|
||||
t.Errorf("depositSize incorrect. Wanted %d, got %d", want, depositSize)
|
||||
}
|
||||
}
|
||||
115
sharding/vmc.go
Normal file
115
sharding/vmc.go
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user