Skip to content

Commit 71860e6

Browse files
authored
Merge pull request #3 from prestonvanloon/sharding
geth sharding entrypoint and contract deployment Former-commit-id: ba96e81a08e862ab78d7aaf96eba5709875d9704 [formerly b3969a9] Former-commit-id: 4b8e449
2 parents da87a34 + 200075f commit 71860e6

File tree

8 files changed

+427
-0
lines changed

8 files changed

+427
-0
lines changed

cmd/geth/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ func init() {
164164
consoleCommand,
165165
attachCommand,
166166
javascriptCommand,
167+
// See shardingcmd.go:
168+
shardingClientCommand,
167169
// See misccmd.go:
168170
makecacheCommand,
169171
makedagCommand,

cmd/geth/shardingcmd.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package main
2+
3+
import (
4+
"github.com/ethereum/go-ethereum/sharding"
5+
6+
"github.com/ethereum/go-ethereum/cmd/utils"
7+
cli "gopkg.in/urfave/cli.v1"
8+
)
9+
10+
var (
11+
shardingClientCommand = cli.Command{
12+
Action: utils.MigrateFlags(shardingClient),
13+
Name: "sharding",
14+
Aliases: []string{"shard"},
15+
Usage: "Start a sharding client",
16+
ArgsUsage: "[endpoint]",
17+
Flags: []cli.Flag{utils.DataDirFlag, utils.PasswordFileFlag, utils.NetworkIdFlag},
18+
Category: "SHARDING COMMANDS",
19+
Description: `
20+
The Geth sharding client connects to a running geth node in sharding mode. This feature is a work in progress.
21+
`,
22+
}
23+
)
24+
25+
func shardingClient(ctx *cli.Context) error {
26+
c := sharding.MakeShardingClient(ctx)
27+
if err := c.Start(); err != nil {
28+
return err
29+
}
30+
c.Wait()
31+
return nil
32+
}

password.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
123456

sharding/client.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package sharding
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"strings"
7+
8+
"github.com/ethereum/go-ethereum/accounts"
9+
"github.com/ethereum/go-ethereum/accounts/keystore"
10+
"github.com/ethereum/go-ethereum/cmd/utils"
11+
"github.com/ethereum/go-ethereum/ethclient"
12+
"github.com/ethereum/go-ethereum/log"
13+
"github.com/ethereum/go-ethereum/node"
14+
"github.com/ethereum/go-ethereum/rpc"
15+
cli "gopkg.in/urfave/cli.v1"
16+
)
17+
18+
const (
19+
clientIdentifier = "geth" // Used to determine the ipc name.
20+
)
21+
22+
// Client for sharding. Communicates to geth node via JSON RPC.
23+
type Client struct {
24+
endpoint string // Endpoint to JSON RPC
25+
client *ethclient.Client // Ethereum RPC client.
26+
keystore *keystore.KeyStore // Keystore containing the single signer
27+
ctx *cli.Context // Command line context
28+
}
29+
30+
// MakeShardingClient for interfacing with geth full node.
31+
func MakeShardingClient(ctx *cli.Context) *Client {
32+
path := node.DefaultDataDir()
33+
if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
34+
path = ctx.GlobalString(utils.DataDirFlag.Name)
35+
}
36+
37+
endpoint := ctx.Args().First()
38+
if endpoint == "" {
39+
endpoint = fmt.Sprintf("%s/%s.ipc", path, clientIdentifier)
40+
}
41+
42+
config := &node.Config{
43+
DataDir: path,
44+
}
45+
scryptN, scryptP, keydir, err := config.AccountConfig()
46+
if err != nil {
47+
panic(err) // TODO(prestonvanloon): handle this
48+
}
49+
ks := keystore.NewKeyStore(keydir, scryptN, scryptP)
50+
51+
return &Client{
52+
endpoint: endpoint,
53+
keystore: ks,
54+
ctx: ctx,
55+
}
56+
}
57+
58+
// Start the sharding client.
59+
// * Connects to node.
60+
// * Verifies or deploys the validator management contract.
61+
func (c *Client) Start() error {
62+
log.Info("Starting sharding client")
63+
rpcClient, err := dialRPC(c.endpoint)
64+
if err != nil {
65+
return err
66+
}
67+
c.client = ethclient.NewClient(rpcClient)
68+
defer rpcClient.Close()
69+
if err := c.verifyVMC(); err != nil {
70+
return err
71+
}
72+
73+
// TODO: Wait to be selected as collator in goroutine?
74+
75+
return nil
76+
}
77+
78+
// Wait until sharding client is shutdown.
79+
func (c *Client) Wait() {
80+
// TODO: Blocking lock.
81+
}
82+
83+
// dialRPC endpoint to node.
84+
func dialRPC(endpoint string) (*rpc.Client, error) {
85+
if endpoint == "" {
86+
endpoint = node.DefaultIPCEndpoint(clientIdentifier)
87+
}
88+
return rpc.Dial(endpoint)
89+
}
90+
91+
// UnlockAccount will unlock the specified account using utils.PasswordFileFlag or empty string if unset.
92+
func (c *Client) unlockAccount(account accounts.Account) error {
93+
pass := ""
94+
95+
if c.ctx.GlobalIsSet(utils.PasswordFileFlag.Name) {
96+
blob, err := ioutil.ReadFile(c.ctx.GlobalString(utils.PasswordFileFlag.Name))
97+
if err != nil {
98+
return fmt.Errorf("unable to read account password contents in file %s. %v", utils.PasswordFileFlag.Value, err)
99+
}
100+
// TODO: Use bufio.Scanner or other reader that doesn't include a trailing newline character.
101+
pass = strings.Trim(string(blob), "\n") // Some text files end in new line, remove with strings.Trim.
102+
}
103+
104+
return c.keystore.Unlock(account, pass)
105+
}

