diff --git a/beacon-chain/BUILD.bazel b/beacon-chain/BUILD.bazel index d977faeaa8..661b55309c 100644 --- a/beacon-chain/BUILD.bazel +++ b/beacon-chain/BUILD.bazel @@ -7,7 +7,8 @@ go_library( visibility = ["//beacon-chain:__subpackages__"], deps = [ "//beacon-chain/node:go_default_library", - "//beacon-chain/types:go_default_library", + "//beacon-chain/utils:go_default_library", + "//shared/cmd:go_default_library", "//shared/debug:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_urfave_cli//:go_default_library", diff --git a/beacon-chain/VALIDATOR_REGISTER.md b/beacon-chain/VALIDATOR_REGISTER.md new file mode 100644 index 0000000000..988a52e8ce --- /dev/null +++ b/beacon-chain/VALIDATOR_REGISTER.md @@ -0,0 +1,34 @@ +# Validator Registration Workflow + +This doc summarizes the work flow of registering to become a validator in the beacon chain. The scope is within Ruby Release. + +### Step 1: Deploy validator registration contract if it hasn't been done +To deploy VRC, we can use [deployVRC](https://github.com/terenc3t/geth-sharding/tree/contract-util/contracts/deployVRC) utility. +Once we get the VRC contract address, we can launch our beacon chain node +``` +# Deploy VRC with keystore UTCJSON and password +go run deployVRC.go --UTCPath /path/to/your/keystore/UTCJSON --passwordFile /path/to/your/password.txt +# Deploy VRC with private key +go run deployVRC.go --privKey 8a6db3b30934439c9f71f1fa777019810fd538c9c1e396809bcf9fd5535e20ca + +INFO[0039] New contract deployed at 0x559eDab2b5896C2Bc37951325666ed08CD41099d +``` + +### Step 2: Launch beacon chain node +Launch beacon chain node with account holder's public key and the VRC address we just deployed +``` +./bazel-bin/path/to/your/beacon-chain/binary --vrcaddr 0x527580dd995c0ab81d01f9993eb39166796877a1 --pubkey aaace816cdab194b4bc6c0de3575ccf917a9b9ecfead263720968e0e1b45739c + +``` + +### Step 3: Send a transaction to the deposit function in VRC with 32 ETH and beacon chain node account holder's public key as argument + + +### Step 4: Wait for deposit transaction to mine. +After the deposit transaction gets mined, beacon chain node will report account holder has been registered. Congrats! Now, you are contributing to the security of Ethereum 2.0 : ) +``` +INFO[0000] Starting beacon node +INFO[0000] Starting web3 PoW chain service at ws://127.0.0.1:8546 +INFO[0152] Validator registered in VRC with public key: aaace816cdab194b4bc6c0de3575ccf917a9b9ecfead263720968e0e1b45739c +``` + diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel new file mode 100644 index 0000000000..ce5d0869ee --- /dev/null +++ b/beacon-chain/blockchain/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "core.go", + "service.go", + ], + importpath = "github.com/prysmaticlabs/geth-sharding/beacon-chain/blockchain", + visibility = ["//beacon-chain:__subpackages__"], + deps = [ + "//beacon-chain/database:go_default_library", + "//beacon-chain/types:go_default_library", + "@com_github_ethereum_go_ethereum//ethdb:go_default_library", + "@com_github_ethereum_go_ethereum//rlp:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", + "@com_github_syndtr_goleveldb//leveldb/errors:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "core_test.go", + "service_test.go", + ], + embed = [":go_default_library"], + deps = [ + "//beacon-chain/database:go_default_library", + "//beacon-chain/types:go_default_library", + "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_sirupsen_logrus//hooks/test:go_default_library", + ], +) diff --git a/beacon-chain/blockchain/core.go b/beacon-chain/blockchain/core.go new file mode 100644 index 0000000000..6c5b52ebaa --- /dev/null +++ b/beacon-chain/blockchain/core.go @@ -0,0 +1,87 @@ +package blockchain + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/rlp" + "github.com/prysmaticlabs/geth-sharding/beacon-chain/types" + log "github.com/sirupsen/logrus" + leveldberrors "github.com/syndtr/goleveldb/leveldb/errors" +) + +var stateLookupKey = "beaconchainstate" + +// BeaconChain represents the core PoS blockchain object containing +// both a crystallized and active state. +type BeaconChain struct { + state *beaconState + lock sync.Mutex + db ethdb.Database +} + +type beaconState struct { + ActiveState *types.ActiveState + CrystallizedState *types.CrystallizedState +} + +// NewBeaconChain initializes an instance using genesis state parameters if +// none provided. +func NewBeaconChain(db ethdb.Database) (*BeaconChain, error) { + beaconChain := &BeaconChain{ + db: db, + state: &beaconState{}, + } + enc, err := db.Get([]byte(stateLookupKey)) + if err != nil && err.Error() == leveldberrors.ErrNotFound.Error() { + log.Info("No chainstate found on disk, initializing beacon from genesis") + active, crystallized := types.NewGenesisStates() + beaconChain.state.ActiveState = active + beaconChain.state.CrystallizedState = crystallized + return beaconChain, nil + } + if err != nil { + return nil, err + } + // Deserializes the encoded object into a beacon chain. + if err := rlp.DecodeBytes(enc, &beaconChain.state); err != nil { + return nil, fmt.Errorf("could not deserialize chainstate from disk: %v", err) + } + return beaconChain, nil +} + +// ActiveState exposes a getter to external services. +func (b *BeaconChain) ActiveState() *types.ActiveState { + return b.state.ActiveState +} + +// CrystallizedState exposes a getter to external services. +func (b *BeaconChain) CrystallizedState() *types.CrystallizedState { + return b.state.CrystallizedState +} + +// MutateActiveState allows external services to modify the active state. +func (b *BeaconChain) MutateActiveState(activeState *types.ActiveState) error { + defer b.lock.Unlock() + b.lock.Lock() + b.state.ActiveState = activeState + return b.persist() +} + +// MutateCrystallizedState allows external services to modify the crystallized state. +func (b *BeaconChain) MutateCrystallizedState(crystallizedState *types.CrystallizedState) error { + defer b.lock.Unlock() + b.lock.Lock() + b.state.CrystallizedState = crystallizedState + return b.persist() +} + +// persist stores the RLP encoding of the latest beacon chain state into the db. +func (b *BeaconChain) persist() error { + encodedState, err := rlp.EncodeToBytes(b.state) + if err != nil { + return err + } + return b.db.Put([]byte(stateLookupKey), encodedState) +} diff --git a/beacon-chain/blockchain/core_test.go b/beacon-chain/blockchain/core_test.go new file mode 100644 index 0000000000..ee665ed7d6 --- /dev/null +++ b/beacon-chain/blockchain/core_test.go @@ -0,0 +1,121 @@ +package blockchain + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/prysmaticlabs/geth-sharding/beacon-chain/database" + "github.com/prysmaticlabs/geth-sharding/beacon-chain/types" + logTest "github.com/sirupsen/logrus/hooks/test" +) + +func TestNewBeaconChain(t *testing.T) { + hook := logTest.NewGlobal() + tmp := fmt.Sprintf("%s/beacontest", os.TempDir()) + config := &database.BeaconDBConfig{DataDir: tmp, Name: "beacontest", InMemory: false} + db, err := database.NewBeaconDB(config) + if err != nil { + t.Fatalf("unable to setup db: %v", err) + } + db.Start() + beaconChain, err := NewBeaconChain(db.DB()) + if err != nil { + t.Fatalf("unable to setup beacon chain: %v", err) + } + + msg := hook.LastEntry().Message + want := "No chainstate found on disk, initializing beacon from genesis" + if msg != want { + t.Errorf("incorrect log, expected %s, got %s", want, msg) + } + + hook.Reset() + active, crystallized := types.NewGenesisStates() + if !reflect.DeepEqual(beaconChain.ActiveState(), active) { + t.Errorf("active states not equal. received: %v, wanted: %v", beaconChain.ActiveState(), active) + } + if !reflect.DeepEqual(beaconChain.CrystallizedState(), crystallized) { + t.Errorf("crystallized states not equal. received: %v, wanted: %v", beaconChain.CrystallizedState(), crystallized) + } +} + +func TestMutateActiveState(t *testing.T) { + tmp := fmt.Sprintf("%s/beacontest", os.TempDir()) + config := &database.BeaconDBConfig{DataDir: tmp, Name: "beacontest2", InMemory: false} + db, err := database.NewBeaconDB(config) + if err != nil { + t.Fatalf("unable to setup db: %v", err) + } + db.Start() + beaconChain, err := NewBeaconChain(db.DB()) + if err != nil { + t.Fatalf("unable to setup beacon chain: %v", err) + } + + randao := common.BytesToHash([]byte("hello")) + active := &types.ActiveState{ + Height: 100, + Randao: randao, + } + if err := beaconChain.MutateActiveState(active); err != nil { + t.Fatalf("unable to mutate active state: %v", err) + } + if !reflect.DeepEqual(beaconChain.state.ActiveState, active) { + t.Errorf("active state was not updated. wanted %v, got %v", active, beaconChain.state.ActiveState) + } + + // Initializing a new beacon chain should deserialize persisted state from disk. + newBeaconChain, err := NewBeaconChain(db.DB()) + if err != nil { + t.Fatalf("unable to setup beacon chain: %v", err) + } + // The active state should still be the one we mutated and persited earlier. + if active.Height != newBeaconChain.state.ActiveState.Height { + t.Errorf("active state height incorrect. wanted %v, got %v", active.Height, newBeaconChain.state.ActiveState.Height) + } + if active.Randao.Hex() != newBeaconChain.state.ActiveState.Randao.Hex() { + t.Errorf("active state randao incorrect. wanted %v, got %v", active.Randao.Hex(), newBeaconChain.state.ActiveState.Randao.Hex()) + } +} + +func TestMutateCrystallizedState(t *testing.T) { + tmp := fmt.Sprintf("%s/beacontest", os.TempDir()) + config := &database.BeaconDBConfig{DataDir: tmp, Name: "beacontest3", InMemory: false} + db, err := database.NewBeaconDB(config) + if err != nil { + t.Fatalf("unable to setup db: %v", err) + } + db.Start() + beaconChain, err := NewBeaconChain(db.DB()) + if err != nil { + t.Fatalf("unable to setup beacon chain: %v", err) + } + + currentCheckpoint := common.BytesToHash([]byte("checkpoint")) + crystallized := &types.CrystallizedState{ + Dynasty: 3, + CurrentCheckpoint: currentCheckpoint, + } + if err := beaconChain.MutateCrystallizedState(crystallized); err != nil { + t.Fatalf("unable to mutate crystallized state: %v", err) + } + if !reflect.DeepEqual(beaconChain.state.CrystallizedState, crystallized) { + t.Errorf("crystallized state was not updated. wanted %v, got %v", crystallized, beaconChain.state.CrystallizedState) + } + + // Initializing a new beacon chain should deserialize persisted state from disk. + newBeaconChain, err := NewBeaconChain(db.DB()) + if err != nil { + t.Fatalf("unable to setup beacon chain: %v", err) + } + // The crystallized state should still be the one we mutated and persited earlier. + if crystallized.Dynasty != newBeaconChain.state.CrystallizedState.Dynasty { + t.Errorf("crystallized state dynasty incorrect. wanted %v, got %v", crystallized.Dynasty, newBeaconChain.state.CrystallizedState.Dynasty) + } + if crystallized.CurrentCheckpoint.Hex() != newBeaconChain.state.CrystallizedState.CurrentCheckpoint.Hex() { + t.Errorf("crystallized state current checkpoint incorrect. wanted %v, got %v", crystallized.CurrentCheckpoint.Hex(), newBeaconChain.state.CrystallizedState.CurrentCheckpoint.Hex()) + } +} diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go new file mode 100644 index 0000000000..72ee1ce1d2 --- /dev/null +++ b/beacon-chain/blockchain/service.go @@ -0,0 +1,39 @@ +package blockchain + +import ( + "context" + + "github.com/prysmaticlabs/geth-sharding/beacon-chain/database" + log "github.com/sirupsen/logrus" +) + +// ChainService represents a service that handles the internal +// logic of managing the full PoS beacon chain. +type ChainService struct { + ctx context.Context + cancel context.CancelFunc + beaconDB *database.BeaconDB + chain *BeaconChain +} + +// NewChainService instantiates a new service instance that will +// be registered into a running beacon node. +func NewChainService(ctx context.Context, beaconDB *database.BeaconDB) (*ChainService, error) { + ctx, cancel := context.WithCancel(ctx) + return &ChainService{ctx, cancel, beaconDB, nil}, nil +} + +// Start a blockchain service's main event loop. +func (c *ChainService) Start() { + log.Infof("Starting blockchain service") + if _, err := NewBeaconChain(c.beaconDB.DB()); err != nil { + log.Errorf("Unable to setup blockchain: %v", err) + } +} + +// Stop the blockchain service's main event loop and associated goroutines. +func (c *ChainService) Stop() error { + defer c.cancel() + log.Info("Stopping blockchain service") + return nil +} diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go new file mode 100644 index 0000000000..2862babcae --- /dev/null +++ b/beacon-chain/blockchain/service_test.go @@ -0,0 +1,63 @@ +package blockchain + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/prysmaticlabs/geth-sharding/beacon-chain/database" + logTest "github.com/sirupsen/logrus/hooks/test" +) + +func TestStartStop(t *testing.T) { + hook := logTest.NewGlobal() + ctx := context.Background() + tmp := fmt.Sprintf("%s/beacontest", os.TempDir()) + config := &database.BeaconDBConfig{DataDir: tmp, Name: "beacontestdata", InMemory: false} + db, err := database.NewBeaconDB(config) + if err != nil { + t.Fatalf("could not setup beaconDB: %v", err) + } + db.Start() + chainService, err := NewChainService(ctx, db) + if err != nil { + t.Fatalf("unable to setup chain service: %v", err) + } + + chainService.Start() + + if err := chainService.Stop(); err != nil { + t.Fatalf("unable to stop chain service: %v", err) + } + + msg := hook.AllEntries()[0].Message + want := "Starting beaconDB service" + if msg != want { + t.Errorf("incorrect log, expected %s, got %s", want, msg) + } + + msg = hook.AllEntries()[1].Message + want = "Starting blockchain service" + if msg != want { + t.Errorf("incorrect log, expected %s, got %s", want, msg) + } + + msg = hook.AllEntries()[2].Message + want = "No chainstate found on disk, initializing beacon from genesis" + if msg != want { + t.Errorf("incorrect log, expected %s, got %s", want, msg) + } + + msg = hook.AllEntries()[3].Message + want = "Stopping blockchain service" + if msg != want { + t.Errorf("incorrect log, expected %s, got %s", want, msg) + } + + // The context should have been canceled. + if chainService.ctx.Err() == nil { + t.Error("context was not canceled") + } + hook.Reset() +} diff --git a/beacon-chain/database/BUILD.bazel b/beacon-chain/database/BUILD.bazel new file mode 100644 index 0000000000..acfbfb588d --- /dev/null +++ b/beacon-chain/database/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["database.go"], + importpath = "github.com/prysmaticlabs/geth-sharding/beacon-chain/database", + visibility = ["//beacon-chain:__subpackages__"], + deps = [ + "//shared/database:go_default_library", + "@com_github_ethereum_go_ethereum//ethdb:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["database_test.go"], + embed = [":go_default_library"], + deps = [ + "//shared:go_default_library", + "@com_github_sirupsen_logrus//hooks/test:go_default_library", + "@com_github_syndtr_goleveldb//leveldb/errors:go_default_library", + ], +) diff --git a/beacon-chain/database/database.go b/beacon-chain/database/database.go new file mode 100644 index 0000000000..e46706a913 --- /dev/null +++ b/beacon-chain/database/database.go @@ -0,0 +1,73 @@ +// Package database defines a beacon chain DB service that can be +// initialized with either a persistent db, or an in-memory kv-store. +package database + +import ( + "fmt" + "path/filepath" + + "github.com/ethereum/go-ethereum/ethdb" + sharedDB "github.com/prysmaticlabs/geth-sharding/shared/database" + log "github.com/sirupsen/logrus" +) + +// BeaconDB defines a service for the beacon chain system's persistent storage. +type BeaconDB struct { + inmemory bool + dataDir string + name string + cache int + handles int + db ethdb.Database +} + +// BeaconDBConfig specifies configuration options for the db service. +type BeaconDBConfig struct { + DataDir string + Name string + InMemory bool +} + +// NewBeaconDB initializes a beaconDB instance. +func NewBeaconDB(config *BeaconDBConfig) (*BeaconDB, error) { + // Uses default cache and handles values. + // TODO: allow these arguments to be set based on cli context. + beaconDB := &BeaconDB{ + name: config.Name, + dataDir: config.DataDir, + } + if config.InMemory { + beaconDB.inmemory = true + beaconDB.db = sharedDB.NewKVStore() + } else { + beaconDB.inmemory = false + beaconDB.cache = 16 + beaconDB.handles = 16 + } + return beaconDB, nil +} + +// Start the beacon DB service. +func (b *BeaconDB) Start() { + log.Info("Starting beaconDB service") + if !b.inmemory { + db, err := ethdb.NewLDBDatabase(filepath.Join(b.dataDir, b.name), b.cache, b.handles) + if err != nil { + log.Error(fmt.Sprintf("Could not start beaconDB: %v", err)) + return + } + b.db = db + } +} + +// Stop the beaconDB service gracefully. +func (b *BeaconDB) Stop() error { + log.Info("Stopping beaconDB service") + b.db.Close() + return nil +} + +// DB returns the attached ethdb instance. +func (b *BeaconDB) DB() ethdb.Database { + return b.db +} diff --git a/beacon-chain/database/database_test.go b/beacon-chain/database/database_test.go new file mode 100644 index 0000000000..051b0bb582 --- /dev/null +++ b/beacon-chain/database/database_test.go @@ -0,0 +1,139 @@ +package database + +import ( + "fmt" + "os" + "strconv" + "sync" + "testing" + + "github.com/prysmaticlabs/geth-sharding/shared" + logTest "github.com/sirupsen/logrus/hooks/test" + leveldberrors "github.com/syndtr/goleveldb/leveldb/errors" +) + +// Verifies that BeaconDB implements the sharding Service inteface. +var _ = shared.Service(&BeaconDB{}) + +var testDB *BeaconDB + +func init() { + tmp := fmt.Sprintf("%s/datadir", os.TempDir()) + config := &BeaconDBConfig{DataDir: tmp, Name: "beaconchaindata", InMemory: false} + beaconDB, _ := NewBeaconDB(config) + testDB = beaconDB + testDB.Start() +} + +func TestLifecycle(t *testing.T) { + hook := logTest.NewGlobal() + + tmp := fmt.Sprintf("%s/lifecycledir", os.TempDir()) + config := &BeaconDBConfig{DataDir: tmp, Name: "beaconchaindata", InMemory: false} + b, err := NewBeaconDB(config) + if err != nil { + t.Fatalf("could not initialize a new DB: %v", err) + } + + b.Start() + msg := hook.LastEntry().Message + if msg != "Starting beaconDB service" { + t.Errorf("incorrect log, expected %s, got %s", "Starting beaconDB service", msg) + } + + b.Stop() + msg = hook.LastEntry().Message + if msg != "Stopping beaconDB service" { + t.Errorf("incorrect log, expected %s, got %s", "Stopping beaconDB service", msg) + } + + // Access DB after it's stopped, this should fail. + _, err = b.db.Get([]byte("ralph merkle")) + + if err.Error() != "leveldb: closed" { + t.Fatalf("beaconDB close function did not work") + } +} + +// Testing the concurrency with multiple processes attempting to write. +func Test_DBConcurrent(t *testing.T) { + var wg sync.WaitGroup + wg.Add(100) + for i := 0; i < 100; i++ { + go func(val string) { + defer wg.Done() + if err := testDB.db.Put([]byte("ralph merkle"), []byte(val)); err != nil { + t.Errorf("could not save value in db: %v", err) + } + }(strconv.Itoa(i)) + } + wg.Wait() +} + +func Test_DBPut(t *testing.T) { + if err := testDB.db.Put([]byte("ralph merkle"), []byte{1, 2, 3}); err != nil { + t.Errorf("could not save value in db: %v", err) + } +} + +func Test_DBHas(t *testing.T) { + key := []byte("ralph merkle") + + if err := testDB.db.Put(key, []byte{1, 2, 3}); err != nil { + t.Fatalf("could not save value in db: %v", err) + } + + has, err := testDB.db.Has(key) + if err != nil { + t.Errorf("could not check if db has key: %v", err) + } + if !has { + t.Errorf("db should have key: %v", key) + } + + key2 := []byte{} + has2, err := testDB.db.Has(key2) + if err != nil { + t.Errorf("could not check if db has key: %v", err) + } + if has2 { + t.Errorf("db should not have non-existent key: %v", key2) + } +} + +func Test_DBGet(t *testing.T) { + key := []byte("ralph merkle") + + if err := testDB.db.Put(key, []byte{1, 2, 3}); err != nil { + t.Fatalf("could not save value in db: %v", err) + } + + val, err := testDB.db.Get(key) + if err != nil { + t.Errorf("get failed: %v", err) + } + if len(val) == 0 { + t.Errorf("no value stored for key") + } + + key2 := []byte{} + val2, err := testDB.db.Get(key2) + if err == nil || err.Error() != leveldberrors.ErrNotFound.Error() { + t.Errorf("Expected error %v but got %v", leveldberrors.ErrNotFound, err) + } + if len(val2) != 0 { + t.Errorf("non-existent key should not have a value. key=%v, value=%v", key2, val2) + } +} + +func Test_DBDelete(t *testing.T) { + key := []byte("ralph merkle") + + if err := testDB.db.Put(key, []byte{1, 2, 3}); err != nil { + t.Fatalf("could not save value in db: %v", err) + } + + if err := testDB.db.Delete(key); err != nil { + t.Errorf("could not delete key: %v", key) + } +} diff --git a/beacon-chain/main.go b/beacon-chain/main.go index 446ff738ac..537a8d805a 100644 --- a/beacon-chain/main.go +++ b/beacon-chain/main.go @@ -5,7 +5,8 @@ import ( "runtime" "github.com/prysmaticlabs/geth-sharding/beacon-chain/node" - "github.com/prysmaticlabs/geth-sharding/beacon-chain/types" + "github.com/prysmaticlabs/geth-sharding/beacon-chain/utils" + "github.com/prysmaticlabs/geth-sharding/shared/cmd" "github.com/prysmaticlabs/geth-sharding/shared/debug" log "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -44,7 +45,7 @@ VERSION: app.Usage = "this is a beacon chain implementation for Ethereum 2.0" app.Action = startNode - app.Flags = []cli.Flag{types.Web3ProviderFlag, debug.PProfFlag, debug.PProfAddrFlag, debug.PProfPortFlag, debug.MemProfileRateFlag, debug.CPUProfileFlag, debug.TraceFlag} + app.Flags = []cli.Flag{cmd.DataDirFlag, utils.VrcContractFlag, utils.PubKeyFlag, utils.Web3ProviderFlag, debug.PProfFlag, debug.PProfAddrFlag, debug.PProfPortFlag, debug.MemProfileRateFlag, debug.CPUProfileFlag, debug.TraceFlag} app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/beacon-chain/node/BUILD.bazel b/beacon-chain/node/BUILD.bazel index fedad9d6db..3e9a46201e 100644 --- a/beacon-chain/node/BUILD.bazel +++ b/beacon-chain/node/BUILD.bazel @@ -6,10 +6,14 @@ go_library( importpath = "github.com/prysmaticlabs/geth-sharding/beacon-chain/node", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//beacon-chain/blockchain:go_default_library", + "//beacon-chain/database:go_default_library", "//beacon-chain/powchain:go_default_library", - "//beacon-chain/types:go_default_library", + "//beacon-chain/utils:go_default_library", "//shared:go_default_library", + "//shared/cmd:go_default_library", "//shared/debug:go_default_library", + "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_urfave_cli//:go_default_library", ], diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index 16e757d0f2..8ed4fb6b8d 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -8,14 +8,20 @@ import ( "sync" "syscall" + "github.com/ethereum/go-ethereum/common" + "github.com/prysmaticlabs/geth-sharding/beacon-chain/blockchain" + "github.com/prysmaticlabs/geth-sharding/beacon-chain/database" "github.com/prysmaticlabs/geth-sharding/beacon-chain/powchain" - "github.com/prysmaticlabs/geth-sharding/beacon-chain/types" + "github.com/prysmaticlabs/geth-sharding/beacon-chain/utils" "github.com/prysmaticlabs/geth-sharding/shared" + "github.com/prysmaticlabs/geth-sharding/shared/cmd" "github.com/prysmaticlabs/geth-sharding/shared/debug" log "github.com/sirupsen/logrus" "github.com/urfave/cli" ) +var beaconChainDBName = "beaconchaindata" + // BeaconNode defines a struct that handles the services running a random beacon chain // full PoS node. It handles the lifecycle of the entire system and registers // services to a service registry. @@ -30,13 +36,23 @@ type BeaconNode struct { // every required service to the node. func New(ctx *cli.Context) (*BeaconNode, error) { registry := shared.NewServiceRegistry() + beacon := &BeaconNode{ ctx: ctx, services: registry, stop: make(chan struct{}), } - if err := beacon.registerWeb3Service(); err != nil { + path := ctx.GlobalString(cmd.DataDirFlag.Name) + if err := beacon.registerBeaconDB(path); err != nil { + return nil, err + } + + if err := beacon.registerBlockchainService(); err != nil { + return nil, err + } + + if err := beacon.registerPOWChainService(); err != nil { return nil, err } @@ -85,11 +101,36 @@ func (b *BeaconNode) Close() { close(b.stop) } -func (b *BeaconNode) registerWeb3Service() error { - endpoint := b.ctx.GlobalString(types.Web3ProviderFlag.Name) - web3Service, err := powchain.NewWeb3Service(context.TODO(), endpoint) +func (b *BeaconNode) registerBeaconDB(path string) error { + config := &database.BeaconDBConfig{DataDir: path, Name: beaconChainDBName, InMemory: false} + beaconDB, err := database.NewBeaconDB(config) if err != nil { - return fmt.Errorf("could not register web3Service: %v", err) + return fmt.Errorf("could not register beaconDB service: %v", err) + } + return b.services.RegisterService(beaconDB) +} + +func (b *BeaconNode) registerBlockchainService() error { + var beaconDB *database.BeaconDB + if err := b.services.FetchService(&beaconDB); err != nil { + return err + } + + blockchainService, err := blockchain.NewChainService(context.TODO(), beaconDB) + if err != nil { + return fmt.Errorf("could not register blockchain service: %v", err) + } + return b.services.RegisterService(blockchainService) +} + +func (b *BeaconNode) registerPOWChainService() error { + web3Service, err := powchain.NewWeb3Service(context.TODO(), &powchain.Web3ServiceConfig{ + Endpoint: b.ctx.GlobalString(utils.Web3ProviderFlag.Name), + Pubkey: b.ctx.GlobalString(utils.PubKeyFlag.Name), + VrcAddr: common.HexToAddress(b.ctx.GlobalString(utils.VrcContractFlag.Name)), + }) + if err != nil { + return fmt.Errorf("could not register proof-of-work chain web3Service: %v", err) } return b.services.RegisterService(web3Service) } diff --git a/beacon-chain/powchain/BUILD.bazel b/beacon-chain/powchain/BUILD.bazel index 0ae4f8329d..a91dd37a93 100644 --- a/beacon-chain/powchain/BUILD.bazel +++ b/beacon-chain/powchain/BUILD.bazel @@ -21,6 +21,7 @@ go_test( embed = [":go_default_library"], deps = [ "@com_github_ethereum_go_ethereum//:go_default_library", + "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_ethereum_go_ethereum//core/types:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", ], diff --git a/beacon-chain/powchain/service.go b/beacon-chain/powchain/service.go index 156e867133..80fc6182e8 100644 --- a/beacon-chain/powchain/service.go +++ b/beacon-chain/powchain/service.go @@ -6,7 +6,7 @@ import ( "math/big" "strings" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" @@ -19,6 +19,11 @@ type Reader interface { SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error) } +// Logger subscribe filtered log on the PoW chain +type Logger interface { + SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) +} + // Web3Service fetches important information about the canonical // Ethereum PoW chain via a web3 endpoint using an ethclient. The Random // Beacon Chain requires synchronization with the PoW chain's current @@ -26,34 +31,49 @@ type Reader interface { // Validator Registration Contract on the PoW chain to kick off the beacon // chain's validator registration process. type Web3Service struct { - ctx context.Context - cancel context.CancelFunc - headerChan chan *gethTypes.Header - endpoint string - blockNumber *big.Int // the latest PoW chain blocknumber. - blockHash common.Hash // the latest PoW chain blockhash. + ctx context.Context + cancel context.CancelFunc + headerChan chan *gethTypes.Header + logChan chan gethTypes.Log + pubKey string + endpoint string + validatorRegistered bool + vrcAddress common.Address + blockNumber *big.Int // the latest PoW chain blocknumber. + blockHash common.Hash // the latest PoW chain blockhash. +} + +// Web3ServiceConfig defines a config struct for web3 service to use through its life cycle. +type Web3ServiceConfig struct { + Endpoint string + Pubkey string + VrcAddr common.Address } // NewWeb3Service sets up a new instance with an ethclient when // given a web3 endpoint as a string. -func NewWeb3Service(ctx context.Context, endpoint string) (*Web3Service, error) { - if !strings.HasPrefix(endpoint, "ws") && !strings.HasPrefix(endpoint, "ipc") { - return nil, fmt.Errorf("web3service requires either an IPC or WebSocket endpoint, provided %s", endpoint) +func NewWeb3Service(ctx context.Context, config *Web3ServiceConfig) (*Web3Service, error) { + if !strings.HasPrefix(config.Endpoint, "ws") && !strings.HasPrefix(config.Endpoint, "ipc") { + return nil, fmt.Errorf("web3service requires either an IPC or WebSocket endpoint, provided %s", config.Endpoint) } web3ctx, cancel := context.WithCancel(ctx) return &Web3Service{ - ctx: web3ctx, - cancel: cancel, - headerChan: make(chan *gethTypes.Header), - endpoint: endpoint, - blockNumber: nil, - blockHash: common.BytesToHash([]byte{}), + ctx: web3ctx, + cancel: cancel, + headerChan: make(chan *gethTypes.Header), + logChan: make(chan gethTypes.Log), + pubKey: config.Pubkey, + endpoint: config.Endpoint, + validatorRegistered: false, + blockNumber: nil, + blockHash: common.BytesToHash([]byte{}), + vrcAddress: config.VrcAddr, }, nil } // Start a web3 service's main event loop. func (w *Web3Service) Start() { - log.Infof("Starting web3 PoW chain service at %s", w.endpoint) + log.Infof("Starting web3 proof-of-work chain service at %s", w.endpoint) rpcClient, err := rpc.Dial(w.endpoint) if err != nil { log.Errorf("Cannot connect to PoW chain RPC client: %v", err) @@ -61,13 +81,14 @@ func (w *Web3Service) Start() { } client := ethclient.NewClient(rpcClient) go w.latestPOWChainInfo(client, w.ctx.Done()) + go w.queryValidatorStatus(client, w.ctx.Done()) } // Stop the web3 service's main event loop and associated goroutines. func (w *Web3Service) Stop() error { defer w.cancel() defer close(w.headerChan) - log.Info("Stopping web3 PoW chain service") + log.Info("Stopping web3 proof-of-work chain service") return nil } @@ -89,6 +110,33 @@ func (w *Web3Service) latestPOWChainInfo(reader Reader, done <-chan struct{}) { } } +func (w *Web3Service) queryValidatorStatus(logger Logger, done <-chan struct{}) { + query := ethereum.FilterQuery{ + Addresses: []common.Address{ + w.vrcAddress, + }, + } + _, err := logger.SubscribeFilterLogs(context.Background(), query, w.logChan) + if err != nil { + log.Errorf("Unable to query logs from VRC: %v", err) + return + } + for { + select { + case <-done: + return + case VRClog := <-w.logChan: + // public key is the second topic from validatorRegistered log and strip off 0x + pubKeyLog := VRClog.Topics[1].Hex()[2:] + if pubKeyLog == w.pubKey { + log.Infof("Validator registered in VRC with public key: %v", pubKeyLog) + w.validatorRegistered = true + return + } + } + } +} + // LatestBlockNumber is a getter for blockNumber to make it read-only. func (w *Web3Service) LatestBlockNumber() *big.Int { return w.blockNumber @@ -98,3 +146,8 @@ func (w *Web3Service) LatestBlockNumber() *big.Int { func (w *Web3Service) LatestBlockHash() common.Hash { return w.blockHash } + +// ValidatorRegistered is a getter for validatorRegistered to make it read-only. +func (w *Web3Service) ValidatorRegistered() bool { + return w.validatorRegistered +} diff --git a/beacon-chain/powchain/service_test.go b/beacon-chain/powchain/service_test.go index 17f3555d26..96f7a4c94b 100644 --- a/beacon-chain/powchain/service_test.go +++ b/beacon-chain/powchain/service_test.go @@ -7,7 +7,8 @@ import ( "strings" "testing" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" logTest "github.com/sirupsen/logrus/hooks/test" ) @@ -24,22 +25,34 @@ func (g *goodReader) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes. return nil, nil } +type badLogger struct{} + +func (b *badLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) { + return nil, errors.New("subscription has failed") +} + +type goodLogger struct{} + +func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) { + return nil, nil +} + func TestNewWeb3Service(t *testing.T) { endpoint := "http://127.0.0.1" ctx := context.Background() - if _, err := NewWeb3Service(ctx, endpoint); err == nil { + if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err == nil { t.Errorf("passing in an HTTP endpoint should throw an error, received nil") } endpoint = "ftp://127.0.0.1" - if _, err := NewWeb3Service(ctx, endpoint); err == nil { + if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err == nil { t.Errorf("passing in a non-ws, wss, or ipc endpoint should throw an error, received nil") } endpoint = "ws://127.0.0.1" - if _, err := NewWeb3Service(ctx, endpoint); err != nil { + if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err != nil { t.Errorf("passing in as ws endpoint should not throw error, received %v", err) } endpoint = "ipc://geth.ipc" - if _, err := NewWeb3Service(ctx, endpoint); err != nil { + if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err != nil { t.Errorf("passing in an ipc endpoint should not throw error, received %v", err) } } @@ -48,7 +61,7 @@ func TestStart(t *testing.T) { hook := logTest.NewGlobal() endpoint := "ws://127.0.0.1" - web3Service, err := NewWeb3Service(context.Background(), endpoint) + web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}) if err != nil { t.Fatalf("unable to setup web3 PoW chain service: %v", err) } @@ -67,7 +80,7 @@ func TestStop(t *testing.T) { hook := logTest.NewGlobal() endpoint := "ws://127.0.0.1" - web3Service, err := NewWeb3Service(context.Background(), endpoint) + web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}) if err != nil { t.Fatalf("unable to setup web3 PoW chain service: %v", err) } @@ -77,7 +90,7 @@ func TestStop(t *testing.T) { } msg := hook.LastEntry().Message - want := "Stopping web3 PoW chain service" + want := "Stopping web3 proof-of-work chain service" if msg != want { t.Errorf("incorrect log, expected %s, got %s", want, msg) } @@ -92,7 +105,7 @@ func TestStop(t *testing.T) { func TestBadReader(t *testing.T) { hook := logTest.NewGlobal() endpoint := "ws://127.0.0.1" - web3Service, err := NewWeb3Service(context.Background(), endpoint) + web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}) if err != nil { t.Fatalf("unable to setup web3 PoW chain service: %v", err) } @@ -107,7 +120,7 @@ func TestBadReader(t *testing.T) { func TestLatestMainchainInfo(t *testing.T) { endpoint := "ws://127.0.0.1" - web3Service, err := NewWeb3Service(context.Background(), endpoint) + web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}) if err != nil { t.Fatalf("unable to setup web3 PoW chain service: %v", err) } @@ -134,3 +147,55 @@ func TestLatestMainchainInfo(t *testing.T) { t.Errorf("block hash not set, expected %v, got %v", header.Hash().Hex(), web3Service.blockHash.Hex()) } } + +func TestBadLogger(t *testing.T) { + hook := logTest.NewGlobal() + endpoint := "ws://127.0.0.1" + web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}) + if err != nil { + t.Fatalf("unable to setup web3 PoW chain service: %v", err) + } + web3Service.queryValidatorStatus(&badLogger{}, web3Service.ctx.Done()) + msg := hook.LastEntry().Message + want := "Unable to query logs from VRC: subscription has failed" + if msg != want { + t.Errorf("incorrect log, expected %s, got %s", want, msg) + } + hook.Reset() +} + +func TestGoodLogger(t *testing.T) { + hook := logTest.NewGlobal() + endpoint := "ws://127.0.0.1" + web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}) + if err != nil { + t.Fatalf("unable to setup web3 PoW chain service: %v", err) + } + + web3Service.pubKey = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + pubkey := common.HexToHash(web3Service.pubKey) + + doneChan := make(chan struct{}) + exitRoutine := make(chan bool) + + go func() { + web3Service.queryValidatorStatus(&goodLogger{}, doneChan) + <-exitRoutine + }() + + log := gethTypes.Log{Topics: []common.Hash{[32]byte{}, pubkey}} + web3Service.logChan <- log + exitRoutine <- true + + msg := hook.LastEntry().Message + want := "Validator registered in VRC with public key: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" + if msg != want { + t.Errorf("incorrect log, expected %s, got %s", want, msg) + } + + if !web3Service.validatorRegistered { + t.Error("validatorRegistered status expected true, got %v", web3Service.validatorRegistered) + } + + hook.Reset() +} diff --git a/beacon-chain/types/BUILD.bazel b/beacon-chain/types/BUILD.bazel index 74f0dc26ff..3c37f00066 100644 --- a/beacon-chain/types/BUILD.bazel +++ b/beacon-chain/types/BUILD.bazel @@ -4,13 +4,9 @@ go_library( name = "go_default_library", srcs = [ "block.go", - "flags.go", "state.go", ], importpath = "github.com/prysmaticlabs/geth-sharding/beacon-chain/types", visibility = ["//beacon-chain:__subpackages__"], - deps = [ - "@com_github_ethereum_go_ethereum//common:go_default_library", - "@com_github_urfave_cli//:go_default_library", - ], + deps = ["@com_github_ethereum_go_ethereum//common:go_default_library"], ) diff --git a/beacon-chain/types/flags.go b/beacon-chain/types/flags.go deleted file mode 100644 index 258e256f87..0000000000 --- a/beacon-chain/types/flags.go +++ /dev/null @@ -1,12 +0,0 @@ -package types - -import "github.com/urfave/cli" - -var ( - // Web3ProviderFlag defines a flag for a mainchain RPC endpoint. - Web3ProviderFlag = cli.StringFlag{ - Name: "web3provider", - Usage: "A mainchain web3 provider string endpoint. Can either be an IPC file string or a WebSocket endpoint. Uses WebSockets by default at ws://127.0.0.1:8546. Cannot be an HTTP endpoint.", - Value: "ws://127.0.0.1:8546", - } -) diff --git a/beacon-chain/types/state.go b/beacon-chain/types/state.go index 52e1b0bc85..9a981afd9e 100644 --- a/beacon-chain/types/state.go +++ b/beacon-chain/types/state.go @@ -60,3 +60,28 @@ type CrosslinkRecord struct { Epoch uint64 // Epoch records the epoch the crosslink was submitted in. Hash common.Hash // Hash is the block hash. } + +// NewGenesisStates initializes a beacon chain with starting parameters. +func NewGenesisStates() (*ActiveState, *CrystallizedState) { + active := &ActiveState{ + Height: 0, + Randao: common.BytesToHash([]byte{}), + FfgVoterBitmask: []byte{}, + BalanceDeltas: []uint{}, + PartialCrosslinks: []PartialCrosslinkRecord{}, + TotalSkipCount: 0, + } + crystallized := &CrystallizedState{ + ActiveValidators: []ValidatorRecord{}, + QueuedValidators: []ValidatorRecord{}, + ExitedValidators: []ValidatorRecord{}, + CurrentShuffling: []uint16{}, + CurrentEpoch: 0, + LastJustifiedEpoch: 0, + LastFinalizedEpoch: 0, + Dynasty: 0, + TotalDeposits: 0, + CrosslinkSeed: common.BytesToHash([]byte{}), + } + return active, crystallized +} diff --git a/beacon-chain/utils/BUILD.bazel b/beacon-chain/utils/BUILD.bazel new file mode 100644 index 0000000000..283eb827ad --- /dev/null +++ b/beacon-chain/utils/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["flags.go"], + importpath = "github.com/prysmaticlabs/geth-sharding/beacon-chain/utils", + visibility = ["//beacon-chain:__subpackages__"], + deps = ["@com_github_urfave_cli//:go_default_library"], +) diff --git a/beacon-chain/utils/flags.go b/beacon-chain/utils/flags.go new file mode 100644 index 0000000000..f90cdbf0fc --- /dev/null +++ b/beacon-chain/utils/flags.go @@ -0,0 +1,24 @@ +package utils + +import ( + "github.com/urfave/cli" +) + +var ( + // Web3ProviderFlag defines a flag for a mainchain RPC endpoint. + Web3ProviderFlag = cli.StringFlag{ + Name: "web3provider", + Usage: "A mainchain web3 provider string endpoint. Can either be an IPC file string or a WebSocket endpoint. Uses WebSockets by default at ws://127.0.0.1:8546. Cannot be an HTTP endpoint.", + Value: "ws://127.0.0.1:8546", + } + // VrcContractFlag defines a flag for VRC contract address. + VrcContractFlag = cli.StringFlag{ + Name: "vrcaddr", + Usage: "Validator registration contract address. Beacon chain node will listen logs coming from VRC to determine when validator is eligible to participate.", + } + // PubKeyFlag defines a flag for validator's public key on the mainchain + PubKeyFlag = cli.StringFlag{ + Name: "pubkey", + Usage: "Validator's public key. Beacon chain node will listen to VRC log to determine when registration has completed based on this public key address.", + } +) diff --git a/contracts/validator_registration.go b/contracts/validator_registration.go index 1a5b600f1f..10abc32fd9 100644 --- a/contracts/validator_registration.go +++ b/contracts/validator_registration.go @@ -16,10 +16,10 @@ import ( ) // ValidatorRegistrationABI is the input ABI used to generate the binding from. -const ValidatorRegistrationABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"usedPubkey\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"VALIDATOR_DEPOSIT\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_pubkey\",\"type\":\"bytes32\"},{\"name\":\"_withdrawalShardID\",\"type\":\"uint256\"},{\"name\":\"_withdrawalAddressbytes32\",\"type\":\"address\"},{\"name\":\"_randaoCommitment\",\"type\":\"bytes32\"}],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"pubKey\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"withdrawalShardID\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"withdrawalAddressbytes32\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"randaoCommitment\",\"type\":\"bytes32\"}],\"name\":\"ValidatorRegistered\",\"type\":\"event\"}]" +const ValidatorRegistrationABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"usedPubkey\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"VALIDATOR_DEPOSIT\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_pubkey\",\"type\":\"bytes32\"},{\"name\":\"_withdrawalShardID\",\"type\":\"uint256\"},{\"name\":\"_withdrawalAddressbytes32\",\"type\":\"address\"},{\"name\":\"_randaoCommitment\",\"type\":\"bytes32\"}],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"pubKey\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"withdrawalShardID\",\"type\":\"uint256\"},{\"indexed\":true,\"name\":\"withdrawalAddressbytes32\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"randaoCommitment\",\"type\":\"bytes32\"}],\"name\":\"ValidatorRegistered\",\"type\":\"event\"}]" // ValidatorRegistrationBin is the compiled bytecode used for deploying new contracts. -const ValidatorRegistrationBin = `0x608060405234801561001057600080fd5b506101d3806100206000396000f3006080604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166301110845811461005b578063441d92cc14610087578063881d2135146100ae575b600080fd5b34801561006757600080fd5b506100736004356100da565b604080519115158252519081900360200190f35b34801561009357600080fd5b5061009c6100ef565b60408051918252519081900360200190f35b6100d860043560243573ffffffffffffffffffffffffffffffffffffffff604435166064356100fc565b005b60006020819052908152604090205460ff1681565b6801bc16d674ec80000081565b346801bc16d674ec8000001461011157600080fd5b60008481526020819052604090205460ff161561012d57600080fd5b60008481526020818152604091829020805460ff19166001179055815186815290810185905273ffffffffffffffffffffffffffffffffffffffff8416818301526060810183905290517f7b0678aab009b61a805f5004869728b53a444f9a3e6bb9e22b8537c89af512749181900360800190a1505050505600a165627a7a7230582030b51cf5829c9fac611cd2060acc062996b9cc9cf3d57d32b2b54c509a32b85d0029` +const ValidatorRegistrationBin = `0x608060405234801561001057600080fd5b506101c7806100206000396000f3006080604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166301110845811461005b578063441d92cc14610087578063881d2135146100ae575b600080fd5b34801561006757600080fd5b506100736004356100da565b604080519115158252519081900360200190f35b34801561009357600080fd5b5061009c6100ef565b60408051918252519081900360200190f35b6100d860043560243573ffffffffffffffffffffffffffffffffffffffff604435166064356100fc565b005b60006020819052908152604090205460ff1681565b6801bc16d674ec80000081565b346801bc16d674ec8000001461011157600080fd5b60008481526020819052604090205460ff161561012d57600080fd5b60008481526020818152604091829020805460ff1916600117905581518581529151839273ffffffffffffffffffffffffffffffffffffffff86169288927f7b0678aab009b61a805f5004869728b53a444f9a3e6bb9e22b8537c89af512749281900390910190a4505050505600a165627a7a7230582010cee12b801046464a3d4ffaad0081c2c3cf734f886a556d1e3b4f3565b649380029` // DeployValidatorRegistration deploys a new Ethereum contract, binding an instance of ValidatorRegistration to it. func DeployValidatorRegistration(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ValidatorRegistration, error) { @@ -327,10 +327,24 @@ type ValidatorRegistrationValidatorRegistered struct { // FilterValidatorRegistered is a free log retrieval operation binding the contract event 0x7b0678aab009b61a805f5004869728b53a444f9a3e6bb9e22b8537c89af51274. // -// Solidity: event ValidatorRegistered(pubKey bytes32, withdrawalShardID uint256, withdrawalAddressbytes32 address, randaoCommitment bytes32) -func (_ValidatorRegistration *ValidatorRegistrationFilterer) FilterValidatorRegistered(opts *bind.FilterOpts) (*ValidatorRegistrationValidatorRegisteredIterator, error) { +// Solidity: event ValidatorRegistered(pubKey indexed bytes32, withdrawalShardID uint256, withdrawalAddressbytes32 indexed address, randaoCommitment indexed bytes32) +func (_ValidatorRegistration *ValidatorRegistrationFilterer) FilterValidatorRegistered(opts *bind.FilterOpts, pubKey [][32]byte, withdrawalAddressbytes32 []common.Address, randaoCommitment [][32]byte) (*ValidatorRegistrationValidatorRegisteredIterator, error) { - logs, sub, err := _ValidatorRegistration.contract.FilterLogs(opts, "ValidatorRegistered") + var pubKeyRule []interface{} + for _, pubKeyItem := range pubKey { + pubKeyRule = append(pubKeyRule, pubKeyItem) + } + + var withdrawalAddressbytes32Rule []interface{} + for _, withdrawalAddressbytes32Item := range withdrawalAddressbytes32 { + withdrawalAddressbytes32Rule = append(withdrawalAddressbytes32Rule, withdrawalAddressbytes32Item) + } + var randaoCommitmentRule []interface{} + for _, randaoCommitmentItem := range randaoCommitment { + randaoCommitmentRule = append(randaoCommitmentRule, randaoCommitmentItem) + } + + logs, sub, err := _ValidatorRegistration.contract.FilterLogs(opts, "ValidatorRegistered", pubKeyRule, withdrawalAddressbytes32Rule, randaoCommitmentRule) if err != nil { return nil, err } @@ -339,10 +353,24 @@ func (_ValidatorRegistration *ValidatorRegistrationFilterer) FilterValidatorRegi // WatchValidatorRegistered is a free log subscription operation binding the contract event 0x7b0678aab009b61a805f5004869728b53a444f9a3e6bb9e22b8537c89af51274. // -// Solidity: event ValidatorRegistered(pubKey bytes32, withdrawalShardID uint256, withdrawalAddressbytes32 address, randaoCommitment bytes32) -func (_ValidatorRegistration *ValidatorRegistrationFilterer) WatchValidatorRegistered(opts *bind.WatchOpts, sink chan<- *ValidatorRegistrationValidatorRegistered) (event.Subscription, error) { +// Solidity: event ValidatorRegistered(pubKey indexed bytes32, withdrawalShardID uint256, withdrawalAddressbytes32 indexed address, randaoCommitment indexed bytes32) +func (_ValidatorRegistration *ValidatorRegistrationFilterer) WatchValidatorRegistered(opts *bind.WatchOpts, sink chan<- *ValidatorRegistrationValidatorRegistered, pubKey [][32]byte, withdrawalAddressbytes32 []common.Address, randaoCommitment [][32]byte) (event.Subscription, error) { - logs, sub, err := _ValidatorRegistration.contract.WatchLogs(opts, "ValidatorRegistered") + var pubKeyRule []interface{} + for _, pubKeyItem := range pubKey { + pubKeyRule = append(pubKeyRule, pubKeyItem) + } + + var withdrawalAddressbytes32Rule []interface{} + for _, withdrawalAddressbytes32Item := range withdrawalAddressbytes32 { + withdrawalAddressbytes32Rule = append(withdrawalAddressbytes32Rule, withdrawalAddressbytes32Item) + } + var randaoCommitmentRule []interface{} + for _, randaoCommitmentItem := range randaoCommitment { + randaoCommitmentRule = append(randaoCommitmentRule, randaoCommitmentItem) + } + + logs, sub, err := _ValidatorRegistration.contract.WatchLogs(opts, "ValidatorRegistered", pubKeyRule, withdrawalAddressbytes32Rule, randaoCommitmentRule) if err != nil { return nil, err } diff --git a/contracts/validator_registration.sol b/contracts/validator_registration.sol index c6f357132e..ec19c2e085 100644 --- a/contracts/validator_registration.sol +++ b/contracts/validator_registration.sol @@ -2,10 +2,10 @@ pragma solidity 0.4.23; contract ValidatorRegistration { event ValidatorRegistered( - bytes32 pubKey, + bytes32 indexed pubKey, uint256 withdrawalShardID, - address withdrawalAddressbytes32, - bytes32 randaoCommitment + address indexed withdrawalAddressbytes32, + bytes32 indexed randaoCommitment ); mapping (bytes32 => bool) public usedPubkey; diff --git a/contracts/validator_registration_test.go b/contracts/validator_registration_test.go index aa4d7f1f92..092c5906a8 100644 --- a/contracts/validator_registration_test.go +++ b/contracts/validator_registration_test.go @@ -136,7 +136,7 @@ func TestRegister(t *testing.T) { if err != nil { t.Errorf("Validator registration failed: %v", err) } - log, err := testAccount.contract.FilterValidatorRegistered(&bind.FilterOpts{}) + log, err := testAccount.contract.FilterValidatorRegistered(&bind.FilterOpts{}, [][32]byte{}, []common.Address{}, [][32]byte{}) if err != nil { t.Fatal(err) } diff --git a/sharding/BUILD.bazel b/sharding/BUILD.bazel index 9a9a48cb66..427a08af19 100644 --- a/sharding/BUILD.bazel +++ b/sharding/BUILD.bazel @@ -8,6 +8,7 @@ go_library( deps = [ "//sharding/node:go_default_library", "//sharding/utils:go_default_library", + "//shared/cmd:go_default_library", "//shared/debug:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_urfave_cli//:go_default_library", diff --git a/sharding/database/BUILD.bazel b/sharding/database/BUILD.bazel index dd9f811d8d..c046d71cb6 100644 --- a/sharding/database/BUILD.bazel +++ b/sharding/database/BUILD.bazel @@ -2,14 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = [ - "database.go", - "inmemory.go", - ], + srcs = ["database.go"], importpath = "github.com/prysmaticlabs/geth-sharding/sharding/database", visibility = ["//sharding:__subpackages__"], deps = [ - "@com_github_ethereum_go_ethereum//common:go_default_library", + "//shared/database:go_default_library", "@com_github_ethereum_go_ethereum//ethdb:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", ], @@ -17,14 +14,10 @@ go_library( go_test( name = "go_default_test", - srcs = [ - "database_test.go", - "inmemory_test.go", - ], + srcs = ["database_test.go"], embed = [":go_default_library"], deps = [ "//sharding/types:go_default_library", - "@com_github_ethereum_go_ethereum//ethdb:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", "@com_github_syndtr_goleveldb//leveldb/errors:go_default_library", ], diff --git a/sharding/database/database.go b/sharding/database/database.go index ea27d76583..b6a72f0713 100644 --- a/sharding/database/database.go +++ b/sharding/database/database.go @@ -8,9 +8,11 @@ import ( "path/filepath" "github.com/ethereum/go-ethereum/ethdb" + sharedDB "github.com/prysmaticlabs/geth-sharding/shared/database" log "github.com/sirupsen/logrus" ) +// ShardDB defines a service for the sharding system's persistent storage. type ShardDB struct { inmemory bool dataDir string @@ -20,27 +22,30 @@ type ShardDB struct { db ethdb.Database } +// ShardDBConfig specifies configuration options for the db service. +type ShardDBConfig struct { + DataDir string + Name string + InMemory bool +} + // NewShardDB initializes a shardDB. -func NewShardDB(dataDir string, name string, inmemory bool) (*ShardDB, error) { +func NewShardDB(config *ShardDBConfig) (*ShardDB, error) { // Uses default cache and handles values. // TODO: allow these arguments to be set based on cli context. - if inmemory { - return &ShardDB{ - inmemory: inmemory, - dataDir: dataDir, - name: name, - cache: 16, - handles: 16, - db: NewShardKV(), - }, nil + shardDB := &ShardDB{ + name: config.Name, + dataDir: config.DataDir, } - return &ShardDB{ - dataDir: dataDir, - name: name, - cache: 16, - handles: 16, - db: nil, - }, nil + if config.InMemory { + shardDB.inmemory = true + shardDB.db = sharedDB.NewKVStore() + } else { + shardDB.inmemory = false + shardDB.cache = 16 + shardDB.handles = 16 + } + return shardDB, nil } // Start the shard DB service. diff --git a/sharding/database/database_test.go b/sharding/database/database_test.go index 49ad46996a..f0cf887071 100644 --- a/sharding/database/database_test.go +++ b/sharding/database/database_test.go @@ -1,7 +1,10 @@ package database import ( + "fmt" + "os" "strconv" + "sync" "testing" "github.com/prysmaticlabs/geth-sharding/sharding/types" @@ -15,15 +18,18 @@ var _ = types.Service(&ShardDB{}) var testDB *ShardDB func init() { - shardDB, _ := NewShardDB("/tmp/datadir", "shardchaindata", false) + tmp := fmt.Sprintf("%s/datadir", os.TempDir()) + config := &ShardDBConfig{DataDir: tmp, Name: "shardchaindata", InMemory: false} + shardDB, _ := NewShardDB(config) testDB = shardDB testDB.Start() } func TestLifecycle(t *testing.T) { hook := logTest.NewGlobal() - - s, err := NewShardDB("/tmp/datadir", "shardchaindb", false) + tmp := fmt.Sprintf("%s/lifecycledir", os.TempDir()) + config := &ShardDBConfig{DataDir: tmp, Name: "shardchaindata", InMemory: false} + s, err := NewShardDB(config) if err != nil { t.Fatalf("could not initialize a new sb: %v", err) } @@ -50,13 +56,17 @@ func TestLifecycle(t *testing.T) { // Testing the concurrency of the shardDB with multiple processes attempting to write. func Test_DBConcurrent(t *testing.T) { + var wg sync.WaitGroup + wg.Add(100) for i := 0; i < 100; i++ { go func(val string) { + defer wg.Done() if err := testDB.db.Put([]byte("ralph merkle"), []byte(val)); err != nil { t.Errorf("could not save value in db: %v", err) } }(strconv.Itoa(i)) } + wg.Wait() } func Test_DBPut(t *testing.T) { diff --git a/sharding/database/inmemory.go b/sharding/database/inmemory.go deleted file mode 100644 index 7b703c91ff..0000000000 --- a/sharding/database/inmemory.go +++ /dev/null @@ -1,67 +0,0 @@ -package database - -import ( - "fmt" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" -) - -// ShardKV is an in-memory mapping of hashes to RLP encoded values. -type ShardKV struct { - kv map[common.Hash][]byte - lock sync.RWMutex -} - -// NewShardKV creates an in-memory, key-value store. -func NewShardKV() *ShardKV { - return &ShardKV{kv: make(map[common.Hash][]byte)} -} - -// Get fetches a val from the mappping by key. -func (sb *ShardKV) Get(k []byte) ([]byte, error) { - sb.lock.RLock() - defer sb.lock.RUnlock() - v, ok := sb.kv[common.BytesToHash(k)] - if !ok { - return []byte{}, fmt.Errorf("key not found: %v", k) - } - return v, nil -} - -// Has checks if the key exists in the mapping. -func (sb *ShardKV) Has(k []byte) (bool, error) { - sb.lock.RLock() - defer sb.lock.RUnlock() - v := sb.kv[common.BytesToHash(k)] - return v != nil, nil -} - -// Put updates a key's value in the mapping. -func (sb *ShardKV) Put(k []byte, v []byte) error { - sb.lock.Lock() - defer sb.lock.Unlock() - // there is no error in a simple setting of a value in a go map. - sb.kv[common.BytesToHash(k)] = v - return nil -} - -// Delete removes the key and value from the mapping. -func (sb *ShardKV) Delete(k []byte) error { - sb.lock.Lock() - defer sb.lock.Unlock() - // There is no return value for deleting a simple key in a go map. - delete(sb.kv, common.BytesToHash(k)) - return nil -} - -func (sb *ShardKV) Close() { - //TODO: Implement Close for ShardKV - panic("ShardKV Close() isnt implemented yet") -} - -func (sb *ShardKV) NewBatch() ethdb.Batch { - //TODO: Implement NewBatch for ShardKV - panic("ShardKV NewBatch() isnt implemented yet") -} diff --git a/sharding/main.go b/sharding/main.go index 7cb6bf302e..5644787f93 100644 --- a/sharding/main.go +++ b/sharding/main.go @@ -6,6 +6,7 @@ import ( "github.com/prysmaticlabs/geth-sharding/sharding/node" "github.com/prysmaticlabs/geth-sharding/sharding/utils" + "github.com/prysmaticlabs/geth-sharding/shared/cmd" "github.com/prysmaticlabs/geth-sharding/shared/debug" log "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -46,7 +47,7 @@ VERSION: app.Usage = `launches a sharding client that interacts with a beacon chain, starts proposer services, shardp2p connections, and more ` app.Action = startNode - app.Flags = []cli.Flag{utils.ActorFlag, utils.DataDirFlag, utils.PasswordFileFlag, utils.NetworkIdFlag, utils.IPCPathFlag, utils.DepositFlag, utils.ShardIDFlag, debug.PProfFlag, debug.PProfAddrFlag, debug.PProfPortFlag, debug.MemProfileRateFlag, debug.CPUProfileFlag, debug.TraceFlag} + app.Flags = []cli.Flag{utils.ActorFlag, cmd.DataDirFlag, cmd.PasswordFileFlag, cmd.NetworkIdFlag, cmd.IPCPathFlag, utils.DepositFlag, utils.ShardIDFlag, debug.PProfFlag, debug.PProfAddrFlag, debug.PProfPortFlag, debug.MemProfileRateFlag, debug.CPUProfileFlag, debug.TraceFlag} app.Before = func(ctx *cli.Context) error { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/sharding/node/BUILD.bazel b/sharding/node/BUILD.bazel index 9b3f8a3c1f..e16891ac5f 100644 --- a/sharding/node/BUILD.bazel +++ b/sharding/node/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//sharding/txpool:go_default_library", "//sharding/utils:go_default_library", "//shared:go_default_library", + "//shared/cmd:go_default_library", "//shared/debug:go_default_library", "@com_github_ethereum_go_ethereum//node:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", diff --git a/sharding/node/backend.go b/sharding/node/backend.go index abd7028b0b..2c1c283df8 100644 --- a/sharding/node/backend.go +++ b/sharding/node/backend.go @@ -25,6 +25,7 @@ import ( "github.com/prysmaticlabs/geth-sharding/sharding/txpool" "github.com/prysmaticlabs/geth-sharding/sharding/utils" "github.com/prysmaticlabs/geth-sharding/shared" + "github.com/prysmaticlabs/geth-sharding/shared/cmd" "github.com/prysmaticlabs/geth-sharding/shared/debug" log "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -131,10 +132,11 @@ func (s *ShardEthereum) Close() { // registerShardChainDB attaches a LevelDB wrapped object to the shardEthereum instance. func (s *ShardEthereum) registerShardChainDB(ctx *cli.Context) error { path := node.DefaultDataDir() - if ctx.GlobalIsSet(utils.DataDirFlag.Name) { - path = ctx.GlobalString(utils.DataDirFlag.Name) + if ctx.GlobalIsSet(cmd.DataDirFlag.Name) { + path = ctx.GlobalString(cmd.DataDirFlag.Name) } - shardDB, err := database.NewShardDB(path, shardChainDBName, false) + config := &database.ShardDBConfig{DataDir: path, Name: shardChainDBName, InMemory: false} + shardDB, err := database.NewShardDB(config) if err != nil { return fmt.Errorf("could not register shardDB service: %v", err) } @@ -152,18 +154,18 @@ func (s *ShardEthereum) registerP2P() error { func (s *ShardEthereum) registerMainchainClient(ctx *cli.Context) error { path := node.DefaultDataDir() - if ctx.GlobalIsSet(utils.DataDirFlag.Name) { - path = ctx.GlobalString(utils.DataDirFlag.Name) + if ctx.GlobalIsSet(cmd.DataDirFlag.Name) { + path = ctx.GlobalString(cmd.DataDirFlag.Name) } endpoint := ctx.Args().First() if endpoint == "" { endpoint = fmt.Sprintf("%s/%s.ipc", path, mainchain.ClientIdentifier) } - if ctx.GlobalIsSet(utils.IPCPathFlag.Name) { - endpoint = ctx.GlobalString(utils.IPCPathFlag.Name) + if ctx.GlobalIsSet(cmd.IPCPathFlag.Name) { + endpoint = ctx.GlobalString(cmd.IPCPathFlag.Name) } - passwordFile := ctx.GlobalString(utils.PasswordFileFlag.Name) + passwordFile := ctx.GlobalString(cmd.PasswordFileFlag.Name) depositFlag := ctx.GlobalBool(utils.DepositFlag.Name) client, err := mainchain.NewSMCClient(endpoint, path, depositFlag, passwordFile) diff --git a/sharding/observer/service_test.go b/sharding/observer/service_test.go index e73ab1c4da..833a58da2d 100644 --- a/sharding/observer/service_test.go +++ b/sharding/observer/service_test.go @@ -23,7 +23,8 @@ func TestStartStop(t *testing.T) { if err != nil { t.Fatalf("Unable to setup p2p server: %v", err) } - shardChainDB, err := database.NewShardDB("", "", true) + config := &database.ShardDBConfig{Name: "", DataDir: "", InMemory: true} + shardChainDB, err := database.NewShardDB(config) if err != nil { t.Fatalf("Unable to setup db: %v", err) } diff --git a/sharding/syncer/service_test.go b/sharding/syncer/service_test.go index 6f2debca49..59bb76215c 100644 --- a/sharding/syncer/service_test.go +++ b/sharding/syncer/service_test.go @@ -28,7 +28,8 @@ func init() { func TestStop(t *testing.T) { hook := logTest.NewGlobal() - shardChainDB, err := database.NewShardDB("", "", true) + config := &database.ShardDBConfig{Name: "", DataDir: "", InMemory: true} + shardChainDB, err := database.NewShardDB(config) if err != nil { t.Fatalf("unable to setup db: %v", err) } @@ -70,7 +71,8 @@ func TestStop(t *testing.T) { func TestHandleCollationBodyRequests(t *testing.T) { hook := logTest.NewGlobal() - shardChainDB, err := database.NewShardDB("", "", true) + config := &database.ShardDBConfig{Name: "", DataDir: "", InMemory: true} + shardChainDB, err := database.NewShardDB(config) if err != nil { t.Fatalf("unable to setup db: %v", err) } diff --git a/sharding/types/BUILD.bazel b/sharding/types/BUILD.bazel index 59d231b8c2..204c71a885 100644 --- a/sharding/types/BUILD.bazel +++ b/sharding/types/BUILD.bazel @@ -29,8 +29,8 @@ go_test( ], embed = [":go_default_library"], deps = [ - "//sharding/database:go_default_library", "//shared:go_default_library", + "//shared/database:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_ethereum_go_ethereum//core/types:go_default_library", "@com_github_ethereum_go_ethereum//crypto/sha3:go_default_library", diff --git a/sharding/types/shard_test.go b/sharding/types/shard_test.go index 837ce770fd..b76b06e52b 100644 --- a/sharding/types/shard_test.go +++ b/sharding/types/shard_test.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" - "github.com/prysmaticlabs/geth-sharding/sharding/database" + sharedDB "github.com/prysmaticlabs/geth-sharding/shared/database" ) type mockShardDB struct { @@ -52,7 +52,7 @@ func TestShard_ValidateShardID(t *testing.T) { emptyHash := common.BytesToHash([]byte{}) emptyAddr := common.BytesToAddress([]byte{}) header := NewCollationHeader(big.NewInt(1), &emptyHash, big.NewInt(1), &emptyAddr, [32]byte{}) - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(big.NewInt(3), shardDB) if err := shard.ValidateShardID(header); err == nil { @@ -76,7 +76,7 @@ func TestShard_HeaderByHash(t *testing.T) { mockDB := &mockShardDB{kv: make(map[common.Hash][]byte)} // creates a well-functioning shardDB. - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() // creates a shard with a functioning DB and another one with a faulty DB. shard := NewShard(big.NewInt(1), shardDB) @@ -115,7 +115,7 @@ func TestShard_CollationByHeaderHash(t *testing.T) { body: []byte{1, 2, 3}, } - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(big.NewInt(1), shardDB) // should throw error if saving the collation before setting the chunk root @@ -171,7 +171,7 @@ func TestShard_ChunkRootfromHeaderHash(t *testing.T) { collation := NewCollation(header, []byte{1, 2, 3}, nil) collation.CalculateChunkRoot() - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(shardID, shardDB) if err := shard.SaveCollation(collation); err != nil { @@ -208,7 +208,7 @@ func TestShard_CanonicalHeaderHash(t *testing.T) { collation.CalculateChunkRoot() - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(shardID, shardDB) // should not be able to set as canonical before saving the header and body first. @@ -248,7 +248,7 @@ func TestShard_CanonicalCollation(t *testing.T) { emptyHash := common.BytesToHash([]byte{}) header := NewCollationHeader(shardID, &emptyHash, period, &proposerAddress, proposerSignature) - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(shardID, shardDB) collation := &Collation{ @@ -288,7 +288,7 @@ func TestShard_SetCanonical(t *testing.T) { chunkRoot := common.BytesToHash([]byte{}) header := NewCollationHeader(big.NewInt(1), &chunkRoot, big.NewInt(1), nil, [32]byte{}) - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(big.NewInt(1), shardDB) otherShard := NewShard(big.NewInt(2), shardDB) @@ -311,7 +311,7 @@ func TestShard_SetCanonical(t *testing.T) { func TestShard_BodyByChunkRoot(t *testing.T) { body := []byte{1, 2, 3, 4, 5} shardID := big.NewInt(1) - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(shardID, shardDB) if err := shard.SaveBody(body); err != nil { @@ -354,7 +354,7 @@ func TestShard_CheckAvailability(t *testing.T) { emptyHash := common.BytesToHash([]byte{}) header := NewCollationHeader(shardID, &emptyHash, period, &proposerAddress, proposerSignature) - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(shardID, shardDB) collation := &Collation{ @@ -391,7 +391,7 @@ func TestShard_SetAvailability(t *testing.T) { mockDB := &mockShardDB{kv: make(map[common.Hash][]byte)} // creates a well-functioning shardDB. - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() // creates a shard with a functioning DB and another one with a faulty DB. shard := NewShard(big.NewInt(1), shardDB) @@ -425,7 +425,7 @@ func TestShard_SaveCollation(t *testing.T) { emptyHash := common.BytesToHash([]byte{}) header := NewCollationHeader(headerShardID, &emptyHash, period, &proposerAddress, proposerSignature) - shardDB := database.NewShardKV() + shardDB := sharedDB.NewKVStore() shard := NewShard(big.NewInt(2), shardDB) collation := &Collation{ diff --git a/sharding/utils/BUILD.bazel b/sharding/utils/BUILD.bazel index 011303b560..a7045cab14 100644 --- a/sharding/utils/BUILD.bazel +++ b/sharding/utils/BUILD.bazel @@ -1,22 +1,12 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", - srcs = [ - "customflags.go", - "flags.go", - ], + srcs = ["flags.go"], importpath = "github.com/prysmaticlabs/geth-sharding/sharding/utils", visibility = ["//sharding:__subpackages__"], deps = [ "//sharding/params:go_default_library", - "@com_github_ethereum_go_ethereum//node:go_default_library", "@com_github_urfave_cli//:go_default_library", ], ) - -go_test( - name = "go_default_test", - srcs = ["customflags_test.go"], - embed = [":go_default_library"], -) diff --git a/sharding/utils/flags.go b/sharding/utils/flags.go index ee981e0f44..2b46f8690a 100644 --- a/sharding/utils/flags.go +++ b/sharding/utils/flags.go @@ -1,60 +1,24 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -// Package utils contains internal helper functions for go-ethereum commands. package utils import ( "math/big" - "github.com/ethereum/go-ethereum/node" shardparams "github.com/prysmaticlabs/geth-sharding/sharding/params" "github.com/urfave/cli" ) var ( - // General settings - IPCPathFlag = DirectoryFlag{ - Name: "ipcpath", - Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", - } - DataDirFlag = DirectoryFlag{ - Name: "datadir", - Usage: "Data directory for the databases and keystore", - Value: DirectoryString{node.DefaultDataDir()}, - } - NetworkIdFlag = cli.Uint64Flag{ - Name: "networkid", - Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)", - Value: 1, - } - PasswordFileFlag = cli.StringFlag{ - Name: "password", - Usage: "Password file to use for non-interactive password input", - Value: "", - } - // Sharding Settings + // DepositFlag defines whether a node will withdraw ETH from the user's account. DepositFlag = cli.BoolFlag{ Name: "deposit", Usage: "To become a notary in a sharding node, " + new(big.Int).Div(shardparams.DefaultConfig.NotaryDeposit, new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)).String() + " ETH will be deposited into SMC", } + // ActorFlag defines the role of the sharding client. Either proposer, notary, or simulator. ActorFlag = cli.StringFlag{ Name: "actor", Usage: `use the --actor notary or --actor proposer to start a notary or proposer service in the sharding node. If omitted, the sharding node registers an Observer service that simply observes the activity in the sharded network`, } + // ShardIDFlag specifies which shard to listen to. ShardIDFlag = cli.IntFlag{ Name: "shardid", Usage: `use the --shardid to determine which shard to start p2p server, listen for incoming transactions and perform proposer/observer duties`, diff --git a/shared/cmd/BUILD.bazel b/shared/cmd/BUILD.bazel new file mode 100644 index 0000000000..938f6fecfe --- /dev/null +++ b/shared/cmd/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "customflags.go", + "flags.go", + ], + importpath = "github.com/prysmaticlabs/geth-sharding/shared/cmd", + visibility = ["//visibility:public"], + deps = [ + "@com_github_ethereum_go_ethereum//node:go_default_library", + "@com_github_urfave_cli//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["customflags_test.go"], + embed = [":go_default_library"], +) diff --git a/sharding/utils/customflags.go b/shared/cmd/customflags.go similarity index 99% rename from sharding/utils/customflags.go rename to shared/cmd/customflags.go index 9fa5aa8d6e..b3429c02b5 100644 --- a/sharding/utils/customflags.go +++ b/shared/cmd/customflags.go @@ -1,4 +1,4 @@ -package utils +package cmd import ( "flag" diff --git a/sharding/utils/customflags_test.go b/shared/cmd/customflags_test.go similarity index 98% rename from sharding/utils/customflags_test.go rename to shared/cmd/customflags_test.go index de39ca36a1..1184957b56 100644 --- a/sharding/utils/customflags_test.go +++ b/shared/cmd/customflags_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with go-ethereum. If not, see . -package utils +package cmd import ( "os" diff --git a/shared/cmd/flags.go b/shared/cmd/flags.go new file mode 100644 index 0000000000..d73f36947d --- /dev/null +++ b/shared/cmd/flags.go @@ -0,0 +1,32 @@ +package cmd + +import ( + "github.com/ethereum/go-ethereum/node" + "github.com/urfave/cli" +) + +var ( + // IPCPathFlag defines the filename of a pipe within the datadir. + IPCPathFlag = DirectoryFlag{ + Name: "ipcpath", + Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", + } + // DataDirFlag defines a path on disk. + DataDirFlag = DirectoryFlag{ + Name: "datadir", + Usage: "Data directory for the databases and keystore", + Value: DirectoryString{node.DefaultDataDir()}, + } + // NetworkIdFlag defines the specific network identifier. + NetworkIdFlag = cli.Uint64Flag{ + Name: "networkid", + Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)", + Value: 1, + } + // PasswordFileFlag defines the path to the user's account password file. + PasswordFileFlag = cli.StringFlag{ + Name: "password", + Usage: "Password file to use for non-interactive password input", + Value: "", + } +) diff --git a/shared/database/BUILD.bazel b/shared/database/BUILD.bazel new file mode 100644 index 0000000000..b4b11d49ae --- /dev/null +++ b/shared/database/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["inmemory.go"], + importpath = "github.com/prysmaticlabs/geth-sharding/shared/database", + visibility = ["//visibility:public"], + deps = [ + "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_ethereum_go_ethereum//ethdb:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["inmemory_test.go"], + embed = [":go_default_library"], + deps = ["@com_github_ethereum_go_ethereum//ethdb:go_default_library"], +) diff --git a/shared/database/inmemory.go b/shared/database/inmemory.go new file mode 100644 index 0000000000..f0af9de10f --- /dev/null +++ b/shared/database/inmemory.go @@ -0,0 +1,71 @@ +package database + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" + log "github.com/sirupsen/logrus" +) + +// KVStore is an in-memory mapping of hashes to RLP encoded values. +type KVStore struct { + kv map[common.Hash][]byte + lock sync.RWMutex +} + +// NewKVStore creates an in-memory, key-value store. +func NewKVStore() *KVStore { + return &KVStore{kv: make(map[common.Hash][]byte)} +} + +// Get fetches a val from the mappping by key. +func (s *KVStore) Get(k []byte) ([]byte, error) { + s.lock.RLock() + defer s.lock.RUnlock() + v, ok := s.kv[common.BytesToHash(k)] + if !ok { + return []byte{}, fmt.Errorf("key not found: %v", k) + } + return v, nil +} + +// Has checks if the key exists in the mapping. +func (s *KVStore) Has(k []byte) (bool, error) { + s.lock.RLock() + defer s.lock.RUnlock() + v := s.kv[common.BytesToHash(k)] + return v != nil, nil +} + +// Put updates a key's value in the mapping. +func (s *KVStore) Put(k []byte, v []byte) error { + s.lock.Lock() + defer s.lock.Unlock() + // there is no error in a simple setting of a value in a go map. + s.kv[common.BytesToHash(k)] = v + return nil +} + +// Delete removes the key and value from the mapping. +func (s *KVStore) Delete(k []byte) error { + s.lock.Lock() + defer s.lock.Unlock() + // There is no return value for deleting a simple key in a go map. + delete(s.kv, common.BytesToHash(k)) + return nil +} + +// Close satisfies ethdb.Database. +func (s *KVStore) Close() { + //TODO: Implement Close for KVStore + log.Debug("ShardKV Close() isnt implemented yet") +} + +// NewBatch satisfies ethdb.Database. +func (s *KVStore) NewBatch() ethdb.Batch { + //TODO: Implement NewBatch for KVStore + log.Debug("ShardKV NewBatch() isnt implemented yet") + return nil +} diff --git a/sharding/database/inmemory_test.go b/shared/database/inmemory_test.go similarity index 82% rename from sharding/database/inmemory_test.go rename to shared/database/inmemory_test.go index c0b4b96b57..dcfa3eb31c 100644 --- a/sharding/database/inmemory_test.go +++ b/shared/database/inmemory_test.go @@ -6,19 +6,19 @@ import ( "github.com/ethereum/go-ethereum/ethdb" ) -// Verifies that ShardKV implements the ethdb interface. -var _ = ethdb.Database(&ShardKV{}) +// Verifies that KVStore implements the ethdb interface. +var _ = ethdb.Database(&KVStore{}) -func Test_ShardKVPut(t *testing.T) { - kv := NewShardKV() +func Test_KVStorePut(t *testing.T) { + kv := NewKVStore() if err := kv.Put([]byte("ralph merkle"), []byte{1, 2, 3}); err != nil { t.Errorf("could not save value in kv store: %v", err) } } -func Test_ShardKVHas(t *testing.T) { - kv := NewShardKV() +func Test_KVStoreHas(t *testing.T) { + kv := NewKVStore() key := []byte("ralph merkle") if err := kv.Put(key, []byte{1, 2, 3}); err != nil { @@ -43,8 +43,8 @@ func Test_ShardKVHas(t *testing.T) { } } -func Test_ShardKVGet(t *testing.T) { - kv := NewShardKV() +func Test_KVStoreGet(t *testing.T) { + kv := NewKVStore() key := []byte("ralph merkle") if err := kv.Put(key, []byte{1, 2, 3}); err != nil { @@ -69,8 +69,8 @@ func Test_ShardKVGet(t *testing.T) { } } -func Test_ShardKVDelete(t *testing.T) { - kv := NewShardKV() +func Test_KVStoreDelete(t *testing.T) { + kv := NewKVStore() key := []byte("ralph merkle") if err := kv.Put(key, []byte{1, 2, 3}); err != nil {