From 4048596eab12f95f6088be61103b97977f68ad2e Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 6 Dec 2023 11:02:55 +0900 Subject: [PATCH 1/2] Merge tag '3.9.0' into port/3.9.0-to-4.0.0 Libplanet 3.9.0 --- .circleci/config.yml | 2 +- CHANGES.md | 166 +++++- .../ActionEvaluationExtensions.cs | 41 -- .../ActionEvaluationTest.cs | 10 +- Libplanet.Action.Tests/Common/Attack.cs | 2 +- .../Common/DetectRehearsal.cs | 38 -- Libplanet.Action.Tests/Common/DumbAction.cs | 48 +- .../Common/DumbModernAction.cs | 39 +- .../Common/ExecuteRecord.cs | 6 +- Libplanet.Action.Tests/Common/RandomAction.cs | 7 - .../Common/SetStatesAtBlock.cs | 2 +- .../Common/ThrowException.cs | 6 +- .../Loader/IndexedActionLoaderTest.cs | 6 +- .../State/KeyConvertersTest.cs | 4 +- .../State/WorldBaseStateTest.cs | 3 - Libplanet.Action.Tests/Sys/InitializeTest.cs | 6 +- Libplanet.Action.Tests/TestUtils.cs | 8 +- Libplanet.Action/ActionContext.cs | 7 +- Libplanet.Action/ActionEvaluator.cs | 266 ++++------ Libplanet.Action/CommittedActionContext.cs | 7 - Libplanet.Action/IActionContext.cs | 8 - Libplanet.Action/ICommittedActionContext.cs | 8 - Libplanet.Action/State/Account.cs | 141 ++--- Libplanet.Action/State/AccountDelta.cs | 65 --- .../State/AccountDeltaExtensions.cs | 72 --- Libplanet.Action/State/AccountExtensions.cs | 28 - .../{AccountBaseState.cs => AccountState.cs} | 35 +- Libplanet.Action/State/AccountStateCache.cs | 76 --- Libplanet.Action/State/IAccount.cs | 7 - Libplanet.Action/State/IAccountDelta.cs | 120 ----- Libplanet.Action/State/IWorld.cs | 14 +- Libplanet.Action/State/IWorldDelta.cs | 24 - Libplanet.Action/State/World.cs | 61 +-- Libplanet.Action/State/WorldBaseState.cs | 2 +- Libplanet.Action/State/WorldDelta.cs | 24 - .../State/WorldDeltaExtensions.cs | 62 --- .../UnexpectedlyTerminatedActionException.cs | 36 +- Libplanet.Benchmarks/AppendBlock.cs | 4 +- .../DataModel/DataModelBenchmark.LeafModel.cs | 4 +- .../DataModel/DataModelBenchmark.MidModel.cs | 4 +- .../DataModel/DataModelBenchmark.RootModel.cs | 4 +- Libplanet.Benchmarks/ProposeBlock.cs | 4 +- .../NameValueCollectionExtensions.cs | 52 ++ Libplanet.Crypto/Address.cs | 2 +- Libplanet.Crypto/AddressExtensions.cs | 41 -- Libplanet.Crypto/PrivateKey.cs | 6 + Libplanet.Crypto/PublicKey.cs | 5 + .../GeneratedBlockChainFixture.cs | 49 +- .../Indexing/BlockChainIndexTest.cs | 8 +- .../Queries/StateQueryTest.cs | 6 +- .../Queries/TransactionQueryGeneratedTest.cs | 6 +- .../Queries/TransactionQueryTest.cs | 20 +- .../Queries/TransactionQuery.cs | 2 +- .../Commands/StoreCommandTest.cs | 4 +- .../Commands/KeyCommand.cs | 4 +- .../Commands/MptCommand.cs | 20 +- Libplanet.Net.Tests/AppProtocolVersionTest.cs | 8 +- .../Consensus/ContextNonProposerTest.cs | 6 +- .../ContextProposerValidRoundTest.cs | 2 +- Libplanet.Net.Tests/Consensus/ContextTest.cs | 7 +- .../Messages/NetMQMessageCodecTest.cs | 2 +- Libplanet.Net.Tests/Protocols/ProtocolTest.cs | 2 +- .../Protocols/RoutingTableTest.cs | 18 +- .../Protocols/TestTransport.cs | 8 +- .../SwarmTest.AppProtocolVersion.cs | 4 +- Libplanet.Net.Tests/SwarmTest.Broadcast.cs | 6 +- Libplanet.Net.Tests/SwarmTest.Fixtures.cs | 2 +- Libplanet.Net.Tests/SwarmTest.Preload.cs | 8 +- Libplanet.Net.Tests/SwarmTest.cs | 18 +- Libplanet.Net.Tests/TestUtils.cs | 2 +- .../Transports/TransportTest.cs | 4 +- Libplanet.Net/Consensus/Context.Mutate.cs | 2 +- Libplanet.Net/Consensus/Context.cs | 2 +- .../Consensus/ContextTimeoutOption.cs | 12 +- .../GossipConsensusMessageCommunicator.cs | 35 +- Libplanet.Net/Consensus/VoteSet.cs | 2 +- Libplanet.Net/Swarm.cs | 4 +- .../RocksDBKeyValueStoreTest.cs | 41 ++ .../RocksDBStoreTest.cs | 14 - .../Libplanet.RocksDBStore.csproj | 2 +- Libplanet.RocksDBStore/RocksDBInstanceType.cs | 27 + .../RocksDBKeyValueStore.cs | 17 +- Libplanet.RocksDBStore/RocksDBStore.cs | 55 +- Libplanet.RocksDBStore/RocksDBUtils.cs | 32 +- Libplanet.Store/HashNodeCache.cs | 88 ++++ Libplanet.Store/Trie/MerkleTrie.cs | 31 +- Libplanet.Store/TrieStateStore.Commit.cs | 44 +- Libplanet.Store/TrieStateStore.cs | 5 +- Libplanet.Tests/Action/AccountDiffTest.cs | 55 +- Libplanet.Tests/Action/AccountTest.cs | 57 +- Libplanet.Tests/Action/AccountV0Test.cs | 3 - Libplanet.Tests/Action/AccountV1Test.cs | 46 +- Libplanet.Tests/Action/ActionEvaluatorTest.cs | 492 +++++++++--------- Libplanet.Tests/AddressExtensionsTest.cs | 24 - Libplanet.Tests/Assets/CurrencyTest.cs | 2 +- .../Blockchain/BlockChainTest.Append.cs | 37 +- .../Blockchain/BlockChainTest.Internals.cs | 10 +- .../Blockchain/BlockChainTest.ProposeBlock.cs | 49 +- .../BlockChainTest.ValidateNextBlock.cs | 10 +- Libplanet.Tests/Blockchain/BlockChainTest.cs | 91 ++-- .../Blockchain/Policies/BlockPolicyTest.cs | 8 +- .../Renderers/LoggedActionRendererTest.cs | 43 +- Libplanet.Tests/Blocks/BlockContentTest.cs | 16 +- Libplanet.Tests/Blocks/BlockMarshalerTest.cs | 4 +- Libplanet.Tests/Blocks/BlockMetadataTest.cs | 6 +- Libplanet.Tests/Blocks/BlockTest.cs | 2 +- ...validBlockTxCountPerSignerExceptionTest.cs | 2 +- Libplanet.Tests/Consensus/ValidatorSetTest.cs | 2 +- Libplanet.Tests/Crypto/PrivateKeyTest.cs | 15 + Libplanet.Tests/Crypto/PublicKeyTest.cs | 16 + .../Fixtures/BlockContentFixture.cs | 24 +- Libplanet.Tests/Fixtures/IntegerSet.cs | 20 +- .../KeyStore/ProtectedPrivateKeyTest.cs | 8 +- Libplanet.Tests/Store/DataModelTest.cs | 12 +- Libplanet.Tests/Store/StoreFixture.cs | 1 - Libplanet.Tests/Store/StoreTest.cs | 4 +- Libplanet.Tests/Store/Trie/TrieTest.cs | 4 +- Libplanet.Tests/TestUtils.cs | 5 +- .../Tx/TransactionExtensionsTest.cs | 5 +- Libplanet.Tests/Tx/TransactionTest.cs | 82 ++- Libplanet.Tests/Tx/TxActionListTest.cs | 2 - Libplanet.Tests/Tx/TxFixture.cs | 32 +- Libplanet.Tests/Tx/TxInvoiceTest.cs | 47 +- Libplanet.Tests/Tx/TxMarshalerTest.cs | 2 +- Libplanet.Tests/Tx/TxMetadataTest.cs | 34 +- Libplanet.Tests/Tx/TxSigningMetadataTest.cs | 8 +- Libplanet.Tests/Tx/UnsignedTxTest.cs | 29 +- Libplanet.Types/AssemblyInfo.cs | 4 + Libplanet.Types/Assets/Currency.cs | 6 +- Libplanet.Types/Blocks/BlockMarshaler.cs | 4 +- Libplanet.Types/Blocks/BlockMetadata.cs | 6 +- Libplanet.Types/Consensus/Validator.cs | 2 +- Libplanet.Types/Tx/ITxInvoice.cs | 17 +- Libplanet.Types/Tx/Transaction.cs | 9 +- Libplanet.Types/Tx/TxInvoice.cs | 81 ++- Libplanet.Types/Tx/TxMarshaler.cs | 4 +- Libplanet.Types/Tx/TxMetadata.cs | 4 +- .../Blockchain/BlockChain.ProposeBlock.cs | 2 +- Libplanet/Blockchain/BlockChain.cs | 6 +- Libplanet/Blockchain/BlockChainStates.cs | 2 +- .../Renderers/LoggedActionRenderer.cs | 81 +-- Libplanet/KeyStore/ProtectedPrivateKey.cs | 4 +- 142 files changed, 1592 insertions(+), 2161 deletions(-) delete mode 100644 Libplanet.Action.Tests/ActionEvaluationExtensions.cs delete mode 100644 Libplanet.Action.Tests/Common/DetectRehearsal.cs delete mode 100644 Libplanet.Action/State/AccountDelta.cs delete mode 100644 Libplanet.Action/State/AccountDeltaExtensions.cs delete mode 100644 Libplanet.Action/State/AccountExtensions.cs rename Libplanet.Action/State/{AccountBaseState.cs => AccountState.cs} (58%) delete mode 100644 Libplanet.Action/State/AccountStateCache.cs delete mode 100644 Libplanet.Action/State/IAccountDelta.cs delete mode 100644 Libplanet.Action/State/IWorldDelta.cs delete mode 100644 Libplanet.Action/State/WorldDelta.cs delete mode 100644 Libplanet.Action/State/WorldDeltaExtensions.cs delete mode 100644 Libplanet.Crypto/AddressExtensions.cs create mode 100644 Libplanet.RocksDBStore/RocksDBInstanceType.cs create mode 100644 Libplanet.Store/HashNodeCache.cs delete mode 100644 Libplanet.Tests/AddressExtensionsTest.cs create mode 100644 Libplanet.Types/AssemblyInfo.cs diff --git a/.circleci/config.yml b/.circleci/config.yml index e03e63a741c..cda7d2084c0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -289,7 +289,7 @@ commands: - run: name: Install lib6c-dev (for RocksDBSharp) shell: bash - command: apt update -y && apt install -y libc6-dev + command: apt update -y && apt install -y libc6-dev liblz4-dev zlib1g-dev libsnappy-dev libzstd-dev - netcore_test_base: collect_tests_from: "<>" locale: "<>" diff --git a/CHANGES.md b/CHANGES.md index 480a7efc1c4..4e41000d8dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,13 +20,6 @@ To be released. instead of `IAccount`. [[#3462]] - (Libplanet.Action) `IActionEvaluator.OutputState` became `IWorld`. (was `IAccount`) [[#3462]] - - (Libplanet.Action) Added - `ActionEvaluationsExtensions.GetLegacyRawTotalDelta()` extension method. - [[#3462]] - - (Libplanet.Action) `ActionEvaluationsExtensions.GetRawTotalDelta()` - became to return - `IImmutableDictionary>`. - [[#3462]] - (Libplanet.Action) `IAction.Execute()` became to return `IWorld`. (was `IAccount`) [[#3462]] - (Libplanet.Action) `IActionContext.PreviousState` became `IWorld`. @@ -36,8 +29,6 @@ To be released. - `IFeeCollector.Mortgage()` - `IFeeCollector.Refund()` - `IFeeCollector.Reward()` - - (Libplanet.Action) Renamed `AccountState` class to `AccountBaseState`. - [[#3462]] - (Libplanet.Action) `IBlockChainStates` interface has modified. [[#3462]] - Added `IBlockChainStates.GetWorldState()` method. - Added `IBlockChainStates.GetAccountState(Address, BlockHash?)` method. @@ -67,16 +58,6 @@ To be released. method. - Removed `IBlockChainStates.GetTotalSupply(Currency, BlockHash?)` method. - Removed `IBlockChainStates.GetValidatorSet(BlockHash?)` method. - - (Libplanet.Action) `Initialize.Initialize()` method became to accept - `IImmutableDictionary>`. - (was `IImmutableDictionary`) [[#3462]] - - (Libplanet.Action) Type of `Initialize.States` property became - `IImmutableDictionary>?`. - (was `IImmutableDictionary?`) [[#3462]] - - (Libplanet.Action) New property `SystemAccounts` added to - `IActionContext` interface. [[#3494]] - - (Libplanet.Action) New property `SystemAccountsGetter` added to - `IBlockPolicy` interface. [[#3494]] - (@planetarium/tx) Remove the `T` generic argument of `SignedTx`. [[#3512]] - (Libplanet.Action) @@ -96,16 +77,13 @@ To be released. - (Libplanet.Action) Added `IWorld` interface and its implementation. [[#3462]] - Added `World` class. - - (Libplanet.Action) Added `IWorldDelta` interface. [[#3462]] - (Libplanet.Action) Added `IWorldState` interface and its implementation. [[#3462]] - Added `WorldBaseState` class. - (Libplanet.Action) Added `ReservedAddresses` static class. [[#3462]] - - (Libplanet.Action) Added `WorldDeltaExtensions` static class. [[#3462]] - (Libplanet.Action) Added `TrieMetadata` class. [[#3540]] - (Libplanet.Explorer) Added `AccountStateType` class. [[#3462]] - (Libplanet.Explorer) Added `WorldStateType` class. [[#3462]] - - (Libplanet.Explorer) Added `WorldStateType` class. [[#3462]] - (Libplanet.Explorer) Modified some fields of `StateQuery` query. [[#3462]] - Added `StateQuery.worldState` field. - Added `StateQuery.accountState` field. @@ -140,6 +118,125 @@ To be released. [#3524]: https://github.com/planetarium/libplanet/pull/3524 [#3540]: https://github.com/planetarium/libplanet/pull/3540 + +Version 3.9.0 +------------- + +Released on December 5, 2023. + +Due to changes in [#3529], a network ran with a prior version may not +be compatible with this version, specifically, those that ran with +[Libplanet 2.0.0] and onwards prior to this release that have included +`Transaction`s that aren't compatible with the updated specification in [#3529]. + +### Backward-incompatible API changes + + - (Libplanet.Action) Removed unnecessary extension methods: [[#3520]] + - `IReadOnlyList.GetRawTotalDelta()` + - `IReadOnlyList.OrderedSum()` + - `IAccountDelta.ToRawDelta()` + - `IAccount.GetUpdatedStates()` + - `IAccount.GetUpdatedBalances()` + - `IAccount.GetUpdatedTotalSupplies()` + - (Libplanet.Action) Changed `IAccount` to no longer track `IAccountDelta`. + [[#3520]] + - (Libplanet.Action) Removed `IAccountDelta` as parameter for constructors + of `Account` class. [[#3520]] + - (Libplanet.Action) Removed `hashedSignature` of type `byte[]` parameter + from `ActionEvaluator.GenerateRandomSeed()`. [[#3523]] + - Changed `TxInvoice` to no longer allow having the null-ness of + `MaxGasPrice` and `GasLimit` to be different, i.e. either both should be + null or both should not be null at the same time. [[#3529]] + - (Libplanet.Action) Removed `IAccountDelta` interface. [[#3535]] + - (Libplanet.Action) Removed `IAccount.Delta` interface property. [[#3535]] + - (Libplanet.Action) Changed constructor `IAccount(IAccountState, + IImmutableDictionary<(Address, Currency), BigInteger>)` to + `IAccount(IAccountState, IImmutableHashSet<(Address, Currency)>)`. + [[#3537]] + +[#3520]: https://github.com/planetarium/libplanet/pull/3520 +[#3523]: https://github.com/planetarium/libplanet/pull/3523 +[#3529]: https://github.com/planetarium/libplanet/pull/3529 +[#3535]: https://github.com/planetarium/libplanet/pull/3535 +[#3537]: https://github.com/planetarium/libplanet/pull/3537 +[Libplanet 2.0.0]: https://www.nuget.org/packages/Libplanet/2.0.0 + + +Version 3.8.1 +------------- + +Released on November 27, 2023. + + - (Libplanet.Net) Fixed a bug where `GossipConsensusMessageCommunicator` + does not clear `_peerCatchupRounds` on `OnStartHeight()`. [[#3519]] + - (Libplanet.Net) `GossipConsensusMessageCommunicator` now filters + `ConsensusVoteMsg` which height is different from latest `Context`. + [[#3519]] + - (Libplanet.Action) Fixed a bug where initialization of `AccountMetrics` + is absent. [[#3521]] + +[#3519]: https://github.com/planetarium/libplanet/pull/3519 +[#3521]: https://github.com/planetarium/libplanet/pull/3521 + + +Version 3.8.0 +------------- + +Released on November 24, 2023. + +### Backward-incompatible API changes + + - Removed `updatedAddresses` parameter from `BlockChain.MakeTransaction()` + [[#3480]] + - Removed `updatedAddresses` parameter from `Transaction.Create()`. [[#3480]] + - Removed `updatedAddresses` parameter from all `TxInvoice()`. [[#3480]] + - Removed `Rehearsal` property from `IActionContext` and + `ICommittedActionContext`. [[#3485]] + - (Libplanet.Crypto) Removed `ToAddress()` extension method for + `PrivateKey` and `PublicKey`. [[#3486]] + - (Libplanet.Crypto) Added `Address` property to `PrivateKey` and `PublicKey`. + [[#3486]] + +### Backward-incompatible storage format changes + + - (Libplanet.Store) Changed `Libplanet.RocksDBStore` to use + [`RocksDb`] instead of [`RocksDBSharp`]. + *Note* Cannot read new version of `Libplanet.RocksDBStore` + storage from under `Libplanet.RocksDBStore` version 3.6.1. + [[#1848], [#3487]] + +### Added APIs + + - (Libplanet.RocksDBStore) Added `RocksDBInstanceType` enum. [[#3488]] + - (Libplanet.RocksDBStore) Changed `RocksDBStore` and `RocksDBKeyValueStore` + to accept `RocksDBInstanceType` type `instanceType` parameter instead of + `@readonly` parameter in their constructor. + [[#3488], [RocksDb Instance Types]] + +[#1848]: https://github.com/planetarium/libplanet/issues/1848 +[#3480]: https://github.com/planetarium/libplanet/pull/3480 +[#3485]: https://github.com/planetarium/libplanet/pull/3485 +[#3486]: https://github.com/planetarium/libplanet/pull/3486 +[#3487]: https://github.com/planetarium/libplanet/pull/3487 +[#3488]: https://github.com/planetarium/libplanet/pull/3488 +[`RocksDb`]: https://www.nuget.org/packages/RocksDB +[`RocksDBSharp`]: https://www.nuget.org/packages/Planetarium.RocksDbSharp +[RocksDb Instance Types]: https://github.com/facebook/rocksdb/wiki/Read-only-and-Secondary-instances + + +Version 3.7.1 +------------- + +Released on November 21, 2023. + + - Ported changes from [Libplanet 3.6.1] release. [[#3500]] + - Ported changes from [Libplanet 3.6.2] release. [[#3509]] + +[#3500]: https://github.com/planetarium/libplanet/pull/3500 +[#3509]: https://github.com/planetarium/libplanet/pull/3509 +[Libplanet 3.6.1]: https://www.nuget.org/packages/Libplanet/3.6.1 +[Libplanet 3.6.2]: https://www.nuget.org/packages/Libplanet/3.6.2 + Version 3.7.0 ------------- @@ -168,6 +265,31 @@ Released on October 27, 2023. [Bencodex.Json 0.16.0]: https://www.nuget.org/packages/Bencodex.json/0.16.0 +Version 3.6.2 +------------- + +Released on November 21, 2023. + + - (Libplanet.Net) Changed default `ContextTimeoutOption` values for + more consistent and stable consensus. [[#3506]] + +[#3506]: https://github.com/planetarium/libplanet/pull/3506 + + +Version 3.6.1 +------------- + +Released on November 20, 2023. + + - (Libplanet.Store) Added optional `cache` parameter of type `HashNodeCache` + to `MerkleTrie()` constructors. [[#3495]] + - (Libplanet.Store) Added `HashNodeCache` class. [[#3495]] + - (Libplanet.Store) Changed internal caching strategy of `TrieStateStore` for + read/write optimization. [[#3495]] + +[#3495]: https://github.com/planetarium/libplanet/pull/3495 + + Version 3.6.0 ------------- diff --git a/Libplanet.Action.Tests/ActionEvaluationExtensions.cs b/Libplanet.Action.Tests/ActionEvaluationExtensions.cs deleted file mode 100644 index 7fd1b273ab4..00000000000 --- a/Libplanet.Action.Tests/ActionEvaluationExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Immutable; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; - -namespace Libplanet.Action.Tests -{ - public static class ActionEvaluationExtensions - { - public static IImmutableDictionary GetDirtyStates( - this IEnumerable evaluations - ) => - evaluations.Aggregate( - ImmutableDictionary.Empty, - (dirty, ev) => dirty.SetItems( - ev.OutputState.GetAccount( - ReservedAddresses.LegacyAccount).GetUpdatedStates()) - ); - - public static IImmutableDictionary<(Address, Currency), FungibleAssetValue> - GetDirtyBalances( - this IEnumerable evaluations - ) => - evaluations.Aggregate( - ImmutableDictionary<(Address, Currency), FungibleAssetValue>.Empty, - (dirty, ev) => dirty.SetItems( - ev.OutputState.GetAccount( - ReservedAddresses.LegacyAccount).GetUpdatedBalances()) - ); - - public static IImmutableDictionary - GetDirtyTotalSupplies(this IEnumerable evaluations) => - evaluations.Aggregate( - ImmutableDictionary.Empty, - (dirty, ev) => dirty.SetItems( - ev.OutputState.GetAccount( - ReservedAddresses.LegacyAccount).GetUpdatedTotalSupplies()) - ); - } -} diff --git a/Libplanet.Action.Tests/ActionEvaluationTest.cs b/Libplanet.Action.Tests/ActionEvaluationTest.cs index ea3235b59d6..127778ad3bd 100644 --- a/Libplanet.Action.Tests/ActionEvaluationTest.cs +++ b/Libplanet.Action.Tests/ActionEvaluationTest.cs @@ -27,12 +27,12 @@ public ActionEvaluationTest(ITestOutputHelper output) [Fact] public void Constructor() { - Address address = new PrivateKey().ToAddress(); + var txid = new System.Random().NextTxId(); + Address address = new PrivateKey().Address; IWorld world = new World(new MockWorldState()); world = world.SetAccount( ReservedAddresses.LegacyAccount, world.GetAccount(ReservedAddresses.LegacyAccount).SetState(address, (Text)"item")); - var txid = new System.Random().NextTxId(); var evaluation = new ActionEvaluation( new DumbAction(address, "item"), new ActionContext( @@ -43,10 +43,8 @@ public void Constructor() Block.CurrentProtocolVersion, new World(new MockWorldState()), 123, - 0, - false), - world - ); + 0), + world); var action = (DumbAction)evaluation.Action; Assert.Equal(address, action.TargetAddress); diff --git a/Libplanet.Action.Tests/Common/Attack.cs b/Libplanet.Action.Tests/Common/Attack.cs index 62b3ad40bcc..3c4ac8f5afe 100644 --- a/Libplanet.Action.Tests/Common/Attack.cs +++ b/Libplanet.Action.Tests/Common/Attack.cs @@ -13,7 +13,7 @@ public class Attack : BaseAction .Add("values", Dictionary.Empty .Add("weapon", Weapon) .Add("target", Target) - .Add("target_address", TargetAddress.ByteArray)); + .Add("target_address", TargetAddress.Bencoded)); public string Weapon { get; set; } diff --git a/Libplanet.Action.Tests/Common/DetectRehearsal.cs b/Libplanet.Action.Tests/Common/DetectRehearsal.cs deleted file mode 100644 index a0c7fc948ef..00000000000 --- a/Libplanet.Action.Tests/Common/DetectRehearsal.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; - -namespace Libplanet.Action.Tests.Common -{ - [ActionType("detect_rehearsal")] - public class DetectRehearsal : BaseAction - { - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", TypeId) - .Add("values", Dictionary.Empty - .Add("target_address", TargetAddress.ByteArray)); - - public bool ResultState { get; set; } - - public Address TargetAddress { get; set; } - - public override void LoadPlainValue(IValue plainValue) - { - Dictionary values = (Dictionary)GetValues(plainValue); - TargetAddress = new Address(values["target_address"]); - } - - public override IWorld Execute(IActionContext context) - { - IWorld previousState = context.PreviousState; - IAccount legacyAccount = previousState.GetAccount(ReservedAddresses.LegacyAccount); - ResultState = context.Rehearsal; - return previousState.SetAccount( - ReservedAddresses.LegacyAccount, - legacyAccount.SetState( - TargetAddress, - new Bencodex.Types.Boolean(context.Rehearsal) - )); - } - } -} diff --git a/Libplanet.Action.Tests/Common/DumbAction.cs b/Libplanet.Action.Tests/Common/DumbAction.cs index fe2d64ec9be..c5a80928dcb 100644 --- a/Libplanet.Action.Tests/Common/DumbAction.cs +++ b/Libplanet.Action.Tests/Common/DumbAction.cs @@ -30,7 +30,6 @@ public DumbAction(IEnumerable validators) public DumbAction( Address targetAddress, string item, - bool recordRehearsal = false, bool recordRandom = false, bool idempotent = false, Tuple transfer = null) @@ -38,7 +37,6 @@ public DumbAction( Idempotent = idempotent; TargetAddress = targetAddress; Item = item; - RecordRehearsal = recordRehearsal; RecordRandom = recordRandom; Transfer = transfer; } @@ -49,14 +47,12 @@ public DumbAction( Address transferFrom, Address transferTo, BigInteger transferAmount, - bool recordRehearsal = false, bool recordRandom = false, bool idempotent = false ) : this( targetAddress, item, - recordRehearsal, recordRandom, idempotent, Tuple.Create(transferFrom, transferTo, transferAmount) @@ -67,16 +63,10 @@ public DumbAction( public static AsyncLocal> ExecuteRecords { get; } = new AsyncLocal>(); - public static AsyncLocal> - RehearsalRecords { get; } = - new AsyncLocal>(); - public Address TargetAddress { get; private set; } public string Item { get; private set; } - public bool RecordRehearsal { get; private set; } - public bool RecordRandom { get; private set; } public bool Idempotent { get; private set; } @@ -96,8 +86,7 @@ public IValue PlainValue new Dictionary { ["item"] = (Text)Item, - ["target_address"] = new Binary(TargetAddress.ByteArray), - ["record_rehearsal"] = new Bencodex.Types.Boolean(RecordRehearsal), + ["target_address"] = TargetAddress.Bencoded, }); } @@ -116,8 +105,8 @@ public IValue PlainValue if (!(Transfer is null)) { plainValue = plainValue - .Add("transfer_from", Transfer.Item1.ByteArray) - .Add("transfer_to", Transfer.Item2.ByteArray) + .Add("transfer_from", Transfer.Item1.Bencoded) + .Add("transfer_to", Transfer.Item2.Bencoded) .Add("transfer_amount", Transfer.Item3); } @@ -133,17 +122,6 @@ public IValue PlainValue public IWorld Execute(IActionContext context) { - if (RehearsalRecords.Value is null) - { - RehearsalRecords.Value = ImmutableList<(Address, string)>.Empty; - } - - if (context.Rehearsal) - { - RehearsalRecords.Value = - RehearsalRecords.Value.Add((TargetAddress, Item)); - } - IWorld world = context.PreviousState; if (Item is null) { @@ -152,13 +130,10 @@ public IWorld Execute(IActionContext context) IAccount account = world.GetAccount(ReservedAddresses.LegacyAccount); string items = (Text?)account.GetState(TargetAddress); - string item = RecordRehearsal - ? $"{Item}:{context.Rehearsal}" - : Item; if (Idempotent) { - var splitItems = items is null ? new[] { item } : (items + "," + item).Split(','); + var splitItems = items is null ? new[] { Item } : (items + "," + Item).Split(','); items = string.Join( ",", splitItems.OrderBy(x => @@ -172,7 +147,7 @@ public IWorld Execute(IActionContext context) } else { - items = items is null ? item : $"{items},{item}"; + items = items is null ? Item : $"{items},{Item}"; } if (RecordRandom) @@ -183,7 +158,7 @@ public IWorld Execute(IActionContext context) ); } - if (Item.Equals("D") && !context.Rehearsal) + if (Item.Equals("D")) { Item = Item.ToUpperInvariant(); } @@ -218,7 +193,6 @@ public IWorld Execute(IActionContext context) { Action = this, NextState = account, - Rehearsal = context.Rehearsal, }); return world.SetAccount(ReservedAddresses.LegacyAccount, account); @@ -233,7 +207,6 @@ public void LoadPlainValue(Dictionary plainValue) { Item = (Text)plainValue["item"]; TargetAddress = new Address(plainValue["target_address"]); - RecordRehearsal = (Boolean)plainValue["record_rehearsal"]; RecordRandom = plainValue.ContainsKey((IKey)(Text)"record_random") && plainValue["record_random"] is Boolean r && @@ -264,9 +237,7 @@ public bool Equals(DumbAction other) return !(other is null) && ( ReferenceEquals(this, other) || ( TargetAddress.Equals(other.TargetAddress) && - string.Equals(Item, other.Item) && - RecordRehearsal == other.RecordRehearsal - ) + string.Equals(Item, other.Item)) ); } @@ -283,9 +254,7 @@ public override int GetHashCode() unchecked { int hashCode = TargetAddress.GetHashCode(); - hashCode = (hashCode * 397) ^ - (Item != null ? Item.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ RecordRehearsal.GetHashCode(); + hashCode = (hashCode * 397) ^ (Item != null ? Item.GetHashCode() : 0); return hashCode; } } @@ -304,7 +273,6 @@ public override string ToString() return $"{nameof(DumbAction)} {{ " + $"{nameof(TargetAddress)} = {TargetAddress}, " + $"{nameof(Item)} = {Item ?? string.Empty}, " + - $"{nameof(RecordRehearsal)} = {(RecordRehearsal ? T : F)}, " + $"{nameof(RecordRandom)} = {(RecordRandom ? T : F)}, " + $"{nameof(Idempotent)} = {(Idempotent ? T : F)}, " + $"{nameof(Transfer)} = {transfer} " + diff --git a/Libplanet.Action.Tests/Common/DumbModernAction.cs b/Libplanet.Action.Tests/Common/DumbModernAction.cs index 9e3b2852688..31c42774d61 100644 --- a/Libplanet.Action.Tests/Common/DumbModernAction.cs +++ b/Libplanet.Action.Tests/Common/DumbModernAction.cs @@ -33,7 +33,6 @@ public DumbModernAction(IEnumerable validators) public DumbModernAction( Address targetAddress, string item, - bool recordRehearsal = false, bool recordRandom = false, bool idempotent = false, Tuple transfer = null) @@ -41,7 +40,6 @@ public DumbModernAction( Idempotent = idempotent; TargetAddress = targetAddress; Item = item; - RecordRehearsal = recordRehearsal; RecordRandom = recordRandom; Transfer = transfer; } @@ -52,14 +50,12 @@ public DumbModernAction( Address transferFrom, Address transferTo, BigInteger transferAmount, - bool recordRehearsal = false, bool recordRandom = false, bool idempotent = false ) : this( targetAddress, item, - recordRehearsal, recordRandom, idempotent, Tuple.Create(transferFrom, transferTo, transferAmount) @@ -71,17 +67,10 @@ public static AsyncLocal> ExecuteRecords { get; } = new AsyncLocal>(); - public static AsyncLocal> - RehearsalRecords - { get; } = - new AsyncLocal>(); - public Address TargetAddress { get; private set; } public string Item { get; private set; } - public bool RecordRehearsal { get; private set; } - public bool RecordRandom { get; private set; } public bool Idempotent { get; private set; } @@ -102,7 +91,6 @@ public IValue PlainValue { ["item"] = (Text)Item, ["target_address"] = new Binary(TargetAddress.ByteArray), - ["record_rehearsal"] = new Bencodex.Types.Boolean(RecordRehearsal), }); } @@ -138,17 +126,6 @@ public IValue PlainValue public IWorld Execute(IActionContext context) { - if (RehearsalRecords.Value is null) - { - RehearsalRecords.Value = ImmutableList<(Address, string)>.Empty; - } - - if (context.Rehearsal) - { - RehearsalRecords.Value = - RehearsalRecords.Value.Add((TargetAddress, Item)); - } - IWorld world = context.PreviousState; if (Item is null) { @@ -157,13 +134,10 @@ public IWorld Execute(IActionContext context) IAccount account = world.GetAccount(DumbModernAddress); string items = (Text?)account.GetState(TargetAddress); - string item = RecordRehearsal - ? $"{Item}:{context.Rehearsal}" - : Item; if (Idempotent) { - var splitItems = items is null ? new[] { item } : (items + "," + item).Split(','); + var splitItems = items is null ? new[] { Item } : (items + "," + Item).Split(','); items = string.Join( ",", splitItems.OrderBy(x => @@ -177,7 +151,7 @@ public IWorld Execute(IActionContext context) } else { - items = items is null ? item : $"{items},{item}"; + items = items is null ? Item : $"{items},{Item}"; } if (RecordRandom) @@ -188,7 +162,7 @@ public IWorld Execute(IActionContext context) ); } - if (Item.Equals("D") && !context.Rehearsal) + if (Item.Equals("D")) { Item = Item.ToUpperInvariant(); } @@ -223,7 +197,6 @@ public IWorld Execute(IActionContext context) { Action = this, NextState = account, - Rehearsal = context.Rehearsal, }); world = world.SetAccount(DumbModernAddress, account); @@ -239,7 +212,6 @@ public void LoadPlainValue(Dictionary plainValue) { Item = plainValue.GetValue("item"); TargetAddress = new Address(plainValue.GetValue("target_address")); - RecordRehearsal = plainValue.GetValue("record_rehearsal").Value; RecordRandom = plainValue.ContainsKey((IKey)(Text)"record_random") && plainValue["record_random"] is Boolean r && @@ -270,8 +242,7 @@ public bool Equals(DumbModernAction other) return !(other is null) && ( ReferenceEquals(this, other) || ( TargetAddress.Equals(other.TargetAddress) && - string.Equals(Item, other.Item) && - RecordRehearsal == other.RecordRehearsal + string.Equals(Item, other.Item) ) ); } @@ -291,7 +262,6 @@ public override int GetHashCode() int hashCode = TargetAddress.GetHashCode(); hashCode = (hashCode * 397) ^ (Item != null ? Item.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ RecordRehearsal.GetHashCode(); return hashCode; } } @@ -310,7 +280,6 @@ public override string ToString() return $"{nameof(DumbModernAction)} {{ " + $"{nameof(TargetAddress)} = {TargetAddress}, " + $"{nameof(Item)} = {Item ?? string.Empty}, " + - $"{nameof(RecordRehearsal)} = {(RecordRehearsal ? T : F)}, " + $"{nameof(RecordRandom)} = {(RecordRandom ? T : F)}, " + $"{nameof(Idempotent)} = {(Idempotent ? T : F)}, " + $"{nameof(Transfer)} = {transfer} " + diff --git a/Libplanet.Action.Tests/Common/ExecuteRecord.cs b/Libplanet.Action.Tests/Common/ExecuteRecord.cs index b7d3315d076..c7d50626cf8 100644 --- a/Libplanet.Action.Tests/Common/ExecuteRecord.cs +++ b/Libplanet.Action.Tests/Common/ExecuteRecord.cs @@ -8,10 +8,8 @@ public struct ExecuteRecord public IAccount NextState { get; set; } - public bool Rehearsal { get; set; } - public override string ToString() => - $"{nameof(ExecuteRecord)} {{ {nameof(Action)} = {Action}, {nameof(NextState)} = " + - $"{NextState}, {nameof(Rehearsal)} = {Rehearsal} }}"; + $"{nameof(ExecuteRecord)} {{ {nameof(Action)} = {Action}, " + + $"{nameof(NextState)} = {NextState} }}"; } } diff --git a/Libplanet.Action.Tests/Common/RandomAction.cs b/Libplanet.Action.Tests/Common/RandomAction.cs index f91d86b8406..1928fa19715 100644 --- a/Libplanet.Action.Tests/Common/RandomAction.cs +++ b/Libplanet.Action.Tests/Common/RandomAction.cs @@ -32,13 +32,6 @@ public IWorld Execute(IActionContext context) { IWorld states = context.PreviousState; IAccount legacyAccount = states.GetAccount(ReservedAddresses.LegacyAccount); - if (context.Rehearsal) - { - return states.SetAccount( - ReservedAddresses.LegacyAccount, - legacyAccount.SetState(Address, Null.Value)); - } - legacyAccount = legacyAccount.SetState(Address, (Integer)context.GetRandom().Next()); return states.SetAccount(ReservedAddresses.LegacyAccount, legacyAccount); } diff --git a/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs b/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs index 360b43d22db..2810cfaabf0 100644 --- a/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs +++ b/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs @@ -25,7 +25,7 @@ public SetStatesAtBlock( } public IValue PlainValue => Bencodex.Types.Dictionary.Empty - .Add("address", _address.ByteArray) + .Add("address", _address.Bencoded) .Add("value", _value) .Add("account_address", _accountAddress.ByteArray) .Add("block_index", _blockIndex); diff --git a/Libplanet.Action.Tests/Common/ThrowException.cs b/Libplanet.Action.Tests/Common/ThrowException.cs index c5daa541651..a72411a8f2f 100644 --- a/Libplanet.Action.Tests/Common/ThrowException.cs +++ b/Libplanet.Action.Tests/Common/ThrowException.cs @@ -10,8 +10,6 @@ public ThrowException() { } - public bool ThrowOnRehearsal { get; set; } - public bool ThrowOnExecution { get; set; } public bool Deterministic { get; set; } = true; @@ -19,7 +17,6 @@ public ThrowException() public IValue PlainValue => new Bencodex.Types.Dictionary(new Dictionary { - ["throw_on_rehearsal"] = ThrowOnRehearsal, ["throw_on_execution"] = ThrowOnExecution, ["deterministic"] = Deterministic, }); @@ -31,14 +28,13 @@ public void LoadPlainValue(IValue plainValue) public void LoadPlainValue(Dictionary plainValue) { - ThrowOnRehearsal = (Boolean)plainValue["throw_on_rehearsal"]; ThrowOnExecution = (Boolean)plainValue["throw_on_execution"]; Deterministic = (Boolean)plainValue["deterministic"]; } public IWorld Execute(IActionContext context) { - if (context.Rehearsal ? ThrowOnRehearsal : ThrowOnExecution) + if (ThrowOnExecution) { if (Deterministic) { diff --git a/Libplanet.Action.Tests/Loader/IndexedActionLoaderTest.cs b/Libplanet.Action.Tests/Loader/IndexedActionLoaderTest.cs index cace54b1acf..01f9263ae5e 100644 --- a/Libplanet.Action.Tests/Loader/IndexedActionLoaderTest.cs +++ b/Libplanet.Action.Tests/Loader/IndexedActionLoaderTest.cs @@ -39,15 +39,15 @@ public void LoadAction() var loader1 = new SingleActionLoader(typeof(DumbAction)); var loader2 = new SingleActionLoader(typeof(Attack)); var loader3 = new SingleActionLoader(typeof(RandomAction)); - var action1 = new DumbAction(new PrivateKey().PublicKey.ToAddress(), "foo"); + var action1 = new DumbAction(new PrivateKey().Address, "foo"); var action2 = new Attack(); action2.LoadPlainValue(Dictionary.Empty .Add("type_id", "attack") .Add("values", Dictionary.Empty .Add("weapon", "sword") .Add("target", "dummy") - .Add("target_address", new PrivateKey().PublicKey.ToAddress().ByteArray))); - var action3 = new RandomAction(new PrivateKey().PublicKey.ToAddress()); + .Add("target_address", new PrivateKey().Address.Bencoded))); + var action3 = new RandomAction(new PrivateKey().Address); var loader = new IndexedActionLoader( new List<(long, IActionLoader)> diff --git a/Libplanet.Action.Tests/State/KeyConvertersTest.cs b/Libplanet.Action.Tests/State/KeyConvertersTest.cs index 1c973c8a218..8e41273ba08 100644 --- a/Libplanet.Action.Tests/State/KeyConvertersTest.cs +++ b/Libplanet.Action.Tests/State/KeyConvertersTest.cs @@ -15,8 +15,8 @@ public KeyConvertersTest() [Fact] public void ToKeysSpec() { - var address = new PrivateKey().ToAddress(); - var currency = Currency.Uncapped("Foo", 2, new PrivateKey().ToAddress()); + var address = new PrivateKey().Address; + var currency = Currency.Uncapped("Foo", 2, new PrivateKey().Address); Assert.Equal( new KeyBytes(ByteUtil.Hex(address.ByteArray)), diff --git a/Libplanet.Action.Tests/State/WorldBaseStateTest.cs b/Libplanet.Action.Tests/State/WorldBaseStateTest.cs index b872fa6d30f..fb0c9cb2c1e 100644 --- a/Libplanet.Action.Tests/State/WorldBaseStateTest.cs +++ b/Libplanet.Action.Tests/State/WorldBaseStateTest.cs @@ -24,9 +24,6 @@ public WorldBaseStateTest() public void Constructor() { ITrie trie = new MerkleTrie(_kvStore); - trie = trie.SetMetadata(new TrieMetadata(BlockMetadata.CurrentProtocolVersion)); - Assert.Throws(() => new WorldBaseState(trie, _stateStore)); - trie = new MerkleTrie(_kvStore); var legacyBaseState = new WorldBaseState(trie, _stateStore); Assert.True(legacyBaseState.Legacy); trie = new MerkleTrie(_kvStore); diff --git a/Libplanet.Action.Tests/Sys/InitializeTest.cs b/Libplanet.Action.Tests/Sys/InitializeTest.cs index af8aefac8a7..dbdf6195bc9 100644 --- a/Libplanet.Action.Tests/Sys/InitializeTest.cs +++ b/Libplanet.Action.Tests/Sys/InitializeTest.cs @@ -53,8 +53,7 @@ public void Execute() blockProtocolVersion: Block.CurrentProtocolVersion, previousState: prevState, randomSeed: 123, - gasLimit: 0, - rehearsal: false); + gasLimit: 0); var initialize = new Initialize( states: _states, validatorSet: _validatorSet @@ -85,8 +84,7 @@ public void ExecuteInNonGenesis() blockProtocolVersion: Block.CurrentProtocolVersion, previousState: prevState, randomSeed: 123, - gasLimit: long.MaxValue, - rehearsal: false); + gasLimit: long.MaxValue); var initialize = new Initialize( states: _states, validatorSet: _validatorSet diff --git a/Libplanet.Action.Tests/TestUtils.cs b/Libplanet.Action.Tests/TestUtils.cs index 5f2e4be7a20..953c805bf29 100644 --- a/Libplanet.Action.Tests/TestUtils.cs +++ b/Libplanet.Action.Tests/TestUtils.cs @@ -62,16 +62,10 @@ public static void AssertAccountEqual(IAccount expected, IAccount actual) throw new XunitException("Accounts should be of type Account"); } - if (!ea.TotalUpdatedFungibleAssets.SequenceEqual(aa.TotalUpdatedFungibleAssets) || - !DictionaryEquals(ea.TotalUpdatedFungibles, aa.TotalUpdatedFungibles)) + if (!ea.Trie.Hash.Equals(aa.Trie.Hash)) { Assert.Equal(expected, actual); } - - if (!DictionaryEquals(ea.Delta.ToRawDelta(), aa.Delta.ToRawDelta())) - { - Assert.Equal(ea.Delta, aa.Delta); - } } public static bool DictionaryEquals( diff --git a/Libplanet.Action/ActionContext.cs b/Libplanet.Action/ActionContext.cs index f6cb0903329..cd06da2736a 100644 --- a/Libplanet.Action/ActionContext.cs +++ b/Libplanet.Action/ActionContext.cs @@ -21,15 +21,13 @@ public ActionContext( int blockProtocolVersion, IWorld previousState, int randomSeed, - long gasLimit, - bool rehearsal = false) + long gasLimit) { Signer = signer; TxId = txid; Miner = miner; BlockIndex = blockIndex; BlockProtocolVersion = blockProtocolVersion; - Rehearsal = rehearsal; PreviousState = previousState; RandomSeed = randomSeed; _gasLimit = gasLimit; @@ -52,9 +50,6 @@ public ActionContext( /// public int BlockProtocolVersion { get; } - /// - public bool Rehearsal { get; } - /// public IWorld PreviousState { get; } diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index 52c6b476b4a..7f9b6fe507c 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -10,11 +10,13 @@ using Libplanet.Action.Loader; using Libplanet.Action.State; using Libplanet.Common; +using Libplanet.Crypto; using Libplanet.Store; using Libplanet.Store.Trie; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Serilog; +using static Libplanet.Action.State.KeyConverters; namespace Libplanet.Action { @@ -59,23 +61,37 @@ private delegate (ITrie, int) StateCommitter( /// /// Creates a random seed. /// - /// The previous evaluation hash turned into bytes. + /// The pre-evaluation hash as bytes. /// - /// The hashed signature. - /// The signature. - /// The offset of the action. + /// The signature of the the target + /// belongs to. Must be empty if the target + /// is a block action. + /// The offset of the target . /// An integer of the random seed. /// + /// Thrown when + /// is empty. [Pure] public static int GenerateRandomSeed( byte[] preEvaluationHashBytes, - byte[] hashedSignature, byte[] signature, int actionOffset) { - return (preEvaluationHashBytes.Length > 0 - ? BitConverter.ToInt32(preEvaluationHashBytes, 0) : 0) - ^ (signature.Any() ? BitConverter.ToInt32(hashedSignature, 0) : 0) - actionOffset; + using (var sha1 = SHA1.Create()) + { + unchecked + { + return ((preEvaluationHashBytes.Length > 0 + ? BitConverter.ToInt32(preEvaluationHashBytes, 0) + : throw new ArgumentException( + $"Given {nameof(preEvaluationHashBytes)} cannot be empty", + nameof(preEvaluationHashBytes))) + ^ (signature.Any() + ? BitConverter.ToInt32(sha1.ComputeHash(signature), 0) + : 0)) + + actionOffset; + } + } } /// @@ -154,39 +170,18 @@ public IReadOnlyList Evaluate( /// The states immediately before /// being executed. /// Actions to evaluate. + /// An to use. /// An optional logger. /// An enumeration of s for each /// in . /// - /// - /// Each object has an unconsumed state. - /// - /// The returned enumeration has the following properties: - /// - /// - /// The first in the enumerated result, - /// if any, has with - /// that is a - /// "superset" of 's - /// (possibly except for - /// ). - /// - /// - /// Each in the enumerated result - /// has with - /// that is a "superset" - /// of the previous one, if any (possibly except for - /// ). - /// - /// - /// - /// [Pure] internal static IEnumerable EvaluateActions( IPreEvaluationBlockHeader blockHeader, ITransaction? tx, IWorld previousState, IImmutableList actions, + IStateStore stateStore, ILogger? logger = null) { IActionContext CreateActionContext( @@ -207,15 +202,9 @@ IActionContext CreateActionContext( long gasLimit = tx?.GasLimit ?? long.MaxValue; - byte[] signature = tx?.Signature ?? Array.Empty(); - byte[] hashedSignature; - using (var hasher = SHA1.Create()) - { - hashedSignature = hasher.ComputeHash(signature); - } - byte[] preEvaluationHashBytes = blockHeader.PreEvaluationHash.ToByteArray(); - int seed = GenerateRandomSeed(preEvaluationHashBytes, hashedSignature, signature, 0); + byte[] signature = tx?.Signature ?? Array.Empty(); + int seed = GenerateRandomSeed(preEvaluationHashBytes, signature, 0); IWorld state = previousState; foreach (IAction action in actions) @@ -226,6 +215,7 @@ IActionContext CreateActionContext( tx, context, action, + stateStore, logger); yield return result.Evaluation; @@ -245,8 +235,16 @@ internal static (ActionEvaluation Evaluation, long NextGasLimit) EvaluateAction( ITransaction? tx, IActionContext context, IAction action, + IStateStore stateStore, ILogger? logger = null) { + if (!context.PreviousState.Trie.Recorded) + { + throw new InvalidOperationException( + $"Given {nameof(context)} must have its previous state's " + + $"{nameof(ITrie)} recorded."); + } + IActionContext inputContext = context; IWorld state = inputContext.PreviousState; Exception? exc = null; @@ -337,6 +335,21 @@ IActionContext CreateActionContext(IWorld newPrevState) state = feeCollector.Refund(state); state = feeCollector.Reward(state); + if (state.Legacy) + { + state = CommitLegacyWorld(state, stateStore); + } + else + { + state = CommitWorld(state, stateStore); + } + + if (!state.Trie.Recorded) + { + throw new InvalidOperationException( + $"Failed to record {nameof(IAccount)}'s {nameof(ITrie)}."); + } + return ( new ActionEvaluation( action: action, @@ -406,7 +419,6 @@ internal IEnumerable EvaluateBlock( { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); - delta = World.Flush(delta); IEnumerable evaluations = EvaluateTx( blockHeader: block, @@ -453,6 +465,7 @@ internal IEnumerable EvaluateTx( tx: tx, previousState: previousState, actions: actions, + stateStore: _stateStore, logger: _logger); } @@ -489,7 +502,9 @@ internal ActionEvaluation EvaluatePolicyBlockAction( blockHeader: blockHeader, tx: null, previousState: previousState, - actions: new[] { policyBlockAction }.ToImmutableList()).Single(); + actions: new[] { policyBlockAction }.ToImmutableList(), + stateStore: _stateStore, + logger: _logger).Single(); } internal IWorld PrepareInitialDelta(HashDigest? stateRootHash) @@ -507,32 +522,12 @@ internal IReadOnlyList Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); - ITrie worldTrie = _stateStore.GetStateRoot(baseStateRootHash); - - IWorldState nextWorld; - ITrie nextWorldTrie; - + ITrie trie = _stateStore.GetStateRoot(baseStateRootHash); var committedEvaluations = new List(); - int setCount = 0; - int subCount; foreach (var evaluation in evaluations) { - nextWorld = evaluation.OutputState; - StateCommitter committer; - - if (nextWorld.Legacy) - { - committer = CommitLegacyState; - } - else - { - committer = CommitModernState; - } - - (nextWorldTrie, subCount) = committer(worldTrie, evaluation); - setCount += subCount; - +#pragma warning disable SA1118 var committedEvaluation = new CommittedActionEvaluation( action: evaluation.Action, inputContext: new CommittedActionContext( @@ -541,137 +536,62 @@ internal IReadOnlyList miner: evaluation.InputContext.Miner, blockIndex: evaluation.InputContext.BlockIndex, blockProtocolVersion: evaluation.InputContext.BlockProtocolVersion, - rehearsal: evaluation.InputContext.Rehearsal, - previousState: worldTrie.Hash, + previousState: evaluation.InputContext.PreviousState.Trie.Recorded + ? evaluation.InputContext.PreviousState.Trie.Hash + : throw new ArgumentException("Trie is not recorded"), randomSeed: evaluation.InputContext.RandomSeed, blockAction: evaluation.InputContext.BlockAction), - outputState: nextWorldTrie.Hash, + outputState: evaluation.OutputState.Trie.Recorded + ? evaluation.OutputState.Trie.Hash + : throw new ArgumentException("Trie is not recorded"), exception: evaluation.Exception); committedEvaluations.Add(committedEvaluation); +#pragma warning restore SA1118 - worldTrie = nextWorldTrie; + trie = evaluation.OutputState.Trie; } - _logger - .ForContext("Tag", "Metric") - .ForContext("Subtag", "StateUpdateDuration") - .Information( - "Took {DurationMs} ms to update the states with {Count} key changes " + - "and resulting in state root hash {StateRootHash} for " + - "block #{BlockIndex} pre-evaluation hash {PreEvaluationHash}", - stopwatch.ElapsedMilliseconds, - setCount, - worldTrie.Hash, - block.Index, - block.PreEvaluationHash); - return committedEvaluations; } - internal (ITrie, int) CommitLegacyState( - ITrie worldTrie, IActionEvaluation evaluation) + internal IWorld MigrateLegacyStates(IWorld prevWorld, int version) { - Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); - - var totalDelta = evaluation.OutputState.GetAccount( - ReservedAddresses.LegacyAccount).Delta.ToRawDelta(); - - int setCount = 0; - - foreach (var kv in totalDelta) - { - worldTrie = worldTrie.Set(kv.Key, kv.Value); - setCount++; - } - + var worldTrie = _stateStore.GetStateRoot(null).Set( + ToStateKey(ReservedAddresses.LegacyAccount), + new Binary(prevWorld.Trie.Hash.ByteArray)); + worldTrie = worldTrie.SetMetadata(new TrieMetadata(version)); worldTrie = _stateStore.Commit(worldTrie); - - _logger - .ForContext("Tag", "Metric") - .ForContext("Subtag", "CommitDuration") - .Information( - "Took {DurationMs} ms to commit the trie with {KeyCount} key changes " + - "and resulting in state root hash {StateRootHash} ", - stopwatch.ElapsedMilliseconds, - setCount, - worldTrie.Hash); - - return (worldTrie, setCount); + var world = new World( + new WorldBaseState(worldTrie, _stateStore), + ImmutableDictionary.Empty, + prevWorld.BlockDelta); + return world; } - internal (ITrie, int) CommitModernState( - ITrie worldTrie, IActionEvaluation evaluation) + private static IWorld CommitLegacyWorld(IWorld prevWorld, IStateStore stateStore) { - Stopwatch stopwatch = new Stopwatch(); - - stopwatch.Start(); - - IImmutableDictionary> - accountSubStateDelta = evaluation.OutputState.Delta.ToRawDelta(); - - IImmutableDictionary?> - accountSubStateRoot = GetAccountSubStateRootHashes(worldTrie, evaluation); - - int setCount = 0; - - foreach (var kv in accountSubStateDelta) - { - ITrie accountTrie = _stateStore.GetStateRoot(accountSubStateRoot[kv.Key]); - var accountDelta = kv.Value; - - foreach (KeyValuePair pair in accountDelta) - { - accountTrie = accountTrie.Set(pair.Key, pair.Value); - setCount++; - } - - accountTrie = _stateStore.Commit(accountTrie); - worldTrie = worldTrie.Set(kv.Key, new Binary(accountTrie.Hash.ByteArray)); - } - - worldTrie = _stateStore.Commit(worldTrie); - - _logger - .ForContext("Tag", "Metric") - .ForContext("Subtag", "CommitDuration") - .Information( - "Took {DurationMs} ms to commit the trie with {KeyCount} key changes " + - "and resulting in state root hash {StateRootHash} ", - stopwatch.ElapsedMilliseconds, - setCount, - worldTrie); - - return (worldTrie, setCount); + return new World( + new WorldBaseState( + stateStore.Commit(prevWorld.GetAccount(ReservedAddresses.LegacyAccount).Trie), + stateStore), + ImmutableDictionary.Empty, + prevWorld.BlockDelta); } - internal IImmutableDictionary?> - GetAccountSubStateRootHashes( - ITrie worldTrie, IActionEvaluation evaluation) + private static IWorld CommitWorld(IWorld world, IStateStore stateStore) { - var result = new Dictionary?>(); - foreach (var updatedAddress in evaluation.OutputState.Delta.UpdatedAddresses) + var worldTrie = world.Trie; + foreach (var account in world.UncommittedDelta) { - var key = KeyConverters.ToStateKey(updatedAddress); - HashDigest? hash = worldTrie.Get(key) is { } value - ? new HashDigest(value) - : (HashDigest?)null; - - result.Add(key, hash); + var accountTrie = stateStore.Commit(account.Value.Trie); + worldTrie = worldTrie.Set( + ToStateKey(account.Key), new Binary(accountTrie.Hash.ByteArray)); } - return result.ToImmutableDictionary(); - } - - internal IWorld MigrateLegacyStates(IWorld prevWorld, int version) - { - var worldTrie = _stateStore.GetStateRoot(null).Set( - KeyConverters.ToStateKey(ReservedAddresses.LegacyAccount), - new Binary(prevWorld.Trie.Hash.ByteArray)); - worldTrie = worldTrie.SetMetadata(new TrieMetadata(version)); - worldTrie = _stateStore.Commit(worldTrie); - var world = new World(new WorldBaseState(worldTrie, _stateStore)); - return world; + return new World( + new WorldBaseState(stateStore.Commit(worldTrie), stateStore), + ImmutableDictionary.Empty, + world.BlockDelta); } [Pure] diff --git a/Libplanet.Action/CommittedActionContext.cs b/Libplanet.Action/CommittedActionContext.cs index ea819d0790e..f3e34ea18af 100644 --- a/Libplanet.Action/CommittedActionContext.cs +++ b/Libplanet.Action/CommittedActionContext.cs @@ -15,7 +15,6 @@ public CommittedActionContext(IActionContext context) miner: context.Miner, blockIndex: context.BlockIndex, blockProtocolVersion: context.BlockProtocolVersion, - rehearsal: context.Rehearsal, previousState: context.PreviousState.Trie.Hash, randomSeed: context.RandomSeed, blockAction: context.BlockAction) @@ -28,7 +27,6 @@ public CommittedActionContext( Address miner, long blockIndex, int blockProtocolVersion, - bool rehearsal, HashDigest previousState, int randomSeed, bool blockAction) @@ -38,7 +36,6 @@ public CommittedActionContext( Miner = miner; BlockIndex = blockIndex; BlockProtocolVersion = blockProtocolVersion; - Rehearsal = rehearsal; PreviousState = previousState; RandomSeed = randomSeed; BlockAction = blockAction; @@ -64,10 +61,6 @@ public CommittedActionContext( [Pure] public int BlockProtocolVersion { get; } - /// - [Pure] - public bool Rehearsal { get; } - /// [Pure] public HashDigest PreviousState { get; } diff --git a/Libplanet.Action/IActionContext.cs b/Libplanet.Action/IActionContext.cs index 673918e2a02..fb73962fccf 100644 --- a/Libplanet.Action/IActionContext.cs +++ b/Libplanet.Action/IActionContext.cs @@ -51,14 +51,6 @@ public interface IActionContext [Pure] int BlockProtocolVersion { get; } - /// - /// Whether an is being executed during - /// “rehearsal mode”, that there is nothing - /// in . - /// - [Pure] - bool Rehearsal { get; } - /// /// A null delta of states, which means it represents the states /// before executes. diff --git a/Libplanet.Action/ICommittedActionContext.cs b/Libplanet.Action/ICommittedActionContext.cs index d4e2440c92d..ddc66a83e31 100644 --- a/Libplanet.Action/ICommittedActionContext.cs +++ b/Libplanet.Action/ICommittedActionContext.cs @@ -51,14 +51,6 @@ public interface ICommittedActionContext [Pure] int BlockProtocolVersion { get; } - /// - /// Whether an is being executed during - /// “rehearsal mode”, that there is nothing - /// in . - /// - [Pure] - bool Rehearsal { get; } - /// /// The state root hash of the previous state. /// diff --git a/Libplanet.Action/State/Account.cs b/Libplanet.Action/State/Account.cs index f2524d7425b..6c7dbbc47c6 100644 --- a/Libplanet.Action/State/Account.cs +++ b/Libplanet.Action/State/Account.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.Contracts; -using System.Linq; using System.Numerics; using Bencodex.Types; using Libplanet.Crypto; using Libplanet.Store.Trie; using Libplanet.Types.Assets; using Libplanet.Types.Consensus; +using static Libplanet.Action.State.KeyConverters; namespace Libplanet.Action.State { @@ -18,65 +18,35 @@ namespace Libplanet.Action.State [Pure] public class Account : IAccount { - private readonly IAccountState _baseState; + private readonly IAccountState _state; - public Account(IAccountState baseState) - : this(baseState, new AccountDelta()) + public Account(IAccountState state) + : this(state, ImmutableHashSet<(Address, Currency)>.Empty) { } - public Account(IAccountState baseState, IAccountDelta delta) - : this(baseState, delta, ImmutableDictionary<(Address, Currency), BigInteger>.Empty) + public Account( + IAccountState state, + IImmutableSet<(Address, Currency)> totalUpdatedFungibleAssets) { + _state = state; + TotalUpdatedFungibleAssets = totalUpdatedFungibleAssets; } - private Account( - IAccountState baseState, - IAccountDelta delta, - IImmutableDictionary<(Address, Currency), BigInteger> totalUpdatedFungibles) - { - _baseState = baseState; - Delta = delta; - TotalUpdatedFungibles = totalUpdatedFungibles; - } - - /// - public ITrie Trie => _baseState.Trie; - /// - public IAccountDelta Delta { get; } + public ITrie Trie => _state.Trie; /// - public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets => - TotalUpdatedFungibles.Keys.ToImmutableHashSet(); - - public IImmutableDictionary<(Address, Currency), BigInteger> TotalUpdatedFungibles - { get; } + public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets { get; } /// [Pure] - public IValue? GetState(Address address) - { - AccountMetrics.GetStateTimer.Value?.Start(); - AccountMetrics.GetStateCount.Value += 1; - IValue? value = Delta.States.TryGetValue(address, out IValue? updatedValue) - ? updatedValue - : _baseState.GetState(address); - AccountMetrics.GetStateTimer.Value?.Stop(); - return value; - } + public IValue? GetState(Address address) => _state.GetState(address); /// [Pure] - public IReadOnlyList GetStates(IReadOnlyList
addresses) - { - AccountMetrics.GetStateTimer.Value?.Start(); - int length = addresses.Count; - AccountMetrics.GetStateCount.Value += length; - List values = addresses.Select(address => GetState(address)).ToList(); - AccountMetrics.GetStateTimer.Value?.Stop(); - return values; - } + public IReadOnlyList GetStates(IReadOnlyList
addresses) => + _state.GetStates(addresses); /// [Pure] @@ -85,29 +55,16 @@ private Account( /// [Pure] public FungibleAssetValue GetBalance(Address address, Currency currency) => - Delta.Fungibles.TryGetValue((address, currency), out BigInteger balance) - ? FungibleAssetValue.FromRawValue(currency, balance) - : _baseState.GetBalance(address, currency); + _state.GetBalance(address, currency); /// [Pure] - public FungibleAssetValue GetTotalSupply(Currency currency) - { - if (!currency.TotalSupplyTrackable) - { - throw TotalSupplyNotTrackableException.WithDefaultMessage(currency); - } - - // Return dirty state if it exists. - return Delta.TotalSupplies.TryGetValue(currency, out BigInteger totalSupplyValue) - ? FungibleAssetValue.FromRawValue(currency, totalSupplyValue) - : _baseState.GetTotalSupply(currency); - } + public FungibleAssetValue GetTotalSupply(Currency currency) => + _state.GetTotalSupply(currency); /// [Pure] - public ValidatorSet GetValidatorSet() => - Delta.ValidatorSet ?? _baseState.GetValidatorSet(); + public ValidatorSet GetValidatorSet() => _state.GetValidatorSet(); /// [Pure] @@ -222,38 +179,14 @@ public IAccount BurnAsset( public IAccount SetValidator(Validator validator) => UpdateValidatorSet(GetValidatorSet().Update(validator)); - /// - /// Creates a null account while inheriting s - /// total updated fungibles. - /// - /// The previous to use. - /// A null account that is of the same type as . - /// - /// Thrown if given - /// is not . - /// - /// - /// This inherits 's - /// . - /// - internal static IAccount Flush(IAccount account) => - account is Account impl - ? new Account(impl, new AccountDelta(), impl.TotalUpdatedFungibles) - : throw new ArgumentException( - $"Unknown type for {nameof(account)}: {account.GetType()}"); - [Pure] private Account UpdateState( Address address, IValue value) => new Account( - _baseState, - new AccountDelta( - Delta.States.SetItem(address, value), - Delta.Fungibles, - Delta.TotalSupplies, - Delta.ValidatorSet), - TotalUpdatedFungibles); + new AccountState( + Trie.Set(ToStateKey(address), value)), + TotalUpdatedFungibleAssets); [Pure] private Account UpdateFungibleAssets( @@ -262,32 +195,22 @@ private Account UpdateFungibleAssets( BigInteger amount, BigInteger? supplyAmount = null) => supplyAmount is { } sa ? new Account( - _baseState, - new AccountDelta( - Delta.States, - Delta.Fungibles.SetItem((address, currency), amount), - Delta.TotalSupplies.SetItem(currency, sa), - Delta.ValidatorSet), - TotalUpdatedFungibles.SetItem((address, currency), amount)) + new AccountState( + Trie + .Set(ToFungibleAssetKey(address, currency), new Integer(amount)) + .Set(ToTotalSupplyKey(currency), new Integer(sa))), + TotalUpdatedFungibleAssets.Add((address, currency))) : new Account( - _baseState, - new AccountDelta( - Delta.States, - Delta.Fungibles.SetItem((address, currency), amount), - Delta.TotalSupplies, - Delta.ValidatorSet), - TotalUpdatedFungibles.SetItem((address, currency), amount)); + new AccountState( + Trie.Set(ToFungibleAssetKey(address, currency), new Integer(amount))), + TotalUpdatedFungibleAssets.Add((address, currency))); [Pure] private Account UpdateValidatorSet(ValidatorSet validatorSet) => new Account( - _baseState, - new AccountDelta( - Delta.States, - Delta.Fungibles, - Delta.TotalSupplies, - validatorSet), - TotalUpdatedFungibles); + new AccountState( + Trie.Set(ValidatorSetKey, validatorSet.Bencoded)), + TotalUpdatedFungibleAssets); [Pure] private IAccount TransferAssetV0( diff --git a/Libplanet.Action/State/AccountDelta.cs b/Libplanet.Action/State/AccountDelta.cs deleted file mode 100644 index f8c422d7679..00000000000 --- a/Libplanet.Action/State/AccountDelta.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Immutable; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Libplanet.Types.Consensus; - -namespace Libplanet.Action.State -{ - public class AccountDelta : IAccountDelta - { - public AccountDelta() - { - States = ImmutableDictionary.Empty; - Fungibles = ImmutableDictionary<(Address, Currency), BigInteger>.Empty; - TotalSupplies = ImmutableDictionary.Empty; - ValidatorSet = null; - } - - internal AccountDelta( - IImmutableDictionary statesDelta, - IImmutableDictionary<(Address, Currency), BigInteger> fungiblesDelta, - IImmutableDictionary totalSuppliesDelta, - ValidatorSet? validatorSetDelta) - { - States = statesDelta; - Fungibles = fungiblesDelta; - TotalSupplies = totalSuppliesDelta; - ValidatorSet = validatorSetDelta; - } - - /// - public IImmutableSet
UpdatedAddresses => - StateUpdatedAddresses.Union(FungibleUpdatedAddresses); - - /// - public IImmutableSet
StateUpdatedAddresses => - States.Keys.ToImmutableHashSet(); - - /// - public IImmutableDictionary States { get; } - - /// - public IImmutableSet
FungibleUpdatedAddresses => - Fungibles.Keys.Select(pair => pair.Item1).ToImmutableHashSet(); - - /// - public IImmutableSet<(Address, Currency)> UpdatedFungibleAssets => - Fungibles.Keys.ToImmutableHashSet(); - - /// - public IImmutableDictionary<(Address, Currency), BigInteger> Fungibles { get; } - - /// - public IImmutableSet UpdatedTotalSupplyCurrencies => - TotalSupplies.Keys.ToImmutableHashSet(); - - /// - public IImmutableDictionary TotalSupplies { get; } - - /// - public ValidatorSet? ValidatorSet { get; } - } -} diff --git a/Libplanet.Action/State/AccountDeltaExtensions.cs b/Libplanet.Action/State/AccountDeltaExtensions.cs deleted file mode 100644 index 0829cfaa068..00000000000 --- a/Libplanet.Action/State/AccountDeltaExtensions.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Store; -using Libplanet.Store.Trie; -using Libplanet.Types.Assets; -using Libplanet.Types.Consensus; -using static Libplanet.Action.State.KeyConverters; - -namespace Libplanet.Action.State -{ - public static class AccountDeltaExtensions - { - /// - /// Aggregates a list of s in order. - /// - /// The list of s to aggregate. - /// The aggregate of as an - /// . - /// - /// - /// As aggregation is done by partially overwriting previous values, - /// the order in which is important. - /// - public static IAccountDelta OrderedSum(this IReadOnlyList deltas) - { - IImmutableDictionary states = deltas.Aggregate( - ImmutableDictionary.Empty, - (prev, next) => prev.SetItems(next.States)); - IImmutableDictionary<(Address, Currency), BigInteger> fungibles = deltas.Aggregate( - ImmutableDictionary<(Address, Currency), BigInteger>.Empty, - (prev, next) => prev.SetItems(next.Fungibles)); - IImmutableDictionary totalSupplies = deltas.Aggregate( - ImmutableDictionary.Empty, - (prev, next) => prev.SetItems(next.TotalSupplies)); - ValidatorSet? validatorSet = deltas.Aggregate( - (ValidatorSet?)null, - (prev, next) => next.ValidatorSet is { } set ? set : prev); - return new AccountDelta( - states, fungibles, totalSupplies, validatorSet); - } - - /// - /// Gets a raw dictionary representation of that gets - /// actually written to an . - /// - /// The to convert. - /// A raw dictionary representation of to write - /// to an . - public static IImmutableDictionary ToRawDelta(this IAccountDelta delta) - { - var rawStates = delta.States.Select( - kv => new KeyValuePair( - ToStateKey(kv.Key), kv.Value)); - var rawFungibles = delta.Fungibles.Select( - kv => new KeyValuePair( - ToFungibleAssetKey(kv.Key), new Integer(kv.Value))); - var rawTotalSupplies = delta.TotalSupplies.Select( - kv => new KeyValuePair( - ToTotalSupplyKey(kv.Key), new Integer(kv.Value))); - - var rawDelta = ImmutableDictionary.Empty; - rawDelta = rawDelta.SetItems(rawStates.Concat(rawFungibles).Concat(rawTotalSupplies)); - return delta.ValidatorSet is { } validatorSet - ? rawDelta.SetItem(ValidatorSetKey, validatorSet.Bencoded) - : rawDelta; - } - } -} diff --git a/Libplanet.Action/State/AccountExtensions.cs b/Libplanet.Action/State/AccountExtensions.cs deleted file mode 100644 index 9d9a180829a..00000000000 --- a/Libplanet.Action/State/AccountExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using static Libplanet.Action.State.KeyConverters; - -namespace Libplanet.Action.State -{ - public static class AccountExtensions - { - public static IImmutableDictionary GetUpdatedStates( - this IAccount account) => account.Delta.States; - - internal static IImmutableDictionary<(Address, Currency), FungibleAssetValue> - GetUpdatedBalances(this IAccount account) => - account.Delta.Fungibles.ToImmutableDictionary( - kv => kv.Key, - kv => FungibleAssetValue.FromRawValue(kv.Key.Item2, kv.Value)); - - internal static IImmutableDictionary - GetUpdatedTotalSupplies(this IAccount account) => - account.Delta.TotalSupplies.ToImmutableDictionary( - kv => kv.Key, - kv => FungibleAssetValue.FromRawValue(kv.Key, kv.Value)); - } -} diff --git a/Libplanet.Action/State/AccountBaseState.cs b/Libplanet.Action/State/AccountState.cs similarity index 58% rename from Libplanet.Action/State/AccountBaseState.cs rename to Libplanet.Action/State/AccountState.cs index 94c765d7817..9ea1da56130 100644 --- a/Libplanet.Action/State/AccountBaseState.cs +++ b/Libplanet.Action/State/AccountState.cs @@ -13,34 +13,20 @@ namespace Libplanet.Action.State /// /// A default implementation of interface. /// - public class AccountBaseState : IAccountState + public class AccountState : IAccountState { private ITrie _trie; - private AccountStateCache _cache; - public AccountBaseState(ITrie trie) + public AccountState(ITrie trie) { _trie = trie; - _cache = new AccountStateCache(); } /// public ITrie Trie => _trie; /// - public IValue? GetState(Address address) - { - if (_cache.TryGetValue(address, out IValue? cachedValue)) - { - return cachedValue; - } - else - { - IValue? fetched = Trie.Get(ToStateKey(address)); - _cache.AddOrUpdate(address, fetched); - return fetched; - } - } + public IValue? GetState(Address address) => Trie.Get(ToStateKey(address)); /// public IReadOnlyList GetStates(IReadOnlyList
addresses) => @@ -49,9 +35,8 @@ public AccountBaseState(ITrie trie) /// public FungibleAssetValue GetBalance(Address address, Currency currency) { - KeyBytes[] keys = new[] { ToFungibleAssetKey(address, currency) }; - IReadOnlyList rawValues = Trie.Get(keys); - return rawValues.Count > 0 && rawValues[0] is Bencodex.Types.Integer i + IValue? value = Trie.Get(ToFungibleAssetKey(address, currency)); + return value is Integer i ? FungibleAssetValue.FromRawValue(currency, i) : currency * 0; } @@ -64,9 +49,8 @@ public FungibleAssetValue GetTotalSupply(Currency currency) throw TotalSupplyNotTrackableException.WithDefaultMessage(currency); } - KeyBytes[] keys = new[] { ToTotalSupplyKey(currency) }; - IReadOnlyList rawValues = Trie.Get(keys); - return rawValues.Count > 0 && rawValues[0] is Bencodex.Types.Integer i + IValue? value = Trie.Get(ToTotalSupplyKey(currency)); + return value is Integer i ? FungibleAssetValue.FromRawValue(currency, i) : currency * 0; } @@ -74,9 +58,8 @@ public FungibleAssetValue GetTotalSupply(Currency currency) /// public ValidatorSet GetValidatorSet() { - KeyBytes[] keys = new[] { ValidatorSetKey }; - IReadOnlyList rawValues = Trie.Get(keys); - return rawValues.Count > 0 && rawValues[0] is List list + IValue? value = Trie.Get(ValidatorSetKey); + return value is List list ? new ValidatorSet(list) : new ValidatorSet(); } diff --git a/Libplanet.Action/State/AccountStateCache.cs b/Libplanet.Action/State/AccountStateCache.cs deleted file mode 100644 index ad8f9ce1759..00000000000 --- a/Libplanet.Action/State/AccountStateCache.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using Bencodex.Types; -using Libplanet.Crypto; -using LruCacheNet; -using Serilog; - -namespace Libplanet.Action.State -{ - internal class AccountStateCache - { - public const int CacheSize = 1_000; - public const int ReportPeriod = 1_000; - - private LruCache _cache; - private int _getAttempts; - private int _getSuccesses; - - public AccountStateCache() - { - _cache = new LruCache(CacheSize); - _getAttempts = 0; - _getSuccesses = 0; - } - - public bool TryGetValue(Address address, out IValue? value) - { - bool result; - int getAttempts = Interlocked.Increment(ref _getAttempts); - if (_cache.TryGetValue(address, out value)) - { - Interlocked.Increment(ref _getSuccesses); - result = true; - } - else - { - value = null; - result = false; - } - - if (getAttempts == ReportPeriod) - { - // NOTE: This is only an estimation due to concurrency (or lack there of). - Log - .ForContext("Source", nameof(AccountStateCache)) - .ForContext("Tag", "Metric") - .ForContext("Subtag", "StatesCacheReport") - .Debug( - "Successfully fetched {SuccessCount} cached values out of last " + - "{AttemptCount} attempts", - _getSuccesses, - getAttempts); - _getAttempts = 0; - _getSuccesses = 0; - } - - return result; - } - - public void AddOrUpdate(Address address, IValue? value) - { - if (value is { } v) - { - _cache.AddOrUpdate(address, v); - } - } - - public void AddOrUpdate(IReadOnlyList<(Address, IValue?)> bulk) - { - foreach ((Address a, IValue? v) in bulk) - { - AddOrUpdate(a, v); - } - } - } -} diff --git a/Libplanet.Action/State/IAccount.cs b/Libplanet.Action/State/IAccount.cs index 7ffa24284fa..4103f2399ea 100644 --- a/Libplanet.Action/State/IAccount.cs +++ b/Libplanet.Action/State/IAccount.cs @@ -40,13 +40,6 @@ namespace Libplanet.Action.State /// public interface IAccount : IAccountState { - /// - /// The representing the delta part of - /// this . - /// - [Pure] - IAccountDelta Delta { get; } - /// /// A set of and pairs where /// each pair has its asoociated changed diff --git a/Libplanet.Action/State/IAccountDelta.cs b/Libplanet.Action/State/IAccountDelta.cs deleted file mode 100644 index b66b4012e79..00000000000 --- a/Libplanet.Action/State/IAccountDelta.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Collections.Immutable; -using System.Diagnostics.Contracts; -using System.Numerics; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Libplanet.Types.Consensus; - -namespace Libplanet.Action.State -{ - public interface IAccountDelta - { - /// - /// - /// A set of es where each has - /// either its state changed or its changed. - /// - /// - /// This is equivalent to the union of and - /// . - /// - /// - /// - /// - [Pure] - IImmutableSet
UpdatedAddresses { get; } - - /// - /// - /// A set of es where each has - /// its state changed. - /// - /// - /// This is equivalent to the set of keys in . - /// - /// - /// - [Pure] - IImmutableSet
StateUpdatedAddresses { get; } - - /// - /// A dictionary representing changed states for each . - /// - [Pure] - IImmutableDictionary States { get; } - - /// - /// - /// A set of es where each has - /// its changed. - /// - /// - /// This is equivalent to the set of es that appear in - /// , and in turn those that appear in - /// . - /// - /// - /// - /// - [Pure] - IImmutableSet
FungibleUpdatedAddresses { get; } - - /// - /// - /// A set of and pairs where - /// each pair has its asoociated changed. - /// - /// - /// For example, if A transfers 10 FOO to B and B transfers 20 BAR to C, - /// become likes - /// { (A, FOO), (B, FOO), (B, BAR), (C, BAR) }. - /// - /// - /// Furthermore, this represents any pair that has been "touched", i.e., - /// if A transfers 10 FOO to B and B transfers 10 FOO back to A, - /// this becomes { (A, FOO), (B, BAR) } not an empty set. - /// - /// - /// This is equivalent to the keys of . - /// - /// - /// - /// - [Pure] - IImmutableSet<(Address, Currency)> UpdatedFungibleAssets { get; } - - /// - /// A dictionary representing the changed s for each - /// and pair. - /// - /// - [Pure] - IImmutableDictionary<(Address, Currency), BigInteger> Fungibles { get; } - - /// - /// - /// The set of Currencies with their total supplies updated. - /// - /// - /// This is equivalent to the set of keys in . - /// - /// - /// - [Pure] - IImmutableSet UpdatedTotalSupplyCurrencies { get; } - - /// - /// A dictionary representing the changed total supply for each . - /// - /// - [Pure] - IImmutableDictionary TotalSupplies { get; } - - /// - /// A representing a change in - /// , if not . - /// - ValidatorSet? ValidatorSet { get; } - } -} diff --git a/Libplanet.Action/State/IWorld.cs b/Libplanet.Action/State/IWorld.cs index 7cbd17b131f..bc3b27b64f2 100644 --- a/Libplanet.Action/State/IWorld.cs +++ b/Libplanet.Action/State/IWorld.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.Contracts; using Libplanet.Crypto; @@ -35,11 +36,18 @@ namespace Libplanet.Action.State public interface IWorld : IWorldState { /// - /// The representing the delta part of - /// this . + /// A dictionary representing cumulative changed account states for each + /// between blocks. /// [Pure] - IWorldDelta Delta { get; } + IImmutableDictionary BlockDelta { get; } + + /// + /// A dictionary representing uncommitted changed account states for each + /// . + /// + [Pure] + IImmutableDictionary UncommittedDelta { get; } /// /// Gets a new instance that the world state of the given diff --git a/Libplanet.Action/State/IWorldDelta.cs b/Libplanet.Action/State/IWorldDelta.cs deleted file mode 100644 index 8bcaf4a9672..00000000000 --- a/Libplanet.Action/State/IWorldDelta.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Immutable; -using System.Diagnostics.Contracts; -using Libplanet.Crypto; - -namespace Libplanet.Action.State -{ - public interface IWorldDelta - { - /// - /// A dictionary representing changed account states for each . - /// - [Pure] - IImmutableDictionary Accounts { get; } - - /// - /// - /// A set of es where each has - /// its account changed. - /// - /// - [Pure] - IImmutableSet
UpdatedAddresses { get; } - } -} diff --git a/Libplanet.Action/State/World.cs b/Libplanet.Action/State/World.cs index 220a6938887..d8f7a30a766 100644 --- a/Libplanet.Action/State/World.cs +++ b/Libplanet.Action/State/World.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.Contracts; using Libplanet.Crypto; using Libplanet.Store.Trie; @@ -15,20 +14,34 @@ public class World : IWorld private readonly IWorldState _baseState; public World(IWorldState baseState) - : this(baseState, new WorldDelta()) + : this(baseState, ImmutableDictionary.Empty) { } - public World(IWorldState baseState, IWorldDelta delta) + public World( + IWorldState baseState, + IImmutableDictionary uncommittedDelta) + : this( + baseState, + uncommittedDelta, + ImmutableDictionary.Empty) + { + } + + public World( + IWorldState baseState, + IImmutableDictionary uncommittedDelta, + IImmutableDictionary blockDelta) { _baseState = baseState; - Delta = delta; + UncommittedDelta = uncommittedDelta; + BlockDelta = blockDelta; Legacy = baseState.Legacy; } - /// - [Pure] - public IWorldDelta Delta { get; private set; } + public IImmutableDictionary BlockDelta { get; } + + public IImmutableDictionary UncommittedDelta { get; } /// [Pure] @@ -38,33 +51,11 @@ public World(IWorldState baseState, IWorldDelta delta) [Pure] public bool Legacy { get; private set; } - /// - /// Creates a null worlds from given . - /// - /// The previous to use. - /// A null world that is of the same type as . - /// - /// Thrown if given - /// is not . - /// - public static IWorld Flush(IWorld world) - { - foreach (KeyValuePair kv in world.Delta.Accounts) - { - world = world.SetAccount(kv.Key, Account.Flush(kv.Value)); - } - - return world is World impl - ? new World(impl) { Legacy = impl.Legacy } - : throw new ArgumentException( - $"Unknown type for {nameof(world)}: {world.GetType()}"); - } - /// [Pure] public IAccount GetAccount(Address address) { - return Delta.Accounts.TryGetValue(address, out IAccount? account) + return BlockDelta.TryGetValue(address, out IAccount? account) ? account : _baseState.GetAccount(address); } @@ -74,13 +65,15 @@ public IAccount GetAccount(Address address) public IWorld SetAccount(Address address, IAccount account) { if (!address.Equals(ReservedAddresses.LegacyAccount) - && account.Delta.UpdatedFungibleAssets.Count > 0) + && account.TotalUpdatedFungibleAssets.Count > 0) { return this; } - return new World(this, new WorldDelta(Delta.Accounts.SetItem(address, account))) - { Legacy = Legacy && address.Equals(ReservedAddresses.LegacyAccount) }; + return new World( + this, + UncommittedDelta.SetItem(address, account), + BlockDelta.SetItem(address, account)); } } } diff --git a/Libplanet.Action/State/WorldBaseState.cs b/Libplanet.Action/State/WorldBaseState.cs index fc29083f0b9..31d00250645 100644 --- a/Libplanet.Action/State/WorldBaseState.cs +++ b/Libplanet.Action/State/WorldBaseState.cs @@ -74,6 +74,6 @@ value is Binary stateRootHash : _stateStore.GetStateRoot(null); private IAccount CreateAccount(ITrie trie) => - new Account(new AccountBaseState(trie)); + new Account(new AccountState(trie)); } } diff --git a/Libplanet.Action/State/WorldDelta.cs b/Libplanet.Action/State/WorldDelta.cs deleted file mode 100644 index da4b1e8e080..00000000000 --- a/Libplanet.Action/State/WorldDelta.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Immutable; -using Libplanet.Crypto; - -namespace Libplanet.Action.State -{ - internal class WorldDelta : IWorldDelta - { - internal WorldDelta() - { - Accounts = ImmutableDictionary.Empty; - } - - internal WorldDelta(IImmutableDictionary accounts) - { - Accounts = accounts; - } - - /// - public IImmutableSet
UpdatedAddresses => Accounts.Keys.ToImmutableHashSet(); - - /// - public IImmutableDictionary Accounts { get; } - } -} diff --git a/Libplanet.Action/State/WorldDeltaExtensions.cs b/Libplanet.Action/State/WorldDeltaExtensions.cs deleted file mode 100644 index 6d27378d898..00000000000 --- a/Libplanet.Action/State/WorldDeltaExtensions.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Libplanet.Crypto; -using Libplanet.Store; -using Libplanet.Store.Trie; -using static Libplanet.Action.State.KeyConverters; - -namespace Libplanet.Action.State -{ - public static class WorldDeltaExtensions - { - /// - /// Aggregates a list of s in order. - /// - /// The list of s to aggregate. - /// The aggregate of as an - /// . - /// - /// - /// As aggregation is done by partially overwriting previous values, - /// the order in which is important. - /// - public static IWorldDelta OrderedSum(this IReadOnlyList deltas) - { - IImmutableDictionary accounts = deltas.Aggregate( - ImmutableDictionary.Empty, - (prev, next) => prev.SetItems(next.Accounts)); - return new WorldDelta(accounts); - } - - /// - /// Gets a raw dictionary representation of that gets - /// actually written to an . - /// - /// The to convert. - /// A raw dictionary representation of to write - /// to an . - public static IImmutableDictionary> - ToRawDelta(this IWorldDelta delta) - { - // NOTE: Is this key correct? - var rawStates = delta.Accounts.Select( - kv => new KeyValuePair>( - ToStateKey(kv.Key), - kv.Value.Delta.ToRawDelta())); - return ImmutableDictionary>.Empty - .SetItems(rawStates) - .ToImmutableDictionary(); - } - - public static IImmutableSet ToUpdatedStateKeys( - this IEnumerable deltas) - { - return deltas - .SelectMany(delta => delta.UpdatedAddresses) - .Select(address => ToStateKey(address)) - .ToImmutableHashSet(); - } - } -} diff --git a/Libplanet.Action/UnexpectedlyTerminatedActionException.cs b/Libplanet.Action/UnexpectedlyTerminatedActionException.cs index 35d0b369c27..eb8ae646948 100644 --- a/Libplanet.Action/UnexpectedlyTerminatedActionException.cs +++ b/Libplanet.Action/UnexpectedlyTerminatedActionException.cs @@ -23,27 +23,24 @@ public sealed class UnexpectedlyTerminatedActionException : Exception ///
/// Specifies a . /// The of the - /// that belongs to. - /// This can be on rehearsal mode. + /// that belongs to. /// The of the - /// that belongs to. - /// This can be on rehearsal mode. - /// + /// that belongs to. /// The of /// the that belongs to. - /// This can be on rehearsal mode or if is + /// This can be if is /// a . /// /// The object which threw an exception. /// The of states until - /// previous action execution. This can be null on rehearsal mode or if the chain which + /// previous action execution. This can be if the chain which /// executed the action, was not using . /// The actual exception that the threw. /// public UnexpectedlyTerminatedActionException( string message, - HashDigest? preEvaluationHash, - long? blockIndex, + HashDigest preEvaluationHash, + long blockIndex, TxId? txid, HashDigest? previousStateRootHash, IAction action, @@ -122,20 +119,20 @@ StreamingContext context /// /// The of the that - /// belongs to. This can be on rehearsal mode. + /// belongs to. /// - public HashDigest? PreEvaluationHash { get; } + public HashDigest PreEvaluationHash { get; } /// /// The of the that - /// belongs to. This can be on rehearsal mode. + /// belongs to. /// - public long? BlockIndex { get; } + public long BlockIndex { get; } /// /// The of the that /// belongs to. - /// This can be on rehearsal mode or + /// This can be /// if is a . /// public TxId? TxId { get; } @@ -151,15 +148,8 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont { base.GetObjectData(info, context); - if (PreEvaluationHash is { } preEvaluationHash) - { - info.AddValue(nameof(PreEvaluationHash), preEvaluationHash.ToByteArray()); - } - - if (BlockIndex is long blockIndex) - { - info.AddValue(nameof(BlockIndex), blockIndex); - } + info.AddValue(nameof(PreEvaluationHash), PreEvaluationHash.ToByteArray()); + info.AddValue(nameof(BlockIndex), BlockIndex); if (TxId is TxId txId) { diff --git a/Libplanet.Benchmarks/AppendBlock.cs b/Libplanet.Benchmarks/AppendBlock.cs index d028c3f67ff..19c5b107efa 100644 --- a/Libplanet.Benchmarks/AppendBlock.cs +++ b/Libplanet.Benchmarks/AppendBlock.cs @@ -56,7 +56,7 @@ public void PrepareAppendMakeTenTransactionsNoAction() public void PrepareAppendMakeOneTransactionWithActions() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var actions = new[] { new DumbAction(address, "foo"), @@ -74,7 +74,7 @@ public void PrepareAppendMakeTenTransactionsWithActions() for (var i = 0; i < 10; i++) { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var actions = new[] { new DumbAction(address, "foo"), diff --git a/Libplanet.Benchmarks/DataModel/DataModelBenchmark.LeafModel.cs b/Libplanet.Benchmarks/DataModel/DataModelBenchmark.LeafModel.cs index de861663acc..1da903dd644 100644 --- a/Libplanet.Benchmarks/DataModel/DataModelBenchmark.LeafModel.cs +++ b/Libplanet.Benchmarks/DataModel/DataModelBenchmark.LeafModel.cs @@ -22,8 +22,8 @@ public LeafModel() BigDict = Enumerable .Range(0, 1000) .Select(_ => new KeyValuePair( - new PrivateKey().ToAddress(), - new PrivateKey().ToAddress().ToString())) + new PrivateKey().Address, + new PrivateKey().Address.ToString())) .ToImmutableDictionary(); } diff --git a/Libplanet.Benchmarks/DataModel/DataModelBenchmark.MidModel.cs b/Libplanet.Benchmarks/DataModel/DataModelBenchmark.MidModel.cs index dfd4194c01d..a18fd4c688a 100644 --- a/Libplanet.Benchmarks/DataModel/DataModelBenchmark.MidModel.cs +++ b/Libplanet.Benchmarks/DataModel/DataModelBenchmark.MidModel.cs @@ -22,8 +22,8 @@ public MidModel() BigDict = Enumerable .Range(0, 1000) .Select(_ => new KeyValuePair( - new PrivateKey().ToAddress(), - new PrivateKey().ToAddress().ToString())) + new PrivateKey().Address, + new PrivateKey().Address.ToString())) .ToImmutableDictionary(); } diff --git a/Libplanet.Benchmarks/DataModel/DataModelBenchmark.RootModel.cs b/Libplanet.Benchmarks/DataModel/DataModelBenchmark.RootModel.cs index 0cdc2646323..b54cc13502d 100644 --- a/Libplanet.Benchmarks/DataModel/DataModelBenchmark.RootModel.cs +++ b/Libplanet.Benchmarks/DataModel/DataModelBenchmark.RootModel.cs @@ -22,8 +22,8 @@ public RootModel() BigDict = Enumerable .Range(0, 1000) .Select(_ => new KeyValuePair( - new PrivateKey().ToAddress(), - new PrivateKey().ToAddress().ToString())) + new PrivateKey().Address, + new PrivateKey().Address.ToString())) .ToImmutableDictionary(); } diff --git a/Libplanet.Benchmarks/ProposeBlock.cs b/Libplanet.Benchmarks/ProposeBlock.cs index ab29b8214c2..ce411c0770b 100644 --- a/Libplanet.Benchmarks/ProposeBlock.cs +++ b/Libplanet.Benchmarks/ProposeBlock.cs @@ -77,7 +77,7 @@ public void MakeTenTransactionsNoAction() public void MakeOneTransactionWithActions() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var actions = new[] { new DumbAction(address, "foo"), @@ -95,7 +95,7 @@ public void MakeTenTransactionsWithActions() for (var i = 0; i < 10; i++) { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var actions = new[] { new DumbAction(address, "foo"), diff --git a/Libplanet.Common/NameValueCollectionExtensions.cs b/Libplanet.Common/NameValueCollectionExtensions.cs index 227abe2bde7..6b0034dca9e 100644 --- a/Libplanet.Common/NameValueCollectionExtensions.cs +++ b/Libplanet.Common/NameValueCollectionExtensions.cs @@ -10,6 +10,58 @@ namespace Libplanet.Common ///
public static class NameValueCollectionExtensions { + /// + /// Tries to get the text associated with the specified as + /// given type value from the specified + /// name-value . + /// + /// The that contains the entry + /// to find. + /// The key of the entry that contains the value to + /// find. + /// The type to parse to. + /// A value converted from the text value associated with + /// the specified key from the , + /// if found; otherwise, . + /// Thrown when the value cannot be parsed to type + /// . + /// This method assumes the contains zero or + /// one entry for the specified . + public static T? GetEnum(this NameValueCollection collection, string name) + where T : struct, Enum + => collection.Get(name) is { } value + ? (T?)(Enum.TryParse(value, out T result) ? result : throw new ArgumentException()) + : (T?)null; + + /// + /// Tries to get the text associated with the specified as + /// given type value from the specified + /// name-value . + /// + /// The that contains the entry + /// to find. + /// The key of the entry that contains the value to + /// find. + /// Returns this value if the specified key + /// is not found in the , or + /// the associated value cannot be parsed to type . + /// The type to parse to. + /// A value converted from the text value associated with + /// the specified key from the , + /// if found; otherwise, . + public static T GetEnum(this NameValueCollection collection, string name, T defaultValue) + where T : struct, Enum + { + try + { + return GetEnum(collection, name) ?? defaultValue; + } + catch (ArgumentException) + { + return defaultValue; + } + } + /// /// Tries to get the numeric text associated with the specified as /// an value from the specified name-value . diff --git a/Libplanet.Crypto/Address.cs b/Libplanet.Crypto/Address.cs index b3b92114909..aef93102397 100644 --- a/Libplanet.Crypto/Address.cs +++ b/Libplanet.Crypto/Address.cs @@ -106,7 +106,7 @@ public Address(byte[] address) /// cref="PublicKey"/>. /// Note that there is an equivalent extension method /// , which enables - /// a code like publicKey.ToAddress() instead of + /// a code like publicKey.Address instead of /// new Address(publicKey), for convenience. /// /// A to derive diff --git a/Libplanet.Crypto/AddressExtensions.cs b/Libplanet.Crypto/AddressExtensions.cs deleted file mode 100644 index 4e78123e9da..00000000000 --- a/Libplanet.Crypto/AddressExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Libplanet.Crypto -{ - /// - /// This extension class enables some convenient methods (sugar for - /// the most part) to deal with . - /// - /// - public static class AddressExtensions - { - /// - /// Derives the corresponding from a . - /// This enables a code like publicKey.ToAddress() instead - /// of new Address(publicKey). - /// - /// A to derive - /// the corresponding from. - /// The corresponding derived from - /// . - /// - public static Address ToAddress(this PublicKey publicKey) - { - return new Address(publicKey); - } - - /// - /// Derives the corresponding from a . - /// This enables a code like privateKey.ToAddress() instead - /// of new Address(privateKey.PublicKey). - /// - /// A to derive - /// the corresponding from. - /// The corresponding derived from - /// . - public static Address ToAddress(this PrivateKey privateKey) - { - return new Address(privateKey.PublicKey); - } - } -} diff --git a/Libplanet.Crypto/PrivateKey.cs b/Libplanet.Crypto/PrivateKey.cs index e038bdcccc5..94644818f45 100644 --- a/Libplanet.Crypto/PrivateKey.cs +++ b/Libplanet.Crypto/PrivateKey.cs @@ -134,6 +134,12 @@ public PublicKey PublicKey } } + /// + /// The corresponding derived from a . + /// This is the same as the one derived from . + /// + public Address Address => new Address(PublicKey); + /// /// An encoded array representation. /// diff --git a/Libplanet.Crypto/PublicKey.cs b/Libplanet.Crypto/PublicKey.cs index 9869ec32921..c63a976d273 100644 --- a/Libplanet.Crypto/PublicKey.cs +++ b/Libplanet.Crypto/PublicKey.cs @@ -60,6 +60,11 @@ internal PublicKey(ECPublicKeyParameters keyParam) KeyParam = keyParam; } + /// + /// The corresponding derived from a . + /// + public Address Address => new Address(this); + internal ECPublicKeyParameters KeyParam { get; } public static bool operator ==(PublicKey left, PublicKey right) => left.Equals(right); diff --git a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs index 734db62b40b..0b71dcfb3dd 100644 --- a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs +++ b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs @@ -58,7 +58,7 @@ public GeneratedBlockChainFixture( .Empty, (dict, pk) => dict.SetItem( - pk.ToAddress(), + pk.Address, ImmutableArray.Empty)); SignedTxs = PrivateKeys.Aggregate( ImmutableDictionary< @@ -66,7 +66,7 @@ public GeneratedBlockChainFixture( ImmutableArray>.Empty, (dict, pk) => dict.SetItem( - pk.ToAddress(), + pk.Address, ImmutableArray.Empty)); InvolvedTxs = PrivateKeys.Aggregate( ImmutableDictionary< @@ -74,7 +74,7 @@ public GeneratedBlockChainFixture( ImmutableArray>.Empty, (dict, pk) => dict.SetItem( - pk.ToAddress(), + pk.Address, ImmutableArray.Empty)); var privateKey = new PrivateKey(); @@ -90,7 +90,7 @@ public GeneratedBlockChainFixture( Block genesisBlock = BlockChain.ProposeGenesisBlock( actionEvaluator, transactions: PrivateKeys - .OrderBy(pk => pk.ToAddress().ToHex()) + .OrderBy(pk => pk.Address.ToHex()) .Select( (pk, i) => Transaction.Create( nonce: i, @@ -126,7 +126,7 @@ public GeneratedBlockChainFixture( random.Next(), actionsForTransactions.Select(actions => Transaction.Create( - Chain.GetNextTxNonce(pk.ToAddress()), + Chain.GetNextTxNonce(pk.Address), pk, Chain.Genesis.Hash, actions.ToPlainValues())) @@ -150,7 +150,7 @@ public GeneratedBlockChainFixture( random.Next(), actionsForTransactions.Select(actions => Transaction.Create( - Chain.GetNextTxNonce(pk.ToAddress()), + Chain.GetNextTxNonce(pk.Address), pk, Chain.Genesis.Hash, actions.ToPlainValues())) @@ -172,7 +172,7 @@ private ImmutableArray GetRandomTransactions( var pk = PrivateKeys[random.Next(PrivateKeys.Length)]; if (!nonces.TryGetValue(pk, out var nonce)) { - nonce = Chain.GetNextTxNonce(pk.ToAddress()); + nonce = Chain.GetNextTxNonce(pk.Address); } nonces = nonces.SetItem(pk, nonce + 1); @@ -187,18 +187,23 @@ private Transaction GetRandomTransaction(int seed, PrivateKey pk, long nonce) { var random = new System.Random(seed); - var addr = pk.ToAddress(); - var bal = (int)(Chain.GetBalance(addr, TestCurrency, ReservedAddresses.LegacyAccount).MajorUnit & int.MaxValue); - return Transaction.Create( - nonce, - pk, - Chain.Genesis.Hash, - random.Next() % 2 == 0 - ? GetRandomActions(random.Next()).ToPlainValues() - : ImmutableHashSet.Empty.ToPlainValues(), - null, - null, - GetRandomAddresses(random.Next())); + var addr = pk.Address; + var bal = (int)(Chain.GetBalance( + addr, TestCurrency, ReservedAddresses.LegacyAccount).MajorUnit & int.MaxValue); + return + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: Chain.Genesis.Hash, + updatedAddresses: GetRandomAddresses(random.Next()), + timestamp: DateTimeOffset.UtcNow, + actions: new TxActionList(random.Next() % 2 == 0 + ? GetRandomActions(random.Next()).ToPlainValues() + : ImmutableHashSet.Empty.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(pk.PublicKey, nonce)), + pk); } private ImmutableArray GetRandomActions(int seed) @@ -216,7 +221,7 @@ private IImmutableSet
GetRandomAddresses(int seed) return Enumerable.Range(0, random.Next(PrivateKeys.Length - 1) + 1) .Aggregate( ImmutableHashSet
.Empty, - (arr, _) => arr.Add(PrivateKeys[random.Next(PrivateKeys.Length)].ToAddress())); + (arr, _) => arr.Add(PrivateKeys[random.Next(PrivateKeys.Length)].Address)); } private void AddBlock( @@ -243,7 +248,7 @@ private void AddBlock( 0, block.Hash, PrivateKeys - .OrderBy(pk => pk.ToAddress().ToHex()) + .OrderBy(pk => pk.Address.ToHex()) .Select(pk => new VoteMetadata( Chain.Tip.Index + 1, 0, @@ -252,7 +257,7 @@ private void AddBlock( pk.PublicKey, VoteFlag.PreCommit).Sign(pk)).ToImmutableArray())); MinedBlocks = - MinedBlocks.SetItem(pk.ToAddress(), MinedBlocks[pk.ToAddress()].Add(block)); + MinedBlocks.SetItem(pk.Address, MinedBlocks[pk.Address].Add(block)); SignedTxs = transactions.Aggregate( SignedTxs, (dict, tx) => diff --git a/Libplanet.Explorer.Tests/Indexing/BlockChainIndexTest.cs b/Libplanet.Explorer.Tests/Indexing/BlockChainIndexTest.cs index e825dc5993b..8a8ae4aaf89 100644 --- a/Libplanet.Explorer.Tests/Indexing/BlockChainIndexTest.cs +++ b/Libplanet.Explorer.Tests/Indexing/BlockChainIndexTest.cs @@ -50,7 +50,7 @@ await index.SynchronizeAsync( 0, divergentBlock.Hash, ChainFx.PrivateKeys - .OrderBy(pk => pk.ToAddress().ToHex()) + .OrderBy(pk => pk.Address.ToHex()) .Select(pk => new VoteMetadata( forkedChain.Tip.Index + 1, 0, @@ -85,7 +85,7 @@ public async Task GetLastNonceByAddress() { foreach (var pk in ChainFx.PrivateKeys) { - var address = pk.ToAddress(); + var address = pk.Address; Assert.Equal( ChainFx.Chain.GetNextTxNonce(address) - 1, // ReSharper disable once MethodHasAsyncOverload @@ -251,7 +251,7 @@ public async Task GetBlockHashesByMiner(bool fromHalfway, bool throughHalfway, b { foreach (var pk in ChainFx.PrivateKeys) { - var address = pk.ToAddress(); + var address = pk.Address; var inChain = ChainFx.MinedBlocks[address].ToArray(); inChain = desc ? inChain.Reverse().ToArray() : inChain; int? fromHeight = fromHalfway ? inChain.Length / 4 : null; @@ -305,7 +305,7 @@ public async Task GetSignedTxIdsByAddress(bool fromHalfway, bool throughHalfway, { foreach (var pk in ChainFx.PrivateKeys) { - var address = pk.ToAddress(); + var address = pk.Address; var inChain = ChainFx.SignedTxs[address].ToArray(); inChain = desc ? inChain.Reverse().ToArray() : inChain; int? fromNonce = fromHalfway ? inChain.Length / 4 : null; diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index 1c164efb36c..c7fcdf83cb7 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -461,7 +461,9 @@ public MockWorld() public bool Legacy => true; - public IWorldDelta Delta => throw new System.NotImplementedException(); + public IImmutableDictionary BlockDelta => throw new System.NotImplementedException(); + + public IImmutableDictionary UncommittedDelta => throw new System.NotImplementedException(); public IAccount GetAccount(Address address) => new MockAccount(); @@ -481,8 +483,6 @@ public MockAccount() public ITrie Trie { get; } - public IAccountDelta Delta => throw new System.NotImplementedException(); - public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets => throw new System.NotImplementedException(); public IValue GetState(Address address) => GetStates(new[] { address }).First(); diff --git a/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs b/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs index fda7e5b6f05..bbb0bb8b68e 100644 --- a/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs +++ b/Libplanet.Explorer.Tests/Queries/TransactionQueryGeneratedTest.cs @@ -54,7 +54,7 @@ public async Task TransactionResult() var successTx = successBlock.Transactions.First(); var pk = Fx.PrivateKeys[0]; var stagingTx = Transaction.Create( - Fx.Chain.GetNextTxNonce(pk.ToAddress()), + Fx.Chain.GetNextTxNonce(pk.Address), pk, Fx.Chain.Genesis.Hash, ImmutableArray.Empty.Add(new SimpleAction1()).ToPlainValues()); @@ -89,11 +89,11 @@ public virtual async Task Transactions() { var allBlocks = Fx.Chain.IterateBlocks().ToImmutableArray(); await AssertTransactionsQueryPermutation(allBlocks, null, null); - foreach (var signer in Fx.PrivateKeys.Select(pk => pk.ToAddress())) + foreach (var signer in Fx.PrivateKeys.Select(pk => pk.Address)) { await AssertTransactionsQueryPermutation(allBlocks, signer, null); await AssertTransactionsQueryPermutation(allBlocks, null, signer); - foreach (var involved in Fx.PrivateKeys.Select(pk => pk.ToAddress())) + foreach (var involved in Fx.PrivateKeys.Select(pk => pk.Address)) { await AssertTransactionsQueryPermutation(allBlocks, signer, involved); } diff --git a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs index 13e4dd48d47..3caab136c52 100644 --- a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs @@ -122,19 +122,19 @@ async Task AssertNextNonce(long expected, Address address) { var key1 = new PrivateKey(); // account nonce is 0 in the beginning - await AssertNextNonce(0, key1.ToAddress()); + await AssertNextNonce(0, key1.Address); // staged txs increase next nonce Source.BlockChain.MakeTransaction(key1, ImmutableList.Empty.Add(new NullAction())); - await AssertNextNonce(1, key1.ToAddress()); + await AssertNextNonce(1, key1.Address); Source.BlockChain.MakeTransaction(key1, ImmutableList.Empty.Add(new NullAction())); - await AssertNextNonce(2, key1.ToAddress()); + await AssertNextNonce(2, key1.Address); var block = Source.BlockChain.ProposeBlock(new PrivateKey()); Source.BlockChain.Append(block, Libplanet.Tests.TestUtils.CreateBlockCommit(block)); - await AssertNextNonce(2, key1.ToAddress()); + await AssertNextNonce(2, key1.Address); var key2 = new PrivateKey(); - await AssertNextNonce(0, key2.ToAddress()); + await AssertNextNonce(0, key2.Address); // staging txs of key2 does not increase nonce of key1 Source.BlockChain.MakeTransaction(key2, ImmutableList.Empty.Add(new NullAction())); @@ -142,18 +142,18 @@ async Task AssertNextNonce(long expected, Address address) { new PrivateKey(), Libplanet.Tests.TestUtils.CreateBlockCommit(block)); Source.BlockChain.Append(block, Libplanet.Tests.TestUtils.CreateBlockCommit(block)); - await AssertNextNonce(1, key2.ToAddress()); - await AssertNextNonce(2, key1.ToAddress()); + await AssertNextNonce(1, key2.Address); + await AssertNextNonce(2, key1.Address); // unstaging txs decrease nonce Source.BlockChain.MakeTransaction(key1, ImmutableList.Empty.Add(new NullAction())); - await AssertNextNonce(3, key1.ToAddress()); + await AssertNextNonce(3, key1.Address); Source.BlockChain.MakeTransaction(key1, ImmutableList.Empty.Add(new NullAction())); - await AssertNextNonce(4, key1.ToAddress()); + await AssertNextNonce(4, key1.Address); Source.BlockChain.GetStagedTransactionIds() .Select(Source.BlockChain.GetTransaction) .Select(Source.BlockChain.UnstageTransaction) .ToImmutableList(); - await AssertNextNonce(2, key1.ToAddress()); + await AssertNextNonce(2, key1.Address); } } diff --git a/Libplanet.Explorer/Queries/TransactionQuery.cs b/Libplanet.Explorer/Queries/TransactionQuery.cs index 5b8c5198db6..00c0975ed0b 100644 --- a/Libplanet.Explorer/Queries/TransactionQuery.cs +++ b/Libplanet.Explorer/Queries/TransactionQuery.cs @@ -147,7 +147,7 @@ public TransactionQuery(IBlockChainContext context) var publicKey = new PublicKey( ByteUtil.ParseHex(context.GetArgument("publicKey")) ); - Address signer = publicKey.ToAddress(); + Address signer = publicKey.Address; long nonce = context.GetArgument("nonce") ?? chain.GetNextTxNonce(signer); var sigMeta = new TxSigningMetadata(publicKey, nonce); diff --git a/Libplanet.Extensions.Cocona.Tests/Commands/StoreCommandTest.cs b/Libplanet.Extensions.Cocona.Tests/Commands/StoreCommandTest.cs index 4d096b96545..d33f7db4e89 100644 --- a/Libplanet.Extensions.Cocona.Tests/Commands/StoreCommandTest.cs +++ b/Libplanet.Extensions.Cocona.Tests/Commands/StoreCommandTest.cs @@ -369,8 +369,6 @@ private Transaction DummyTransaction() new[] { new Utils.DummyAction() }.ToPlainValues(), null, null, - null, - DateTimeOffset.UtcNow - ); + DateTimeOffset.UtcNow); } } diff --git a/Libplanet.Extensions.Cocona/Commands/KeyCommand.cs b/Libplanet.Extensions.Cocona/Commands/KeyCommand.cs index d7ba336ba19..fcbac521f53 100644 --- a/Libplanet.Extensions.Cocona/Commands/KeyCommand.cs +++ b/Libplanet.Extensions.Cocona/Commands/KeyCommand.cs @@ -185,7 +185,7 @@ public void Generate( { var key = new PrivateKey(); string priv = ByteUtil.Hex(key.ByteArray); - string addr = key.ToAddress().ToString(); + string addr = key.Address.ToString(); string pub = ByteUtil.Hex(key.PublicKey.Format(compress: true)); if (!noAddress && publicKey) @@ -281,7 +281,7 @@ public void Derive( PublicKey pubKey = publicKey ? PublicKey.FromHex(key) : ValidateRawHex(key).PublicKey; - string addr = pubKey.ToAddress().ToString(); + string addr = pubKey.Address.ToString(); string pub = ByteUtil.Hex(pubKey.Format(compress: true)); Utils.PrintTable(("Public Key", "Address"), new[] { (pub, addr) }); } diff --git a/Libplanet.Extensions.Cocona/Commands/MptCommand.cs b/Libplanet.Extensions.Cocona/Commands/MptCommand.cs index 4dc7ac6a9b7..5d35c05e3da 100644 --- a/Libplanet.Extensions.Cocona/Commands/MptCommand.cs +++ b/Libplanet.Extensions.Cocona/Commands/MptCommand.cs @@ -69,14 +69,12 @@ public void Diff( kvStoreUri = ConvertKVStoreUri(kvStoreUri, toolConfiguration); otherKvStoreUri = ConvertKVStoreUri(otherKvStoreUri, toolConfiguration); - IKeyValueStore keyValueStore = LoadKVStoreFromURI(kvStoreUri); - IKeyValueStore otherKeyValueStore = LoadKVStoreFromURI(otherKvStoreUri); - var trie = new MerkleTrie( - keyValueStore, - HashDigest.FromString(stateRootHashHex)); - var otherTrie = new MerkleTrie( - otherKeyValueStore, - HashDigest.FromString(otherStateRootHashHex)); + IStateStore stateStore = new TrieStateStore(LoadKVStoreFromURI(kvStoreUri)); + IStateStore otherStateStore = new TrieStateStore(LoadKVStoreFromURI(otherKvStoreUri)); + var trie = + stateStore.GetStateRoot(HashDigest.FromString(stateRootHashHex)); + var otherTrie = + otherStateStore.GetStateRoot(HashDigest.FromString(otherStateRootHashHex)); var codec = new Codec(); HashDigest originRootHash = trie.Hash; @@ -113,10 +111,8 @@ public void Export( ToolConfiguration toolConfiguration = configurationService.Load(); kvStoreUri = ConvertKVStoreUri(kvStoreUri, toolConfiguration); - IKeyValueStore keyValueStore = LoadKVStoreFromURI(kvStoreUri); - var trie = new MerkleTrie( - keyValueStore, - HashDigest.FromString(stateRootHashHex)); + IStateStore stateStore = new TrieStateStore(LoadKVStoreFromURI(kvStoreUri)); + var trie = stateStore.GetStateRoot(HashDigest.FromString(stateRootHashHex)); var codec = new Codec(); // This assumes the original key was encoded from a sensible string. diff --git a/Libplanet.Net.Tests/AppProtocolVersionTest.cs b/Libplanet.Net.Tests/AppProtocolVersionTest.cs index fc243c50c4c..8897a4a6782 100644 --- a/Libplanet.Net.Tests/AppProtocolVersionTest.cs +++ b/Libplanet.Net.Tests/AppProtocolVersionTest.cs @@ -20,7 +20,7 @@ public class AppProtocolVersionTest private static readonly AppProtocolVersion ValidClaimFixture = new AppProtocolVersion( version: 1, extra: null, - signer: SignerFixture.ToAddress(), + signer: SignerFixture.Address, signature: new byte[] { 0x30, 0x45, 0x02, 0x21, 0x00, 0x89, 0x95, 0x9c, 0x59, 0x25, 0x83, 0x4e, @@ -35,7 +35,7 @@ public class AppProtocolVersionTest private static readonly AppProtocolVersion ValidClaimWExtraFixture = new AppProtocolVersion( version: 123, extra: (Bencodex.Types.Text)"foo", - signer: SignerFixture.ToAddress(), + signer: SignerFixture.Address, signature: new byte[] { 0x30, 0x44, 0x02, 0x20, 0x08, 0x5d, 0xd4, 0x4d, 0x2f, 0xa1, 0x57, 0xe0, @@ -105,7 +105,7 @@ public void Verify() var invalidSigner = new AppProtocolVersion( version: ValidClaimFixture.Version, extra: ValidClaimFixture.Extra, - signer: otherPartyPublicKey.ToAddress(), + signer: otherPartyPublicKey.Address, signature: ValidClaimFixture.Signature ); Assert.False(invalidSigner.Verify(signerPublicKey)); @@ -163,7 +163,7 @@ public void Equality() version, extra, signature, - new PrivateKey().ToAddress()); + new PrivateKey().Address); Assert.False(((IEquatable)claim).Equals(claim5)); Assert.False(((object)claim).Equals(claim5)); Assert.NotEqual(claim.GetHashCode(), claim5.GetHashCode()); diff --git a/Libplanet.Net.Tests/Consensus/ContextNonProposerTest.cs b/Libplanet.Net.Tests/Consensus/ContextNonProposerTest.cs index 55187a9e6bc..57f9dd6599c 100644 --- a/Libplanet.Net.Tests/Consensus/ContextNonProposerTest.cs +++ b/Libplanet.Net.Tests/Consensus/ContextNonProposerTest.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Security.Cryptography; using System.Text.Json; using System.Threading.Tasks; @@ -231,7 +230,7 @@ public async Task EnterPreVoteNilOnInvalidBlockHeader() protocolVersion: BlockMetadata.CurrentProtocolVersion, index: blockChain.Tip.Index + 2, timestamp: blockChain.Tip.Timestamp.Subtract(TimeSpan.FromSeconds(1)), - miner: TestUtils.PrivateKeys[1].PublicKey.ToAddress(), + miner: TestUtils.PrivateKeys[1].Address, publicKey: TestUtils.PrivateKeys[1].PublicKey, previousHash: blockChain.Tip.Hash, txHash: null, @@ -266,7 +265,7 @@ public async Task EnterPreVoteNilOnInvalidBlockContent() TxPolicyViolationException? IsSignerValid( BlockChain chain, Transaction tx) { - var validAddress = TestUtils.PrivateKeys[1].PublicKey.ToAddress(); + var validAddress = TestUtils.PrivateKeys[1].Address; return tx.Signer.Equals(validAddress) ? null : new TxPolicyViolationException("invalid signer", tx.Id); @@ -367,7 +366,6 @@ message is ConsensusPreCommitMsg commit && var unsignedInvalidTx = new UnsignedTx( new TxInvoice( blockChain.Genesis.Hash, - ImmutableHashSet
.Empty, DateTimeOffset.UtcNow, new TxActionList((IValue)List.Empty.Add(new Text("Foo")))), // Invalid action new TxSigningMetadata(txSigner.PublicKey, 0)); diff --git a/Libplanet.Net.Tests/Consensus/ContextProposerValidRoundTest.cs b/Libplanet.Net.Tests/Consensus/ContextProposerValidRoundTest.cs index 0720804c431..5ff5a10143a 100644 --- a/Libplanet.Net.Tests/Consensus/ContextProposerValidRoundTest.cs +++ b/Libplanet.Net.Tests/Consensus/ContextProposerValidRoundTest.cs @@ -172,7 +172,7 @@ public async void EnterValidRoundPreVoteNil() protocolVersion: BlockMetadata.CurrentProtocolVersion, index: blockChain.Tip.Index + 1, timestamp: blockChain.Tip.Timestamp.Add(TimeSpan.FromSeconds(1)), - miner: key.PublicKey.ToAddress(), + miner: key.Address, publicKey: key.PublicKey, previousHash: blockChain.Tip.Hash, txHash: null, diff --git a/Libplanet.Net.Tests/Consensus/ContextTest.cs b/Libplanet.Net.Tests/Consensus/ContextTest.cs index 6f138e00dcc..c93f8942014 100644 --- a/Libplanet.Net.Tests/Consensus/ContextTest.cs +++ b/Libplanet.Net.Tests/Consensus/ContextTest.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Immutable; using System.Linq; using System.Text.Json; using System.Threading.Tasks; @@ -362,9 +361,7 @@ void BroadcastMessage(ConsensusMsg message) => nonce: 0, privateKey: TestUtils.PrivateKeys[1], genesisHash: blockChain.Genesis.Hash, - actions: new[] { action }.ToPlainValues(), - updatedAddresses: ImmutableHashSet.Create(DelayAction.TrivialUpdatedAddress) - ); + actions: new[] { action }.ToPlainValues()); blockChain.StageTransaction(tx); var block = blockChain.ProposeBlock(TestUtils.PrivateKeys[1]); @@ -454,7 +451,7 @@ public async void CanReplaceProposal() var codec = new Codec(); var privateKeys = Enumerable.Range(0, 4).Select(_ => new PrivateKey()).ToArray(); // Order keys as validator set's order to run test as intended. - privateKeys = privateKeys.OrderBy(key => key.ToAddress()).ToArray(); + privateKeys = privateKeys.OrderBy(key => key.Address).ToArray(); var proposer = privateKeys[1]; var key1 = privateKeys[2]; var key2 = privateKeys[3]; diff --git a/Libplanet.Net.Tests/Messages/NetMQMessageCodecTest.cs b/Libplanet.Net.Tests/Messages/NetMQMessageCodecTest.cs index 12ac58e29d1..eb452a3bb3c 100644 --- a/Libplanet.Net.Tests/Messages/NetMQMessageCodecTest.cs +++ b/Libplanet.Net.Tests/Messages/NetMQMessageCodecTest.cs @@ -110,7 +110,7 @@ private MessageContent CreateMessage(MessageContent.MessageType type) case MessageContent.MessageType.Tx: return new Libplanet.Net.Messages.TxMsg(transaction.Serialize()); case MessageContent.MessageType.FindNeighbors: - return new FindNeighborsMsg(privateKey.ToAddress()); + return new FindNeighborsMsg(privateKey.Address); case MessageContent.MessageType.Neighbors: return new NeighborsMsg(new[] { boundPeer }); case MessageContent.MessageType.BlockHeaderMessage: diff --git a/Libplanet.Net.Tests/Protocols/ProtocolTest.cs b/Libplanet.Net.Tests/Protocols/ProtocolTest.cs index 45adda6359f..1c267cf5715 100644 --- a/Libplanet.Net.Tests/Protocols/ProtocolTest.cs +++ b/Libplanet.Net.Tests/Protocols/ProtocolTest.cs @@ -514,7 +514,7 @@ public async Task RefreshTable() const int peersCount = 10; var privateKey = new PrivateKey(); var privateKeys = Enumerable.Range(0, peersCount).Select( - i => GeneratePrivateKeyOfBucketIndex(privateKey.ToAddress(), i / 2)); + i => GeneratePrivateKeyOfBucketIndex(privateKey.Address, i / 2)); TestTransport transport = CreateTestTransport(privateKey); TestTransport[] transports = privateKeys.Select(key => CreateTestTransport(key)).ToArray(); diff --git a/Libplanet.Net.Tests/Protocols/RoutingTableTest.cs b/Libplanet.Net.Tests/Protocols/RoutingTableTest.cs index e8fc87fab83..d1ef12b9c7e 100644 --- a/Libplanet.Net.Tests/Protocols/RoutingTableTest.cs +++ b/Libplanet.Net.Tests/Protocols/RoutingTableTest.cs @@ -38,7 +38,7 @@ public RoutingTableTest(ITestOutputHelper output) public void AddSelf() { var pubKey = new PrivateKey().PublicKey; - var table = new RoutingTable(pubKey.ToAddress()); + var table = new RoutingTable(pubKey.Address); var peer = new BoundPeer(pubKey, new DnsEndPoint("0.0.0.0", 1234)); Assert.Throws(() => table.AddPeer(peer)); } @@ -50,7 +50,7 @@ public void AddPeer() var pubKey1 = new PrivateKey().PublicKey; var pubKey2 = new PrivateKey().PublicKey; var pubKey3 = new PrivateKey().PublicKey; - var table = new RoutingTable(pubKey0.ToAddress(), 1, 2); + var table = new RoutingTable(pubKey0.Address, 1, 2); var peer1 = new BoundPeer(pubKey1, new DnsEndPoint("0.0.0.0", 1234)); var peer2 = new BoundPeer(pubKey2, new DnsEndPoint("0.0.0.0", 1234)); var peer3 = new BoundPeer(pubKey3, new DnsEndPoint("0.0.0.0", 1234)); @@ -70,7 +70,7 @@ public void RemovePeer() { var pubKey1 = new PrivateKey().PublicKey; var pubKey2 = new PrivateKey().PublicKey; - var table = new RoutingTable(pubKey1.ToAddress(), 1, 2); + var table = new RoutingTable(pubKey1.Address, 1, 2); var peer1 = new BoundPeer(pubKey1, new DnsEndPoint("0.0.0.0", 1234)); var peer2 = new BoundPeer(pubKey2, new DnsEndPoint("0.0.0.0", 1234)); @@ -101,14 +101,14 @@ public void Generate() count++; publicKey = new PrivateKey().PublicKey; } - while (table.GetBucketIndexOf(publicKey.ToAddress()) != targetBucket); + while (table.GetBucketIndexOf(publicKey.Address) != targetBucket); Log.Debug( "Found public key of bucket index {Index} in {Count} tries: {Key}", - table.GetBucketIndexOf(publicKey.ToAddress()), + table.GetBucketIndexOf(publicKey.Address), count, ByteArrayToString(publicKey.Format(true))); - Assert.Equal(targetBucket, table.GetBucketIndexOf(publicKey.ToAddress())); + Assert.Equal(targetBucket, table.GetBucketIndexOf(publicKey.Address)); } [Fact] @@ -116,7 +116,7 @@ public void PeersToBroadcast() { var (publicKey, publicKeys) = GeneratePeersDifferentBuckets(); - var table = new RoutingTable(publicKey.ToAddress()); + var table = new RoutingTable(publicKey.Address); var peers = publicKeys .Select(pk => new BoundPeer(pk, new DnsEndPoint("0.0.0.0", 1234))) .ToArray(); @@ -144,7 +144,7 @@ public void PeersToBroadcast() public void PeersToRefresh() { var (publicKey, publicKeys) = GeneratePeersDifferentBuckets(); - var table = new RoutingTable(publicKey.ToAddress()); + var table = new RoutingTable(publicKey.Address); int peerCount = publicKeys.Length; BoundPeer[] peers = publicKeys .Select( @@ -171,7 +171,7 @@ public void PeersToRefresh() public void PeersToRefreshInSingleBucket() { var publicKey = new PrivateKey().PublicKey; - var table = new RoutingTable(publicKey.ToAddress(), 1); + var table = new RoutingTable(publicKey.Address, 1); const int peerCount = 10; BoundPeer[] peers = Enumerable.Range(0, peerCount) .Select( diff --git a/Libplanet.Net.Tests/Protocols/TestTransport.cs b/Libplanet.Net.Tests/Protocols/TestTransport.cs index e362f89d2dd..730e94d32fb 100644 --- a/Libplanet.Net.Tests/Protocols/TestTransport.cs +++ b/Libplanet.Net.Tests/Protocols/TestTransport.cs @@ -48,7 +48,7 @@ public TestTransport( _runningEvent = new TaskCompletionSource(); _privateKey = privateKey; _blockBroadcast = blockBroadcast; - var loggerId = _privateKey.ToAddress().ToHex(); + var loggerId = _privateKey.Address.ToHex(); _logger = Log.ForContext() .ForContext("Address", loggerId); @@ -57,7 +57,7 @@ public TestTransport( ReceivedMessages = new ConcurrentBag(); MessageReceived = new AsyncAutoResetEvent(); _transports = transports; - _transports[privateKey.ToAddress()] = this; + _transports[privateKey.Address] = this; _networkDelay = networkDelay ?? TimeSpan.Zero; _requests = new AsyncCollection(); _ignoreTestMessageWithData = new List(); @@ -72,7 +72,7 @@ public TestTransport( public AsyncAutoResetEvent MessageReceived { get; } - public Address Address => _privateKey.ToAddress(); + public Address Address => _privateKey.Address; public BoundPeer AsPeer => new BoundPeer( _privateKey.PublicKey, @@ -359,7 +359,7 @@ public async Task SendMessageAsync( var bytes = new byte[10]; _random.NextBytes(bytes); var sendTime = DateTimeOffset.UtcNow; - var identity = _privateKey.ToAddress().ByteArray.Concat(bytes).ToArray(); + var identity = _privateKey.Address.ByteArray.Concat(bytes).ToArray(); _logger.Debug("Adding request of {Content} of {Identity}", content, identity); await _requests.AddAsync( new Request diff --git a/Libplanet.Net.Tests/SwarmTest.AppProtocolVersion.cs b/Libplanet.Net.Tests/SwarmTest.AppProtocolVersion.cs index d8f13efdcac..8e39c2f777b 100644 --- a/Libplanet.Net.Tests/SwarmTest.AppProtocolVersion.cs +++ b/Libplanet.Net.Tests/SwarmTest.AppProtocolVersion.cs @@ -101,8 +101,8 @@ public async Task IgnoreUntrustedAppProtocolVersion() AppProtocolVersion untrustedOlder = AppProtocolVersion.Sign(untrustedSigner, 2); AppProtocolVersion untrustedNewer = AppProtocolVersion.Sign(untrustedSigner, 3); - _output.WriteLine("Trusted version signer: {0}", signer.ToAddress()); - _output.WriteLine("Untrusted version signer: {0}", untrustedSigner.ToAddress()); + _output.WriteLine("Trusted version signer: {0}", signer.Address); + _output.WriteLine("Untrusted version signer: {0}", untrustedSigner.Address); var logs = new ConcurrentDictionary(); diff --git a/Libplanet.Net.Tests/SwarmTest.Broadcast.cs b/Libplanet.Net.Tests/SwarmTest.Broadcast.cs index e866975e125..d6b496bfbe1 100644 --- a/Libplanet.Net.Tests/SwarmTest.Broadcast.cs +++ b/Libplanet.Net.Tests/SwarmTest.Broadcast.cs @@ -347,7 +347,7 @@ public async Task BroadcastTxWhileMining() BlockChain chainC = swarmC.BlockChain; var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var txCount = 10; var txs = Enumerable.Range(0, txCount).Select(_ => @@ -568,7 +568,7 @@ public async Task DoNotRebroadcastTxsWithLowerNonce() swarmB.RoutingTable.RemovePeer(swarmA.AsPeer); chainA.UnstageTransaction(tx2); - Assert.Equal(1, chainA.GetNextTxNonce(privateKey.ToAddress())); + Assert.Equal(1, chainA.GetNextTxNonce(privateKey.Address)); await StopAsync(swarmA); await StopAsync(swarmB); @@ -941,7 +941,7 @@ await swarmC.PullBlocksAsync( public async Task CanFillWithInvalidTransaction() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var swarm1 = await CreateSwarm().ConfigureAwait(false); var swarm2 = await CreateSwarm().ConfigureAwait(false); diff --git a/Libplanet.Net.Tests/SwarmTest.Fixtures.cs b/Libplanet.Net.Tests/SwarmTest.Fixtures.cs index 5b0036abe00..1bd32a0bb47 100644 --- a/Libplanet.Net.Tests/SwarmTest.Fixtures.cs +++ b/Libplanet.Net.Tests/SwarmTest.Fixtures.cs @@ -39,7 +39,7 @@ private static (Address, Block[]) MakeBlockChain(policy, storeFx.Store, storeFx.StateStore); var miner = new PrivateKey(); var signer = new PrivateKey(); - Address address = signer.ToAddress(); + Address address = signer.Address; Log.Logger.Information("Fixture blocks:"); for (int i = 0; i < 20; i++) { diff --git a/Libplanet.Net.Tests/SwarmTest.Preload.cs b/Libplanet.Net.Tests/SwarmTest.Preload.cs index e2b11a8cd48..b3d7b736e71 100644 --- a/Libplanet.Net.Tests/SwarmTest.Preload.cs +++ b/Libplanet.Net.Tests/SwarmTest.Preload.cs @@ -77,8 +77,8 @@ public async Task InitialBlockDownloadStates() BlockChain receiverChain = receiverSwarm.BlockChain; var key = new PrivateKey(); - var address1 = key.ToAddress(); - var address2 = new PrivateKey().ToAddress(); + var address1 = key.Address; + var address2 = new PrivateKey().Address; var action = new DumbAction( address1, @@ -468,9 +468,7 @@ public async Task PreloadWithFailedActions() new[] { action }.ToPlainValues(), null, null, - ImmutableHashSet
.Empty, - DateTimeOffset.UtcNow - ); + DateTimeOffset.UtcNow); Block block = minerChain.ProposeBlock( ChainPrivateKey, diff --git a/Libplanet.Net.Tests/SwarmTest.cs b/Libplanet.Net.Tests/SwarmTest.cs index 5f4ecb5920a..8ee5f11e290 100644 --- a/Libplanet.Net.Tests/SwarmTest.cs +++ b/Libplanet.Net.Tests/SwarmTest.cs @@ -1026,8 +1026,8 @@ public async void RestageTransactionsOnceLocallyMinedAfterReorg(bool restage) var privateKeyA = new PrivateKey(); var privateKeyB = new PrivateKey(); - var targetAddress1 = new PrivateKey().ToAddress(); - var targetAddress2 = new PrivateKey().ToAddress(); + var targetAddress1 = new PrivateKey().Address; + var targetAddress2 = new PrivateKey().Address; try { @@ -1110,9 +1110,9 @@ public async Task UnstageInvalidTransaction() TxPolicyViolationException IsSignerValid( BlockChain chain, Transaction tx) { - var validAddress = validKey.PublicKey.ToAddress(); + var validAddress = validKey.Address; return tx.Signer.Equals(validAddress) || - tx.Signer.Equals(GenesisProposer.ToAddress()) + tx.Signer.Equals(GenesisProposer.Address) ? null : new TxPolicyViolationException("invalid signer", tx.Id); } @@ -1169,9 +1169,9 @@ public async Task IgnoreTransactionFromDifferentGenesis() TxPolicyViolationException IsSignerValid( BlockChain chain, Transaction tx) { - var validAddress = validKey.PublicKey.ToAddress(); + var validAddress = validKey.Address; return tx.Signer.Equals(validAddress) || - tx.Signer.Equals(GenesisProposer.ToAddress()) + tx.Signer.Equals(GenesisProposer.Address) ? null : new TxPolicyViolationException("invalid signer", tx.Id); } @@ -1262,8 +1262,8 @@ public async Task CreateNewChainWhenBranchPointNotExist() stateRootHash: genesis.StateRootHash, lastCommit: CreateBlockCommit(bBlock1)); - policyA.BlockedMiners.Add(keyB.ToAddress()); - policyB.BlockedMiners.Add(keyA.ToAddress()); + policyA.BlockedMiners.Add(keyB.Address); + policyB.BlockedMiners.Add(keyA.Address); var minerSwarmA = await CreateSwarm(keyA, policy: policyA, genesis: genesis).ConfigureAwait(false); @@ -1345,7 +1345,7 @@ public async Task DoNotReceiveBlockFromNodeHavingDifferenceGenesisBlock() var privateKeyB = new PrivateKey(keyB); var privateKeyC = new PrivateKey(keyC); - var signerAddress = new PrivateKey().ToAddress(); + var signerAddress = new PrivateKey().Address; var actionsA = new[] { new DumbAction(signerAddress, "1") }; var actionsB = new[] { new DumbAction(signerAddress, "2") }; diff --git a/Libplanet.Net.Tests/TestUtils.cs b/Libplanet.Net.Tests/TestUtils.cs index 8f55848a9db..a9013fa7791 100644 --- a/Libplanet.Net.Tests/TestUtils.cs +++ b/Libplanet.Net.Tests/TestUtils.cs @@ -76,7 +76,7 @@ public static PrivateKey GeneratePrivateKeyOfBucketIndex(Address tableAddress, i { privateKey = new PrivateKey(); } - while (table.GetBucketIndexOf(privateKey.ToAddress()) != target); + while (table.GetBucketIndexOf(privateKey.Address) != target); return privateKey; } diff --git a/Libplanet.Net.Tests/Transports/TransportTest.cs b/Libplanet.Net.Tests/Transports/TransportTest.cs index 1c5a434fb42..9b8a3b2a241 100644 --- a/Libplanet.Net.Tests/Transports/TransportTest.cs +++ b/Libplanet.Net.Tests/Transports/TransportTest.cs @@ -135,7 +135,7 @@ public async Task AsPeer() try { var peer = transport.AsPeer; - Assert.Equal(privateKey.ToAddress(), peer.Address); + Assert.Equal(privateKey.Address, peer.Address); Assert.Equal(host, peer.EndPoint.Host); } finally @@ -353,7 +353,7 @@ public async Task SendMessageAsyncCancelWhenTransportStop() [SkippableFact(Timeout = Timeout)] public async Task BroadcastMessage() { - var address = new PrivateKey().ToAddress(); + var address = new PrivateKey().Address; ITransport transportA = null; ITransport transportB = await CreateTransportAsync( privateKey: GeneratePrivateKeyOfBucketIndex(address, 0)); diff --git a/Libplanet.Net/Consensus/Context.Mutate.cs b/Libplanet.Net/Consensus/Context.Mutate.cs index 7776e2659d3..e6f2c9e193e 100644 --- a/Libplanet.Net/Consensus/Context.Mutate.cs +++ b/Libplanet.Net/Consensus/Context.Mutate.cs @@ -135,7 +135,7 @@ private bool AddMessage(ConsensusMsg message) voteMsg, voteMsg.Height, voteMsg.Round, - voteMsg.ValidatorPublicKey.ToAddress(), + voteMsg.ValidatorPublicKey.Address, voteMsg.BlockHash, ToString()); return true; diff --git a/Libplanet.Net/Consensus/Context.cs b/Libplanet.Net/Consensus/Context.cs index e9177de1ca2..008ad294281 100644 --- a/Libplanet.Net/Consensus/Context.cs +++ b/Libplanet.Net/Consensus/Context.cs @@ -356,7 +356,7 @@ public override string ToString() { var dict = new Dictionary { - { "node_id", _privateKey.ToAddress().ToString() }, + { "node_id", _privateKey.Address.ToString() }, { "number_of_validators", _validatorSet.TotalCount }, { "height", Height }, { "round", Round }, diff --git a/Libplanet.Net/Consensus/ContextTimeoutOption.cs b/Libplanet.Net/Consensus/ContextTimeoutOption.cs index 36d6c74b9af..78a5515ffd2 100644 --- a/Libplanet.Net/Consensus/ContextTimeoutOption.cs +++ b/Libplanet.Net/Consensus/ContextTimeoutOption.cs @@ -9,12 +9,12 @@ namespace Libplanet.Net.Consensus public class ContextTimeoutOption { public ContextTimeoutOption( - int proposeSecondBase = 5, - int preVoteSecondBase = 5, - int preCommitSecondBase = 5, - int proposeMultiplier = 1, - int preVoteMultiplier = 1, - int preCommitMultiplier = 1) + int proposeSecondBase = 8, + int preVoteSecondBase = 4, + int preCommitSecondBase = 4, + int proposeMultiplier = 4, + int preVoteMultiplier = 2, + int preCommitMultiplier = 2) { if (proposeSecondBase <= 0) { diff --git a/Libplanet.Net/Consensus/GossipConsensusMessageCommunicator.cs b/Libplanet.Net/Consensus/GossipConsensusMessageCommunicator.cs index 6acf5d0a5e9..07316ad88d1 100644 --- a/Libplanet.Net/Consensus/GossipConsensusMessageCommunicator.cs +++ b/Libplanet.Net/Consensus/GossipConsensusMessageCommunicator.cs @@ -66,6 +66,7 @@ public void PublishMessage(ConsensusMsg message) public void OnStartHeight(long height) { _height = height; + _peerCatchupRounds.Clear(); Gossip.ClearDenySet(); } @@ -86,6 +87,7 @@ private void ValidateMessageToReceive(Message message) { if (message.Content is ConsensusVoteMsg voteMsg) { + FilterDifferentHeightVote(voteMsg); FilterHigherRoundVoteSpam(voteMsg, message.Remote); } } @@ -98,10 +100,33 @@ private void ValidateMessageToReceive(Message message) /// to validate. private void ValidateMessageToSend(MessageContent content) { - if (content is ConsensusVoteMsg voteMsg && voteMsg.Round > _round) + if (content is ConsensusVoteMsg voteMsg) + { + if (voteMsg.Height != _height) + { + throw new InvalidConsensusMessageException( + $"Cannot send vote of height different from context's", voteMsg); + } + + if (voteMsg.Round > _round) + { + throw new InvalidConsensusMessageException( + $"Cannot send vote of round higher than context's", voteMsg); + } + } + } + + /// + /// Filter logic for different height s. + /// + /// to filter. + private void FilterDifferentHeightVote(ConsensusVoteMsg voteMsg) + { + if (voteMsg.Height != _height) { throw new InvalidConsensusMessageException( - $"Cannot send vote of round higher than context", voteMsg); + $"Filtered vote from different height: {voteMsg.Height}", + voteMsg); } } @@ -113,7 +138,8 @@ private void ValidateMessageToSend(MessageContent content) /// private void FilterHigherRoundVoteSpam(ConsensusVoteMsg voteMsg, BoundPeer peer) { - if (voteMsg.Round > _round) + if (voteMsg.Height == _height && + voteMsg.Round > _round) { if (!_peerCatchupRounds.ContainsKey(peer)) { @@ -130,7 +156,8 @@ private void FilterHigherRoundVoteSpam(ConsensusVoteMsg voteMsg, BoundPeer peer) { Gossip.DenyPeer(peer); throw new InvalidConsensusMessageException( - $"Repetitively found higher rounds, add {peer} to deny set", + $"Add {peer} to deny set, since repetitively found higher rounds: " + + $"{string.Join(", ", _peerCatchupRounds[peer])}", voteMsg); } } diff --git a/Libplanet.Net/Consensus/VoteSet.cs b/Libplanet.Net/Consensus/VoteSet.cs index 22138c3a8dd..23531b03aa8 100644 --- a/Libplanet.Net/Consensus/VoteSet.cs +++ b/Libplanet.Net/Consensus/VoteSet.cs @@ -220,7 +220,7 @@ public bool[] BitArrayByBlockHash(BlockHash blockHash) /// A copy of the list of s stored by the . /// public List List() - => _votes.Values.OrderBy(vote => vote.ValidatorPublicKey.ToAddress()).ToList(); + => _votes.Values.OrderBy(vote => vote.ValidatorPublicKey.Address).ToList(); /// /// Returns a copy of the list of s stored by the . diff --git a/Libplanet.Net/Swarm.cs b/Libplanet.Net/Swarm.cs index eafabd2437c..9d4d8317cff 100644 --- a/Libplanet.Net/Swarm.cs +++ b/Libplanet.Net/Swarm.cs @@ -79,7 +79,7 @@ public Swarm( _runningMutex = new AsyncLock(); - string loggerId = _privateKey.ToAddress().ToHex(); + string loggerId = _privateKey.Address.ToHex(); _logger = Log .ForContext() .ForContext("Source", nameof(Swarm)) @@ -138,7 +138,7 @@ public Swarm( public DnsEndPoint EndPoint => AsPeer is BoundPeer boundPeer ? boundPeer.EndPoint : null; - public Address Address => _privateKey.ToAddress(); + public Address Address => _privateKey.Address; public BoundPeer AsPeer => Transport?.AsPeer; diff --git a/Libplanet.RocksDBStore.Tests/RocksDBKeyValueStoreTest.cs b/Libplanet.RocksDBStore.Tests/RocksDBKeyValueStoreTest.cs index b60593e41b3..664bf3e8dfe 100644 --- a/Libplanet.RocksDBStore.Tests/RocksDBKeyValueStoreTest.cs +++ b/Libplanet.RocksDBStore.Tests/RocksDBKeyValueStoreTest.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; +using Libplanet.Store.Trie; using Libplanet.Tests.Store.Trie; +using RocksDbSharp; using Xunit; namespace Libplanet.RocksDBStore.Tests @@ -24,6 +27,44 @@ public RocksDBKeyValueStoreTest() } } + [Fact] + public void ReadOnlyRocksDb() + { + var basePath = Path.Combine( + Path.GetTempPath(), + $"rocksdb_key_value_test_{Guid.NewGuid()}"); + var primaryRocksDb = new RocksDBKeyValueStore(basePath); + var readonlyRocksDb = new RocksDBKeyValueStore(basePath, RocksDBInstanceType.ReadOnly); + + var key = new KeyBytes("new"); + var value = new byte[] { 1, 2, 3 }; + primaryRocksDb.Set(key, value); + Assert.Equal(value, primaryRocksDb.Get(key)); + Assert.Throws(() => readonlyRocksDb.Get(key)); + Assert.Throws(() => readonlyRocksDb.TryCatchUpWithPrimary()); + } + + [Fact] + public void SecondaryRocksDb() + { + var basePath = Path.Combine( + Path.GetTempPath(), + $"rocksdb_key_value_test_{Guid.NewGuid()}"); + var primaryRocksDb = new RocksDBKeyValueStore(basePath); + var secondaryRocksDb = new RocksDBKeyValueStore( + basePath, + RocksDBInstanceType.Secondary); + + var key = new KeyBytes("new"); + var value = new byte[] { 1, 2, 3 }; + primaryRocksDb.Set(key, value); + Assert.Equal(value, primaryRocksDb.Get(key)); + Assert.Throws(() => secondaryRocksDb.Get(key)); + + secondaryRocksDb.TryCatchUpWithPrimary(); + Assert.Equal(value, secondaryRocksDb.Get(key)); + } + public void Dispose() { _rocksDbKeyValueStore.Dispose(); diff --git a/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs b/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs index d11c0f37b6d..85abca8abc7 100644 --- a/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs +++ b/Libplanet.RocksDBStore.Tests/RocksDBStoreTest.cs @@ -278,19 +278,5 @@ int KeysWithChainId(RocksDb db, Guid cid) Directory.Delete(path, true); } } - - private long ToInt64(byte[] value) - { - byte[] bytes = new byte[sizeof(long)]; - value.CopyTo(bytes, 0); - - // Use Big-endian to order index lexicographically. - if (BitConverter.IsLittleEndian) - { - Array.Reverse(bytes); - } - - return BitConverter.ToInt64(bytes, 0); - } } } diff --git a/Libplanet.RocksDBStore/Libplanet.RocksDBStore.csproj b/Libplanet.RocksDBStore/Libplanet.RocksDBStore.csproj index 3905765b9f6..e186bb9e425 100644 --- a/Libplanet.RocksDBStore/Libplanet.RocksDBStore.csproj +++ b/Libplanet.RocksDBStore/Libplanet.RocksDBStore.csproj @@ -48,7 +48,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + all diff --git a/Libplanet.RocksDBStore/RocksDBInstanceType.cs b/Libplanet.RocksDBStore/RocksDBInstanceType.cs new file mode 100644 index 00000000000..3dfa2ee6e18 --- /dev/null +++ b/Libplanet.RocksDBStore/RocksDBInstanceType.cs @@ -0,0 +1,27 @@ +namespace Libplanet.RocksDBStore +{ + /// + /// Represent 's instance type. + /// can be instantiated as primary, read-only, or secondary. + /// Please refer a + /// RocksDB's document for more details. + /// + public enum RocksDBInstanceType + { + /// + /// Primary instance. + /// + Primary, + + /// + /// ReadOnly instance. + /// + ReadOnly, + + /// + /// Secondary instance. + /// + Secondary, + } +} diff --git a/Libplanet.RocksDBStore/RocksDBKeyValueStore.cs b/Libplanet.RocksDBStore/RocksDBKeyValueStore.cs index 82e2b108839..4140a0f4d99 100644 --- a/Libplanet.RocksDBStore/RocksDBKeyValueStore.cs +++ b/Libplanet.RocksDBStore/RocksDBKeyValueStore.cs @@ -75,15 +75,21 @@ public class RocksDBKeyValueStore : IKeyValueStore /// Creates a new . /// /// The path of the storage file will be saved. - /// If it is true, it will open rocksdb in read-only mode. - public RocksDBKeyValueStore(string path, bool @readonly = false) + /// Determines the instance type of the internal + /// instances. by default. + public RocksDBKeyValueStore( + string path, + RocksDBInstanceType type = RocksDBInstanceType.Primary) { var options = new DbOptions() .SetCreateIfMissing(); - _keyValueDb = RocksDBUtils.OpenRocksDb(options, path, @readonly: @readonly); + _keyValueDb = RocksDBUtils.OpenRocksDb(options, path, type: type); + Type = type; } + public RocksDBInstanceType Type { get; } + /// public byte[] Get(in KeyBytes key) => _keyValueDb.Get(key.ToByteArray()) ?? throw new KeyNotFoundException($"No such key: ${key}."); @@ -145,5 +151,10 @@ public IEnumerable ListKeys() yield return new KeyBytes(it.Key()); } } + + public void TryCatchUpWithPrimary() + { + _keyValueDb.TryCatchUpWithPrimary(); + } } } diff --git a/Libplanet.RocksDBStore/RocksDBStore.cs b/Libplanet.RocksDBStore/RocksDBStore.cs index 484fbade7dd..e3f741a730a 100644 --- a/Libplanet.RocksDBStore/RocksDBStore.cs +++ b/Libplanet.RocksDBStore/RocksDBStore.cs @@ -113,8 +113,6 @@ public class RocksDBStore : BaseStore private static readonly byte[] ChainBlockCommitKeyPrefix = { (byte)'M' }; private static readonly byte[] BlockCommitKeyPrefix = { (byte)'m' }; - private static readonly byte[] EmptyBytes = Array.Empty(); - private static readonly Codec Codec = new Codec(); private readonly ILogger _logger; @@ -123,8 +121,9 @@ public class RocksDBStore : BaseStore private readonly LruCache _blockCache; private readonly DbOptions _options; + private readonly ColumnFamilyOptions _colOptions; private readonly string _path; - private readonly bool _readonly; + private readonly RocksDBInstanceType _instanceType; private readonly int _txEpochUnitSeconds; private readonly int _blockEpochUnitSeconds; @@ -164,7 +163,8 @@ public class RocksDBStore : BaseStore /// containing blocks. 86,400 seconds by default. /// The capacity of the block and transaction /// RocksDB connection cache. 100 by default. - /// If it is true, it will open rocksdb in read-only mode. + /// Determines the instance type of the internal + /// instances. by default. public RocksDBStore( string path, int blockCacheSize = 512, @@ -175,7 +175,7 @@ public RocksDBStore( int txEpochUnitSeconds = 86400, int blockEpochUnitSeconds = 86400, int dbConnectionCacheSize = 100, - bool @readonly = false + RocksDBInstanceType type = RocksDBInstanceType.Primary ) { _logger = Log.ForContext(); @@ -197,7 +197,7 @@ public RocksDBStore( _indexCache = new LruCache>>(64); _path = path; - _readonly = @readonly; + _instanceType = type; _txEpochUnitSeconds = txEpochUnitSeconds > 0 ? txEpochUnitSeconds : throw new ArgumentException( @@ -210,44 +210,48 @@ public RocksDBStore( nameof(blockEpochUnitSeconds)); _options = new DbOptions() .SetCreateIfMissing(); + _colOptions = new ColumnFamilyOptions(); if (maxTotalWalSize is ulong maxTotalWalSizeValue) { _options = _options.SetMaxTotalWalSize(maxTotalWalSizeValue); + _colOptions = _colOptions.SetMaxTotalWalSize(maxTotalWalSizeValue); } if (keepLogFileNum is ulong keepLogFileNumValue) { _options = _options.SetKeepLogFileNum(keepLogFileNumValue); + _colOptions = _colOptions.SetKeepLogFileNum(keepLogFileNumValue); } if (maxLogFileSize is ulong maxLogFileSizeValue) { _options = _options.SetMaxLogFileSize(maxLogFileSizeValue); + _colOptions = _colOptions.SetMaxLogFileSize(maxLogFileSizeValue); } _blockIndexDb = RocksDBUtils.OpenRocksDb( - _options, BlockDbPath(BlockIndexDbName), @readonly: _readonly); + _options, BlockDbPath(BlockIndexDbName), type: _instanceType); _blockPerceptionDb = RocksDBUtils.OpenRocksDb( - _options, RocksDbPath(BlockPerceptionDbName), @readonly: _readonly); + _options, RocksDbPath(BlockPerceptionDbName), type: _instanceType); _txIndexDb = RocksDBUtils.OpenRocksDb( - _options, TxDbPath(TxIndexDbName), @readonly: _readonly); + _options, TxDbPath(TxIndexDbName), type: _instanceType); _txExecutionDb = RocksDBUtils.OpenRocksDb( - _options, RocksDbPath(TxExecutionDbName), @readonly: _readonly); + _options, RocksDbPath(TxExecutionDbName), type: _instanceType); _txIdBlockHashIndexDb = RocksDBUtils.OpenRocksDb( - _options, RocksDbPath(TxIdBlockHashIndexDbName), @readonly: _readonly); + _options, RocksDbPath(TxIdBlockHashIndexDbName), type: _instanceType); _blockCommitDb = RocksDBUtils.OpenRocksDb( - _options, RocksDbPath(BlockCommitDbName), @readonly: _readonly); + _options, RocksDbPath(BlockCommitDbName), type: _instanceType); // When opening a DB in a read-write mode, you need to specify all Column Families that // currently exist in a DB. https://github.com/facebook/rocksdb/wiki/Column-Families var chainDbColumnFamilies = GetColumnFamilies(_options, ChainDbName); _chainDb = RocksDBUtils.OpenRocksDb( - _options, RocksDbPath(ChainDbName), chainDbColumnFamilies, _readonly); + _options, RocksDbPath(ChainDbName), chainDbColumnFamilies, _instanceType); _rwTxLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); _rwBlockLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); @@ -270,12 +274,13 @@ public RocksDBStore( public static bool MigrateChainDBFromColumnFamilies(string path) { var opt = new DbOptions(); + var colOpt = new ColumnFamilyOptions(); opt.SetCreateIfMissing(); List cfns = RocksDb.ListColumnFamilies(opt, path).ToList(); var cfs = new ColumnFamilies(); foreach (string name in cfns) { - cfs.Add(name, opt); + cfs.Add(name, colOpt); } RocksDb db = RocksDb.Open(opt, path, cfs); @@ -688,7 +693,7 @@ public override Transaction GetTransaction(TxId txid) if (!_txDbCache.TryGetValue(txDbName, out txDb)) { txDb = RocksDBUtils.OpenRocksDb( - _options, TxDbPath(txDbName), @readonly: _readonly); + _options, TxDbPath(txDbName), type: _instanceType); _txDbCache.AddOrUpdate(txDbName, txDb); } } @@ -736,7 +741,7 @@ public override void PutTransaction(Transaction tx) if (!_txDbCache.TryGetValue(txDbName, out txDb)) { txDb = RocksDBUtils.OpenRocksDb( - _options, TxDbPath(txDbName), @readonly: _readonly); + _options, TxDbPath(txDbName), type: _instanceType); _txDbCache.AddOrUpdate(txDbName, txDb); } } @@ -815,7 +820,7 @@ public override IEnumerable IterateBlockHashes() if (!_blockDbCache.TryGetValue(blockDbName, out blockDb)) { blockDb = RocksDBUtils.OpenRocksDb( - _options, BlockDbPath(blockDbName), @readonly: _readonly); + _options, BlockDbPath(blockDbName), type: _instanceType); _blockDbCache.AddOrUpdate(blockDbName, blockDb); } } @@ -874,7 +879,7 @@ public override void PutBlock(Block block) if (!_blockDbCache.TryGetValue(blockDbName, out blockDb)) { blockDb = RocksDBUtils.OpenRocksDb( - _options, BlockDbPath(blockDbName), @readonly: _readonly); + _options, BlockDbPath(blockDbName), type: _instanceType); _blockDbCache.AddOrUpdate(blockDbName, blockDb); } } @@ -916,7 +921,7 @@ public override bool DeleteBlock(BlockHash blockHash) if (!_blockDbCache.TryGetValue(blockDbName, out blockDb)) { blockDb = RocksDBUtils.OpenRocksDb( - _options, BlockDbPath(blockDbName), @readonly: _readonly); + _options, BlockDbPath(blockDbName), type: _instanceType); _blockDbCache.AddOrUpdate(blockDbName, blockDb); } } @@ -1367,7 +1372,9 @@ private static (IStore Store, IStateStore StateStore) Loader(Uri storeUri) int txEpochUnitSeconds = query.GetInt32("tx-epoch-unit-secs", 86400); int blockEpochUnitSeconds = query.GetInt32("block-epoch-unit-secs", 86400); int dbConnectionCacheSize = query.GetInt32("connection-cache", 100); - bool @readonly = query.GetBoolean("readonly", false); + RocksDBInstanceType instanceType = query.GetEnum( + "instance-type", RocksDBInstanceType.Primary); + string statesKvPath = query.Get("states-dir") ?? StatesKvPathDefault; var store = new RocksDBStore( storeUri.LocalPath, @@ -1379,10 +1386,12 @@ private static (IStore Store, IStateStore StateStore) Loader(Uri storeUri) txEpochUnitSeconds, blockEpochUnitSeconds, dbConnectionCacheSize, - @readonly); + instanceType); string statesDirPath = Path.Combine(storeUri.LocalPath, statesKvPath); var stateStore = new TrieStateStore( - new RocksDBKeyValueStore(statesDirPath, @readonly)); + new RocksDBKeyValueStore( + statesDirPath, + instanceType)); return (store, stateStore); } @@ -1521,7 +1530,7 @@ private ColumnFamilies GetColumnFamilies(DbOptions options, string dbName) foreach (string name in listColumnFamilies) { - columnFamilies.Add(name, _options); + columnFamilies.Add(name, _colOptions); } return columnFamilies; diff --git a/Libplanet.RocksDBStore/RocksDBUtils.cs b/Libplanet.RocksDBStore/RocksDBUtils.cs index fe9e033882b..8563d7433a5 100644 --- a/Libplanet.RocksDBStore/RocksDBUtils.cs +++ b/Libplanet.RocksDBStore/RocksDBUtils.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using RocksDbSharp; @@ -9,23 +10,40 @@ internal static RocksDb OpenRocksDb( DbOptions options, string dbPath, ColumnFamilies? columnFamilies = null, - bool @readonly = false) + RocksDBInstanceType type = RocksDBInstanceType.Primary) { if (!Directory.Exists(dbPath)) { Directory.CreateDirectory(dbPath); } - if (@readonly) + return type switch { - return columnFamilies is null + RocksDBInstanceType.Primary => columnFamilies is null + ? RocksDb.Open(options, dbPath) : RocksDb.Open(options, dbPath, columnFamilies), + RocksDBInstanceType.ReadOnly => columnFamilies is null ? RocksDb.OpenReadOnly(options, dbPath, false) - : RocksDb.OpenReadOnly(options, dbPath, columnFamilies, false); + : RocksDb.OpenReadOnly(options, dbPath, columnFamilies, false), + RocksDBInstanceType.Secondary => columnFamilies is null + ? RocksDb.OpenAsSecondary(options, dbPath, CreateSecondaryPath(dbPath)) + : RocksDb.OpenAsSecondary( + options, + dbPath, + CreateSecondaryPath(dbPath), + columnFamilies), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null), + }; + } + + private static string CreateSecondaryPath(string dbPath) + { + string secondaryPath = Path.Combine(dbPath, $"SECONDARY-{Guid.NewGuid()}"); + if (!Directory.Exists(secondaryPath)) + { + Directory.CreateDirectory(secondaryPath); } - return columnFamilies is null - ? RocksDb.Open(options, dbPath) - : RocksDb.Open(options, dbPath, columnFamilies); + return secondaryPath; } } } diff --git a/Libplanet.Store/HashNodeCache.cs b/Libplanet.Store/HashNodeCache.cs new file mode 100644 index 00000000000..02db81f3ee1 --- /dev/null +++ b/Libplanet.Store/HashNodeCache.cs @@ -0,0 +1,88 @@ +using System.Diagnostics; +using System.Security.Cryptography; +using System.Threading; +using Bencodex.Types; +using Libplanet.Common; +using Libplanet.Store.Trie; +using LruCacheNet; +using Serilog; + +namespace Libplanet.Store +{ + /// + /// A class used for internally caching hashed nodes of s. + /// + public class HashNodeCache + { + // FIXME: Tuned to 9c mainnet. Should be refactored to accept cache size as an argument. + private const int _cacheSize = 1_048_576; + private const int _reportPeriod = 60_000; + + private LruCache, IValue> _cache; + private Stopwatch _stopwatch; + private int _attempts; + private int _hits; + private object _reportLock; + + internal HashNodeCache() + { + _cache = new LruCache, IValue>(_cacheSize); + _stopwatch = new Stopwatch(); + _attempts = 0; + _hits = 0; + _reportLock = new object(); + _stopwatch.Start(); + } + + public bool TryGetValue(HashDigest hash, out IValue? value) + { + lock (_reportLock) + { + Report(); + } + + Interlocked.Increment(ref _attempts); + if (_cache.TryGetValue(hash, out value)) + { + Interlocked.Increment(ref _hits); + return true; + } + else + { + value = null; + return false; + } + } + + public void AddOrUpdate(HashDigest hash, IValue value) + { + _cache.AddOrUpdate(hash, value); + } + + private void Report() + { + long period = _stopwatch.ElapsedMilliseconds; + if (period > _reportPeriod) + { + Log + .ForContext("Source", nameof(HashNodeCache)) + .ForContext("Tag", "Metric") + .ForContext("Subtag", "HashNodeCacheReport") + .Debug( + "Successfully fetched {HitCount} cached values out of last " + + "{AttemptCount} attempts with hitrate of {HitRate} and " + + "{CacheUsed}/{CacheSize} cache in use during last {PeriodMs} ms", + _hits, + _attempts, + (double)_hits / _attempts, + _cache.Count, + _cache.Capacity, + period); + + _stopwatch.Restart(); + Interlocked.Exchange(ref _attempts, 0); + Interlocked.Exchange(ref _hits, 0); + } + } + } +} diff --git a/Libplanet.Store/Trie/MerkleTrie.cs b/Libplanet.Store/Trie/MerkleTrie.cs index c5b0317d242..c2f5d04c770 100644 --- a/Libplanet.Store/Trie/MerkleTrie.cs +++ b/Libplanet.Store/Trie/MerkleTrie.cs @@ -22,6 +22,8 @@ public partial class MerkleTrie : ITrie private static readonly Codec _codec; + private readonly HashNodeCache _cache; + static MerkleTrie() { _codec = new Codec(); @@ -36,10 +38,12 @@ static MerkleTrie() /// nodes. /// The root of /// . + /// The to use as cache. public MerkleTrie( IKeyValueStore keyValueStore, - HashDigest rootHash) - : this(keyValueStore, new HashNode(rootHash)) + HashDigest rootHash, + HashNodeCache? cache = null) + : this(keyValueStore, new HashNode(rootHash), cache) { } @@ -50,13 +54,18 @@ public MerkleTrie( /// nodes. /// The root node of . If it is /// , it will be treated like empty trie. - public MerkleTrie(IKeyValueStore keyValueStore, INode? root = null) + /// The to use as cache. + public MerkleTrie( + IKeyValueStore keyValueStore, + INode? root = null, + HashNodeCache? cache = null) { // FIXME: It might be a good idea to have something like IReadOnlyKeyValueStore. KeyValueStore = keyValueStore; Root = root is HashNode hashNode && hashNode.HashDigest.Equals(EmptyRootHash) ? null : root; + _cache = cache ?? new HashNodeCache(); } /// @@ -84,7 +93,7 @@ public ITrie Set(in KeyBytes key, IValue value) new ValueNode(value), true); - return new MerkleTrie(KeyValueStore, newRootNode); + return new MerkleTrie(KeyValueStore, newRootNode, _cache); } /// @@ -439,8 +448,18 @@ private INode InsertToHashNode( /// private INode UnhashNode(HashNode hashNode) { - IValue intermediateEncoding = _codec.Decode( - KeyValueStore.Get(new KeyBytes(hashNode.HashDigest.ByteArray))); + IValue intermediateEncoding; + if (_cache.TryGetValue(hashNode.HashDigest, out var value)) + { + intermediateEncoding = value!; + } + else + { + intermediateEncoding = + _codec.Decode(KeyValueStore.Get(new KeyBytes(hashNode.HashDigest.ByteArray))); + _cache.AddOrUpdate(hashNode.HashDigest, intermediateEncoding); + } + return NodeDecoder.Decode(intermediateEncoding, NodeDecoder.HashEmbeddedNodeType) ?? throw new NullReferenceException(); } diff --git a/Libplanet.Store/TrieStateStore.Commit.cs b/Libplanet.Store/TrieStateStore.Commit.cs index e9f33ed9fbe..615e93e5121 100644 --- a/Libplanet.Store/TrieStateStore.Commit.cs +++ b/Libplanet.Store/TrieStateStore.Commit.cs @@ -29,23 +29,25 @@ public ITrie Commit(ITrie trie) else { var writeBatch = new WriteBatch(StateKeyValueStore, 4096); - INode newRoot = Commit(root, writeBatch); + INode newRoot = Commit(root, writeBatch, _cache); // It assumes embedded node if it's not HashNode. if (!(newRoot is HashNode)) { - byte[] serialized = _codec.Encode(newRoot.ToBencodex()); - writeBatch.Add( - new KeyBytes(SHA256.Create().ComputeHash(serialized)), serialized); + IValue bencoded = newRoot.ToBencodex(); + byte[] serialized = _codec.Encode(bencoded); + byte[] hash = SHA256.Create().ComputeHash(serialized); + + writeBatch.Add(new KeyBytes(hash), serialized); } writeBatch.Flush(); - return new MerkleTrie(StateKeyValueStore, newRoot); + return new MerkleTrie(StateKeyValueStore, newRoot, _cache); } } - private static INode Commit(INode node, WriteBatch writeBatch) + private static INode Commit(INode node, WriteBatch writeBatch, HashNodeCache cache) { switch (node) { @@ -54,23 +56,24 @@ private static INode Commit(INode node, WriteBatch writeBatch) return node; case FullNode fullNode: - return CommitFullNode(fullNode, writeBatch); + return CommitFullNode(fullNode, writeBatch, cache); case ShortNode shortNode: - return CommitShortNode(shortNode, writeBatch); + return CommitShortNode(shortNode, writeBatch, cache); case ValueNode valueNode: - return CommitValueNode(valueNode, writeBatch); + return CommitValueNode(valueNode, writeBatch, cache); default: throw new NotSupportedException("Not supported node came."); } } - private static INode CommitFullNode(FullNode fullNode, WriteBatch writeBatch) + private static INode CommitFullNode( + FullNode fullNode, WriteBatch writeBatch, HashNodeCache cache) { var virtualChildren = fullNode.Children - .Select(c => c is null ? null : Commit(c, writeBatch)) + .Select(c => c is null ? null : Commit(c, writeBatch, cache)) .ToImmutableArray(); fullNode = new FullNode(virtualChildren); @@ -81,13 +84,14 @@ private static INode CommitFullNode(FullNode fullNode, WriteBatch writeBatch) return fullNode; } - return Write(fullNode.ToBencodex(), writeBatch); + return Write(fullNode.ToBencodex(), writeBatch, cache); } - private static INode CommitShortNode(ShortNode shortNode, WriteBatch writeBatch) + private static INode CommitShortNode( + ShortNode shortNode, WriteBatch writeBatch, HashNodeCache cache) { // FIXME: Assumes value is not null. - var committedValueNode = Commit(shortNode.Value!, writeBatch); + var committedValueNode = Commit(shortNode.Value!, writeBatch, cache); shortNode = new ShortNode(shortNode.Key, committedValueNode); IValue encoded = shortNode.ToBencodex(); if (encoded.EncodingLength <= HashDigest.Size) @@ -95,10 +99,11 @@ private static INode CommitShortNode(ShortNode shortNode, WriteBatch writeBatch) return shortNode; } - return Write(encoded, writeBatch); + return Write(encoded, writeBatch, cache); } - private static INode CommitValueNode(ValueNode valueNode, WriteBatch writeBatch) + private static INode CommitValueNode( + ValueNode valueNode, WriteBatch writeBatch, HashNodeCache cache) { IValue encoded = valueNode.ToBencodex(); var nodeSize = encoded.EncodingLength; @@ -107,7 +112,7 @@ private static INode CommitValueNode(ValueNode valueNode, WriteBatch writeBatch) return valueNode; } - return Write(encoded, writeBatch); + return Write(encoded, writeBatch, cache); } /// @@ -118,10 +123,13 @@ private static INode CommitValueNode(ValueNode valueNode, WriteBatch writeBatch) /// A batched writer to use for performance reasons. /// A already written to storage with /// embedded inside. - private static HashNode Write(IValue bencodedNode, WriteBatch writeBatch) + /// A to cache nodes. + private static HashNode Write( + IValue bencodedNode, WriteBatch writeBatch, HashNodeCache cache) { byte[] serialized = _codec.Encode(bencodedNode); var nodeHash = HashDigest.DeriveFrom(serialized); + cache.AddOrUpdate(nodeHash, bencodedNode); writeBatch.Add(new KeyBytes(nodeHash.ByteArray), serialized); return new HashNode(nodeHash); } diff --git a/Libplanet.Store/TrieStateStore.cs b/Libplanet.Store/TrieStateStore.cs index 0b8ae337ec1..62efeb5db50 100644 --- a/Libplanet.Store/TrieStateStore.cs +++ b/Libplanet.Store/TrieStateStore.cs @@ -15,6 +15,7 @@ namespace Libplanet.Store public partial class TrieStateStore : IStateStore { private readonly ILogger _logger; + private readonly HashNodeCache _cache; private bool _disposed = false; /// @@ -25,6 +26,7 @@ public partial class TrieStateStore : IStateStore public TrieStateStore(IKeyValueStore stateKeyValueStore) { StateKeyValueStore = stateKeyValueStore; + _cache = new HashNodeCache(); _logger = Log.ForContext(); } @@ -122,7 +124,8 @@ public void CopyStates( public ITrie GetStateRoot(HashDigest? stateRootHash) => new MerkleTrie( StateKeyValueStore, - stateRootHash is { } h2 ? new HashNode(h2) : null); + stateRootHash is { } h2 ? new HashNode(h2) : null, + _cache); /// public void Dispose() diff --git a/Libplanet.Tests/Action/AccountDiffTest.cs b/Libplanet.Tests/Action/AccountDiffTest.cs index a47fdec6bb6..6849fe183a2 100644 --- a/Libplanet.Tests/Action/AccountDiffTest.cs +++ b/Libplanet.Tests/Action/AccountDiffTest.cs @@ -40,14 +40,14 @@ public void EmptyAccountStateSource() Assert.Empty(diff.TotalSupplyDiffs); Assert.Null(diff.ValidatorSetDiff); - IAccount targetAccount = new Account(new AccountBaseState(targetTrie)); + IAccount targetAccount = new Account(new AccountState(targetTrie)); PrivateKey signer = new PrivateKey(); - IActionContext context = CreateActionContext(signer.ToAddress(), targetTrie); + IActionContext context = CreateActionContext(signer.Address, targetTrie); targetAccount = targetAccount.MintAsset( - context, signer.ToAddress(), new FungibleAssetValue(USD, 123, 45)); - targetAccount = targetAccount.SetState(signer.ToAddress(), new Text("Foo")); + context, signer.Address, new FungibleAssetValue(USD, 123, 45)); + targetAccount = targetAccount.SetState(signer.Address, new Text("Foo")); - targetTrie = Commit(stateStore, targetTrie, targetAccount.Delta); + targetTrie = stateStore.Commit(targetAccount.Trie); diff = AccountDiff.Create(targetTrie, sourceTrie); Assert.Empty(diff.StateDiffs); @@ -73,31 +73,31 @@ public void Diff() Assert.Empty(diff.TotalSupplyDiffs); Assert.Null(diff.ValidatorSetDiff); - IAccount targetAccount = new Account(new AccountBaseState(targetTrie)); + IAccount targetAccount = new Account(new AccountState(targetTrie)); PrivateKey signer = new PrivateKey(); - IActionContext context = CreateActionContext(signer.ToAddress(), targetTrie); + IActionContext context = CreateActionContext(signer.Address, targetTrie); targetAccount = targetAccount.SetState(addr1, new Text("One")); targetAccount = targetAccount.SetState(addr2, new Text("Two")); targetAccount = targetAccount.MintAsset( - context, signer.ToAddress(), new FungibleAssetValue(USD, 123, 45)); + context, signer.Address, new FungibleAssetValue(USD, 123, 45)); + targetTrie = stateStore.Commit(targetAccount.Trie); - targetTrie = Commit(stateStore, targetTrie, targetAccount.Delta); sourceTrie = targetTrie; - IAccount sourceAccount = new Account(new AccountBaseState(sourceTrie)); + IAccount sourceAccount = new Account(new AccountState(sourceTrie)); sourceAccount = sourceAccount.SetState(addr2, new Text("Two_")); sourceAccount = sourceAccount.SetState(addr3, new Text("Three")); sourceAccount = sourceAccount.MintAsset( - context, signer.ToAddress(), new FungibleAssetValue(USD, 456, 78)); + context, signer.Address, new FungibleAssetValue(USD, 456, 78)); sourceAccount = sourceAccount.MintAsset( - context, signer.ToAddress(), new FungibleAssetValue(KRW, 10, 0)); + context, signer.Address, new FungibleAssetValue(KRW, 10, 0)); sourceAccount = sourceAccount.BurnAsset( - context, signer.ToAddress(), new FungibleAssetValue(KRW, 10, 0)); + context, signer.Address, new FungibleAssetValue(KRW, 10, 0)); sourceAccount = sourceAccount.MintAsset( - context, signer.ToAddress(), new FungibleAssetValue(JPY, 321, 0)); + context, signer.Address, new FungibleAssetValue(JPY, 321, 0)); sourceAccount = sourceAccount.SetValidator(new Validator(signer.PublicKey, 1)); - sourceTrie = Commit(stateStore, sourceTrie, sourceAccount.Delta); + sourceTrie = stateStore.Commit(sourceAccount.Trie); diff = AccountDiff.Create(targetTrie, sourceTrie); Assert.Equal(2, diff.StateDiffs.Count); @@ -107,10 +107,10 @@ public void Diff() Assert.Equal(2, diff.FungibleAssetValueDiffs.Count); // KRW is treated as unchanged Assert.Equal( (new Integer(12345), new Integer(12345 + 45678)), - diff.FungibleAssetValueDiffs[(signer.ToAddress(), USD.Hash)]); + diff.FungibleAssetValueDiffs[(signer.Address, USD.Hash)]); Assert.Equal( (new Integer(0), new Integer(321)), - diff.FungibleAssetValueDiffs[(signer.ToAddress(), JPY.Hash)]); + diff.FungibleAssetValueDiffs[(signer.Address, JPY.Hash)]); Assert.Equal(2, diff.TotalSupplyDiffs.Count); // KRW is treated as unchanged Assert.Equal( @@ -133,7 +133,7 @@ public void Diff() Assert.Single(diff.FungibleAssetValueDiffs); // Only USD is tracked Assert.Equal( (new Integer(12345 + 45678), new Integer(12345)), - diff.FungibleAssetValueDiffs[(signer.ToAddress(), USD.Hash)]); + diff.FungibleAssetValueDiffs[(signer.Address, USD.Hash)]); Assert.Single(diff.TotalSupplyDiffs); // Only USD is tracked Assert.Equal( (new Integer(12345 + 45678), new Integer(12345)), @@ -153,23 +153,6 @@ public IActionContext CreateActionContext(Address signer, ITrie trie) => trie, new TrieStateStore(new MemoryKeyValueStore()))), 0, - 0, - false); - - public ITrie Commit( - IStateStore stateStore, - ITrie baseTrie, - IAccountDelta accountDelta) - { - var trie = baseTrie; - var rawDelta = accountDelta.ToRawDelta(); - - foreach (var kv in rawDelta) - { - trie = trie.Set(kv.Key, kv.Value); - } - - return stateStore.Commit(trie); - } + 0); } } diff --git a/Libplanet.Tests/Action/AccountTest.cs b/Libplanet.Tests/Action/AccountTest.cs index 0ce9e29055e..1ab2fa4d235 100644 --- a/Libplanet.Tests/Action/AccountTest.cs +++ b/Libplanet.Tests/Action/AccountTest.cs @@ -28,6 +28,8 @@ public abstract class AccountTest protected readonly Currency[] _currencies; protected readonly IAccount _initAccount; protected readonly IActionContext _initContext; + protected readonly Address _accountAddress + = new Address("2000000000000000000000000000000000000000"); protected AccountTest(ITestOutputHelper output) { @@ -38,7 +40,7 @@ protected AccountTest(ITestOutputHelper output) new PrivateKey(), }; - _addr = _keys.Select(AddressExtensions.ToAddress).ToArray(); + _addr = _keys.Select(key => key.Address).ToArray(); _currencies = new[] { @@ -92,9 +94,6 @@ public abstract IActionContext CreateContext( [Fact] public virtual void NullDelta() { - Assert.Empty(_initAccount.Delta.UpdatedAddresses); - Assert.Empty(_initAccount.Delta.StateUpdatedAddresses); - Assert.Empty(_initAccount.Delta.UpdatedFungibleAssets); Assert.Equal("a", (Text)_initAccount.GetState(_addr[0])); Assert.Equal("b", (Text)_initAccount.GetState(_addr[1])); Assert.Null(_initAccount.GetState(_addr[2])); @@ -113,22 +112,21 @@ public virtual void NullDelta() public virtual void States() { IAccount a = _initAccount.SetState(_addr[0], (Text)"A"); + AccountDiff diffa = AccountDiff.Create(_initAccount.Trie, a.Trie); Assert.Equal("A", (Text)a.GetState(_addr[0])); Assert.Equal("a", (Text)_initAccount.GetState(_addr[0])); Assert.Equal("b", (Text)a.GetState(_addr[1])); Assert.Equal("b", (Text)_initAccount.GetState(_addr[1])); Assert.Null(a.GetState(_addr[2])); Assert.Null(_initAccount.GetState(_addr[2])); - Assert.Equal(new[] { _addr[0] }.ToImmutableHashSet(), a.Delta.StateUpdatedAddresses); - Assert.Equal(a.Delta.StateUpdatedAddresses, a.Delta.UpdatedAddresses); - Assert.Empty(a.Delta.UpdatedFungibleAssets); - Assert.Empty(a.Delta.UpdatedTotalSupplyCurrencies); - Assert.Empty(_initAccount.Delta.UpdatedAddresses); - Assert.Empty(_initAccount.Delta.StateUpdatedAddresses); - Assert.Empty(_initAccount.Delta.UpdatedFungibleAssets); - Assert.Empty(_initAccount.Delta.UpdatedTotalSupplyCurrencies); + Assert.Equal( + _addr[0], + Assert.Single(diffa.StateDiffs).Key); + Assert.Empty(diffa.FungibleAssetValueDiffs); + Assert.Empty(diffa.TotalSupplyDiffs); IAccount b = a.SetState(_addr[0], (Text)"z"); + AccountDiff diffb = AccountDiff.Create(a.Trie, b.Trie); Assert.Equal("z", (Text)b.GetState(_addr[0])); Assert.Equal("A", (Text)a.GetState(_addr[0])); Assert.Equal("a", (Text)_initAccount.GetState(_addr[0])); @@ -136,16 +134,15 @@ public virtual void States() Assert.Equal("b", (Text)a.GetState(_addr[1])); Assert.Null(b.GetState(_addr[2])); Assert.Null(a.GetState(_addr[2])); - Assert.Equal(new[] { _addr[0] }.ToImmutableHashSet(), a.Delta.StateUpdatedAddresses); - Assert.Equal(a.Delta.StateUpdatedAddresses, a.Delta.UpdatedAddresses); - Assert.Empty(_initAccount.Delta.UpdatedAddresses); - Assert.Empty(_initAccount.Delta.StateUpdatedAddresses); + Assert.Equal( + _addr[0], + Assert.Single(diffb.StateDiffs).Key); + Assert.Empty(diffb.FungibleAssetValueDiffs); + Assert.Empty(diffb.TotalSupplyDiffs); IAccount c = b.SetState(_addr[0], (Text)"a"); Assert.Equal("a", (Text)c.GetState(_addr[0])); Assert.Equal("z", (Text)b.GetState(_addr[0])); - Assert.Empty(_initAccount.Delta.UpdatedAddresses); - Assert.Empty(_initAccount.Delta.StateUpdatedAddresses); } [Fact] @@ -162,19 +159,19 @@ public virtual void FungibleAssets() Assert.Equal(Value(1, 15), a.GetBalance(_addr[1], _currencies[1])); Assert.Equal(Zero(0), a.GetBalance(_addr[2], _currencies[0])); Assert.Equal(Zero(1), a.GetBalance(_addr[2], _currencies[1])); + + var diff = a.Trie + .Diff(_initContext.PreviousState.Trie) + .ToDictionary(triple => triple.Path, triple => triple.SourceValue); + var accountDiff = AccountDiff.Create( + _initContext.PreviousState.GetAccount(_accountAddress).Trie, + a.Trie); Assert.Equal( - ImmutableHashSet<(Address, Currency)>.Empty - .Add((_addr[1], _currencies[2])) - .Add((_addr[2], _currencies[2])), - a.Delta.UpdatedFungibleAssets); - Assert.Equal( - a.Delta.UpdatedFungibleAssets.Select(pair => pair.Item1).ToImmutableHashSet(), - a.Delta.UpdatedAddresses); - Assert.Empty(a.Delta.StateUpdatedAddresses); - Assert.Empty(_initAccount.Delta.UpdatedAddresses); - Assert.Empty(_initAccount.Delta.StateUpdatedAddresses); - Assert.Empty(_initAccount.Delta.UpdatedFungibleAssets); - Assert.Empty(_initAccount.Delta.UpdatedTotalSupplyCurrencies); + new[] { (_addr[1], _currencies[2].Hash), (_addr[2], _currencies[2].Hash) } + .ToImmutableHashSet(), + accountDiff.FungibleAssetValueDiffs.Select(kv => (kv.Key.Item1, kv.Key.Item2)) + .ToImmutableHashSet()); + Assert.Empty(accountDiff.StateDiffs); } [Fact] diff --git a/Libplanet.Tests/Action/AccountV0Test.cs b/Libplanet.Tests/Action/AccountV0Test.cs index b2de5edc7fc..82ce6ef82f2 100644 --- a/Libplanet.Tests/Action/AccountV0Test.cs +++ b/Libplanet.Tests/Action/AccountV0Test.cs @@ -13,9 +13,6 @@ namespace Libplanet.Tests.Action { public class AccountV0Test : AccountTest { - private readonly Address _accountAddress - = new Address("2000000000000000000000000000000000000000"); - public AccountV0Test(ITestOutputHelper output) : base(output) { diff --git a/Libplanet.Tests/Action/AccountV1Test.cs b/Libplanet.Tests/Action/AccountV1Test.cs index 25c31e782d1..f7ccc405b72 100644 --- a/Libplanet.Tests/Action/AccountV1Test.cs +++ b/Libplanet.Tests/Action/AccountV1Test.cs @@ -1,12 +1,11 @@ -using System.Collections.Generic; using System.Linq; +using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Action.Tests.Common; using Libplanet.Action.Tests.Mocks; using Libplanet.Blockchain; using Libplanet.Crypto; -using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Xunit; @@ -16,9 +15,6 @@ namespace Libplanet.Tests.Action { public class AccountV1Test : AccountTest { - private readonly Address _accountAddress - = new Address("2000000000000000000000000000000000000000"); - public AccountV1Test(ITestOutputHelper output) : base(output) { @@ -96,41 +92,32 @@ public void TotalSupplyTracking() { IAccount account = _initAccount; IActionContext context = _initContext; - - Assert.Empty(account.GetUpdatedTotalSupplies()); - Assert.Empty(account.Delta.UpdatedTotalSupplyCurrencies); + AccountDiff diff = AccountDiff.Create(_initAccount.Trie, account.Trie); Assert.Throws(() => - _initAccount.GetTotalSupply(_currencies[0])); - Assert.DoesNotContain( - new KeyValuePair( - _currencies[0], Value(0, 5)), - account.GetUpdatedTotalSupplies()); - Assert.DoesNotContain(_currencies[0], account.Delta.UpdatedTotalSupplyCurrencies); + account.GetTotalSupply(_currencies[0])); + Assert.DoesNotContain(_currencies[0].Hash, diff.TotalSupplyDiffs.Keys); Assert.Equal(Value(4, 0), _initAccount.GetTotalSupply(_currencies[4])); - Assert.DoesNotContain(_currencies[4], account.Delta.UpdatedTotalSupplyCurrencies); + Assert.DoesNotContain(_currencies[4].Hash, diff.TotalSupplyDiffs.Keys); account = account.MintAsset(context, _addr[0], Value(0, 10)); + diff = AccountDiff.Create(_initAccount.Trie, account.Trie); Assert.Throws(() => - _initAccount.GetTotalSupply(_currencies[0])); - Assert.DoesNotContain(_currencies[0], account.Delta.UpdatedTotalSupplyCurrencies); + account.GetTotalSupply(_currencies[0])); + Assert.DoesNotContain(_currencies[0].Hash, diff.TotalSupplyDiffs.Keys); account = account.MintAsset(context, _addr[0], Value(4, 10)); + diff = AccountDiff.Create(_initAccount.Trie, account.Trie); Assert.Equal(Value(4, 10), account.GetTotalSupply(_currencies[4])); - Assert.Contains( - new KeyValuePair( - _currencies[4], Value(4, 10)), - account.GetUpdatedTotalSupplies()); - Assert.Contains(_currencies[4], account.Delta.UpdatedTotalSupplyCurrencies); + Assert.Contains(_currencies[4].Hash, diff.TotalSupplyDiffs.Keys); + Assert.Equal((Integer)10, diff.TotalSupplyDiffs[_currencies[4].Hash].Item2); account = account.BurnAsset(context, _addr[0], Value(4, 5)); + diff = AccountDiff.Create(_initAccount.Trie, account.Trie); Assert.Equal(Value(4, 5), account.GetTotalSupply(_currencies[4])); - Assert.Contains( - new KeyValuePair( - _currencies[4], Value(4, 5)), - account.GetUpdatedTotalSupplies()); - Assert.Contains(_currencies[4], account.Delta.UpdatedTotalSupplyCurrencies); + Assert.Contains(_currencies[4].Hash, diff.TotalSupplyDiffs.Keys); + Assert.Equal((Integer)5, diff.TotalSupplyDiffs[_currencies[4].Hash].Item2); } [Fact] @@ -155,9 +142,6 @@ public virtual void TotalUpdatedFungibleAssets() Assert.DoesNotContain( (_addr[0], Value(1, 0).Currency), delta0.TotalUpdatedFungibleAssets); - // Forcefully create null delta - delta0 = Account.Flush(delta0); - // currencies[1] (BAR) allows _addr[0] & _addr[1] to mint and burn delta0 = delta0.MintAsset(context0, _addr[0], Value(1, 1)); Assert.Contains( @@ -167,8 +151,6 @@ public virtual void TotalUpdatedFungibleAssets() Assert.DoesNotContain( _addr[1], delta0.TotalUpdatedFungibleAssets.Select(pair => pair.Item1)); - // Forcefully create null delta - delta0 = Account.Flush(delta0); context0 = CreateContext(delta0, _addr[1]); delta0 = delta0.BurnAsset(context0, _addr[1], Value(1, 1)); diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index a3abb28556b..d880354e682 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -9,7 +9,6 @@ using Libplanet.Action; using Libplanet.Action.Loader; using Libplanet.Action.State; -using Libplanet.Action.Tests; using Libplanet.Action.Tests.Common; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -27,6 +26,7 @@ using Serilog; using Xunit; using Xunit.Abstractions; +using static Libplanet.Action.State.KeyConverters; using static Libplanet.Tests.TestUtils; namespace Libplanet.Tests.Action @@ -64,7 +64,7 @@ public void Idempotent() const int repeatCount = 2; var signer = new PrivateKey(); var timestamp = DateTimeOffset.UtcNow; - var txAddress = signer.ToAddress(); + var txAddress = signer.Address; var txs = new[] { Transaction.Create( @@ -79,7 +79,7 @@ public void Idempotent() protocolVersion: Block.CurrentProtocolVersion, index: 0, timestamp: timestamp, - miner: GenesisProposer.PublicKey.ToAddress(), + miner: GenesisProposer.Address, publicKey: GenesisProposer.PublicKey, previousHash: null, txHash: BlockContent.DeriveTxHash(txs), @@ -125,14 +125,14 @@ public void Idempotent() public void Evaluate() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; long blockIndex = 1; var action = new EvaluateTestAction() { - BlockIndexKey = new PrivateKey().ToAddress(), - MinerKey = new PrivateKey().ToAddress(), - SignerKey = new PrivateKey().ToAddress(), + BlockIndexKey = new PrivateKey().Address, + MinerKey = new PrivateKey().Address, + SignerKey = new PrivateKey().Address, }; var store = new MemoryStore(); @@ -165,7 +165,7 @@ public void Evaluate() Assert.Equal( chain.GetWorldState() .GetAccount(ReservedAddresses.LegacyAccount).GetState(action.MinerKey), - (Text)miner.ToAddress().ToHex()); + (Text)miner.Address.ToHex()); var state = chain.GetWorldState() .GetAccount(ReservedAddresses.LegacyAccount).GetState(action.BlockIndexKey); Assert.Equal((long)(Integer)state, blockIndex); @@ -175,9 +175,9 @@ public void Evaluate() public void EvaluateWithException() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; - var action = new ThrowException { ThrowOnRehearsal = false, ThrowOnExecution = true }; + var action = new ThrowException { ThrowOnExecution = true }; var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); @@ -210,11 +210,10 @@ public void EvaluateWithException() public void EvaluateWithCriticalException() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var action = new ThrowException { - ThrowOnRehearsal = false, ThrowOnExecution = true, Deterministic = false, }; @@ -264,7 +263,6 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu return new DumbAction( targetAddress: address, item: identifier.ToString(), - recordRehearsal: false, recordRandom: true, transfer: transferTo is Address to ? Tuple.Create(address, to, 5) @@ -288,32 +286,50 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu Transaction[] block1Txs = { - Transaction.Create( - nonce: 0, - privateKey: _txFx.PrivateKey1, - genesisHash: genesis.Hash, - actions: new[] - { - MakeAction(addresses[0], 'A', addresses[1]), - MakeAction(addresses[1], 'B', addresses[2]), - }.ToPlainValues(), - updatedAddresses: new[] { addresses[0], addresses[1] }.ToImmutableHashSet(), - timestamp: DateTimeOffset.MinValue.AddSeconds(2)), - Transaction.Create( - nonce: 0, - privateKey: _txFx.PrivateKey2, - genesisHash: genesis.Hash, - actions: new[] - { - MakeAction(addresses[2], 'C', addresses[3]), - }.ToPlainValues(), - timestamp: DateTimeOffset.MinValue.AddSeconds(4)), - Transaction.Create( - nonce: 0, - privateKey: _txFx.PrivateKey3, - genesisHash: genesis.Hash, - actions: Array.Empty().ToPlainValues(), - timestamp: DateTimeOffset.MinValue.AddSeconds(7)), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: genesis.Hash, + updatedAddresses: new[] + { + addresses[0], + addresses[1], + }.ToImmutableHashSet(), + timestamp: DateTimeOffset.MinValue.AddSeconds(2), + actions: new TxActionList(new[] + { + MakeAction(addresses[0], 'A', addresses[1]), + MakeAction(addresses[1], 'B', addresses[2]), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(_txFx.PrivateKey1.PublicKey, 0)), + _txFx.PrivateKey1), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: genesis.Hash, + updatedAddresses: ImmutableHashSet
.Empty, + timestamp: DateTimeOffset.MinValue.AddSeconds(4), + actions: new TxActionList(new[] + { + MakeAction(addresses[2], 'C', addresses[3]), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(_txFx.PrivateKey2.PublicKey, 0)), + _txFx.PrivateKey2), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: genesis.Hash, + updatedAddresses: ImmutableHashSet
.Empty, + timestamp: DateTimeOffset.MinValue.AddSeconds(7), + actions: TxActionList.Empty, + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(_txFx.PrivateKey3.PublicKey, 0)), + _txFx.PrivateKey3), }; foreach ((var tx, var i) in block1Txs.Zip( Enumerable.Range(0, block1Txs.Length), (x, y) => (x, y))) @@ -334,9 +350,9 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu // have to be updated, since the order may change due to different PreEvaluationHash. (int TxIdx, int ActionIdx, string[] UpdatedStates, Address Signer)[] expectations = { - (0, 0, new[] { "A", null, null, null, null }, _txFx.Address1), - (0, 1, new[] { "A", "B", null, null, null }, _txFx.Address1), - (1, 0, new[] { "A", "B", "C", null, null }, _txFx.Address2), + (1, 0, new[] { null, null, "C", null, null }, _txFx.Address2), + (0, 0, new[] { "A", null, "C", null, null }, _txFx.Address1), + (0, 1, new[] { "A", "B", "C", null, null }, _txFx.Address1), }; Assert.Equal(expectations.Length, evals.Length); foreach (var (expect, eval) in expectations.Zip(evals, (x, y) => (x, y))) @@ -346,7 +362,7 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu block1Txs[expect.TxIdx].Actions[expect.ActionIdx], eval.Action.PlainValue); Assert.Equal(expect.Signer, eval.InputContext.Signer); - Assert.Equal(GenesisProposer.ToAddress(), eval.InputContext.Miner); + Assert.Equal(GenesisProposer.Address, eval.InputContext.Miner); Assert.Equal(block1.Index, eval.InputContext.BlockIndex); randomValue = eval.InputContext.GetRandom().Next(); Assert.Equal( @@ -364,70 +380,81 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu previousState = actionEvaluator.PrepareInitialDelta(null); ActionEvaluation[] evals1 = actionEvaluator.EvaluateBlock(block1, previousState).ToArray(); - IImmutableDictionary dirty1 = evals1.GetDirtyStates(); - IImmutableDictionary<(Address, Currency), FungibleAssetValue> balances1 = - evals1.GetDirtyBalances(); - IImmutableDictionary totalSupplies1 = - evals1.GetDirtyTotalSupplies(); + var dirty1 = evals1.Last().OutputState.Trie + .Diff(evals1.First().InputContext.PreviousState.Trie) + .ToDictionary(kv => kv.Path, kv => kv.SourceValue); + Assert.Equal((Text)"A", dirty1[ToStateKey(addresses[0])]); + Assert.Equal((Text)"B", dirty1[ToStateKey(addresses[1])]); + Assert.Equal((Text)"C", dirty1[ToStateKey(addresses[2])]); Assert.Equal( - new Dictionary - { - [addresses[0]] = (Text)"A", - [addresses[1]] = (Text)"B", - [addresses[2]] = (Text)"C", - [DumbAction.RandomRecordsAddress] = (Integer)randomValue, - }.ToImmutableDictionary(), - dirty1); + (Integer)randomValue, + dirty1[ToStateKey(DumbAction.RandomRecordsAddress)]); Assert.Equal( - new Dictionary<(Address, Currency), FungibleAssetValue> - { - [(addresses[0], DumbAction.DumbCurrency)] = - new FungibleAssetValue(DumbAction.DumbCurrency, -5, 0), - [(addresses[1], DumbAction.DumbCurrency)] = - new FungibleAssetValue(DumbAction.DumbCurrency), - [(addresses[2], DumbAction.DumbCurrency)] = - new FungibleAssetValue(DumbAction.DumbCurrency), - [(addresses[3], DumbAction.DumbCurrency)] = - new FungibleAssetValue(DumbAction.DumbCurrency, 5, 0), - }.ToImmutableDictionary(), - balances1); + (Integer)new FungibleAssetValue(DumbAction.DumbCurrency, -5, 0).RawValue, + dirty1[ToFungibleAssetKey(addresses[0], DumbAction.DumbCurrency)]); + Assert.Equal( + (Integer)new FungibleAssetValue(DumbAction.DumbCurrency).RawValue, + dirty1[ToFungibleAssetKey(addresses[1], DumbAction.DumbCurrency)]); + Assert.Equal( + (Integer)new FungibleAssetValue(DumbAction.DumbCurrency).RawValue, + dirty1[ToFungibleAssetKey(addresses[2], DumbAction.DumbCurrency)]); + Assert.Equal( + (Integer)new FungibleAssetValue(DumbAction.DumbCurrency, 5, 0).RawValue, + dirty1[ToFungibleAssetKey(addresses[3], DumbAction.DumbCurrency)]); Transaction[] block2Txs = { // Note that these timestamps in themselves does not have any meanings but are // only arbitrary. These purpose to make their evaluation order in a block // equal to the order we (the test) intend: - Transaction.Create( - 0, - _txFx.PrivateKey1, - genesis.Hash, - new[] { MakeAction(addresses[0], 'D') }.ToPlainValues(), - updatedAddresses: new[] { addresses[0] }.ToImmutableHashSet(), - timestamp: DateTimeOffset.MinValue.AddSeconds(3)), - Transaction.Create( - 0, - _txFx.PrivateKey2, - genesis.Hash, - new[] { MakeAction(addresses[3], 'E') }.ToPlainValues(), - updatedAddresses: new[] { addresses[3] }.ToImmutableHashSet(), - timestamp: DateTimeOffset.MinValue.AddSeconds(4)), - Transaction.Create( - 0, - _txFx.PrivateKey3, - genesis.Hash, - new[] - { - new DumbAction( - addresses[4], - "RecordRehearsal", - transferFrom: addresses[0], - transferTo: addresses[4], - transferAmount: 8, - recordRehearsal: true, - recordRandom: true), - }.ToPlainValues(), - updatedAddresses: new[] { addresses[4] }.ToImmutableHashSet(), - timestamp: DateTimeOffset.MinValue.AddSeconds(2)), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: genesis.Hash, + updatedAddresses: new[] { addresses[0] }.ToImmutableHashSet(), + timestamp: DateTimeOffset.MinValue.AddSeconds(1), + actions: new TxActionList(new[] + { + MakeAction(addresses[0], 'D'), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(_txFx.PrivateKey1.PublicKey, 0)), + _txFx.PrivateKey1), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: genesis.Hash, + updatedAddresses: new[] { addresses[3] }.ToImmutableHashSet(), + timestamp: DateTimeOffset.MinValue.AddSeconds(2), + actions: new TxActionList(new[] + { + MakeAction(addresses[3], 'E'), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(_txFx.PrivateKey2.PublicKey, 0)), + _txFx.PrivateKey2), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: genesis.Hash, + updatedAddresses: new[] { addresses[4] }.ToImmutableHashSet(), + timestamp: DateTimeOffset.MinValue.AddSeconds(4), + actions: new TxActionList(new[] + { + new DumbAction( + addresses[4], + "RecordRehearsal", + transferFrom: addresses[0], + transferTo: addresses[4], + transferAmount: 8, + recordRandom: true), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(_txFx.PrivateKey3.PublicKey, 0)), + _txFx.PrivateKey3), }; foreach ((var tx, var i) in block2Txs.Zip( Enumerable.Range(0, block2Txs.Length), (x, y) => (x, y))) @@ -453,9 +480,9 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu // have to be updated, since the order may change due to different PreEvaluationHash. expectations = new[] { - (0, 0, new[] { "A,D", "B", "C", null, null }, _txFx.Address1), - (1, 0, new[] { "A,D", "B", "C", "E", null }, _txFx.Address2), - (2, 0, new[] { "A,D", "B", "C", "E", "RecordRehearsal:False" }, _txFx.Address3), + (1, 0, new[] { "A", "B", "C", "E", null }, _txFx.Address2), + (0, 0, new[] { "A,D", "B", "C", "E", null }, _txFx.Address1), + (2, 0, new[] { "A,D", "B", "C", "E", "RecordRehearsal" }, _txFx.Address3), }; Assert.Equal(expectations.Length, evals.Length); foreach (var (expect, eval) in expectations.Zip(evals, (x, y) => (x, y))) @@ -470,9 +497,8 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu block2Txs[expect.TxIdx].Actions[expect.Item2], eval.Action.PlainValue); Assert.Equal(expect.Signer, eval.InputContext.Signer); - Assert.Equal(GenesisProposer.ToAddress(), eval.InputContext.Miner); + Assert.Equal(GenesisProposer.Address, eval.InputContext.Miner); Assert.Equal(block2.Index, eval.InputContext.BlockIndex); - Assert.False(eval.InputContext.Rehearsal); Assert.Null(eval.Exception); randomValue = eval.InputContext.GetRandom().Next(); Assert.Equal( @@ -484,25 +510,20 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu previousState = evals1.Last().OutputState; var evals2 = actionEvaluator.EvaluateBlock(block2, previousState).ToArray(); - IImmutableDictionary dirty2 = evals2.GetDirtyStates(); - IImmutableDictionary<(Address, Currency), FungibleAssetValue> balances2 = - evals2.GetDirtyBalances(); - Assert.Equal( - new Dictionary - { - [addresses[0]] = (Text)"A,D", - [addresses[3]] = (Text)"E", - [addresses[4]] = (Text)"RecordRehearsal:False", - [DumbAction.RandomRecordsAddress] = (Integer)randomValue, - }.ToImmutableDictionary(), - dirty2); + var dirty2 = evals2.Last().OutputState.Trie + .Diff(evals2.First().InputContext.PreviousState.Trie) + .ToDictionary(kv => kv.Path, kv => kv.SourceValue); + Assert.Equal((Text)"A,D", dirty2[ToStateKey(addresses[0])]); + Assert.Equal((Text)"E", dirty2[ToStateKey(addresses[3])]); + Assert.Equal((Text)"RecordRehearsal", dirty2[ToStateKey(addresses[4])]); + Assert.Equal((Integer)randomValue, dirty2[ToStateKey(DumbAction.RandomRecordsAddress)]); } [Fact] public void EvaluateTx() { PrivateKey[] keys = { new PrivateKey(), new PrivateKey(), new PrivateKey() }; - Address[] addresses = keys.Select(AddressExtensions.ToAddress).ToArray(); + Address[] addresses = keys.Select(key => key.Address).ToArray(); DumbAction[] actions = { new DumbAction( @@ -526,7 +547,7 @@ public void EvaluateTx() transferTo: addresses[0], transferAmount: 10, recordRandom: true), - new DumbAction(addresses[2], "R", true, recordRandom: true), + new DumbAction(addresses[2], "R", recordRandom: true), }; var tx = Transaction.Create(0, _txFx.PrivateKey1, null, actions.ToPlainValues()); @@ -545,8 +566,6 @@ public void EvaluateTx() stateStore: new TrieStateStore(new MemoryKeyValueStore()), actionTypeLoader: new SingleActionLoader(typeof(DumbAction))); - DumbAction.RehearsalRecords.Value = - ImmutableList<(Address, string)>.Empty; IWorld previousState = actionEvaluator.PrepareInitialDelta(null); var evaluations = actionEvaluator.EvaluateTx( blockHeader: block, @@ -559,7 +578,7 @@ public void EvaluateTx() new[] { "0", null, null }, new[] { "0", "1", null }, new[] { "0,2", "1", null }, - new[] { "0,2", "1", "R:False" }, + new[] { "0,2", "1", "R" }, }; BigInteger[][] expectedBalances = { @@ -621,31 +640,18 @@ prevEval is null .GetBalance(a, currency).RawValue)); } - Assert.DoesNotContain( - (addresses[2], "R"), - DumbAction.RehearsalRecords.Value); - - DumbAction.RehearsalRecords.Value = - ImmutableList<(Address, string)>.Empty; previousState = actionEvaluator.PrepareInitialDelta(null); IWorld delta = actionEvaluator.EvaluateTx( blockHeader: block, tx: tx, previousState: previousState).Last().OutputState; - Assert.Equal( - evaluations[3].OutputState - .GetAccount(ReservedAddresses.LegacyAccount).GetUpdatedStates(), - delta.GetAccount(ReservedAddresses.LegacyAccount).GetUpdatedStates()); - - Assert.DoesNotContain( - (addresses[2], "R"), - DumbAction.RehearsalRecords.Value); + Assert.Empty(evaluations[3].OutputState.Trie.Diff(delta.Trie)); } [Fact] public void EvaluateTxResultThrowingException() { - var action = new ThrowException { ThrowOnRehearsal = false, ThrowOnExecution = true }; + var action = new ThrowException { ThrowOnExecution = true }; var tx = Transaction.Create( 0, _txFx.PrivateKey1, @@ -653,7 +659,6 @@ public void EvaluateTxResultThrowingException() new[] { action }.ToPlainValues(), null, null, - ImmutableHashSet
.Empty, DateTimeOffset.UtcNow); var txs = new Transaction[] { tx }; var hash = new BlockHash(GetRandomBytes(BlockHash.Size)); @@ -677,7 +682,7 @@ public void EvaluateTxResultThrowingException() tx: tx, previousState: previousState).Last().OutputState; - Assert.Empty(nextState.GetAccount(ReservedAddresses.LegacyAccount).GetUpdatedStates()); + Assert.Empty(nextState.Trie.Diff(previousState.Trie)); } [Fact] @@ -700,7 +705,8 @@ public void EvaluateActions() previousState: fx.CreateWorld(blockA.PreviousHash), actions: txA.Actions .Select(action => (IAction)ToAction(action)) - .ToImmutableArray()).ToArray(); + .ToImmutableArray(), + stateStore: fx.StateStore).ToArray(); Assert.Equal(evalsA.Length, deltaA.Count - 1); Assert.Equal( @@ -723,15 +729,15 @@ public void EvaluateActions() Assert.Equal(blockA.Index, context.BlockIndex); Assert.Equal(txA.Signer, context.Signer); Assert.False(context.BlockAction); - Assert.Equal( - i > 0 ? new[] { txA.Signer } : new Address[0], - prevState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); Assert.Equal( (Integer)deltaA[i].Value, prevState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txA.Signer)); Assert.Equal( - new[] { txA.Signer }, - outputState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); + ToStateKey(txA.Signer), + Assert.Single( + outputState.GetAccount(ReservedAddresses.LegacyAccount).Trie.Diff( + prevState.GetAccount(ReservedAddresses.LegacyAccount).Trie)) + .Path); Assert.Equal( (Integer)deltaA[i + 1].Value, outputState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txA.Signer)); @@ -754,7 +760,8 @@ public void EvaluateActions() previousState: fx.CreateWorld(blockB.PreviousHash), actions: txB.Actions .Select(action => (IAction)ToAction(action)) - .ToImmutableArray()).ToArray(); + .ToImmutableArray(), + stateStore: fx.StateStore).ToArray(); Assert.Equal(evalsB.Length, deltaB.Count - 1); Assert.Equal( @@ -778,25 +785,26 @@ public void EvaluateActions() Assert.Equal(blockB.Index, context.BlockIndex); Assert.Equal(txB.Signer, context.Signer); Assert.False(context.BlockAction); - Assert.Equal( - i > 0 ? new[] { txB.Signer } : new Address[0], - prevState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); Assert.Equal( (Integer)deltaB[i].Value, prevState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txB.Signer)); - Assert.Equal( - new[] { txB.Signer }, - outputState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); Assert.Equal( (Integer)deltaB[i + 1].Value, outputState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txB.Signer)); if (i == 1) { + Assert.Empty(outputState.Trie.Diff(prevState.Trie)); Assert.IsType(eval.Exception); Assert.IsType(eval.Exception.InnerException); } else { + Assert.Equal( + ToStateKey(txB.Signer), + Assert.Single( + outputState.GetAccount(ReservedAddresses.LegacyAccount).Trie.Diff( + prevState.GetAccount(ReservedAddresses.LegacyAccount).Trie)) + .Path); Assert.Null(eval.Exception); } } @@ -884,18 +892,23 @@ public void OrderTxsForEvaluation( (signerNoncesPair, nonce) => (signerNoncesPair.signer, nonce)) .Select(signerNoncePair => { - Address targetAddress = signerNoncePair.signer.ToAddress(); - return Transaction.Create( - nonce: signerNoncePair.nonce, - privateKey: signerNoncePair.signer, - genesisHash: null, - actions: new[] - { - new RandomAction(signerNoncePair.signer.ToAddress()), - }.ToPlainValues(), - updatedAddresses: ImmutableHashSet.Create(targetAddress), - timestamp: epoch - ); + Address targetAddress = signerNoncePair.signer.Address; + return new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: null, + updatedAddresses: ImmutableHashSet.Create(targetAddress), + timestamp: epoch, + actions: new TxActionList(new[] + { + new RandomAction(signerNoncePair.signer.Address), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata( + signerNoncePair.signer.PublicKey, + signerNoncePair.nonce)), + signerNoncePair.signer); }).ToImmutableArray(); // Rearrange transactions so that transactions are not grouped by signers @@ -913,7 +926,7 @@ public void OrderTxsForEvaluation( // Sanity check. Assert.True(originalAddresses.SequenceEqual( - signers.Select(signer => signer.ToAddress().ToString()))); + signers.Select(signer => signer.Address.ToString()))); var orderedTxs = ActionEvaluator.OrderTxsForEvaluation( protocolVersion: protocolVersion, @@ -931,7 +944,7 @@ public void OrderTxsForEvaluation( // Check nonces are ordered. foreach (var signer in signers) { - var signerTxs = orderedTxs.Where(tx => tx.Signer == signer.ToAddress()); + var signerTxs = orderedTxs.Where(tx => tx.Signer == signer.Address); Assert.Equal(signerTxs.OrderBy(tx => tx.Nonce).ToArray(), signerTxs.ToArray()); } @@ -952,7 +965,7 @@ public void TotalUpdatedFungibleAssets() genesisBlock: _storeFx.GenesisBlock, privateKey: ChainPrivateKey); var privateKeys = Enumerable.Range(0, 3).Select(_ => new PrivateKey()).ToList(); - var addresses = privateKeys.Select(privateKey => privateKey.ToAddress()).ToList(); + var addresses = privateKeys.Select(privateKey => privateKey.Address).ToList(); // Only addresses[0] and addresses[1] are able to mint var currency = Currency.Uncapped( @@ -1000,7 +1013,7 @@ public void TotalUpdatedFungibleAssets() public void EvaluateActionAndCollectFee() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; Currency foo = Currency.Uncapped( "FOO", 18, @@ -1058,14 +1071,14 @@ public void EvaluateActionAndCollectFee() FungibleAssetValue.FromRawValue(foo, 1), chain.GetWorldState(evaluations.Single().OutputState) .GetAccount(ReservedAddresses.LegacyAccount) - .GetBalance(miner.ToAddress(), foo)); + .GetBalance(miner.Address, foo)); } [Fact] public void EvaluateThrowingExceedGasLimit() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; Currency foo = Currency.Uncapped( "FOO", 18, @@ -1132,14 +1145,14 @@ public void EvaluateThrowingExceedGasLimit() FungibleAssetValue.FromRawValue(foo, 5), chain.GetWorldState(evaluations.Single().OutputState) .GetAccount(ReservedAddresses.LegacyAccount) - .GetBalance(miner.ToAddress(), foo)); + .GetBalance(miner.Address, foo)); } [Fact] public void EvaluateThrowingInsufficientBalanceForGasFee() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; Currency foo = Currency.Uncapped( "FOO", 18, @@ -1201,7 +1214,7 @@ public void EvaluateThrowingInsufficientBalanceForGasFee() public void EvaluateMinusGasFee() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; Currency foo = Currency.Uncapped( "FOO", 18, @@ -1259,54 +1272,8 @@ public void EvaluateMinusGasFee() evaluations.Single().Exception?.InnerException?.GetType()); } - private (Address[], Transaction[]) MakeFixturesForAppendTests( - PrivateKey privateKey = null, - DateTimeOffset epoch = default) - { - Address[] addresses = - { - _storeFx.Address1, - _storeFx.Address2, - _storeFx.Address3, - _storeFx.Address4, - _storeFx.Address5, - }; - - privateKey = privateKey ?? new PrivateKey(new byte[] - { - 0xa8, 0x21, 0xc7, 0xc2, 0x08, 0xa9, 0x1e, 0x53, 0xbb, 0xb2, - 0x71, 0x15, 0xf4, 0x23, 0x5d, 0x82, 0x33, 0x44, 0xd1, 0x16, - 0x82, 0x04, 0x13, 0xb6, 0x30, 0xe7, 0x96, 0x4f, 0x22, 0xe0, - 0xec, 0xe0, - }); - - Transaction[] txs = - { - _storeFx.MakeTransaction( - new[] - { - new DumbAction(addresses[0], "foo"), - new DumbAction(addresses[1], "bar"), - }, - timestamp: epoch, - nonce: 0, - privateKey: privateKey), - _storeFx.MakeTransaction( - new[] - { - new DumbAction(addresses[2], "baz"), - new DumbAction(addresses[3], "qux"), - }, - timestamp: epoch.AddSeconds(5), - nonce: 1, - privateKey: privateKey), - }; - - return (addresses, txs); - } - [Fact] - private void GenerateRandomSeed() + public void GenerateRandomSeed() { byte[] preEvaluationHashBytes = { @@ -1322,21 +1289,15 @@ private void GenerateRandomSeed() 0xa8, 0x63, 0x04, 0xb0, 0xc3, 0xfe, 0xbb, 0x6c, 0x7a, 0x7b, 0x58, 0x58, 0xe9, 0x7d, 0x37, 0x67, 0xe1, 0xe9, }; - byte[] hashedSignature; - SHA1 hasher = SHA1.Create(); - hashedSignature = hasher.ComputeHash(signature); - int seed = - ActionEvaluator.GenerateRandomSeed( - preEvaluationHashBytes, - hashedSignature, - signature, - 0); + int seed = ActionEvaluator.GenerateRandomSeed(preEvaluationHashBytes, signature, 0); Assert.Equal(353767086, seed); + seed = ActionEvaluator.GenerateRandomSeed(preEvaluationHashBytes, signature, 1); + Assert.Equal(353767087, seed); } [Fact] - private void CheckRandomSeedInAction() + public void CheckRandomSeedInAction() { IntegerSet fx = new IntegerSet(new[] { 5, 10 }); @@ -1354,28 +1315,72 @@ private void CheckRandomSeedInAction() previousState: fx.CreateWorld(blockA.PreviousHash), actions: txA.Actions .Select(action => (IAction)ToAction(action)) - .ToImmutableArray()).ToArray(); - byte[] hashedSignature; - using (var hasher = SHA1.Create()) - { - hashedSignature = hasher.ComputeHash(txA.Signature); - } + .ToImmutableArray(), + stateStore: fx.StateStore).ToArray(); byte[] preEvaluationHashBytes = blockA.PreEvaluationHash.ToByteArray(); - int initialRandomSeed = - ActionEvaluator.GenerateRandomSeed( + int[] randomSeeds = Enumerable + .Range(0, txA.Actions.Count) + .Select(offset => ActionEvaluator.GenerateRandomSeed( preEvaluationHashBytes, - hashedSignature, txA.Signature, - 0); + offset)) + .ToArray(); + for (int i = 0; i < evalsA.Length; i++) { IActionEvaluation eval = evalsA[i]; IActionContext context = eval.InputContext; - Assert.Equal(initialRandomSeed + i, context.RandomSeed); + Assert.Equal(randomSeeds[i], context.RandomSeed); } } + private (Address[], Transaction[]) MakeFixturesForAppendTests( + PrivateKey privateKey = null, + DateTimeOffset epoch = default) + { + Address[] addresses = + { + _storeFx.Address1, + _storeFx.Address2, + _storeFx.Address3, + _storeFx.Address4, + _storeFx.Address5, + }; + + privateKey = privateKey ?? new PrivateKey(new byte[] + { + 0xa8, 0x21, 0xc7, 0xc2, 0x08, 0xa9, 0x1e, 0x53, 0xbb, 0xb2, + 0x71, 0x15, 0xf4, 0x23, 0x5d, 0x82, 0x33, 0x44, 0xd1, 0x16, + 0x82, 0x04, 0x13, 0xb6, 0x30, 0xe7, 0x96, 0x4f, 0x22, 0xe0, + 0xec, 0xe0, + }); + + Transaction[] txs = + { + _storeFx.MakeTransaction( + new[] + { + new DumbAction(addresses[0], "foo"), + new DumbAction(addresses[1], "bar"), + }, + timestamp: epoch, + nonce: 0, + privateKey: privateKey), + _storeFx.MakeTransaction( + new[] + { + new DumbAction(addresses[2], "baz"), + new DumbAction(addresses[3], "qux"), + }, + timestamp: epoch.AddSeconds(5), + nonce: 1, + privateKey: privateKey), + }; + + return (addresses, txs); + } + [Fact] private void MigrateStates() { @@ -1438,10 +1443,9 @@ private sealed class EvaluateTestAction : IAction public Address BlockIndexKey { get; set; } public IValue PlainValue => new List( - (Binary)SignerKey.ByteArray, - (Binary)MinerKey.ByteArray, - (Binary)BlockIndexKey.ByteArray - ); + SignerKey.Bencoded, + MinerKey.Bencoded, + BlockIndexKey.Bencoded); public void LoadPlainValue(IValue plainValue) { diff --git a/Libplanet.Tests/AddressExtensionsTest.cs b/Libplanet.Tests/AddressExtensionsTest.cs deleted file mode 100644 index c2c0389687b..00000000000 --- a/Libplanet.Tests/AddressExtensionsTest.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Libplanet.Crypto; -using Xunit; - -namespace Libplanet.Tests -{ - public class AddressExtensionsTest - { - [Fact] - public void ToAddress() - { - var privateKey = new PrivateKey( - new byte[] - { - 0xbe, 0xe6, 0xf9, 0xcc, 0x62, 0x41, 0x27, 0x60, 0xb3, 0x69, 0x6e, - 0x05, 0xf6, 0xfb, 0x4a, 0xbe, 0xb9, 0xe8, 0x3c, 0x4f, 0x94, 0x4f, - 0x83, 0xfd, 0x62, 0x08, 0x1b, 0x74, 0x54, 0xcb, 0xc0, 0x38, - } - ); - var expected = new Address("f45A22dD63f6428e85eE0a6E13a763278f57626d"); - Assert.Equal(expected, privateKey.ToAddress()); - Assert.Equal(expected, privateKey.PublicKey.ToAddress()); - } - } -} diff --git a/Libplanet.Tests/Assets/CurrencyTest.cs b/Libplanet.Tests/Assets/CurrencyTest.cs index 7ce5e665cf5..b48902bd9ad 100644 --- a/Libplanet.Tests/Assets/CurrencyTest.cs +++ b/Libplanet.Tests/Assets/CurrencyTest.cs @@ -124,7 +124,7 @@ public void Hash() [Fact] public void AllowsToMint() { - Address addressC = new PrivateKey().ToAddress(); + Address addressC = new PrivateKey().Address; Currency currency = Currency.Uncapped("FOO", 0, AddressA); Assert.True(currency.AllowsToMint(AddressA)); Assert.False(currency.AllowsToMint(AddressB)); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index c475a2a7e7c..c00e9218fad 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -193,8 +193,8 @@ Func getTxExecution Transaction tx1Transfer = _fx.MakeTransaction( new[] { - new DumbAction(pk.ToAddress(), "foo", pk.ToAddress(), addresses[1], 10), - new DumbAction(addresses[0], "bar", pk.ToAddress(), addresses[2], 20), + new DumbAction(pk.Address, "foo", pk.Address, addresses[1], 10), + new DumbAction(addresses[0], "bar", pk.Address, addresses[2], 20), }, nonce: 0, privateKey: pk @@ -204,7 +204,7 @@ Func getTxExecution { // As it tries to transfer a negative value, it throws // ArgumentOutOfRangeException: - new DumbAction(pk.ToAddress(), "foo", addresses[0], addresses[1], -5), + new DumbAction(pk.Address, "foo", addresses[0], addresses[1], -5), }, nonce: 1, privateKey: pk @@ -212,7 +212,7 @@ Func getTxExecution Transaction tx3Transfer = _fx.MakeTransaction( new[] { - new DumbAction(pk.ToAddress(), "foo", pk.ToAddress(), addresses[1], 5), + new DumbAction(pk.Address, "foo", pk.Address, addresses[1], 5), }, nonce: 2, privateKey: pk @@ -234,22 +234,22 @@ Func getTxExecution var accountDiff1 = AccountDiff.Create(inputAccount1, outputAccount1); Assert.Equal( - (new Address[] { addresses[0], pk.ToAddress() }).ToImmutableHashSet(), + (new Address[] { addresses[0], pk.Address }).ToImmutableHashSet(), accountDiff1.StateDiffs.Select(kv => kv.Key).ToImmutableHashSet()); Assert.Equal( - (new Address[] { addresses[1], addresses[2], pk.ToAddress() }) + (new Address[] { addresses[1], addresses[2], pk.Address }) .ToImmutableHashSet(), accountDiff1.FungibleAssetValueDiffs.Select(kv => kv.Key.Item1) .ToImmutableHashSet()); Assert.Equal( new Text("foo"), - outputAccount1.GetState(pk.ToAddress())); + outputAccount1.GetState(pk.Address)); Assert.Equal( new Text("foo,bar"), outputAccount1.GetState(addresses[0])); Assert.Equal( DumbAction.DumbCurrency * -30, - outputAccount1.GetBalance(pk.ToAddress(), DumbAction.DumbCurrency)); + outputAccount1.GetBalance(pk.Address, DumbAction.DumbCurrency)); Assert.Equal( DumbAction.DumbCurrency * 10, outputAccount1.GetBalance(addresses[1], DumbAction.DumbCurrency)); @@ -274,7 +274,7 @@ Func getTxExecution .GetAccount(ReservedAddresses.LegacyAccount); Assert.Equal( DumbAction.DumbCurrency * -35, - outputAccount3.GetBalance(pk.ToAddress(), DumbAction.DumbCurrency)); + outputAccount3.GetBalance(pk.Address, DumbAction.DumbCurrency)); Assert.Equal( DumbAction.DumbCurrency * 15, outputAccount3.GetBalance(addresses[1], DumbAction.DumbCurrency)); @@ -421,8 +421,8 @@ public void AppendBlockWithPolicyViolationTx() TxPolicyViolationException IsSignerValid( BlockChain chain, Transaction tx) { - var validAddress = validKey.PublicKey.ToAddress(); - return tx.Signer.Equals(validAddress) || tx.Signer.Equals(_fx.Proposer.ToAddress()) + var validAddress = validKey.Address; + return tx.Signer.Equals(validAddress) || tx.Signer.Equals(_fx.Proposer.Address) ? null : new TxPolicyViolationException("invalid signer", tx.Id); } @@ -656,7 +656,7 @@ public void CachedActionEvaluationWrittenOnAppend() new DumbAction[] { new DumbAction( - dummy.ToAddress(), "foo", dummy.ToAddress(), dummy.ToAddress(), 10), + dummy.Address, "foo", dummy.Address, dummy.Address, 10), }.ToPlainValues()), txA1 = Transaction.Create( 1, @@ -665,7 +665,7 @@ public void CachedActionEvaluationWrittenOnAppend() new DumbAction[] { new DumbAction( - dummy.ToAddress(), "bar", dummy.ToAddress(), dummy.ToAddress(), 20), + dummy.Address, "bar", dummy.Address, dummy.Address, 20), }.ToPlainValues()); _blockChain.StageTransaction(txA0); _blockChain.StageTransaction(txA1); @@ -690,7 +690,6 @@ public void CannotAppendBlockWithInvalidActions() var unsignedInvalidTx = new UnsignedTx( new TxInvoice( _blockChain.Genesis.Hash, - ImmutableHashSet
.Empty, DateTimeOffset.UtcNow, new TxActionList((IValue)List.Empty.Add(new Text("Foo")))), // Invalid action new TxSigningMetadata(txSigner.PublicKey, 1)); @@ -702,17 +701,13 @@ public void CannotAppendBlockWithInvalidActions() nonce: 0, privateKey: txSigner, genesisHash: _blockChain.Genesis.Hash, - actions: Array.Empty().ToPlainValues(), - updatedAddresses: ImmutableHashSet
.Empty - ), + actions: Array.Empty().ToPlainValues()), invalidTx, Transaction.Create( nonce: 2, privateKey: txSigner, genesisHash: _blockChain.Genesis.Hash, - actions: Array.Empty().ToPlainValues(), - updatedAddresses: ImmutableHashSet
.Empty - ), + actions: Array.Empty().ToPlainValues()), }.OrderBy(tx => tx.Id); var metadata = new BlockMetadata( @@ -767,7 +762,7 @@ public void MigrateStateWithoutAction() protocolVersion: BlockMetadata.LegacyStateVersion, index: 0L, timestamp: DateTimeOffset.UtcNow, - miner: fx.Proposer.ToAddress(), + miner: fx.Proposer.Address, publicKey: fx.Proposer.PublicKey, previousHash: null, txHash: BlockContent.DeriveTxHash(txs), diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs index 89049f451a2..36fd6d9853c 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs @@ -35,9 +35,7 @@ Transaction MkTx(PrivateKey key, long nonce, DateTimeOffset? ts = null) => Array.Empty().ToPlainValues(), null, null, - null, - ts ?? DateTimeOffset.UtcNow - ); + ts ?? DateTimeOffset.UtcNow); PrivateKey a = new PrivateKey(); PrivateKey b = new PrivateKey(); @@ -46,7 +44,7 @@ Transaction MkTx(PrivateKey key, long nonce, DateTimeOffset? ts = null) => PrivateKey e = new PrivateKey(); List
signers = new List
() { - a.ToAddress(), b.ToAddress(), c.ToAddress(), d.ToAddress(), e.ToAddress(), + a.Address, b.Address, c.Address, d.Address, e.Address, }; // A normal case and corner cases: @@ -85,13 +83,13 @@ Transaction MkTx(PrivateKey key, long nonce, DateTimeOffset? ts = null) => // A is prioritized over B, C, D, E: IComparer priority = Comparer.Create( - (tx1, tx2) => tx1.Signer.Equals(a.ToAddress()) ? -1 : 1 + (tx1, tx2) => tx1.Signer.Equals(a.Address) ? -1 : 1 ); stagedTransactions = _blockChain.ListStagedTransactions(priority); foreach (var tx in stagedTransactions.Take(3)) { - Assert.True(tx.Signer.Equals(a.ToAddress())); + Assert.True(tx.Signer.Equals(a.Address)); } // List is ordered by nonce. diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs index aab1118259e..b19c9aee4cd 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs @@ -28,7 +28,7 @@ public void ProposeBlock() Func getMaxTransactionsBytes = _blockChain.Policy.GetMaxTransactionsBytes; Assert.Equal(1, _blockChain.Count); AssertBencodexEqual( - (Text)$"{GenesisProposer.ToAddress()}", + (Text)$"{GenesisProposer.Address}", _blockChain.GetWorldState().GetAccount( ReservedAddresses.LegacyAccount).GetState(default)); @@ -40,7 +40,7 @@ public void ProposeBlock() Assert.True( block.MarshalBlock().EncodingLength <= getMaxTransactionsBytes(block.Index)); AssertBencodexEqual( - (Text)$"{GenesisProposer.ToAddress()},{proposerA.ToAddress()}", + (Text)$"{GenesisProposer.Address},{proposerA.Address}", _blockChain.GetWorldState().GetAccount( ReservedAddresses.LegacyAccount).GetState(default) ); @@ -56,7 +56,7 @@ public void ProposeBlock() anotherBlock.MarshalBlock().EncodingLength <= getMaxTransactionsBytes(anotherBlock.Index)); Text expected = new Text( - $"{GenesisProposer.ToAddress()},{proposerA.ToAddress()},{proposerB.ToAddress()}"); + $"{GenesisProposer.Address},{proposerA.Address},{proposerB.Address}"); AssertBencodexEqual( expected, _blockChain.GetWorldState().GetAccount( @@ -71,7 +71,7 @@ public void ProposeBlock() Assert.True( block3.MarshalBlock().EncodingLength <= getMaxTransactionsBytes(block3.Index)); expected = new Text( - $"{GenesisProposer.ToAddress()},{proposerA.ToAddress()},{proposerB.ToAddress()}"); + $"{GenesisProposer.Address},{proposerA.Address},{proposerB.Address}"); AssertBencodexEqual( expected, _blockChain.GetWorldState().GetAccount( @@ -113,9 +113,9 @@ public void ProposeBlock() ); Assert.True( block4.MarshalBlock().EncodingLength <= getMaxTransactionsBytes(block4.Index)); - Assert.Equal(3, block4.Transactions.Count()); + Assert.Equal(4, block4.Transactions.Count()); expected = new Text( - $"{GenesisProposer.ToAddress()},{proposerA.ToAddress()},{proposerB.ToAddress()}"); + $"{GenesisProposer.Address},{proposerA.Address},{proposerB.Address}"); AssertBencodexEqual( expected, _blockChain.GetWorldState().GetAccount( @@ -144,7 +144,7 @@ public void CanProposeInvalidGenesisBlock() null, actions: new[] { - new DumbAction(new PrivateKey().PublicKey.ToAddress(), "foo"), + new DumbAction(new PrivateKey().Address, "foo"), }.ToPlainValues()), }.ToImmutableList()); Assert.Throws(() => BlockChain.Create( @@ -181,7 +181,7 @@ public void CanProposeInvalidBlock() _blockChain.Genesis.Hash, new[] { - new DumbAction(new PrivateKey().PublicKey.ToAddress(), "foo"), + new DumbAction(new PrivateKey().Address, "foo"), }.ToPlainValues()), }.ToImmutableList(); @@ -200,11 +200,11 @@ public void ProposeBlockWithPendingTxs() var keyC = new PrivateKey(); var keyD = new PrivateKey(); var keyE = new PrivateKey(); - var addrA = keyA.ToAddress(); - var addrB = keyB.ToAddress(); - var addrC = keyC.ToAddress(); - var addrD = keyD.ToAddress(); - var addrE = keyE.ToAddress(); + var addrA = keyA.Address; + var addrB = keyB.Address; + var addrC = keyC.Address; + var addrD = keyD.Address; + var addrE = keyE.Address; var txs = new[] { @@ -348,8 +348,8 @@ public void ProposeBlockWithPolicyViolationTx() TxPolicyViolationException IsSignerValid( BlockChain chain, Transaction tx) { - var validAddress = validKey.PublicKey.ToAddress(); - return tx.Signer.Equals(validAddress) || tx.Signer.Equals(_fx.Proposer.ToAddress()) + var validAddress = validKey.Address; + return tx.Signer.Equals(validAddress) || tx.Signer.Equals(_fx.Proposer.Address) ? null : new TxPolicyViolationException("invalid signer", tx.Id); } @@ -459,10 +459,10 @@ public void ProposeBlockWithLowerNonces() public void ProposeBlockWithBlockAction() { var privateKey1 = new PrivateKey(); - var address1 = privateKey1.ToAddress(); + var address1 = privateKey1.Address; var privateKey2 = new PrivateKey(); - var address2 = privateKey2.ToAddress(); + var address2 = privateKey2.Address; var blockAction = new DumbAction(address1, "foo"); var policy = new BlockPolicy(blockAction); @@ -515,9 +515,9 @@ public void ProposeBlockWithTxPriority() var keyA = new PrivateKey(); var keyB = new PrivateKey(); var keyC = new PrivateKey(); - Address a = keyA.ToAddress(); // Rank 0 - Address b = keyB.ToAddress(); // Rank 1 - Address c = keyC.ToAddress(); // Rank 2 + Address a = keyA.Address; // Rank 0 + Address b = keyB.Address; // Rank 1 + Address c = keyC.Address; // Rank 2 int Rank(Address address) => address.Equals(a) ? 0 : address.Equals(b) ? 1 : 2; Transaction[] txsA = Enumerable.Range(0, 50) .Select(nonce => _fx.MakeTransaction(nonce: nonce, privateKey: keyA)) @@ -570,7 +570,7 @@ public void ProposeBlockWithLastCommit() public void IgnoreLowerNonceTxsAndPropose() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var txsA = Enumerable.Range(0, 3) .Select(nonce => _fx.MakeTransaction( nonce: nonce, privateKey: privateKey, timestamp: DateTimeOffset.Now)) @@ -629,9 +629,9 @@ public void GatherTransactionsToPropose() var keyA = new PrivateKey(); var keyB = new PrivateKey(); var keyC = new PrivateKey(); - Address a = keyA.ToAddress(); - Address b = keyB.ToAddress(); - Address c = keyC.ToAddress(); + Address a = keyA.Address; + Address b = keyB.Address; + Address c = keyC.Address; _logger.Verbose("Address {Name}: {Address}", nameof(a), a); _logger.Verbose("Address {Name}: {Address}", nameof(b), b); _logger.Verbose("Address {Name}: {Address}", nameof(c), c); @@ -687,7 +687,6 @@ public void MarkTransactionsToIgnoreWhileProposing() var unsignedInvalidTx = new UnsignedTx( new TxInvoice( _blockChain.Genesis.Hash, - ImmutableHashSet
.Empty, DateTimeOffset.UtcNow, new TxActionList((IValue)List.Empty.Add(new Text("Foo")))), // Invalid action new TxSigningMetadata(keyB.PublicKey, 1)); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs index 2c1ef2202bb..cda6721c0ca 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs @@ -45,7 +45,7 @@ public void ValidateNextBlockProtocolVersion() protocolVersion: protocolVersion, index: 1L, timestamp: _fx.GenesisBlock.Timestamp.AddDays(1), - miner: _fx.Proposer.PublicKey.ToAddress(), + miner: _fx.Proposer.Address, publicKey: protocolVersion >= 2 ? _fx.Proposer.PublicKey : null, previousHash: _fx.GenesisBlock.Hash, txHash: null, @@ -59,7 +59,7 @@ public void ValidateNextBlockProtocolVersion() protocolVersion: protocolVersion - 1, index: 2L, timestamp: _fx.GenesisBlock.Timestamp.AddDays(2), - miner: _fx.Proposer.PublicKey.ToAddress(), + miner: _fx.Proposer.Address, publicKey: protocolVersion - 1 >= 2 ? _fx.Proposer.PublicKey : null, previousHash: block1.Hash, txHash: null, @@ -73,7 +73,7 @@ public void ValidateNextBlockProtocolVersion() protocolVersion: BlockMetadata.CurrentProtocolVersion + 1, index: 2L, timestamp: _fx.GenesisBlock.Timestamp.AddDays(2), - miner: _fx.Proposer.PublicKey.ToAddress(), + miner: _fx.Proposer.Address, publicKey: _fx.Proposer.PublicKey, previousHash: block1.Hash, txHash: null, @@ -202,7 +202,7 @@ public void ValidateNextBlockInvalidStateRootHash() protocolVersion: BlockMetadata.CurrentProtocolVersion, index: 1, timestamp: genesisBlock.Timestamp.AddSeconds(1), - miner: TestUtils.GenesisProposer.PublicKey.ToAddress(), + miner: TestUtils.GenesisProposer.Address, publicKey: TestUtils.GenesisProposer.PublicKey, previousHash: genesisBlock.Hash, txHash: null, @@ -526,7 +526,7 @@ ImmutableArray GenerateVotes( GenerateVote(privateKey2, flag2), GenerateVote(privateKey3, flag3), GenerateVote(privateKey4, flag4), - }.OrderBy(vote => vote.ValidatorPublicKey.ToAddress()).ToImmutableArray(); + }.OrderBy(vote => vote.ValidatorPublicKey.Address).ToImmutableArray(); } var fullBlockCommit = new BlockCommit( diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index 79912e8ef4f..e750413db10 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -10,7 +10,6 @@ using Libplanet.Action.Loader; using Libplanet.Action.State; using Libplanet.Action.Sys; -using Libplanet.Action.Tests; using Libplanet.Action.Tests.Common; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -77,7 +76,7 @@ public BlockChainTest(ITestOutputHelper output) protocolVersion: BlockMetadata.CurrentProtocolVersion, index: 1, timestamp: _fx.GenesisBlock.Timestamp.AddSeconds(1), - miner: _fx.Proposer.PublicKey.ToAddress(), + miner: _fx.Proposer.Address, publicKey: _fx.Proposer.PublicKey, previousHash: _fx.GenesisBlock.Hash, txHash: null, @@ -562,7 +561,7 @@ public void StateAfterForkingAndAddingExistingBlock() { var miner = new PrivateKey(); var signer = new PrivateKey(); - var address = signer.ToAddress(); + var address = signer.Address; var actions1 = new[] { new DumbAction(address, "foo") }; var actions2 = new[] { new DumbAction(address, "bar") }; @@ -603,22 +602,24 @@ public void ForkShouldSkipExecuteAndRenderGenesis() _ => _policy.BlockAction, stateStore, new SingleActionLoader(typeof(DumbAction))); + var privateKey = new PrivateKey(); var genesis = ProposeGenesisBlock( actionEvaluator, ProposeGenesis( GenesisProposer.PublicKey, transactions: new[] { - Transaction.Create( - nonce: 0, - privateKey: new PrivateKey(), - genesisHash: null, - actions: new[] { action }.ToPlainValues(), - maxGasPrice: null, - gasLimit: null, - updatedAddresses: ImmutableHashSet.Create(_fx.Address1), - timestamp: DateTimeOffset.UtcNow - ), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: null, + updatedAddresses: ImmutableHashSet.Create(_fx.Address1), + timestamp: DateTimeOffset.UtcNow, + actions: new TxActionList(new[] { action }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(privateKey.PublicKey, 0)), + privateKey), }), privateKey: GenesisProposer); @@ -695,12 +696,12 @@ public void ForkTxNonce() { // An active account, so that its some recent transactions became "stale" due to a fork. var privateKey = new PrivateKey(); - Address address = privateKey.ToAddress(); + Address address = privateKey.Address; // An inactive account, so that it has no recent transactions but only an old // transaction, so that its all transactions are stale-proof (stale-resistant). var lessActivePrivateKey = new PrivateKey(); - Address lessActiveAddress = lessActivePrivateKey.ToAddress(); + Address lessActiveAddress = lessActivePrivateKey.Address; var actions = new[] { new DumbAction(address, "foo") }; @@ -778,7 +779,7 @@ public void Swap(bool render) MakeFixturesForAppendTests(); var genesis = _blockChain.Genesis; var miner = new PrivateKey(); - var minerAddress = miner.ToAddress(); + var minerAddress = miner.Address; Block block1 = _blockChain.ProposeBlock( miner, txs1.ToImmutableList(), CreateBlockCommit(_blockChain.Tip)); @@ -1131,7 +1132,7 @@ public void GetStateOnlyDrillsDownUntilRequestedAddressesAreFound() for (int i = 0; i < addresses.Length; ++i) { var privateKey = new PrivateKey(); - Address address = privateKey.ToAddress(); + Address address = privateKey.Address; addresses[i] = address; DumbAction[] actions = { @@ -1191,7 +1192,7 @@ public void GetStateReturnsEarlyForNonexistentAccount() } tracker.ClearLogs(); - Address nonexistent = new PrivateKey().ToAddress(); + Address nonexistent = new PrivateKey().Address; IValue result = chain.GetWorldState().GetAccount( ReservedAddresses.LegacyAccount).GetState(nonexistent); Assert.Null(result); @@ -1249,7 +1250,7 @@ public void GetStateReturnsValidStateAfterFork() public void GetStateReturnsLatestStatesWhenMultipleAddresses() { var privateKeys = Enumerable.Range(1, 10).Select(_ => new PrivateKey()).ToList(); - var addresses = privateKeys.Select(AddressExtensions.ToAddress).ToList(); + var addresses = privateKeys.Select(key => key.Address).ToList(); var policy = new NullBlockPolicy(); var blockChainStates = new BlockChainStates(_fx.Store, _fx.StateStore); var chain = new BlockChain( @@ -1382,7 +1383,7 @@ public void FindBranchPoint() public void GetNextTxNonce() { var privateKey = new PrivateKey(); - Address address = privateKey.ToAddress(); + Address address = privateKey.Address; var actions = new[] { new DumbAction(_fx.Address1, "foo") }; var genesis = _blockChain.Genesis; @@ -1451,7 +1452,7 @@ public void GetNextTxNonce() public void GetNextTxNonceWithStaleTx() { var privateKey = new PrivateKey(); - var address = privateKey.ToAddress(); + var address = privateKey.Address; var actions = new[] { new DumbAction(address, "foo") }; Transaction[] txs = @@ -1539,7 +1540,7 @@ IReadOnlyList txs public void MakeTransactionWithSystemAction() { var privateKey = new PrivateKey(); - Address address = privateKey.ToAddress(); + Address address = privateKey.Address; var action = new Initialize( new ValidatorSet( new List() { new Validator(new PrivateKey().PublicKey, 1) }), @@ -1573,7 +1574,7 @@ public void MakeTransactionWithSystemAction() public void MakeTransactionWithCustomActions() { var privateKey = new PrivateKey(); - Address address = privateKey.ToAddress(); + Address address = privateKey.Address; var actions = new[] { new DumbAction(address, "foo") }; _blockChain.MakeTransaction(privateKey, actions); @@ -1601,7 +1602,7 @@ public void MakeTransactionWithCustomActions() public async Task MakeTransactionConcurrency() { var privateKey = new PrivateKey(); - Address address = privateKey.ToAddress(); + Address address = privateKey.Address; var actions = new[] { new DumbAction(address, "foo") }; var tasks = Enumerable.Range(0, 10) @@ -1643,9 +1644,9 @@ public void BlockActionWithMultipleAddress() _blockChain.Append(block3, CreateBlockCommit(block3)); IValue miner1state = _blockChain.GetWorldState().GetAccount( - ReservedAddresses.LegacyAccount).GetState(miner1.ToAddress()); + ReservedAddresses.LegacyAccount).GetState(miner1.Address); IValue miner2state = _blockChain.GetWorldState().GetAccount( - ReservedAddresses.LegacyAccount).GetState(miner2.ToAddress()); + ReservedAddresses.LegacyAccount).GetState(miner2.Address); IValue rewardState = _blockChain.GetWorldState().GetAccount( ReservedAddresses.LegacyAccount).GetState(rewardRecordAddress); @@ -1653,7 +1654,7 @@ public void BlockActionWithMultipleAddress() AssertBencodexEqual((Integer)1, miner2state); AssertBencodexEqual( - (Text)$"{miner0},{miner1.ToAddress()},{miner1.ToAddress()},{miner2.ToAddress()}", + (Text)$"{miner0},{miner1.Address},{miner1.Address},{miner2.Address}", rewardState ); } @@ -1723,7 +1724,7 @@ internal static (Address, Address[] Addresses, BlockChain Chain) blockChainStates: chainStates, actionEvaluator: actionEvaluator); var privateKey = new PrivateKey(); - Address signer = privateKey.ToAddress(); + Address signer = privateKey.Address; void BuildIndex(Guid id, Block block) { @@ -1738,12 +1739,9 @@ void BuildIndex(Guid id, Block block) // Build a store with incomplete states Block b = chain.Genesis; IWorld previousState = actionEvaluator.PrepareInitialDelta(null); - ActionEvaluation[] evals = - actionEvaluator.EvaluateBlock(b, previousState).ToArray(); - IImmutableDictionary dirty = evals.GetDirtyStates(); const int accountsCount = 5; Address[] addresses = Enumerable.Repeat(null, accountsCount) - .Select(_ => new PrivateKey().ToAddress()) + .Select(_ => new PrivateKey().Address) .ToArray(); for (int i = 0; i < 2; ++i) { @@ -1765,7 +1763,10 @@ void BuildIndex(Guid id, Block block) lastCommit: CreateBlockCommit(b)), GenesisProposer); - dirty = actionEvaluator.EvaluateBlock(b, previousState).GetDirtyStates(); + var evals = actionEvaluator.EvaluateBlock(b, previousState); + var dirty = evals.Last().OutputState.Trie + .Diff(evals.First().InputContext.PreviousState.Trie) + .ToList(); Assert.NotEmpty(dirty); store.PutBlock(b); BuildIndex(chain.Id, b); @@ -1802,7 +1803,7 @@ protected virtual StoreFixture GetStoreFixture(IAction blockAction) => ) { Address[] addresses = keys is PrivateKey[] ks - ? ks.Select(AddressExtensions.ToAddress).ToArray() + ? ks.Select(k => k.Address).ToArray() : new[] { _fx.Address1, @@ -1918,13 +1919,17 @@ private void CreateWithGenesisBlock() .ToArray(); var customTxs = new[] { - Transaction.Create( - nonce: systemTxs.Length, - privateKey: privateKey, - genesisHash: null, - actions: customActions.ToPlainValues(), - updatedAddresses: addresses.ToImmutableHashSet() - ), + new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: null, + updatedAddresses: addresses.ToImmutableHashSet(), + timestamp: DateTimeOffset.UtcNow, + actions: new TxActionList(customActions.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(privateKey.PublicKey, systemTxs.Length)), + privateKey), }; var txs = systemTxs.Concat(customTxs).ToImmutableList(); var blockChainStates = new BlockChainStates( @@ -2147,7 +2152,7 @@ private void ValidateNextBlockCommitOnValidatorSetChange() DateTimeOffset.UtcNow, pk.PublicKey, VoteFlag.PreCommit).Sign(pk)) - .OrderBy(vote => vote.ValidatorPublicKey.ToAddress()) + .OrderBy(vote => vote.ValidatorPublicKey.Address) .ToImmutableArray()); blockChain.Append(newBlock, newBlockCommit); @@ -2169,7 +2174,7 @@ private void ValidateNextBlockCommitOnValidatorSetChange() DateTimeOffset.UtcNow, pk.PublicKey, VoteFlag.PreCommit).Sign(pk)) - .OrderBy(vote => vote.ValidatorPublicKey.ToAddress()) + .OrderBy(vote => vote.ValidatorPublicKey.Address) .ToImmutableArray()); blockChain.Append(nextBlock, nextBlockCommit); diff --git a/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs b/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs index 947c0bf12d5..3a6e2eb9650 100644 --- a/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs +++ b/Libplanet.Tests/Blockchain/Policies/BlockPolicyTest.cs @@ -81,7 +81,7 @@ public void ValidateNextBlockTx() TxPolicyViolationException IsSignerValid( BlockChain chain, Transaction tx) { - var validAddress = validKey.PublicKey.ToAddress(); + var validAddress = validKey.Address; return tx.Signer.Equals(validAddress) ? null : new TxPolicyViolationException("invalid signer", tx.Id); @@ -110,7 +110,7 @@ public void ValidateNextBlockTxWithInnerException() TxPolicyViolationException IsSignerValid( BlockChain chain, Transaction tx) { - var validAddress = validKey.PublicKey.ToAddress(); + var validAddress = validKey.Address; return tx.Signer.Equals(validAddress) ? null : new TxPolicyViolationException("invalid signer", tx.Id); @@ -120,7 +120,7 @@ TxPolicyViolationException IsSignerValid( TxPolicyViolationException IsSignerValidWithInnerException( BlockChain chain, Transaction tx) { - var validAddress = validKey.PublicKey.ToAddress(); + var validAddress = validKey.Address; return tx.Signer.Equals(validAddress) ? null : new TxPolicyViolationException( @@ -220,7 +220,7 @@ public void GetMaxTransactionsPerSignerPerBlock() privateKeys.ForEach( key => Assert.Equal( policyLimit, - block.Transactions.Count(tx => tx.Signer.Equals(key.ToAddress())))); + block.Transactions.Count(tx => tx.Signer.Equals(key.Address)))); } } } diff --git a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs index c2893af9b56..662ce0ba7fa 100644 --- a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs @@ -56,15 +56,11 @@ public void Dispose() } [Theory] - [InlineData(false, false, false)] - [InlineData(true, false, false)] - [InlineData(false, true, false)] - [InlineData(true, true, false)] - [InlineData(false, false, true)] - [InlineData(true, false, true)] - [InlineData(false, true, true)] - [InlineData(true, true, true)] - public void ActionRenderings(bool error, bool rehearsal, bool exception) + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public void ActionRenderings(bool error, bool exception) { bool called = false; LogEvent firstLog = null; @@ -77,8 +73,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) 123, _world, default, - 0, - rehearsal)); + 0)); Exception actionError = new Exception(); IActionRenderer actionRenderer; if (error) @@ -173,7 +168,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) const string expected1stLog = "Invoking {MethodName}() for an action {ActionType} at block #{BlockIndex}..."; Assert.Equal( - expected1stLog + (rehearsal ? " (rehearsal: {Rehearsal})" : string.Empty), + expected1stLog, firstLog.MessageTemplate.Text ); string methodName = @@ -188,14 +183,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) firstLog.Properties[Constants.SourceContextPropertyName].ToString() ); Assert.Null(firstLog.Exception); - if (rehearsal) - { - Assert.Equal("True", firstLog.Properties["Rehearsal"].ToString()); - } - else - { - Assert.False(firstLog.Properties.ContainsKey("Rehearsal")); - } + Assert.False(firstLog.Properties.ContainsKey("Rehearsal")); LogEvent secondLog = logEvents[1]; Assert.Equal( @@ -207,14 +195,12 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) { expected2ndLog = "An exception was thrown during {MethodName}() for an action {ActionType} at " + - "block #{BlockIndex}" + - (rehearsal ? " (rehearsal: {Rehearsal})" : string.Empty); + "block #{BlockIndex}"; } else { expected2ndLog = - "Invoked {MethodName}() for an action {ActionType} at block #{BlockIndex}" + - (rehearsal ? " (rehearsal: {Rehearsal})" : string.Empty); + "Invoked {MethodName}() for an action {ActionType} at block #{BlockIndex}"; } Assert.Equal( @@ -237,14 +223,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) Assert.Null(secondLog.Exception); } - if (rehearsal) - { - Assert.Equal("True", firstLog.Properties["Rehearsal"].ToString()); - } - else - { - Assert.False(firstLog.Properties.ContainsKey("Rehearsal")); - } + Assert.False(firstLog.Properties.ContainsKey("Rehearsal")); } [Theory] diff --git a/Libplanet.Tests/Blocks/BlockContentTest.cs b/Libplanet.Tests/Blocks/BlockContentTest.cs index 5da1fff3498..f7ea2619807 100644 --- a/Libplanet.Tests/Blocks/BlockContentTest.cs +++ b/Libplanet.Tests/Blocks/BlockContentTest.cs @@ -78,9 +78,11 @@ public void TransactionsWithDuplicateNonce() new UnsignedTx( new TxInvoice( genesisHash: GenesisHash, - updatedAddresses: new[] { Block1Tx1.Signer }, - timestamp: Block1Tx1.Timestamp - ), + updatedAddresses: new AddressSet(new[] { Block1Tx1.Signer }), + timestamp: Block1Tx1.Timestamp, + actions: TxActionList.Empty, + maxGasPrice: null, + gasLimit: null), new TxSigningMetadata(Block1Tx1.PublicKey, nonce: 1L) ), signature: ByteUtil.ParseHexToImmutable( @@ -111,9 +113,11 @@ public void TransactionsWithMissingNonce() new UnsignedTx( new TxInvoice( genesisHash: GenesisHash, - updatedAddresses: new[] { Block1Tx1.Signer }, - timestamp: Block1Tx1.Timestamp - ), + updatedAddresses: new AddressSet(new[] { Block1Tx1.Signer }), + timestamp: Block1Tx1.Timestamp, + actions: TxActionList.Empty, + maxGasPrice: null, + gasLimit: null), new TxSigningMetadata(Block1Tx1.PublicKey, nonce: 3L) ), signature: ByteUtil.ParseHexToImmutable( diff --git a/Libplanet.Tests/Blocks/BlockMarshalerTest.cs b/Libplanet.Tests/Blocks/BlockMarshalerTest.cs index 80792064ba6..4cd5791c9fa 100644 --- a/Libplanet.Tests/Blocks/BlockMarshalerTest.cs +++ b/Libplanet.Tests/Blocks/BlockMarshalerTest.cs @@ -132,7 +132,7 @@ public void MarshalBlockMetadata() Dictionary.Empty .Add(IndexKey, 0L) .Add(TimestampKey, "2021-09-06T04:46:39.123000Z") - .Add(MinerKey, _content.GenesisContentPv0.Miner.ByteArray), + .Add(MinerKey, _content.GenesisContentPv0.Miner.Bencoded), BlockMarshaler.MarshalBlockMetadata(_content.GenesisContentPv0) ); AssertBencodexEqual( @@ -143,7 +143,7 @@ public void MarshalBlockMetadata() PreviousHashKey, _content.Block1ContentPv1.PreviousHash?.ByteArray ?? default) .Add(TimestampKey, "2021-09-06T08:01:09.045000Z") - .Add(MinerKey, _content.Block1ContentPv1.Miner.ByteArray) + .Add(MinerKey, _content.Block1ContentPv1.Miner.Bencoded) .Add(TxHashKey, _content.Block1ContentPv1.TxHash?.ByteArray ?? default), BlockMarshaler.MarshalBlockMetadata(_content.Block1ContentPv1) ); diff --git a/Libplanet.Tests/Blocks/BlockMetadataTest.cs b/Libplanet.Tests/Blocks/BlockMetadataTest.cs index 4c14c3630f9..a4c6ca2bfe0 100644 --- a/Libplanet.Tests/Blocks/BlockMetadataTest.cs +++ b/Libplanet.Tests/Blocks/BlockMetadataTest.cs @@ -274,7 +274,7 @@ public void ValidateLastCommit() protocolVersion: BlockMetadata.CurrentProtocolVersion, index: 2, timestamp: timestamp, - miner: validatorA.PublicKey.ToAddress(), + miner: validatorA.Address, publicKey: validatorA.PublicKey, previousHash: blockHash, txHash: null, @@ -295,7 +295,7 @@ public void ValidateLastCommit() protocolVersion: BlockMetadata.CurrentProtocolVersion, index: 2, timestamp: timestamp, - miner: validatorA.PublicKey.ToAddress(), + miner: validatorA.Address, publicKey: validatorA.PublicKey, previousHash: GenesisHash, txHash: null, @@ -322,7 +322,7 @@ public void ValidateLastCommit() protocolVersion: BlockMetadata.CurrentProtocolVersion, index: 2, timestamp: timestamp, - miner: validatorA.PublicKey.ToAddress(), + miner: validatorA.Address, publicKey: validatorA.PublicKey, previousHash: blockHash, txHash: null, diff --git a/Libplanet.Tests/Blocks/BlockTest.cs b/Libplanet.Tests/Blocks/BlockTest.cs index 50c8d58d358..8284accd61f 100644 --- a/Libplanet.Tests/Blocks/BlockTest.cs +++ b/Libplanet.Tests/Blocks/BlockTest.cs @@ -68,7 +68,7 @@ public void TransactionOrderIdempotent() null, new[] { - new RandomAction(signer.ToAddress()), + new RandomAction(signer.Address), }.ToPlainValues())).ToImmutableArray(); var blockA = ProposeGenesis(timestamp: timestamp, transactions: txs); var blockB = ProposeGenesis(timestamp: timestamp, transactions: txs); diff --git a/Libplanet.Tests/Blocks/InvalidBlockTxCountPerSignerExceptionTest.cs b/Libplanet.Tests/Blocks/InvalidBlockTxCountPerSignerExceptionTest.cs index 2f444e3aaeb..97671c59f8a 100644 --- a/Libplanet.Tests/Blocks/InvalidBlockTxCountPerSignerExceptionTest.cs +++ b/Libplanet.Tests/Blocks/InvalidBlockTxCountPerSignerExceptionTest.cs @@ -15,7 +15,7 @@ public InvalidBlockTxCountPerSignerExceptionTest() [Fact] public void Serialization() { - Address signer = new PrivateKey().PublicKey.ToAddress(); + Address signer = new PrivateKey().Address; var e = new InvalidBlockTxCountPerSignerException("A message.", signer, 10); var f = new BinaryFormatter(); InvalidBlockTxCountPerSignerException e2; diff --git a/Libplanet.Tests/Consensus/ValidatorSetTest.cs b/Libplanet.Tests/Consensus/ValidatorSetTest.cs index 1aaa752263b..de75c01330b 100644 --- a/Libplanet.Tests/Consensus/ValidatorSetTest.cs +++ b/Libplanet.Tests/Consensus/ValidatorSetTest.cs @@ -126,7 +126,7 @@ public void ValidateBlockCommitValidators() .Select(_ => new PrivateKey()) .ToList(); var orderedPrivateKeys = unorderedPrivateKeys - .OrderBy(key => key.PublicKey.ToAddress()) + .OrderBy(key => key.Address) .ToList(); var validatorSet = new ValidatorSet(unorderedPrivateKeys.Select( key => new Validator(key.PublicKey, BigInteger.One)).ToList()); diff --git a/Libplanet.Tests/Crypto/PrivateKeyTest.cs b/Libplanet.Tests/Crypto/PrivateKeyTest.cs index 2e59a4ada00..a3125b66904 100644 --- a/Libplanet.Tests/Crypto/PrivateKeyTest.cs +++ b/Libplanet.Tests/Crypto/PrivateKeyTest.cs @@ -120,6 +120,21 @@ public void PublicKeyTest() ); } + [Fact] + public void AddressTest() + { + var privateKey = new PrivateKey( + new byte[] + { + 0xbe, 0xe6, 0xf9, 0xcc, 0x62, 0x41, 0x27, 0x60, 0xb3, 0x69, 0x6e, + 0x05, 0xf6, 0xfb, 0x4a, 0xbe, 0xb9, 0xe8, 0x3c, 0x4f, 0x94, 0x4f, + 0x83, 0xfd, 0x62, 0x08, 0x1b, 0x74, 0x54, 0xcb, 0xc0, 0x38, + } + ); + var expected = new Address("f45A22dD63f6428e85eE0a6E13a763278f57626d"); + Assert.Equal(expected, privateKey.Address); + } + [Fact] public void SignTest() { diff --git a/Libplanet.Tests/Crypto/PublicKeyTest.cs b/Libplanet.Tests/Crypto/PublicKeyTest.cs index 06951fce2d4..1a59f41412e 100644 --- a/Libplanet.Tests/Crypto/PublicKeyTest.cs +++ b/Libplanet.Tests/Crypto/PublicKeyTest.cs @@ -74,6 +74,22 @@ public void Format() ); } + [Fact] + public void AddressTest() + { + var privateKey = new PrivateKey( + new byte[] + { + 0xbe, 0xe6, 0xf9, 0xcc, 0x62, 0x41, 0x27, 0x60, 0xb3, 0x69, 0x6e, + 0x05, 0xf6, 0xfb, 0x4a, 0xbe, 0xb9, 0xe8, 0x3c, 0x4f, 0x94, 0x4f, + 0x83, 0xfd, 0x62, 0x08, 0x1b, 0x74, 0x54, 0xcb, 0xc0, 0x38, + } + ); + var publicKey = privateKey.PublicKey; + var expected = new Address("f45A22dD63f6428e85eE0a6E13a763278f57626d"); + Assert.Equal(expected, publicKey.Address); + } + [Fact] public void Verify() { diff --git a/Libplanet.Tests/Fixtures/BlockContentFixture.cs b/Libplanet.Tests/Fixtures/BlockContentFixture.cs index 2af59e76ca0..884ef39f5b2 100644 --- a/Libplanet.Tests/Fixtures/BlockContentFixture.cs +++ b/Libplanet.Tests/Fixtures/BlockContentFixture.cs @@ -74,13 +74,14 @@ public BlockContentFixture() new UnsignedTx( new TxInvoice( genesisHash: GenesisHash, - updatedAddresses: new[] { Block1Tx0Key.ToAddress() }, + updatedAddresses: new AddressSet(new[] { Block1Tx0Key.Address }), timestamp: new DateTimeOffset(2021, 9, 6, 17, 0, 1, 1, default), actions: new TxActionList(new IAction[] { Arithmetic.Add(10), Arithmetic.Add(50), Arithmetic.Sub(25), - }.ToPlainValues()) - ), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), new TxSigningMetadata(Block1Tx0Key.PublicKey, nonce: 0L) ), signature: ByteUtil.ParseHexToImmutable( @@ -94,19 +95,18 @@ public BlockContentFixture() new UnsignedTx( new TxInvoice( genesisHash: GenesisHash, - updatedAddresses: new[] { Block1Tx1Key.ToAddress() }, + updatedAddresses: new AddressSet(new[] { Block1Tx1Key.Address }), timestamp: new DateTimeOffset(2021, 9, 6, 17, 0, 1, 1, default), actions: new TxActionList(new IAction[] { Arithmetic.Add(30), - }.ToPlainValues()) - ), - new TxSigningMetadata(Block1Tx1Key.PublicKey, nonce: 1L) - ), + }.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(Block1Tx1Key.PublicKey, nonce: 1L)), signature: ByteUtil.ParseHexToImmutable( "3045022100abe3caabf2a46a297f2e4496f2c46d7e2f723e75fc42025d19f3ed7fce382" + - "d4e02200ffd36f7bef759b6c7ab43bc0f8959a0c463f88fd0f1faeaa209a8661506c4f0" - ) + "d4e02200ffd36f7bef759b6c7ab43bc0f8959a0c463f88fd0f1faeaa209a8661506c4f0") ); var block1Transactions = new List() { Block1Tx0, Block1Tx1 } @@ -129,7 +129,7 @@ public BlockContentFixture() protocolVersion: 0, index: 0, timestamp: new DateTimeOffset(2021, 9, 6, 13, 46, 39, 123, kst), - miner: GenesisKey.ToAddress(), + miner: GenesisKey.Address, publicKey: null, previousHash: null, txHash: null, @@ -141,7 +141,7 @@ public BlockContentFixture() protocolVersion: 1, index: 1, timestamp: new DateTimeOffset(2021, 9, 6, 17, 1, 9, 45, kst), - miner: Block1Key.ToAddress(), + miner: Block1Key.Address, publicKey: null, previousHash: GenesisHash, txHash: BlockContent.DeriveTxHash(block1Transactions), diff --git a/Libplanet.Tests/Fixtures/IntegerSet.cs b/Libplanet.Tests/Fixtures/IntegerSet.cs index 99966d63b4b..b76d015e6da 100644 --- a/Libplanet.Tests/Fixtures/IntegerSet.cs +++ b/Libplanet.Tests/Fixtures/IntegerSet.cs @@ -45,7 +45,7 @@ public IntegerSet( IEnumerable renderers = null) { PrivateKeys = initialStates.Select(_ => new PrivateKey()).ToImmutableArray(); - Addresses = PrivateKeys.Select(AddressExtensions.ToAddress).ToImmutableArray(); + Addresses = PrivateKeys.Select(key => key.Address).ToImmutableArray(); Actions = initialStates .Select((state, index) => new { State = state, Key = PrivateKeys[index] }) .Where(pair => !(pair.State is null)) @@ -57,16 +57,12 @@ public IntegerSet( .Select(pair => new { State = (BigInteger)pair.State, pair.Key }) .Select(pair => new { Action = Arithmetic.Add(pair.State), pair.Key }) .Select(pair => - Transaction.Create( - 0, - pair.Key, - null, - new[] { pair.Action }.ToPlainValues(), - null, - null, - ImmutableHashSet
.Empty.Add(pair.Key.ToAddress()) - ) - ) + new Transaction( + new UnsignedTx( + new TxInvoice( + actions: new TxActionList(new[] { pair.Action.PlainValue })), + new TxSigningMetadata(pair.Key.PublicKey, 0)), + pair.Key)) .OrderBy(tx => tx.Id) .ToImmutableArray(); Miner = new PrivateKey(); @@ -107,7 +103,7 @@ public IntegerSet( public TxWithContext Sign(PrivateKey signer, params Arithmetic[] actions) { - Address signerAddress = signer.ToAddress(); + Address signerAddress = signer.Address; KeyBytes rawStateKey = KeyConverters.ToStateKey(signerAddress); long nonce = Chain.GetNextTxNonce(signerAddress); Transaction tx = diff --git a/Libplanet.Tests/KeyStore/ProtectedPrivateKeyTest.cs b/Libplanet.Tests/KeyStore/ProtectedPrivateKeyTest.cs index 5c0f23c8fe8..dcd33553d0a 100644 --- a/Libplanet.Tests/KeyStore/ProtectedPrivateKeyTest.cs +++ b/Libplanet.Tests/KeyStore/ProtectedPrivateKeyTest.cs @@ -106,8 +106,8 @@ public class ProtectedPrivateKeyTest public void Unprotect() { Assert.Equal(AddressFixture, Fixture.Address); - Assert.Equal(AddressFixture, Fixture.Unprotect(PassphraseFixture).ToAddress()); - Assert.Equal(AddressFixture2, Fixture2.Unprotect(PassphraseFixture).ToAddress()); + Assert.Equal(AddressFixture, Fixture.Unprotect(PassphraseFixture).Address); + Assert.Equal(AddressFixture2, Fixture2.Unprotect(PassphraseFixture).Address); var incorrectPassphraseException = Assert.Throws( () => Fixture.Unprotect("wrong passphrase") ); @@ -813,7 +813,7 @@ public void WriteJson() // TODO: More decent tests should be written. ProtectedPrivateKey key = ProtectedPrivateKey.FromJson(json); Assert.Equal(AddressFixture, key.Address); - Assert.Equal(AddressFixture, key.Unprotect(PassphraseFixture).ToAddress()); + Assert.Equal(AddressFixture, key.Unprotect(PassphraseFixture).Address); using (var stream = new MemoryStream()) { @@ -823,7 +823,7 @@ public void WriteJson() ProtectedPrivateKey key2 = ProtectedPrivateKey.FromJson(json); Assert.Equal(AddressFixture2, key2.Address); - Assert.Equal(AddressFixture2, key2.Unprotect(PassphraseFixture).ToAddress()); + Assert.Equal(AddressFixture2, key2.Unprotect(PassphraseFixture).Address); } } } diff --git a/Libplanet.Tests/Store/DataModelTest.cs b/Libplanet.Tests/Store/DataModelTest.cs index 0e9945a2748..9d40a6e6af5 100644 --- a/Libplanet.Tests/Store/DataModelTest.cs +++ b/Libplanet.Tests/Store/DataModelTest.cs @@ -283,7 +283,7 @@ public void DecodeFromBadIValues() random.NextBytes(buffer); ImmutableArray randBytes = buffer.ToImmutableArray(); Guid randGuid = Guid.NewGuid(); - Address randAddress = new PrivateKey().ToAddress(); + Address randAddress = new PrivateKey().Address; BTypes.Dictionary encoded; @@ -409,7 +409,7 @@ public void DecodeToBadTypes() random.NextBytes(buffer); ImmutableArray randBytes = buffer.ToImmutableArray(); Guid randGuid = Guid.NewGuid(); - Address randAddress = new PrivateKey().ToAddress(); + Address randAddress = new PrivateKey().Address; BTypes.Dictionary encoded; @@ -465,7 +465,7 @@ public RootModel() random.NextBytes(buffer); Bytes = buffer.ToImmutableArray(); Guid = Guid.NewGuid(); - Addr = new PrivateKey().ToAddress(); + Addr = new PrivateKey().Address; Str = Guid.NewGuid().ToString(); ListEmpty = ImmutableList.Empty; @@ -495,7 +495,7 @@ public RootModel() .ToImmutableList(); ListAddr = Enumerable .Range(0, 2) - .Select(_ => new PrivateKey().ToAddress()) + .Select(_ => new PrivateKey().Address) .ToImmutableList(); ListStr = Enumerable .Range(0, 2) @@ -537,7 +537,7 @@ public RootModel() .Range(0, 2) .Select(_ => new KeyValuePair, Address>( Guid.NewGuid().ToByteArray().ToImmutableArray(), - new PrivateKey().ToAddress())) + new PrivateKey().Address)) .ToImmutableDictionary(); DictBytesStr = Enumerable .Range(0, 2) @@ -554,7 +554,7 @@ public RootModel() DictAddrStr = Enumerable .Range(0, 2) .Select(_ => new KeyValuePair( - new PrivateKey().ToAddress(), + new PrivateKey().Address, Guid.NewGuid().ToString())) .ToImmutableDictionary(); DictStrStr = Enumerable diff --git a/Libplanet.Tests/Store/StoreFixture.cs b/Libplanet.Tests/Store/StoreFixture.cs index 6661eb2b7ca..67257768aa3 100644 --- a/Libplanet.Tests/Store/StoreFixture.cs +++ b/Libplanet.Tests/Store/StoreFixture.cs @@ -219,7 +219,6 @@ public Transaction MakeTransaction( actions?.ToPlainValues() ?? Array.Empty().ToPlainValues(), null, null, - updatedAddresses, timestamp ); } diff --git a/Libplanet.Tests/Store/StoreTest.cs b/Libplanet.Tests/Store/StoreTest.cs index bbef746a61d..bf609d9da37 100644 --- a/Libplanet.Tests/Store/StoreTest.cs +++ b/Libplanet.Tests/Store/StoreTest.cs @@ -741,9 +741,7 @@ int txNonce new[] { action }.ToPlainValues(), null, null, - ImmutableHashSet
.Empty, - DateTimeOffset.UtcNow - ); + DateTimeOffset.UtcNow); } const int taskCount = 5; diff --git a/Libplanet.Tests/Store/Trie/TrieTest.cs b/Libplanet.Tests/Store/Trie/TrieTest.cs index 1541b583e6b..7c2ec961d06 100644 --- a/Libplanet.Tests/Store/Trie/TrieTest.cs +++ b/Libplanet.Tests/Store/Trie/TrieTest.cs @@ -25,7 +25,7 @@ public void GetAndSet(int addressCount) var addresses = Enumerable .Range(0, addressCount) - .Select(_ => new PrivateKey().ToAddress()) + .Select(_ => new PrivateKey().Address) .ToImmutableArray(); var states = new Dictionary(); @@ -64,7 +64,7 @@ public void Commit(int addressCount) var states = new IValue[addressCount]; for (int i = 0; i < addressCount; ++i) { - addresses[i] = new PrivateKey().ToAddress(); + addresses[i] = new PrivateKey().Address; states[i] = (Binary)TestUtils.GetRandomBytes(128); trieA = trieA.Set(new KeyBytes(addresses[i].ByteArray), states[i]); diff --git a/Libplanet.Tests/TestUtils.cs b/Libplanet.Tests/TestUtils.cs index 3c03fc7e686..e7f3e458197 100644 --- a/Libplanet.Tests/TestUtils.cs +++ b/Libplanet.Tests/TestUtils.cs @@ -438,7 +438,7 @@ public static PreEvaluationBlock ProposeGenesis( index: 0, timestamp: timestamp ?? new DateTimeOffset(2018, 11, 29, 0, 0, 0, TimeSpan.Zero), - miner: (proposer ?? GenesisProposer.PublicKey).ToAddress(), + miner: (proposer ?? GenesisProposer.PublicKey).Address, publicKey: protocolVersion >= 2 ? proposer ?? GenesisProposer.PublicKey : null, previousHash: null, txHash: BlockContent.DeriveTxHash(txs), @@ -494,7 +494,7 @@ public static PreEvaluationBlock ProposeNext( index: previousBlock.Index + 1, timestamp: previousBlock.Timestamp.Add( blockInterval ?? TimeSpan.FromSeconds(15)), - miner: miner?.ToAddress() ?? previousBlock.Miner, + miner: miner?.Address ?? previousBlock.Miner, publicKey: protocolVersion >= 2 ? miner ?? previousBlock.PublicKey : null, previousHash: previousBlock.Hash, txHash: BlockContent.DeriveTxHash(txs), @@ -759,7 +759,6 @@ public static bool IsDumbAction(IValue action) { (Text)"item", (Text)"target_address", - (Text)"record_rehearsal", (Text)"record_random", (Text)"idempotent", (Text)"transfer_from", diff --git a/Libplanet.Tests/Tx/TransactionExtensionsTest.cs b/Libplanet.Tests/Tx/TransactionExtensionsTest.cs index 4f95a97df11..69b77696fa6 100644 --- a/Libplanet.Tests/Tx/TransactionExtensionsTest.cs +++ b/Libplanet.Tests/Tx/TransactionExtensionsTest.cs @@ -34,8 +34,9 @@ public void Sign() genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); var privateKey = new PrivateKey("51fb8c2eb261ed761429c297dd1f8952c8ce327d2ec2ec5bcc7728e3362627c2"); Transaction tx = invoice.Sign(privateKey, 123L); diff --git a/Libplanet.Tests/Tx/TransactionTest.cs b/Libplanet.Tests/Tx/TransactionTest.cs index 301f7fbd666..3e92fc712c1 100644 --- a/Libplanet.Tests/Tx/TransactionTest.cs +++ b/Libplanet.Tests/Tx/TransactionTest.cs @@ -83,7 +83,7 @@ public void CreateWithSystemAction() timestamp: timestamp ); - AssertBytesEqual(privateKey.ToAddress(), tx.Signer); + AssertBytesEqual(privateKey.Address, tx.Signer); Assert.Empty(tx.UpdatedAddresses); Assert.Equal(privateKey.PublicKey, tx.PublicKey); Assert.Equal(timestamp, tx.Timestamp); @@ -127,19 +127,16 @@ public void CreateWithCustomActions() 0xdb, 0xc5, 0x56, 0xd9, 0xac, 0x20, 0x41, 0xfe, 0xf9, 0x5f, } ); - DumbAction.RehearsalRecords.Value = - ImmutableList<(Address, string)>.Empty; Transaction tx = Transaction.Create( 0, privateKey, null, new[] { - new DumbAction(stateStore, "RecordRehearsal", true), + new DumbAction(stateStore, "RecordRehearsal", false), }.Select(x => x.PlainValue), null, null, - ImmutableHashSet
.Empty, timestamp ); @@ -152,12 +149,12 @@ public void CreateWithCustomActions() AssertBytesEqual( new byte[] { - 0x30, 0x45, 0x02, 0x21, 0x00, 0xe5, 0x5e, 0xee, 0xb1, 0x95, 0x30, 0x2f, 0xf0, - 0x8d, 0x30, 0xe1, 0xe8, 0xe5, 0xdc, 0x95, 0x1c, 0xcd, 0x4d, 0xb0, 0xf9, 0x94, - 0x1f, 0x49, 0x61, 0x3a, 0x43, 0xd8, 0x73, 0x90, 0x06, 0x68, 0xec, 0x02, 0x20, - 0x1c, 0x37, 0xee, 0xc6, 0x67, 0xd0, 0x63, 0xc9, 0xe2, 0x9c, 0x6f, 0x9d, 0x4e, - 0x88, 0xdc, 0xa4, 0x94, 0xc0, 0xcc, 0xd0, 0xc0, 0xda, 0xa0, 0x17, 0x8d, 0x05, - 0xbb, 0x57, 0x26, 0xb2, 0xda, 0x98, + 0x30, 0x45, 0x02, 0x21, 0x00, 0xf7, 0xc7, 0xa6, 0xd7, 0x48, 0x8b, 0x18, 0x25, + 0x4d, 0x80, 0x9a, 0x6e, 0x08, 0x3e, 0xd9, 0x9c, 0xa0, 0xb0, 0x90, 0x00, 0x00, + 0xa7, 0x9e, 0x0e, 0x53, 0x6f, 0x1d, 0x11, 0x4b, 0xc6, 0x88, 0xfd, 0x02, 0x20, + 0x39, 0xbf, 0x80, 0x76, 0xc1, 0x61, 0x98, 0x50, 0x71, 0x99, 0x13, 0xa6, 0xe5, + 0x4c, 0xb5, 0x79, 0x17, 0x28, 0xe3, 0x3e, 0x38, 0xc5, 0xbe, 0x09, 0x41, 0xfc, + 0x29, 0x8d, 0x25, 0x5a, 0x39, 0x6c, }, tx.Signature ); @@ -165,9 +162,9 @@ public void CreateWithCustomActions() new TxId( new byte[] { - 0x8d, 0x50, 0xf7, 0xfd, 0xd0, 0xf5, 0xaf, 0x99, 0x74, 0xed, 0xe2, 0x42, - 0xdb, 0xfd, 0x4f, 0xf4, 0xf6, 0x0c, 0xf1, 0xc8, 0xd1, 0xf6, 0x9a, 0x37, - 0x3e, 0xbc, 0x8e, 0x27, 0x5f, 0x30, 0xb7, 0x2c, + 0x8d, 0xae, 0xb2, 0x56, 0xfa, 0xf0, 0x26, 0x1c, 0x60, 0x86, 0x27, 0xb5, + 0xe4, 0x06, 0xb5, 0x29, 0x2f, 0x2b, 0x17, 0xf7, 0x23, 0xe0, 0x40, 0x61, + 0x88, 0x87, 0xdf, 0xba, 0x3c, 0xd9, 0x19, 0x24, } ), tx.Id @@ -181,25 +178,8 @@ public void CreateWithDefaultUpdatedAddresses() 0, _fx.PrivateKey1, null, - Array.Empty().Select(x => x.PlainValue) - ); + Array.Empty().Select(x => x.PlainValue)); Assert.Empty(emptyTx.UpdatedAddresses); - - Address updatedAddr = new PrivateKey().ToAddress(); - var txWithAddr = Transaction.Create( - 0, - _fx.PrivateKey1, - null, - _fx.TxWithActions.Actions, - null, - null, - new[] { updatedAddr }.ToImmutableHashSet() - ); - - Assert.Equal( - new[] { updatedAddr }.ToHashSet(), - txWithAddr.UpdatedAddresses.ToHashSet() - ); } [Fact] @@ -212,9 +192,7 @@ public void CreateWithDefaultTimestamp() null, Array.Empty().Select(x => x.PlainValue), null, - null, - ImmutableHashSet
.Empty - ); + null); DateTimeOffset rightAfter = DateTimeOffset.UtcNow; Assert.InRange(tx.Timestamp, rightBefore, rightAfter); @@ -232,7 +210,6 @@ public void CreateWithMissingRequiredArguments() Array.Empty().Select(x => x.PlainValue), null, null, - ImmutableHashSet
.Empty, DateTimeOffset.UtcNow ) ); @@ -346,16 +323,17 @@ public void Equality() genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); var privateKey = new PrivateKey("51fb8c2eb261ed761429c297dd1f8952c8ce327d2ec2ec5bcc7728e3362627c2"); PublicKey publicKey = privateKey.PublicKey; var signingMetadata = new TxSigningMetadata(publicKey, 123L); var unsignedTx = new UnsignedTx(invoice, signingMetadata); ImmutableArray signature = ByteUtil.ParseHexToImmutable( - "304302206354e82d2cb88d63a1fd2fac0f458ce869b72bdc330cdc59d0ebebbea896c" + - "80f021f5a0ba3a5b7a90c541c29ee52cf111d061e130c4141c1e2a67356bd81b4c0e8"); + "3045022100e4df322ba35e0e5ed96043b1c214e4a0f23734a7491b5db4c4a88834d3f47" + + "48a0220691b0972641a8759ac921b731e5750c20505f05fd993d45b24eb989de33018b0"); var tx = new Transaction(unsignedTx, signature: signature); Assert.Equal(invoice, tx); @@ -374,10 +352,11 @@ public void Equality() { var diffInvoice = new TxInvoice( i == 0 ? (BlockHash?)null : invoice.GenesisHash, - i == 1 ? null : invoice.UpdatedAddresses, - i == 2 ? (DateTimeOffset?)DateTimeOffset.MinValue : invoice.Timestamp, - i == 3 ? null : invoice.Actions - ); + i == 1 ? AddressSet.Empty : invoice.UpdatedAddresses, + i == 2 ? DateTimeOffset.MinValue : invoice.Timestamp, + i == 3 ? TxActionList.Empty : invoice.Actions, + null, + null); var diffSigningMetadata = new TxSigningMetadata( i == 4 ? wrongKey.PublicKey : signingMetadata.PublicKey, i == 5 ? 456L : signingMetadata.Nonce @@ -424,16 +403,17 @@ public void JsonSerialization() genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); var privateKey = new PrivateKey("51fb8c2eb261ed761429c297dd1f8952c8ce327d2ec2ec5bcc7728e3362627c2"); PublicKey publicKey = privateKey.PublicKey; var signingMetadata = new TxSigningMetadata(publicKey, 123L); var unsignedTx = new UnsignedTx(invoice, signingMetadata); ImmutableArray signature = ByteUtil.ParseHexToImmutable( - "304302206354e82d2cb88d63a1fd2fac0f458ce869b72bdc330cdc59d0ebebbea896c" + - "80f021f5a0ba3a5b7a90c541c29ee52cf111d061e130c4141c1e2a67356bd81b4c0e8"); + "3045022100e4df322ba35e0e5ed96043b1c214e4a0f23734a7491b5db4c4a88834d3f47" + + "48a0220691b0972641a8759ac921b731e5750c20505f05fd993d45b24eb989de33018b0"); var tx = new Transaction(unsignedTx, signature: signature); #pragma warning disable MEN002 // Long lines are OK for test JSON data. @@ -441,23 +421,21 @@ public void JsonSerialization() tx, @" { - ""id"": ""d1475d7f4c84444a0522989876aa0a1aa5d9ba8fdbf84ea5a33c60bd83cbbe7f"", + ""id"": ""b4ce04152a4d197917c3b29eb4620d8e8d02dbe496f844965ee586ff343c3c77"", ""nonce"": 123, ""signer"": ""89F0eE48e8BeaE3131B17Dc79A1282A0D7EdC6b9"", ""updatedAddresses"": [ ""B61CE2Ce6d28237C1BC6E114616616762f1a12Ab"", ""D6D639DA5a58A78A564C2cD3DB55FA7CeBE244A9"" ], - ""signature"": ""MEMCIGNU6C0suI1jof0vrA9FjOhptyvcMwzcWdDr676olsgPAh9aC6Olt6kMVBwp7lLPER0GHhMMQUHB4qZzVr2BtMDo"", + ""signature"": ""MEUCIQDk3zIro14OXtlgQ7HCFOSg8jc0p0kbXbTEqIg00/R0igIgaRsJcmQah1mskhtzHldQwgUF8F/Zk9RbJOuYneMwGLA="", ""actions"": [ { ""\uFEFFitem"": ""\uFEFFfoo"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0xd6d639da5a58a78a564c2cd3db55fa7cebe244a9"" }, { ""\uFEFFitem"": ""\uFEFFbar"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0xb61ce2ce6d28237c1bc6e114616616762f1a12ab"" } ], diff --git a/Libplanet.Tests/Tx/TxActionListTest.cs b/Libplanet.Tests/Tx/TxActionListTest.cs index 48b48a7d107..c41e5314494 100644 --- a/Libplanet.Tests/Tx/TxActionListTest.cs +++ b/Libplanet.Tests/Tx/TxActionListTest.cs @@ -152,12 +152,10 @@ public void JsonSerialize() [ { ""\uFEFFitem"": ""\uFEFFfoo"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0x0000000000000000000000000000000000000000"" }, { ""\uFEFFitem"": ""\uFEFFbar"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0xd6d639da5a58a78a564c2cd3db55fa7cebe244a9"" } ]"; diff --git a/Libplanet.Tests/Tx/TxFixture.cs b/Libplanet.Tests/Tx/TxFixture.cs index c91f18f5291..85ac0b3217e 100644 --- a/Libplanet.Tests/Tx/TxFixture.cs +++ b/Libplanet.Tests/Tx/TxFixture.cs @@ -80,16 +80,18 @@ public TxFixture(BlockHash? genesisHash) ZoneId = 10, }, }; - TxWithActions = Transaction.Create( - 0, - PrivateKey1, - genesisHash, - actions.ToPlainValues(), - updatedAddresses: ImmutableHashSet.Create( - new Address("c2a86014073d662a4a9bfcf9cb54263dfa4f5cbc") - ), - timestamp: timestamp - ); + TxWithActions = new Transaction( + new UnsignedTx( + new TxInvoice( + genesisHash: genesisHash, + updatedAddresses: ImmutableHashSet.Create( + new Address("c2a86014073d662a4a9bfcf9cb54263dfa4f5cbc")), + timestamp: timestamp, + actions: new TxActionList(actions.ToPlainValues()), + maxGasPrice: null, + gasLimit: null), + new TxSigningMetadata(PrivateKey1.PublicKey, 0)), + PrivateKey1); } public PrivateKey PrivateKey1 { get; } @@ -112,15 +114,15 @@ public TxFixture(BlockHash? genesisHash) public PublicKey PublicKey5 => PrivateKey5.PublicKey; - public Address Address1 => PublicKey1.ToAddress(); + public Address Address1 => PublicKey1.Address; - public Address Address2 => PublicKey2.ToAddress(); + public Address Address2 => PublicKey2.Address; - public Address Address3 => PublicKey3.ToAddress(); + public Address Address3 => PublicKey3.Address; - public Address Address4 => PublicKey4.ToAddress(); + public Address Address4 => PublicKey4.Address; - public Address Address5 => PublicKey5.ToAddress(); + public Address Address5 => PublicKey5.Address; public Transaction Tx { get; } diff --git a/Libplanet.Tests/Tx/TxInvoiceTest.cs b/Libplanet.Tests/Tx/TxInvoiceTest.cs index bd7becda9ad..63c90cf269e 100644 --- a/Libplanet.Tests/Tx/TxInvoiceTest.cs +++ b/Libplanet.Tests/Tx/TxInvoiceTest.cs @@ -36,8 +36,9 @@ public void PlainConstructor() genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); Assert.Equal(genesisHash, invoice.GenesisHash); Assert.True(updatedAddresses.SetEquals(invoice.UpdatedAddresses)); Assert.Equal(timestamp, invoice.Timestamp); @@ -75,8 +76,9 @@ public void CopyConstructor() genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); var copy = new TxInvoice(original); Assert.Equal(genesisHash, copy.GenesisHash); Assert.True(updatedAddresses.SetEquals(copy.UpdatedAddresses)); @@ -109,14 +111,16 @@ public void Equality() genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); var invoice2 = new TxInvoice( genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); Assert.True(invoice1.Equals(invoice2)); Assert.True(invoice1.Equals((object)invoice2)); Assert.Equal(invoice1.GetHashCode(), invoice2.GetHashCode()); @@ -127,20 +131,21 @@ public void Equality() Assert.False(invoice1.Equals(null)); - for (int i = 0; i < 6; i++) + for (int i = 0; i < 5; i++) { + // NOTE: Non-null cases for MaxGasPrice and GasLimit are flipped as existing + // mock object has respective values set to null. var invoice = new TxInvoice( - i == 0 ? (BlockHash?)null : genesisHash, - i == 1 ? null : updatedAddresses, - i == 2 ? (DateTimeOffset?)DateTimeOffset.MinValue : timestamp, - i == 3 ? null : actions, - i == 4 ? (FungibleAssetValue?)null : FungibleAssetValue.FromRawValue( - Currency.Uncapped( - "FOO", - 18, - new PrivateKey().ToAddress()), - 100), - i == 5 ? (long?)null : 10); + i == 0 ? (BlockHash?)null : genesisHash, + i == 1 ? (IImmutableSet
)AddressSet.Empty : updatedAddresses, + i == 2 ? DateTimeOffset.MinValue : timestamp, + i == 3 ? TxActionList.Empty : actions, + i == 4 + ? FungibleAssetValue.FromRawValue( + Currency.Uncapped("FOO", 18, new PrivateKey().Address), + 100) + : (FungibleAssetValue?)null, + i == 4 ? 10 : (long?)null); Assert.False(invoice1.Equals(invoice)); Assert.False(invoice1.Equals((object)invoice)); Assert.NotEqual(invoice1.GetHashCode(), invoice.GetHashCode()); @@ -184,12 +189,10 @@ public void JsonSerialization() ""actions"": [ {{ ""\uFEFFitem"": ""\uFEFFfoo"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0xd6d639da5a58a78a564c2cd3db55fa7cebe244a9"" }}, {{ ""\uFEFFitem"": ""\uFEFFbar"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0xb61ce2ce6d28237c1bc6e114616616762f1a12ab"" }} ], diff --git a/Libplanet.Tests/Tx/TxMarshalerTest.cs b/Libplanet.Tests/Tx/TxMarshalerTest.cs index 6f496719e9f..d4579f23c85 100644 --- a/Libplanet.Tests/Tx/TxMarshalerTest.cs +++ b/Libplanet.Tests/Tx/TxMarshalerTest.cs @@ -314,7 +314,7 @@ public void UnmarshalTransactionWithCustomActions() .Add("values", Dictionary.Empty .Add("weapon", "wand") .Add("target", "orc") - .Add("target_address", new Address(publicKey).ByteArray)), + .Add("target_address", new Address(publicKey).Bencoded)), action0.PlainValue ); diff --git a/Libplanet.Tests/Tx/TxMetadataTest.cs b/Libplanet.Tests/Tx/TxMetadataTest.cs index 234c6e37273..658150296bd 100644 --- a/Libplanet.Tests/Tx/TxMetadataTest.cs +++ b/Libplanet.Tests/Tx/TxMetadataTest.cs @@ -48,7 +48,7 @@ public void Constructor() var meta = new TxMetadata(_key1.PublicKey); DateTimeOffset after = DateTimeOffset.UtcNow; Assert.Equal(0L, meta.Nonce); - AssertBytesEqual(_key1.ToAddress(), meta.Signer); + AssertBytesEqual(_key1.Address, meta.Signer); Assert.Empty(meta.UpdatedAddresses); Assert.InRange(meta.Timestamp, before, after); Assert.Equal(_key1.PublicKey, meta.PublicKey); @@ -78,8 +78,8 @@ public void CopyConstructor() Nonce = 0L, UpdatedAddresses = new[] { - _key1.ToAddress(), - _key2.ToAddress(), + _key1.Address, + _key2.Address, }.ToImmutableHashSet(), Timestamp = new DateTimeOffset(2022, 1, 12, 4, 56, 7, 890, default), GenesisHash = BlockHash.FromString( @@ -99,13 +99,13 @@ public void Deserialize() { Bencodex.Types.Dictionary dict1 = Dictionary.Empty .Add(new byte[] { 0x6e }, 123L) - .Add(new byte[] { 0x73 }, _key1.ToAddress().ByteArray) + .Add(new byte[] { 0x73 }, _key1.Address.Bencoded) .Add(new byte[] { 0x75 }, new List()) .Add(new byte[] { 0x74 }, "2022-05-23T10:02:00.000000Z") .Add(new byte[] { 0x70 }, _key1.PublicKey.ToImmutableArray(compress: false)); var meta1 = new TxMetadata(dict1); Assert.Equal(123L, meta1.Nonce); - AssertBytesEqual(_key1.ToAddress(), meta1.Signer); + AssertBytesEqual(_key1.Address, meta1.Signer); Assert.Empty(meta1.UpdatedAddresses); Assert.Equal( new DateTimeOffset(2022, 5, 23, 10, 2, 0, default), @@ -115,12 +115,12 @@ public void Deserialize() Bencodex.Types.Dictionary dict2 = Dictionary.Empty .Add(new byte[] { 0x6e }, 0L) - .Add(new byte[] { 0x73 }, _key2.ToAddress().ByteArray) + .Add(new byte[] { 0x73 }, _key2.Address.Bencoded) .Add( new byte[] { 0x75 }, Bencodex.Types.List.Empty - .Add(_key1.ToAddress().ToByteArray()) - .Add(_key2.ToAddress().ToByteArray())) + .Add(_key1.Address.Bencoded) + .Add(_key2.Address.Bencoded)) .Add(new byte[] { 0x74 }, "2022-01-12T04:56:07.890000Z") .Add(new byte[] { 0x70 }, _key2.PublicKey.ToImmutableArray(compress: false)) .Add( @@ -129,9 +129,9 @@ public void Deserialize() "83915317ebdbf870c567b263dd2e61ec9dca7fb381c592d80993291b6ffe5ad5")); var meta2 = new TxMetadata(dict2); Assert.Equal(0L, meta2.Nonce); - AssertBytesEqual(_key2.ToAddress(), meta2.Signer); + AssertBytesEqual(_key2.Address, meta2.Signer); Assert.Equal( - new[] { _key1.ToAddress(), _key2.ToAddress() }.ToImmutableHashSet(), + new[] { _key1.Address, _key2.Address }.ToImmutableHashSet(), meta2.UpdatedAddresses); Assert.Equal( new DateTimeOffset(2022, 1, 12, 4, 56, 7, 890, default), @@ -155,7 +155,7 @@ public void ToBencodex(DateTimeOffset timestamp) }; Bencodex.Types.Dictionary expected1 = Dictionary.Empty .Add(new byte[] { 0x6e }, 123L) - .Add(new byte[] { 0x73 }, _key1.ToAddress().ByteArray) + .Add(new byte[] { 0x73 }, _key1.Address.Bencoded) .Add(new byte[] { 0x75 }, new List()) .Add(new byte[] { 0x74 }, "2022-05-23T10:02:00.000000Z") .Add(new byte[] { 0x70 }, _key1.PublicKey.ToImmutableArray(compress: false)); @@ -168,8 +168,8 @@ public void ToBencodex(DateTimeOffset timestamp) Nonce = 0L, UpdatedAddresses = new[] { - _key1.ToAddress(), - _key2.ToAddress(), + _key1.Address, + _key2.Address, }.ToImmutableHashSet(), Timestamp = new DateTimeOffset(2022, 1, 12, 4, 56, 7, 890, default), GenesisHash = BlockHash.FromString( @@ -177,12 +177,12 @@ public void ToBencodex(DateTimeOffset timestamp) }; Bencodex.Types.Dictionary expected2 = Dictionary.Empty .Add(new byte[] { 0x6e }, 0L) - .Add(new byte[] { 0x73 }, _key2.ToAddress().ByteArray) + .Add(new byte[] { 0x73 }, _key2.Address.Bencoded) .Add( new byte[] { 0x75 }, Bencodex.Types.List.Empty - .Add(_key1.ToAddress().ToByteArray()) - .Add(_key2.ToAddress().ToByteArray())) + .Add(_key1.Address.Bencoded) + .Add(_key2.Address.Bencoded)) .Add(new byte[] { 0x74 }, "2022-01-12T04:56:07.890000Z") .Add(new byte[] { 0x70 }, _key2.PublicKey.ToImmutableArray(compress: false)) .Add( @@ -200,7 +200,7 @@ private class MetadataTransaction : ITransaction public long Nonce { get; set; } = 0L; - public Address Signer => PublicKey.ToAddress(); + public Address Signer => PublicKey.Address; public IImmutableSet
UpdatedAddresses { get; set; } = ImmutableHashSet
.Empty; diff --git a/Libplanet.Tests/Tx/TxSigningMetadataTest.cs b/Libplanet.Tests/Tx/TxSigningMetadataTest.cs index c8350659538..91938861a85 100644 --- a/Libplanet.Tests/Tx/TxSigningMetadataTest.cs +++ b/Libplanet.Tests/Tx/TxSigningMetadataTest.cs @@ -16,7 +16,7 @@ public class TxSigningMetadataTest public void Constructor() { var metadata = new TxSigningMetadata(PublicKey, 123L); - Assert.Equal(PublicKey.ToAddress(), metadata.Signer); + Assert.Equal(PublicKey.Address, metadata.Signer); Assert.Equal(PublicKey, metadata.PublicKey); Assert.Equal(123L, metadata.Nonce); @@ -29,12 +29,12 @@ public void CopyConstructor() { var metadata = new TxSigningMetadata(PublicKey, 123L); var copy = new TxSigningMetadata(metadata); - Assert.Equal(PublicKey.ToAddress(), copy.Signer); + Assert.Equal(PublicKey.Address, copy.Signer); Assert.Equal(PublicKey, copy.PublicKey); Assert.Equal(123L, copy.Nonce); var copyFromInterface = new TxSigningMetadata(new MockingTxSigningMetadata()); - Assert.Equal(PublicKey.ToAddress(), copyFromInterface.Signer); + Assert.Equal(PublicKey.Address, copyFromInterface.Signer); Assert.Equal(PublicKey, copyFromInterface.PublicKey); Assert.Equal(123L, copyFromInterface.Nonce); } @@ -90,7 +90,7 @@ private class MockingTxSigningMetadata : ITxSigningMetadata { long ITxSigningMetadata.Nonce => 123L; - Address ITxSigningMetadata.Signer => PublicKey.ToAddress(); + Address ITxSigningMetadata.Signer => PublicKey.Address; PublicKey ITxSigningMetadata.PublicKey => PublicKey; diff --git a/Libplanet.Tests/Tx/UnsignedTxTest.cs b/Libplanet.Tests/Tx/UnsignedTxTest.cs index 9d6339d555b..b957d9b9e66 100644 --- a/Libplanet.Tests/Tx/UnsignedTxTest.cs +++ b/Libplanet.Tests/Tx/UnsignedTxTest.cs @@ -41,8 +41,9 @@ public UnsignedTxTest() genesisHash, updatedAddresses, timestamp, - actions - ); + actions, + null, + null); _signingMetadata = new TxSigningMetadata(PublicKey, 123L); } @@ -92,14 +93,13 @@ public void VerifySignature() var unsignedTx = new UnsignedTx(_invoice, _signingMetadata); var privateKey = new PrivateKey("51fb8c2eb261ed761429c297dd1f8952c8ce327d2ec2ec5bcc7728e3362627c2"); - ImmutableArray signature = ByteUtil.ParseHexToImmutable( - "304302206354e82d2cb88d63a1fd2fac0f458ce869b72bdc330cdc59d0ebebbea896c" + - "80f021f5a0ba3a5b7a90c541c29ee52cf111d061e130c4141c1e2a67356bd81b4c0e8"); - Assert.True(unsignedTx.VerifySignature(signature)); - + var signature = ByteUtil.ParseHexToImmutable( + "3045022100e4df322ba35e0e5ed96043b1c214e4a0f23734a7491b5db4c4a88834d3f47" + + "48a0220691b0972641a8759ac921b731e5750c20505f05fd993d45b24eb989de33018b0"); var wrongSignature = ByteUtil.ParseHexToImmutable( - "304302206354e82d2cb88d63a1fd2fac0f458ce869b72bdc330cdc59d0ebebbea896c" + - "80f021f5a0ba3a5b7a90c541c29ee52cf111d061e130c4141c1e2a67356bd81b4c0e9"); + "3045022100e4df322ba35e0e5ed96043b1c214e4a0f23734a7491b5db4c4a88834d3f47" + + "48a0220691b0972641a8759ac921b731e5750c20505f05fd993d45b24eb989de33018b1"); + Assert.True(unsignedTx.VerifySignature(signature)); Assert.False(unsignedTx.VerifySignature(wrongSignature)); } @@ -120,10 +120,11 @@ public void Equality() { var diffInvoice = new TxInvoice( i == 0 ? (BlockHash?)null : _invoice.GenesisHash, - i == 1 ? null : _invoice.UpdatedAddresses, - i == 2 ? (DateTimeOffset?)DateTimeOffset.MinValue : _invoice.Timestamp, - i == 3 ? null : _invoice.Actions - ); + i == 1 ? AddressSet.Empty : _invoice.UpdatedAddresses, + i == 2 ? DateTimeOffset.MinValue : _invoice.Timestamp, + i == 3 ? TxActionList.Empty : _invoice.Actions, + null, + null); var diffSigningMetadata = new TxSigningMetadata( i == 4 ? wrongKey.PublicKey : _signingMetadata.PublicKey, i == 5 ? 456L : _signingMetadata.Nonce @@ -165,12 +166,10 @@ public void JsonSerialization() ""actions"": [ { ""\uFEFFitem"": ""\uFEFFfoo"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0xd6d639da5a58a78a564c2cd3db55fa7cebe244a9"" }, { ""\uFEFFitem"": ""\uFEFFbar"", - ""\uFEFFrecord_rehearsal"": false, ""\uFEFFtarget_address"": ""0xb61ce2ce6d28237c1bc6e114616616762f1a12ab"" } ], diff --git a/Libplanet.Types/AssemblyInfo.cs b/Libplanet.Types/AssemblyInfo.cs new file mode 100644 index 00000000000..0b2dfbb919c --- /dev/null +++ b/Libplanet.Types/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Libplanet.Tests")] +[assembly: InternalsVisibleTo("Libplanet.Explorer.Tests")] diff --git a/Libplanet.Types/Assets/Currency.cs b/Libplanet.Types/Assets/Currency.cs index f001267b497..6ce48a52c3b 100644 --- a/Libplanet.Types/Assets/Currency.cs +++ b/Libplanet.Types/Assets/Currency.cs @@ -28,7 +28,7 @@ namespace Libplanet.Types.Assets /// Here is how US Dollar can be represented using : /// /// var USMint = new PrivateKey(); - /// var USD = Currency.Uncapped(ticker: "USD", decimalPlaces: 2, minter: USMint.ToAddress()); + /// var USD = Currency.Uncapped(ticker: "USD", decimalPlaces: 2, minter: USMint.Address); /// var twentyThreeBucks = 23 * USD; /// // Or alternatively: USD * 23; /// // Or explicitly: new FungibleAssetValue(USD, 23, 0) @@ -688,7 +688,7 @@ public bool Equals(Currency other) => public IValue Serialize() { IValue minters = Minters is IImmutableSet
m - ? new Bencodex.Types.List(m.Select(a => new Binary(a.ByteArray))) + ? new Bencodex.Types.List(m.Select(a => a.Bencoded)) : (IValue)Null.Value; var serialized = Bencodex.Types.Dictionary.Empty .Add("ticker", Ticker) @@ -727,7 +727,7 @@ private static SHA1 GetSHA1() private IValue SerializeForHash() { IValue minters = Minters is ImmutableHashSet
a - ? new List(a.OrderBy(m => m).Select(m => m.ByteArray)) + ? new List(a.OrderBy(m => m).Select(m => m.Bencoded)) : (IValue)Null.Value; var serialized = Dictionary.Empty diff --git a/Libplanet.Types/Blocks/BlockMarshaler.cs b/Libplanet.Types/Blocks/BlockMarshaler.cs index b6c5595b72e..0700187e4c2 100644 --- a/Libplanet.Types/Blocks/BlockMarshaler.cs +++ b/Libplanet.Types/Blocks/BlockMarshaler.cs @@ -93,7 +93,7 @@ public static Dictionary MarshalBlockMetadata(IBlockMetadata metadata) dict = metadata.PublicKey is { } pubKey ? dict.Add(PublicKeyKey, pubKey.Format(compress: true)) - : dict.Add(MinerKey, metadata.Miner.ByteArray); + : dict.Add(MinerKey, metadata.Miner.Bencoded); if (metadata.LastCommit is { } commit) { @@ -182,7 +182,7 @@ public static BlockMetadata UnmarshalBlockMetadata(Dictionary marshaled) if (marshaled.ContainsKey(PublicKeyKey)) { publicKey = new PublicKey(((Binary)marshaled[PublicKeyKey]).ByteArray); - miner = publicKey.ToAddress(); + miner = publicKey.Address; } else { diff --git a/Libplanet.Types/Blocks/BlockMetadata.cs b/Libplanet.Types/Blocks/BlockMetadata.cs index 4b86b372f84..3357e5c8f60 100644 --- a/Libplanet.Types/Blocks/BlockMetadata.cs +++ b/Libplanet.Types/Blocks/BlockMetadata.cs @@ -95,7 +95,7 @@ public BlockMetadata( protocolVersion: CurrentProtocolVersion, index: index, timestamp: timestamp, - miner: publicKey.ToAddress(), + miner: publicKey.Address, publicKey: publicKey, previousHash: previousHash, txHash: txHash, @@ -185,7 +185,7 @@ public BlockMetadata( $"Argument {nameof(publicKey)} cannot be null for " + $"{nameof(protocolVersion)} >= 2.", publicKey); - Miner = miner == p.ToAddress() + Miner = miner == p.Address ? miner : throw new InvalidBlockPublicKeyException( $"Argument {nameof(miner)} should match the derived address of " + @@ -301,7 +301,7 @@ public Bencodex.Types.Dictionary MakeCandidateData(Nonce nonce) // and its ProtocolVersion is >= 2 when it is not null: dict = PublicKey is { } pubKey && ProtocolVersion >= 2 ? dict.Add("public_key", pubKey.Format(compress: true)) // ProtocolVersion >= 2 - : dict.Add("reward_beneficiary", Miner.ByteArray); /////// ProtocolVersion <= 1 + : dict.Add("reward_beneficiary", Miner.Bencoded); /////// ProtocolVersion <= 1 return dict; } diff --git a/Libplanet.Types/Consensus/Validator.cs b/Libplanet.Types/Consensus/Validator.cs index 50748c579d4..539e7b13e06 100644 --- a/Libplanet.Types/Consensus/Validator.cs +++ b/Libplanet.Types/Consensus/Validator.cs @@ -72,7 +72,7 @@ private Validator(Bencodex.Types.Dictionary bencoded) /// An of the validator operator's . /// [JsonIgnore] - public Address OperatorAddress => PublicKey.ToAddress(); + public Address OperatorAddress => PublicKey.Address; /// [JsonIgnore] diff --git a/Libplanet.Types/Tx/ITxInvoice.cs b/Libplanet.Types/Tx/ITxInvoice.cs index 12305254846..140be4cbac3 100644 --- a/Libplanet.Types/Tx/ITxInvoice.cs +++ b/Libplanet.Types/Tx/ITxInvoice.cs @@ -19,10 +19,21 @@ namespace Libplanet.Types.Tx public interface ITxInvoice : IEquatable { /// - /// An approximated list of addresses whose states would be affected by actions in this - /// transaction. However, it could be wrong. + /// + /// A deprecated property which was used as an approximated list of addresses whose states + /// would be affected by actions in this transaction. + /// + /// + /// This is no longer officially supported in the sense that a + /// cannot be created with a non-empty set of es through normal means + /// (i.e. using intended APIs). + /// + /// + /// It is still possible to create a through other means, + /// such as creating a payload directly by assigning appropriate values and signing + /// an "unsigned transaction". This is not recommended. + /// /// - // See also https://github.com/planetarium/libplanet/issues/368 IImmutableSet
UpdatedAddresses { get; } /// diff --git a/Libplanet.Types/Tx/Transaction.cs b/Libplanet.Types/Tx/Transaction.cs index 4ed6f799916..9d54135779c 100644 --- a/Libplanet.Types/Tx/Transaction.cs +++ b/Libplanet.Types/Tx/Transaction.cs @@ -233,8 +233,6 @@ public static Transaction Deserialize(byte[] bytes) /// The maximum gas price this transaction can pay fee. /// The maximum amount of gas this transaction can consume. /// - /// es whose - /// states affected by . /// The time this /// is created and signed. This goes to the /// property. If (which is default) is passed this will @@ -251,7 +249,6 @@ public static Transaction Create( IEnumerable actions, FungibleAssetValue? maxGasPrice = null, long? gasLimit = null, - IImmutableSet
? updatedAddresses = null, DateTimeOffset? timestamp = null) => Create( nonce, @@ -260,9 +257,7 @@ public static Transaction Create( new TxActionList(actions), maxGasPrice, gasLimit, - updatedAddresses, - timestamp - ); + timestamp); /// /// Encodes this into a array. @@ -311,7 +306,6 @@ private static Transaction Create( TxActionList actions, FungibleAssetValue? maxGasPrice = null, long? gasLimit = null, - IImmutableSet
? updatedAddresses = null, DateTimeOffset? timestamp = null) { if (privateKey is null) @@ -321,7 +315,6 @@ private static Transaction Create( var draftInvoice = new TxInvoice( genesisHash, - updatedAddresses ?? ImmutableHashSet
.Empty, timestamp ?? DateTimeOffset.UtcNow, actions, maxGasPrice, diff --git a/Libplanet.Types/Tx/TxInvoice.cs b/Libplanet.Types/Tx/TxInvoice.cs index eac1242c9b5..dbd7eca0a7c 100644 --- a/Libplanet.Types/Tx/TxInvoice.cs +++ b/Libplanet.Types/Tx/TxInvoice.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.Contracts; using System.Linq; @@ -23,34 +22,29 @@ public sealed class TxInvoice : ITxInvoice, IEquatable /// Creates a new instance by filling data for its fields. ///
/// The value for . - /// The value for . /// The value for . /// The value of . /// The value of . /// The value of limit. /// Thrown when /// or is . + /// Thrown when -ness of + /// and are not the same. + /// public TxInvoice( BlockHash? genesisHash, - IImmutableSet
updatedAddresses, DateTimeOffset timestamp, TxActionList actions, FungibleAssetValue? maxGasPrice, long? gasLimit) + : this( + genesisHash, + ImmutableHashSet
.Empty, + timestamp, + actions, + maxGasPrice, + gasLimit) { - if (updatedAddresses is null) - { - throw new ArgumentNullException(nameof(updatedAddresses)); - } - - GenesisHash = genesisHash; - UpdatedAddresses = updatedAddresses is AddressSet set - ? set - : new AddressSet(updatedAddresses); - Timestamp = timestamp; - Actions = actions ?? throw new ArgumentNullException(nameof(actions)); - MaxGasPrice = maxGasPrice; - GasLimit = gasLimit; } /// @@ -58,8 +52,6 @@ public TxInvoice( /// are some default values for some fields. /// /// The value for . - /// The value for . - /// Empty by default. /// The value for . /// Time of creation by default. /// The value of . @@ -68,20 +60,16 @@ public TxInvoice( /// The value of limit. public TxInvoice( BlockHash? genesisHash = null, - IEnumerable
? updatedAddresses = null, DateTimeOffset? timestamp = null, TxActionList? actions = null, FungibleAssetValue? maxGasPrice = null, - long? gasLimit = null - ) + long? gasLimit = null) : this( genesisHash, - updatedAddresses?.ToImmutableHashSet() ?? ImmutableHashSet
.Empty, timestamp ?? DateTimeOffset.UtcNow, actions ?? TxActionList.Empty, maxGasPrice, - gasLimit - ) + gasLimit) { } @@ -101,6 +89,51 @@ public TxInvoice(ITxInvoice invoice) { } + /// + /// Creates a new instance by filling data for its fields. + /// + /// The value for . + /// The value for . + /// The value for . + /// The value of . + /// The value of . + /// The value of limit. + /// Thrown when + /// or is . + /// Thrown when -ness of + /// and are not the same. + /// + internal TxInvoice( + BlockHash? genesisHash, + IImmutableSet
updatedAddresses, + DateTimeOffset timestamp, + TxActionList actions, + FungibleAssetValue? maxGasPrice, + long? gasLimit) + { + if (updatedAddresses is null) + { + throw new ArgumentNullException(nameof(updatedAddresses)); + } + + if (maxGasPrice is null ^ gasLimit is null) + { + throw new ArgumentException( + $"Either {nameof(maxGasPrice)} (null: {maxGasPrice is null}) and " + + $"{nameof(gasLimit)} (null: {gasLimit is null}) must be both null " + + $"or both non-null."); + } + + GenesisHash = genesisHash; + UpdatedAddresses = updatedAddresses is AddressSet set + ? set + : new AddressSet(updatedAddresses); + Timestamp = timestamp; + Actions = actions ?? throw new ArgumentNullException(nameof(actions)); + MaxGasPrice = maxGasPrice; + GasLimit = gasLimit; + } + /// public BlockHash? GenesisHash { get; } diff --git a/Libplanet.Types/Tx/TxMarshaler.cs b/Libplanet.Types/Tx/TxMarshaler.cs index ccae2700e4e..22d400780d8 100644 --- a/Libplanet.Types/Tx/TxMarshaler.cs +++ b/Libplanet.Types/Tx/TxMarshaler.cs @@ -31,7 +31,7 @@ public static class TxMarshaler public static Bencodex.Types.Dictionary MarshalTxInvoice(this ITxInvoice invoice) { Bencodex.Types.List updatedAddresses = new Bencodex.Types.List( - invoice.UpdatedAddresses.Select(addr => new Binary(addr.ByteArray)) + invoice.UpdatedAddresses.Select(addr => addr.Bencoded) ); string timestamp = invoice.Timestamp .ToUniversalTime() @@ -70,7 +70,7 @@ public static Bencodex.Types.Dictionary MarshalTxSigningMetadata( this ITxSigningMetadata metadata ) => Dictionary.Empty .Add(NonceKey, metadata.Nonce) - .Add(SignerKey, metadata.Signer.ByteArray) + .Add(SignerKey, metadata.Signer.Bencoded) .Add(PublicKeyKey, metadata.PublicKey.ToImmutableArray(compress: false)); [Pure] diff --git a/Libplanet.Types/Tx/TxMetadata.cs b/Libplanet.Types/Tx/TxMetadata.cs index cff1456ea40..411f292a416 100644 --- a/Libplanet.Types/Tx/TxMetadata.cs +++ b/Libplanet.Types/Tx/TxMetadata.cs @@ -125,13 +125,13 @@ public TxMetadata(Bencodex.Types.Dictionary dictionary) public Bencodex.Types.Dictionary ToBencodex() { List updatedAddresses = new List( - UpdatedAddresses.Select(addr => new Binary(addr.ByteArray))); + UpdatedAddresses.Select(addr => addr.Bencoded)); string timestamp = Timestamp .ToUniversalTime() .ToString(TimestampFormat, CultureInfo.InvariantCulture); Bencodex.Types.Dictionary dict = Dictionary.Empty .Add(NonceKey, Nonce) - .Add(SignerKey, Signer.ByteArray) + .Add(SignerKey, Signer.Bencoded) .Add(UpdatedAddressesKey, updatedAddresses) .Add(PublicKeyKey, PublicKey.ToImmutableArray(compress: false)) .Add(TimestampKey, timestamp); diff --git a/Libplanet/Blockchain/BlockChain.ProposeBlock.cs b/Libplanet/Blockchain/BlockChain.ProposeBlock.cs index 8cd118aacbc..cc1a6d1a7f3 100644 --- a/Libplanet/Blockchain/BlockChain.ProposeBlock.cs +++ b/Libplanet/Blockchain/BlockChain.ProposeBlock.cs @@ -156,7 +156,7 @@ internal Block ProposeBlock( protocolVersion: BlockMetadata.CurrentProtocolVersion, index: index, timestamp: DateTimeOffset.UtcNow, - miner: proposer.ToAddress(), + miner: proposer.Address, publicKey: proposer.PublicKey, previousHash: prevHash, txHash: BlockContent.DeriveTxHash(orderedTransactions), diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 04f4b38c530..17a05a7e255 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -589,8 +589,6 @@ public long GetNextTxNonce(Address address) /// The maximum gas price this transaction can pay fee. /// The maximum amount of gas this transaction can consume. /// - /// es whose states affected by - /// . /// The time this is created and /// signed. /// A created new signed by the given @@ -600,7 +598,6 @@ public Transaction MakeTransaction( IEnumerable actions, FungibleAssetValue? maxGasPrice = null, long? gasLimit = null, - IImmutableSet
updatedAddresses = null, DateTimeOffset? timestamp = null) { timestamp = timestamp ?? DateTimeOffset.UtcNow; @@ -608,13 +605,12 @@ public Transaction MakeTransaction( { // FIXME: Exception should be documented when the genesis block does not exist. Transaction tx = Transaction.Create( - GetNextTxNonce(privateKey.ToAddress()), + GetNextTxNonce(privateKey.Address), privateKey, Genesis.Hash, actions.Select(x => x.PlainValue), maxGasPrice, gasLimit, - updatedAddresses, timestamp); StageTransaction(tx); return tx; diff --git a/Libplanet/Blockchain/BlockChainStates.cs b/Libplanet/Blockchain/BlockChainStates.cs index 2937bcc7e83..e98b4611dae 100644 --- a/Libplanet/Blockchain/BlockChainStates.cs +++ b/Libplanet/Blockchain/BlockChainStates.cs @@ -40,7 +40,7 @@ public IAccountState GetAccountState(Address address, BlockHash? offset) /// public IAccountState GetAccountState(HashDigest? stateRootHash) - => new AccountBaseState(GetTrie(stateRootHash)); + => new AccountState(GetTrie(stateRootHash)); /// public IValue? GetState(Address address, Address accountAddress, BlockHash? offset) diff --git a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs index be0220f80aa..1ef0dcd47b3 100644 --- a/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs +++ b/Libplanet/Blockchain/Renderers/LoggedActionRenderer.cs @@ -104,27 +104,12 @@ System.Action callback Type actionType = action.GetType(); const string startMessage = "Invoking {MethodName}() for an action {ActionType} at block #{BlockIndex}..."; - if (context.Rehearsal) - { - Logger.Write( - Level, - startMessage + " (rehearsal: {Rehearsal})", - methodName, - actionType, - context.BlockIndex, - context.Rehearsal - ); - } - else - { - Logger.Write( - Level, - startMessage, - methodName, - actionType, - context.BlockIndex - ); - } + Logger.Write( + Level, + startMessage, + methodName, + actionType, + context.BlockIndex); try { @@ -135,53 +120,23 @@ System.Action callback const string errorMessage = "An exception was thrown during {MethodName}() for an action {ActionType} at " + "block #{BlockIndex}"; - if (context.Rehearsal) - { - Logger.Error( - e, - errorMessage + " (rehearsal: {Rehearsal})", - methodName, - actionType, - context.BlockIndex, - context.Rehearsal); - } - else - { - Logger.Error( - e, - errorMessage, - methodName, - actionType, - context.BlockIndex); - } - + Logger.Error( + e, + errorMessage, + methodName, + actionType, + context.BlockIndex); throw; } const string endMessage = "Invoked {MethodName}() for an action {ActionType} at block #{BlockIndex}"; - - if (context.Rehearsal) - { - Logger.Write( - Level, - endMessage + " (rehearsal: {Rehearsal})", - methodName, - actionType, - context.BlockIndex, - context.Rehearsal - ); - } - else - { - Logger.Write( - Level, - endMessage, - methodName, - actionType, - context.BlockIndex - ); - } + Logger.Write( + Level, + endMessage, + methodName, + actionType, + context.BlockIndex); } } } diff --git a/Libplanet/KeyStore/ProtectedPrivateKey.cs b/Libplanet/KeyStore/ProtectedPrivateKey.cs index 3d722225469..d0d2a3ae233 100644 --- a/Libplanet/KeyStore/ProtectedPrivateKey.cs +++ b/Libplanet/KeyStore/ProtectedPrivateKey.cs @@ -118,7 +118,7 @@ public static ProtectedPrivateKey Protect(PrivateKey privateKey, string passphra var cipher = new Aes128Ctr(iv); ImmutableArray ciphertext = cipher.Encrypt(encKey, privateKey.ByteArray); ImmutableArray mac = CalculateMac(derivedKey, ciphertext); - Address address = privateKey.ToAddress(); + Address address = privateKey.Address; return new ProtectedPrivateKey(address, kdf, mac, cipher, ciphertext); } @@ -326,7 +326,7 @@ public PrivateKey Unprotect(string passphrase) unverifiedKey: plaintext.ToBuilder().ToArray(), informedConsent: true ); - Address actualAddress = key.ToAddress(); + Address actualAddress = key.Address; if (!Address.Equals(actualAddress)) { throw new MismatchedAddressException( From 123fa7cc86917a72619dbcff6c111faa534de050 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 11 Dec 2023 15:27:52 +0900 Subject: [PATCH 2/2] Revive IWorldDelta --- CHANGES.md | 1 + Libplanet.Action/ActionEvaluator.cs | 17 +++--- Libplanet.Action/State/IWorld.cs | 13 +---- Libplanet.Action/State/IWorldDelta.cs | 49 ++++++++++++++++ Libplanet.Action/State/World.cs | 29 +++------- Libplanet.Action/State/WorldDelta.cs | 56 +++++++++++++++++++ .../Queries/StateQueryTest.cs | 4 +- 7 files changed, 125 insertions(+), 44 deletions(-) create mode 100644 Libplanet.Action/State/IWorldDelta.cs create mode 100644 Libplanet.Action/State/WorldDelta.cs diff --git a/CHANGES.md b/CHANGES.md index 4e41000d8dc..d8f93c6c296 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -77,6 +77,7 @@ To be released. - (Libplanet.Action) Added `IWorld` interface and its implementation. [[#3462]] - Added `World` class. + - (Libplanet.Action) Added `IWorldDelta` interface. [[#3462]] - (Libplanet.Action) Added `IWorldState` interface and its implementation. [[#3462]] - Added `WorldBaseState` class. diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index 7f9b6fe507c..a2cd9c42262 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -563,8 +563,7 @@ internal IWorld MigrateLegacyStates(IWorld prevWorld, int version) worldTrie = _stateStore.Commit(worldTrie); var world = new World( new WorldBaseState(worldTrie, _stateStore), - ImmutableDictionary.Empty, - prevWorld.BlockDelta); + prevWorld.Delta); return world; } @@ -574,24 +573,24 @@ private static IWorld CommitLegacyWorld(IWorld prevWorld, IStateStore stateStore new WorldBaseState( stateStore.Commit(prevWorld.GetAccount(ReservedAddresses.LegacyAccount).Trie), stateStore), - ImmutableDictionary.Empty, - prevWorld.BlockDelta); + prevWorld.Delta.CommitAccount(ReservedAddresses.LegacyAccount)); } - private static IWorld CommitWorld(IWorld world, IStateStore stateStore) + private static IWorld CommitWorld(IWorld prevWorld, IStateStore stateStore) { - var worldTrie = world.Trie; - foreach (var account in world.UncommittedDelta) + var worldTrie = prevWorld.Trie; + var worldDelta = prevWorld.Delta; + foreach (var account in prevWorld.Delta.Uncommitted) { var accountTrie = stateStore.Commit(account.Value.Trie); worldTrie = worldTrie.Set( ToStateKey(account.Key), new Binary(accountTrie.Hash.ByteArray)); + worldDelta = worldDelta.CommitAccount(account.Key); } return new World( new WorldBaseState(stateStore.Commit(worldTrie), stateStore), - ImmutableDictionary.Empty, - world.BlockDelta); + worldDelta); } [Pure] diff --git a/Libplanet.Action/State/IWorld.cs b/Libplanet.Action/State/IWorld.cs index bc3b27b64f2..a099e6dd947 100644 --- a/Libplanet.Action/State/IWorld.cs +++ b/Libplanet.Action/State/IWorld.cs @@ -36,18 +36,11 @@ namespace Libplanet.Action.State public interface IWorld : IWorldState { /// - /// A dictionary representing cumulative changed account states for each - /// between blocks. + /// The representing the delta part of + /// this . /// [Pure] - IImmutableDictionary BlockDelta { get; } - - /// - /// A dictionary representing uncommitted changed account states for each - /// . - /// - [Pure] - IImmutableDictionary UncommittedDelta { get; } + IWorldDelta Delta { get; } /// /// Gets a new instance that the world state of the given diff --git a/Libplanet.Action/State/IWorldDelta.cs b/Libplanet.Action/State/IWorldDelta.cs new file mode 100644 index 00000000000..50578ac75df --- /dev/null +++ b/Libplanet.Action/State/IWorldDelta.cs @@ -0,0 +1,49 @@ +using System.Collections.Immutable; +using System.Diagnostics.Contracts; +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + public interface IWorldDelta + { + /// + /// A dictionary representing changed account states for each . + /// This lasts till new empty delta instance has been made. + /// + [Pure] + IImmutableDictionary Accounts { get; } + + /// + /// A dictionary representing changed account states for each . + /// Elements of this collection are removed when called + /// by corresponding . + /// + [Pure] + IImmutableDictionary Uncommitted { get; } + + /// + /// Set account on both of and + /// dictionaries. If already exists on + /// , update with new . + /// + /// to set on. + /// to set. + /// New that account is properly set. + [Pure] + IWorldDelta SetAccount(Address address, IAccount account); + + /// + /// Remove item from dictionary where its key is + /// . + /// + /// of to + /// remove from . + /// + /// New item of removed from + /// . + /// If corresponding uncommitted does not exist, returns identical one. + /// + [Pure] + IWorldDelta CommitAccount(Address address); + } +} diff --git a/Libplanet.Action/State/World.cs b/Libplanet.Action/State/World.cs index d8f7a30a766..01158ad98b9 100644 --- a/Libplanet.Action/State/World.cs +++ b/Libplanet.Action/State/World.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.Diagnostics.Contracts; using Libplanet.Crypto; using Libplanet.Store.Trie; @@ -14,34 +13,21 @@ public class World : IWorld private readonly IWorldState _baseState; public World(IWorldState baseState) - : this(baseState, ImmutableDictionary.Empty) + : this(baseState, new WorldDelta()) { } public World( IWorldState baseState, - IImmutableDictionary uncommittedDelta) - : this( - baseState, - uncommittedDelta, - ImmutableDictionary.Empty) - { - } - - public World( - IWorldState baseState, - IImmutableDictionary uncommittedDelta, - IImmutableDictionary blockDelta) + IWorldDelta delta) { _baseState = baseState; - UncommittedDelta = uncommittedDelta; - BlockDelta = blockDelta; + Delta = delta; Legacy = baseState.Legacy; } - public IImmutableDictionary BlockDelta { get; } - - public IImmutableDictionary UncommittedDelta { get; } + /// + public IWorldDelta Delta { get; } /// [Pure] @@ -55,7 +41,7 @@ public World( [Pure] public IAccount GetAccount(Address address) { - return BlockDelta.TryGetValue(address, out IAccount? account) + return Delta.Accounts.TryGetValue(address, out IAccount? account) ? account : _baseState.GetAccount(address); } @@ -72,8 +58,7 @@ public IWorld SetAccount(Address address, IAccount account) return new World( this, - UncommittedDelta.SetItem(address, account), - BlockDelta.SetItem(address, account)); + Delta.SetAccount(address, account)); } } } diff --git a/Libplanet.Action/State/WorldDelta.cs b/Libplanet.Action/State/WorldDelta.cs new file mode 100644 index 00000000000..cdc2694ad8b --- /dev/null +++ b/Libplanet.Action/State/WorldDelta.cs @@ -0,0 +1,56 @@ +using System.Collections.Immutable; +using System.Linq; +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + public class WorldDelta : IWorldDelta + { + private IImmutableDictionary _accounts; + + public WorldDelta() + { + _accounts = ImmutableDictionary.Empty; + } + + private WorldDelta(IImmutableDictionary accounts) + { + _accounts = accounts; + } + + /// + public IImmutableDictionary Accounts + => _accounts + .ToImmutableDictionary(item => item.Key, item => item.Value.Account); + + /// + public IImmutableDictionary Uncommitted + => _accounts + .Where(item => !item.Value.Committed) + .ToImmutableDictionary(item => item.Key, item => item.Value.Account); + + /// + public IWorldDelta SetAccount(Address address, IAccount account) + => new WorldDelta(_accounts.SetItem(address, new AccountItem(account, false))); + + /// + public IWorldDelta CommitAccount(Address address) + => _accounts.TryGetValue(address, out AccountItem accountItem) + ? new WorldDelta( + _accounts.SetItem(address, new AccountItem(accountItem.Account, true))) + : this; + + internal struct AccountItem + { + public AccountItem(IAccount account, bool committed) + { + Account = account; + Committed = committed; + } + + public IAccount Account { get; } + + public bool Committed { get; set; } + } + } +} diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index c7fcdf83cb7..27f1b6d620c 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -461,9 +461,7 @@ public MockWorld() public bool Legacy => true; - public IImmutableDictionary BlockDelta => throw new System.NotImplementedException(); - - public IImmutableDictionary UncommittedDelta => throw new System.NotImplementedException(); + public IWorldDelta Delta => throw new System.NotImplementedException(); public IAccount GetAccount(Address address) => new MockAccount();