sharding/client_test.go

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package sharding
2+
3+
import (
4+
"context"
5+
"flag"
6+
"fmt"
7+
"math/big"
8+
"math/rand"
9+
"os"
10+
"sync"
11+
"testing"
12+
13+
"github.com/ethereum/go-ethereum/accounts/keystore"
14+
"github.com/ethereum/go-ethereum/core/types"
15+
16+
"github.com/ethereum/go-ethereum/cmd/utils"
17+
"github.com/ethereum/go-ethereum/common"
18+
"github.com/ethereum/go-ethereum/common/hexutil"
19+
"github.com/ethereum/go-ethereum/node"
20+
"github.com/ethereum/go-ethereum/rpc"
21+
cli "gopkg.in/urfave/cli.v1"
22+
)
23+
24+
// FakeEthService based on implementation of internal/ethapi.Client
25+
type FakeEthService struct {
26+
mu sync.Mutex
27+
28+
getCodeResp hexutil.Bytes
29+
getCodeErr error
30+
}
31+
32+
// eth_getCode
33+
func (s *FakeEthService) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
34+
s.mu.Lock()
35+
defer s.mu.Unlock()
36+
return s.getCodeResp, s.getCodeErr
37+
}
38+
39+
// Set return values for eth_getCode
40+
func (s *FakeEthService) SetGetCode(resp hexutil.Bytes, err error) {
41+
s.mu.Lock()
42+
s.getCodeResp = resp
43+
s.getCodeErr = err
44+
s.mu.Unlock()
45+
}
46+
47+
func (s *FakeEthService) GasPrice(ctx context.Context) (*big.Int, error) {
48+
return big.NewInt(10000), nil
49+
}
50+
51+
func (s *FakeEthService) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) {
52+
return nil, nil
53+
}
54+
55+
func (s *FakeEthService) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) {
56+
return common.Hash{}, nil
57+
}
58+
59+
func (s *FakeEthService) GetTransactionReceipt(hash common.Hash) (*types.Receipt, error) {
60+
return &types.Receipt{
61+
ContractAddress: common.StringToAddress("0x1"),
62+
Logs: []*types.Log{},
63+
}, nil
64+
}
65+
66+
func (s *FakeEthService) GetTransactionByHash(hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
67+
return nil, false, nil
68+
}
69+
70+
type FakeNetworkService struct{}
71+
72+
func (s *FakeNetworkService) Version() (string, error) {
73+
return "100", nil
74+
}
75+
76+
func newTestServer(endpoint string) (*rpc.Server, error) {
77+
// Create datadir.
78+
if err := os.Mkdir(endpoint, 0777); err != nil {
79+
return nil, err
80+
}
81+
82+
// Create a default account without password.
83+
scryptN, scryptP, keydir, err := (&node.Config{DataDir: endpoint}).AccountConfig()
84+
if err != nil {
85+
return nil, err
86+
}
87+
if _, err := keystore.StoreKey(keydir, "" /*password*/, scryptN, scryptP); err != nil {
88+
return nil, err
89+
}
90+
91+
// Create server and register eth service with FakeEthService
92+
server := rpc.NewServer()
93+
if err := server.RegisterName("eth", new(FakeEthService)); err != nil {
94+
return nil, err
95+
}
96+
if err := server.RegisterName("net", new(FakeNetworkService)); err != nil {
97+
return nil, err
98+
}
99+
l, err := rpc.CreateIPCListener(endpoint + "/geth.ipc")
100+
if err != nil {
101+
return nil, err
102+
}
103+
go server.ServeListener(l)
104+
105+
return server, nil
106+
}
107+
108+
func createContext() *cli.Context {
109+
set := flag.NewFlagSet("test", 0)
110+
set.String(utils.DataDirFlag.Name, "", "")
111+
return cli.NewContext(nil, set, nil)
112+
}
113+
114+
func TestShardingClient(t *testing.T) {
115+
endpoint := fmt.Sprintf("%s/go-ethereum-test-ipc-%d-%d", os.TempDir(), os.Getpid(), rand.Int63())
116+
server, err := newTestServer(endpoint)
117+
if err != nil {
118+
t.Fatalf("Failed to create a test server: %v", err)
119+
}
120+
defer server.Stop()
121+
122+
ctx := createContext()
123+
if err := ctx.GlobalSet(utils.DataDirFlag.Name, endpoint); err != nil {
124+
t.Fatalf("Failed to set global variable for flag %s. Error: %v", utils.DataDirFlag.Name, err)
125+
}
126+
127+
c := MakeShardingClient(ctx)
128+
129+
if err := c.Start(); err != nil {
130+
t.Errorf("Failed to start server: %v", err)
131+
}
132+
}

