From 89eaa314878a98767d3fccae24e8f2f1aa8c1db5 Mon Sep 17 00:00:00 2001 From: ptrus Date: Mon, 5 Apr 2021 12:12:31 +0200 Subject: [PATCH] interop/protocl-server: support for initializing with mock consensus state --- .../apps/staking/state/interop/interop.go | 204 ++++++++++++++++++ .../mkvs/interop/cmd/protocol_server.go | 40 ++++ .../mkvs/interop/fixtures/consensus_mock.go | 52 +++++ go/storage/mkvs/interop/fixtures/fixture.go | 44 ++++ runtime/src/storage/mkvs/interop/mod.rs | 2 +- .../storage/mkvs/interop/protocol_server.rs | 26 ++- runtime/src/storage/mkvs/mod.rs | 2 +- runtime/src/storage/mkvs/sync/test.rs | 2 +- runtime/src/storage/mkvs/tree/iterator.rs | 4 +- runtime/src/storage/mkvs/tree/tree_test.rs | 12 +- 10 files changed, 373 insertions(+), 15 deletions(-) create mode 100644 go/consensus/tendermint/apps/staking/state/interop/interop.go create mode 100644 go/storage/mkvs/interop/fixtures/consensus_mock.go create mode 100644 go/storage/mkvs/interop/fixtures/fixture.go diff --git a/go/consensus/tendermint/apps/staking/state/interop/interop.go b/go/consensus/tendermint/apps/staking/state/interop/interop.go new file mode 100644 index 00000000000..bc5bc21482b --- /dev/null +++ b/go/consensus/tendermint/apps/staking/state/interop/interop.go @@ -0,0 +1,204 @@ +package interop + +import ( + "context" + "fmt" + + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" + "github.com/oasisprotocol/oasis-core/go/common/quantity" + stakingState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state" + staking "github.com/oasisprotocol/oasis-core/go/staking/api" + "github.com/oasisprotocol/oasis-core/go/storage/mkvs" +) + +var addresses []staking.Address + +// Keep this in sync with tests in runtimes/consensus/state/staking.rs. +func InitializeTestStakingState(ctx context.Context, mkvs mkvs.Tree) error { + state := stakingState.NewMutableState(mkvs) + + // Populate accounts. + for _, acc := range []struct { + address staking.Address + account *staking.Account + }{ + { + addresses[0], + &staking.Account{ + General: staking.GeneralAccount{ + Balance: *quantity.NewFromUint64(23), + Nonce: 13, + }, + Escrow: staking.EscrowAccount{ + Active: staking.SharePool{ + Balance: *quantity.NewFromUint64(100), + TotalShares: *quantity.NewFromUint64(10), + }, + Debonding: staking.SharePool{ + Balance: *quantity.NewFromUint64(5), + TotalShares: *quantity.NewFromUint64(5), + }, + }, + }, + }, + { + addresses[1], + &staking.Account{ + General: staking.GeneralAccount{ + Balance: *quantity.NewFromUint64(23), + Nonce: 1, + }, + Escrow: staking.EscrowAccount{ + Active: staking.SharePool{ + Balance: *quantity.NewFromUint64(500), + TotalShares: *quantity.NewFromUint64(5), + }, + }, + }, + }, + { + addresses[2], + &staking.Account{ + General: staking.GeneralAccount{ + Balance: *quantity.NewFromUint64(113), + Nonce: 17, + }, + Escrow: staking.EscrowAccount{ + Active: staking.SharePool{ + Balance: *quantity.NewFromUint64(400), + TotalShares: *quantity.NewFromUint64(35), + }, + }, + }, + }, + } { + if err := state.SetAccount(ctx, acc.address, acc.account); err != nil { + return fmt.Errorf("setting account: %w", err) + } + } + + // Initialize delegations. + for _, del := range []struct { + from staking.Address + to staking.Address + d *staking.Delegation + }{ + { + from: addresses[0], + to: addresses[0], + d: &staking.Delegation{ + Shares: *quantity.NewFromUint64(5), + }, + }, + { + from: addresses[0], + to: addresses[2], + d: &staking.Delegation{ + Shares: *quantity.NewFromUint64(20), + }, + }, + { + from: addresses[1], + to: addresses[0], + d: &staking.Delegation{ + Shares: *quantity.NewFromUint64(5), + }, + }, + { + from: addresses[1], + to: addresses[2], + d: &staking.Delegation{ + Shares: *quantity.NewFromUint64(6), + }, + }, + { + from: addresses[2], + to: addresses[1], + d: &staking.Delegation{ + Shares: *quantity.NewFromUint64(5), + }, + }, + { + from: addresses[2], + to: addresses[2], + d: &staking.Delegation{ + Shares: *quantity.NewFromUint64(10), + }, + }, + } { + if err := state.SetDelegation(ctx, del.from, del.to, del.d); err != nil { + return err + } + } + + // Initialize debonding delegations. + for _, deb := range []struct { + from staking.Address + to staking.Address + d *staking.DebondingDelegation + }{ + { + from: addresses[0], + to: addresses[0], + d: &staking.DebondingDelegation{ + Shares: *quantity.NewFromUint64(1), + DebondEndTime: 33, + }, + }, + { + from: addresses[1], + to: addresses[0], + d: &staking.DebondingDelegation{ + Shares: *quantity.NewFromUint64(1), + DebondEndTime: 15, + }, + }, + { + from: addresses[1], + to: addresses[0], + d: &staking.DebondingDelegation{ + Shares: *quantity.NewFromUint64(1), + DebondEndTime: 21, + }, + }, + { + from: addresses[2], + to: addresses[0], + d: &staking.DebondingDelegation{ + Shares: *quantity.NewFromUint64(2), + DebondEndTime: 100, + }, + }, + } { + if err := state.SetDebondingDelegation(ctx, deb.from, deb.to, deb.d.DebondEndTime, deb.d); err != nil { + return err + } + } + + // Initialize balances. + if err := state.SetTotalSupply(ctx, quantity.NewFromUint64(10000)); err != nil { + return err + } + if err := state.SetCommonPool(ctx, quantity.NewFromUint64(1000)); err != nil { + return err + } + if err := state.SetLastBlockFees(ctx, quantity.NewFromUint64(33)); err != nil { + return err + } + if err := state.SetGovernanceDeposits(ctx, quantity.NewFromUint64(12)); err != nil { + return err + } + + return nil +} + +func init() { + pk := signature.NewPublicKey("7e57baaad01fffffffffffffffffffffffffffffffffffffffffffffffffffff") + pk2 := signature.NewPublicKey("7e57baaad02fffffffffffffffffffffffffffffffffffffffffffffffffffff") + pk3 := signature.NewPublicKey("7e57baaad03fffffffffffffffffffffffffffffffffffffffffffffffffffff") + addresses = append(addresses, + staking.NewAddress(pk), + staking.NewAddress(pk2), + staking.NewAddress(pk3), + ) +} diff --git a/go/storage/mkvs/interop/cmd/protocol_server.go b/go/storage/mkvs/interop/cmd/protocol_server.go index b182fba6447..132b3ade196 100644 --- a/go/storage/mkvs/interop/cmd/protocol_server.go +++ b/go/storage/mkvs/interop/cmd/protocol_server.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "fmt" "io" "os" @@ -16,13 +17,18 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/logging" genesisTestHelpers "github.com/oasisprotocol/oasis-core/go/genesis/tests" "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/background" + "github.com/oasisprotocol/oasis-core/go/storage/api" storage "github.com/oasisprotocol/oasis-core/go/storage/api" "github.com/oasisprotocol/oasis-core/go/storage/database" + badgerNodedb "github.com/oasisprotocol/oasis-core/go/storage/mkvs/db/badger" + "github.com/oasisprotocol/oasis-core/go/storage/mkvs/interop/fixtures" ) const ( cfgServerSocket = "socket" cfgServerDataDir = "datadir" + + cfgServerFixture = "fixture" ) var ( @@ -96,6 +102,39 @@ func doProtoServer(cmd *cobra.Command, args []string) { InsecureSkipChecks: false, MaxCacheSize: 16 * 1024 * 1024, } + + if fixtureName := viper.GetString(cfgServerFixture); fixtureName != "" { + ctx := context.Background() + ndbCfg := storageCfg.ToNodeDB() + var ndb api.NodeDB + ndb, err = badgerNodedb.New(ndbCfg) + if err != nil { + logger.Error("failed to initialize node db", + "err", err, + ) + return + } + var fixture fixtures.Fixture + fixture, err = fixtures.GetFixture(fixtureName) + if err != nil { + logger.Error("failed getting fixture", + "err", err, + "fixture", fixture.Name(), + ) + return + } + if err = fixture.Populate(ctx, ndb); err != nil { + logger.Error("failed to populate fixture", + "err", err, + "fixture", fixture.Name(), + ) + return + } + + ndb.Close() + + } + backend, err := database.New(&storageCfg) if err != nil { logger.Error("failed to initialize storage backend", @@ -130,5 +169,6 @@ func RegisterProtoServer(parentCmd *cobra.Command) { func init() { protoServerFlags.String(cfgServerSocket, "storage.sock", "path to storage protocol server socket") protoServerFlags.String(cfgServerDataDir, "", "path to data directory") + protoServerFlags.String(cfgServerFixture, "", "fixture for initializing initial state") _ = viper.BindPFlags(protoServerFlags) } diff --git a/go/storage/mkvs/interop/fixtures/consensus_mock.go b/go/storage/mkvs/interop/fixtures/consensus_mock.go new file mode 100644 index 00000000000..1245fbb4a3f --- /dev/null +++ b/go/storage/mkvs/interop/fixtures/consensus_mock.go @@ -0,0 +1,52 @@ +package fixtures + +import ( + "context" + "fmt" + + "github.com/oasisprotocol/oasis-core/go/common" + stakingInterop "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state/interop" + storage "github.com/oasisprotocol/oasis-core/go/storage/api" + "github.com/oasisprotocol/oasis-core/go/storage/mkvs" + db "github.com/oasisprotocol/oasis-core/go/storage/mkvs/db/api" + "github.com/oasisprotocol/oasis-core/go/storage/mkvs/node" +) + +const consensusMockName = "consensus_mock" + +var consensusMockFixture = consensusMock{} + +type consensusMock struct { +} + +func (c *consensusMock) Name() string { + return consensusMockName +} + +func (c *consensusMock) Populate(ctx context.Context, ndb db.NodeDB) error { + var err error + testRoot := storage.Root{ + Type: storage.RootTypeState, + Version: 1, + } + + mkvsTree := mkvs.New(nil, ndb, node.RootTypeState, mkvs.WithoutWriteLog()) + if err = stakingInterop.InitializeTestStakingState(ctx, mkvsTree); err != nil { + return fmt.Errorf("consensus-mock: failed to initialize state: %w", err) + } + _, testRoot.Hash, err = mkvsTree.Commit(ctx, common.Namespace{}, 1) + if err != nil { + return fmt.Errorf("consensus-mock: failed to committ tree: %w", err) + } + if err = ndb.Finalize(ctx, []node.Root{testRoot}); err != nil { + return fmt.Errorf("consensus-mock: failed to finalize test root: %w", err) + } + + fmt.Println("Consensus state root hash:", testRoot.Hash) + + return nil +} + +func init() { + Register(&consensusMockFixture) +} diff --git a/go/storage/mkvs/interop/fixtures/fixture.go b/go/storage/mkvs/interop/fixtures/fixture.go new file mode 100644 index 00000000000..bf3776d2b08 --- /dev/null +++ b/go/storage/mkvs/interop/fixtures/fixture.go @@ -0,0 +1,44 @@ +package fixtures + +import ( + "context" + "fmt" + "sync" + + db "github.com/oasisprotocol/oasis-core/go/storage/mkvs/db/api" +) + +var ( + registeredFixtures sync.Map + + // ErrMissingFixture is the error returned when getting a nonexisting fixture. + ErrMissingFixture = fmt.Errorf("fixture does not exist") +) + +// Fixture is a protocol server fixture. +type Fixture interface { + // Name is the name of the fixture. + Name() string + + // Populate populates the db with the fixture. + Populate(context.Context, db.NodeDB) error +} + +// Register registers a new fixture. +func Register(fixture Fixture) { + name := fixture.Name() + if _, isRegistered := registeredFixtures.Load(name); isRegistered { + panic(fmt.Errorf("fixture already registered: %s", name)) + } + registeredFixtures.Store(name, fixture) +} + +// GetFixture returns a registered fixture by name. +func GetFixture(name string) (Fixture, error) { + h, exists := registeredFixtures.Load(name) + if !exists { + return nil, ErrMissingFixture + } + + return h.(Fixture), nil +} diff --git a/runtime/src/storage/mkvs/interop/mod.rs b/runtime/src/storage/mkvs/interop/mod.rs index 7031a326fa5..b5b1e028578 100644 --- a/runtime/src/storage/mkvs/interop/mod.rs +++ b/runtime/src/storage/mkvs/interop/mod.rs @@ -23,4 +23,4 @@ pub trait Driver { ); } -pub use self::protocol_server::ProtocolServer; +pub use self::protocol_server::{Fixture, ProtocolServer}; diff --git a/runtime/src/storage/mkvs/interop/protocol_server.rs b/runtime/src/storage/mkvs/interop/protocol_server.rs index f2de8089c1b..f4cf50db8b0 100644 --- a/runtime/src/storage/mkvs/interop/protocol_server.rs +++ b/runtime/src/storage/mkvs/interop/protocol_server.rs @@ -4,6 +4,7 @@ //! This should only be used for testing. use std::{ any::Any, + fmt, process::{Child, Command}, sync::Arc, }; @@ -34,9 +35,24 @@ struct ProtocolServerReadSyncer { client: rpc::StorageClient, } +/// Interoperability protocol server fixtures. +pub enum Fixture { + None, + ConsensusMock, +} + +impl fmt::Display for Fixture { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Fixture::ConsensusMock => write!(f, "consensus_mock"), + _ => write!(f, ""), + } + } +} + impl ProtocolServer { /// Create a new protocol server for testing. - pub fn new() -> Self { + pub fn new(fixture: Option) -> Self { let datadir = tempfile::Builder::new() .prefix("oasis-test-storage-protocol-server") .tempdir() @@ -44,14 +60,16 @@ impl ProtocolServer { let socket_path = datadir.path().join("socket"); // Start protocol server. - let server_process = Command::new(PROTOCOL_SERVER_BINARY) + let mut server_cmd = Command::new(PROTOCOL_SERVER_BINARY); + server_cmd .arg("proto-server") .arg("--datadir") .arg(datadir.path()) .arg("--socket") .arg(socket_path.clone()) - .spawn() - .expect("protocol server failed to start"); + .arg("--fixture") + .arg(fixture.unwrap_or(Fixture::None).to_string()); + let server_process = server_cmd.spawn().expect("protocol server failed to start"); // Create connection with the protocol server. let env = Arc::new(EnvBuilder::new().build()); diff --git a/runtime/src/storage/mkvs/mod.rs b/runtime/src/storage/mkvs/mod.rs index af3fe9dca90..448414cffac 100644 --- a/runtime/src/storage/mkvs/mod.rs +++ b/runtime/src/storage/mkvs/mod.rs @@ -16,7 +16,7 @@ use crate::common::{crypto::hash::Hash, namespace::Namespace}; mod tree; mod cache; #[cfg(test)] -mod interop; +pub mod interop; pub mod marshal; pub mod sync; #[cfg(test)] diff --git a/runtime/src/storage/mkvs/sync/test.rs b/runtime/src/storage/mkvs/sync/test.rs index e3841d3db37..4b3f2d5e546 100644 --- a/runtime/src/storage/mkvs/sync/test.rs +++ b/runtime/src/storage/mkvs/sync/test.rs @@ -9,7 +9,7 @@ use crate::storage::mkvs::{ #[test] fn test_nil_pointers() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = Tree::make() .with_root_type(RootType::State) diff --git a/runtime/src/storage/mkvs/tree/iterator.rs b/runtime/src/storage/mkvs/tree/iterator.rs index 31377550a41..3f2db84b06b 100644 --- a/runtime/src/storage/mkvs/tree/iterator.rs +++ b/runtime/src/storage/mkvs/tree/iterator.rs @@ -374,7 +374,7 @@ pub(super) mod test { #[test] fn test_iterator() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = Tree::make() .with_root_type(RootType::State) @@ -566,7 +566,7 @@ pub(super) mod test { #[test] fn test_iterator_eviction() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = OverlayTree::new( Tree::make() diff --git a/runtime/src/storage/mkvs/tree/tree_test.rs b/runtime/src/storage/mkvs/tree/tree_test.rs index 46ed4d5beb9..12ec4097fd2 100644 --- a/runtime/src/storage/mkvs/tree/tree_test.rs +++ b/runtime/src/storage/mkvs/tree/tree_test.rs @@ -670,7 +670,7 @@ fn test_remove() { #[test] fn test_syncer_basic() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = OverlayTree::new( Tree::make() @@ -730,7 +730,7 @@ fn test_syncer_basic() { #[test] fn test_syncer_remove() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = OverlayTree::new( Tree::make() @@ -798,7 +798,7 @@ fn test_syncer_remove() { #[test] fn test_syncer_insert() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = OverlayTree::new( Tree::make() @@ -855,7 +855,7 @@ fn test_syncer_insert() { #[test] fn test_syncer_writelog_remove() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = OverlayTree::new( Tree::make() @@ -893,7 +893,7 @@ fn test_syncer_writelog_remove() { #[test] fn test_syncer_prefetch_prefixes() { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let mut tree = OverlayTree::new( Tree::make() @@ -1028,7 +1028,7 @@ fn test_node_eviction() { const TEST_VECTORS_DIR: &'static str = "../go/storage/mkvs/testdata"; fn test_special_case_from_json(fixture: &'static str) { - let server = ProtocolServer::new(); + let server = ProtocolServer::new(None); let file = File::open(Path::new(TEST_VECTORS_DIR).join(fixture)).expect("failed to open fixture");