sharding/config.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package sharding
2+
3+
import (
4+
"math/big"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
)
8+
9+
var (
10+
// Number of network shards
11+
shardCount = 100
12+
// Address of the validator management contract
13+
validatorManagerAddress = common.HexToAddress("0x0") // TODO
14+
// Gas limit for verifying signatures
15+
sigGasLimit = 40000
16+
// Number of blocks in a period
17+
periodLength = 5
18+
// Number of periods ahead of current period which the contract is able to return the collator of that period.
19+
lookaheadPeriods = 4
20+
// Required deposit size in wei
21+
depositSize = new(big.Int).Exp(big.NewInt(10), big.NewInt(20), nil) // 100 ETH
22+
// Gas limit to create contract
23+
contractGasLimit = uint64(4700000) // Max is 4712388
24+
)

sharding/config_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package sharding
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
)
7+
8+
func TestDepositSize(t *testing.T) {
9+
want, err := new(big.Int).SetString("100000000000000000000", 10) // 100 ETH
10+
if !err {
11+
t.Fatalf("Failed to setup test")
12+
}
13+
if depositSize.Cmp(want) != 0 {
14+
t.Errorf("depositSize incorrect. Wanted %d, got %d", want, depositSize)
15+
}
16+
}

0 commit comments

Comments
 (0)