diff --git a/Lib9c b/Lib9c
index 9c378d993..757b1ca80 160000
--- a/Lib9c
+++ b/Lib9c
@@ -1 +1 @@
-Subproject commit 9c378d99369b8c52bc187b563e47a79bdb8cceaa
+Subproject commit 757b1ca80eb26ad440fa5f680470e10cdf85cf2c
diff --git a/NineChronicles.Headless.Executable.Tests/NineChronicles.Headless.Executable.Tests.csproj b/NineChronicles.Headless.Executable.Tests/NineChronicles.Headless.Executable.Tests.csproj
index 4aabfe3ed..07a7df259 100644
--- a/NineChronicles.Headless.Executable.Tests/NineChronicles.Headless.Executable.Tests.csproj
+++ b/NineChronicles.Headless.Executable.Tests/NineChronicles.Headless.Executable.Tests.csproj
@@ -21,6 +21,7 @@
+
diff --git a/NineChronicles.Headless.Executable/Commands/AccountCommand.cs b/NineChronicles.Headless.Executable/Commands/AccountCommand.cs
index 9531c62f4..767b1bea6 100644
--- a/NineChronicles.Headless.Executable/Commands/AccountCommand.cs
+++ b/NineChronicles.Headless.Executable/Commands/AccountCommand.cs
@@ -75,7 +75,11 @@ public void Balance(
_console.Error.WriteLine("Scanning block #{0} {1}...", digest.Index, digest.Hash);
_console.Error.Flush();
IEnumerable
addrs = digest.TxIds
- .Select(txId => store.GetTransaction(new TxId(txId.ToArray())))
+ .Select(txId =>
+ {
+ return store.GetTransaction(new TxId(txId.ToArray())) ??
+ throw new InvalidOperationException($"Transaction #{txId} is not found in the store.");
+ })
.SelectMany(tx => tx.Actions is { } ca
? ca.Select(a => ToAction(a))
.SelectMany(a =>
diff --git a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs
index 163aacf27..00c25e717 100644
--- a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs
+++ b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs
@@ -82,7 +82,7 @@ public void Tip(
BlockHash tipHash = store.IndexBlockHash(chainId, -1)
?? throw new CommandExitedException("The given chain seems empty.", -1);
- Block tip = store.GetBlock(tipHash);
+ Block tip = GetBlock(store, tipHash);
_console.Out.WriteLine(CoconaUtils.SerializeHumanReadable(tip.Header));
store.Dispose();
}
@@ -129,7 +129,7 @@ public void Inspect(
throw new CommandExitedException($"There is no genesis block: {storePath}", -1);
}
- Block genesisBlock = store.GetBlock(gHash);
+ Block genesisBlock = GetBlock(store, gHash);
var blockChainStates = new BlockChainStates(store, stateStore);
var actionEvaluator = new ActionEvaluator(
_ => blockPolicy.BlockAction,
@@ -163,8 +163,8 @@ public void Inspect(
foreach (var item in
store.IterateIndexes(chain.Id, offset + 1 ?? 1, limit).Select((value, i) => new { i, value }))
{
- var block = store.GetBlock(item.value);
- var previousBlock = store.GetBlock(
+ var block = GetBlock(store, item.value);
+ var previousBlock = GetBlock(store,
block.PreviousHash ?? block.Hash
);
@@ -270,7 +270,7 @@ public void Truncate(
-1);
}
- var tip = store.GetBlock(tipHash);
+ var tip = GetBlock(store, tipHash);
var snapshotTipIndex = Math.Max(tipIndex - (blocksBefore + 1), 0);
BlockHash snapshotTipHash;
@@ -286,7 +286,7 @@ public void Truncate(
}
snapshotTipHash = hash;
- } while (!stateStore.GetStateRoot(store.GetBlock(snapshotTipHash).StateRootHash).Recorded);
+ } while (!stateStore.GetStateRoot(GetBlock(store, snapshotTipHash).StateRootHash).Recorded);
var forkedId = Guid.NewGuid();
@@ -484,11 +484,11 @@ public void Snapshot(
);
var originalChain = new BlockChain(blockPolicy, stagePolicy, store, stateStore,
store.GetBlock(genesisHash), blockChainStates, actionEvaluator);
- var tip = store.GetBlock(tipHash);
+ var tip = GetBlock(store, tipHash);
var potentialSnapshotTipIndex = tipIndex - blockBefore;
var potentialSnapshotTipHash = (BlockHash)store.IndexBlockHash(chainId, potentialSnapshotTipIndex)!;
- var snapshotTip = store.GetBlock(potentialSnapshotTipHash);
+ var snapshotTip = GetBlock(store, potentialSnapshotTipHash);
_console.Out.WriteLine(
"Original Store Tip: #{0}\n1. LastCommit: {1}\n2. BlockCommit in Chain: {2}\n3. BlockCommit in Store: {3}",
@@ -520,24 +520,38 @@ public void Snapshot(
"There is no block commit associated with the potential snapshot tip: #{0}. Snapshot will automatically truncate 1 more block from the original chain tip.",
potentialSnapshotTipIndex);
blockBefore += 1;
- potentialSnapshotTipBlockCommit = store
- .GetBlock((BlockHash)store.IndexBlockHash(chainId, tip.Index - blockBefore + 1)!).LastCommit;
+ potentialSnapshotTipBlockCommit =
+ GetBlock(store, (BlockHash)store.IndexBlockHash(chainId, tip.Index - blockBefore + 1)!).LastCommit;
+ if (potentialSnapshotTipBlockCommit == null)
+ {
+ throw new CommandExitedException(
+ $"The block commit of the potential snapshot tip: #{potentialSnapshotTipIndex} doesn't exist.",
+ -1);
+ }
+
store.PutBlockCommit(tipBlockCommit);
store.PutChainBlockCommit(chainId, tipBlockCommit);
store.PutBlockCommit(potentialSnapshotTipBlockCommit);
store.PutChainBlockCommit(chainId, potentialSnapshotTipBlockCommit);
}
- var blockCommitBlock = store.GetBlock(tipHash);
+ var blockCommitBlock = GetBlock(store, tipHash);
// Add last block commits to store from tip until --block-before + 5 or tip if too short for buffer
var blockCommitRange = blockBefore + 5 < tip.Index ? blockBefore + 5 : tip.Index - 1;
for (var i = 0; i < blockCommitRange; i++)
{
_console.Out.WriteLine("Adding block #{0}'s block commit to the store", blockCommitBlock.Index - 1);
+ if (blockCommitBlock.LastCommit == null)
+ {
+ throw new CommandExitedException(
+ $"The block commit of the block: #{blockCommitBlock.Index} doesn't exist.",
+ -1);
+ }
+
store.PutBlockCommit(blockCommitBlock.LastCommit);
store.PutChainBlockCommit(chainId, blockCommitBlock.LastCommit);
- blockCommitBlock = store.GetBlock((BlockHash)blockCommitBlock.PreviousHash!);
+ blockCommitBlock = GetBlock(store, (BlockHash)blockCommitBlock.PreviousHash!);
}
var snapshotTipIndex = Math.Max(tipIndex - (blockBefore + 1), 0);
@@ -555,7 +569,7 @@ public void Snapshot(
}
snapshotTipHash = hash;
- } while (!stateStore.GetStateRoot(store.GetBlock(snapshotTipHash).StateRootHash).Recorded);
+ } while (!stateStore.GetStateRoot(GetBlock(store, snapshotTipHash).StateRootHash).Recorded);
var forkedId = Guid.NewGuid();
Fork(chainId, forkedId, snapshotTipHash, tip, store);
@@ -745,6 +759,18 @@ public void Snapshot(
}
}
+ private static Block GetBlock(IStore store, BlockHash blockHash)
+ {
+ return store.GetBlock(blockHash) ??
+ throw new CommandExitedException($"The block of {blockHash} doesn't exist.", -1);
+ }
+
+ private static BlockCommit GetBlockCommit(IStore store, BlockHash blockHash)
+ {
+ return store.GetBlockCommit(blockHash) ??
+ throw new CommandExitedException($"The block commit of {blockHash} doesn't exist.", -1);
+ }
+
private string GetPartitionBaseFileName(
int currentMetadataBlockEpoch,
int currentMetadataTxEpoch,
@@ -1025,7 +1051,7 @@ private void Fork(
store.ForkBlockIndexes(src, dest, branchPointHash);
if (store.GetBlockCommit(branchPointHash) is { })
{
- store.PutChainBlockCommit(dest, store.GetBlockCommit(branchPointHash));
+ store.PutChainBlockCommit(dest, GetBlockCommit(store, branchPointHash));
}
store.ForkTxNonces(src, dest);
@@ -1033,7 +1059,7 @@ private void Fork(
Block block = tip;
block.PreviousHash is { } hash
&& !block.Hash.Equals(branchPointHash);
- block = store.GetBlock(hash))
+ block = GetBlock(store, hash))
{
IEnumerable<(Address, int)> signers = block
.Transactions
diff --git a/NineChronicles.Headless.Executable/Commands/StateCommand.cs b/NineChronicles.Headless.Executable/Commands/StateCommand.cs
index 28a930868..9c7eba6dd 100644
--- a/NineChronicles.Headless.Executable/Commands/StateCommand.cs
+++ b/NineChronicles.Headless.Executable/Commands/StateCommand.cs
@@ -121,7 +121,8 @@ IStateStore stateStore
}
Block block =
- store.GetBlock(blockHash);
+ store.GetBlock(blockHash) ??
+ throw new CommandExitedException($"The block of {blockHash} doesn't exist.", -1);
var preEvalBlock = new PreEvaluationBlock(
block,
block.Transactions
diff --git a/NineChronicles.Headless.Executable/Store/StoreExtensions.cs b/NineChronicles.Headless.Executable/Store/StoreExtensions.cs
index e2d56f7ee..8a4186adf 100644
--- a/NineChronicles.Headless.Executable/Store/StoreExtensions.cs
+++ b/NineChronicles.Headless.Executable/Store/StoreExtensions.cs
@@ -18,7 +18,12 @@ public static Block GetGenesisBlock(this IStore store)
}
BlockHash genesisBlockHash = store.IterateIndexes(chainId.Value).First();
- Block genesisBlock = store.GetBlock(genesisBlockHash);
+ Block? genesisBlock = store.GetBlock(genesisBlockHash);
+ if (genesisBlock == null)
+ {
+ throw new InvalidOperationException("The store doesn't have genesis block.");
+ }
+
return genesisBlock;
}
}
diff --git a/NineChronicles.Headless.Tests/ArenaParticipantsWorkerTest.cs b/NineChronicles.Headless.Tests/ArenaParticipantsWorkerTest.cs
index 35c76bed0..9e7d62b87 100644
--- a/NineChronicles.Headless.Tests/ArenaParticipantsWorkerTest.cs
+++ b/NineChronicles.Headless.Tests/ArenaParticipantsWorkerTest.cs
@@ -4,6 +4,7 @@
using Lib9c.Tests;
using Libplanet.Action.State;
using Libplanet.Crypto;
+using Libplanet.Mocks;
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Model.Arena;
@@ -25,7 +26,7 @@ public class ArenaParticipantsWorkerTest
public ArenaParticipantsWorkerTest()
{
- _world = new MockWorld(new MockWorldState());
+ _world = new World(MockWorldState.CreateModern());
_sheets = TableSheetsImporter.ImportSheets();
}
diff --git a/NineChronicles.Headless.Tests/Common/MockAccount.cs b/NineChronicles.Headless.Tests/Common/MockAccount.cs
deleted file mode 100644
index ac2d3acdd..000000000
--- a/NineChronicles.Headless.Tests/Common/MockAccount.cs
+++ /dev/null
@@ -1,295 +0,0 @@
-namespace NineChronicles.Headless.Tests.Common
-{
- using System;
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.Diagnostics.Contracts;
- using System.Numerics;
- using Bencodex.Types;
- using Libplanet.Action;
- using Libplanet.Action.State;
- using Libplanet.Crypto;
- using Libplanet.Store.Trie;
- using Libplanet.Types.Assets;
- using Libplanet.Types.Consensus;
- using static KeyConverters;
-
- ///
- /// A rough replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/Account.cs
- /// except this has its constructors exposed as public for testing.
- ///
- [Pure]
- public class MockAccount : IAccount
- {
- private readonly MockAccountState _state;
-
- public MockAccount(MockAccountState state)
- : this(state, ImmutableHashSet<(Address, Currency)>.Empty)
- {
- }
-
- public MockAccount(
- MockAccountState state,
- IImmutableSet<(Address, Currency)> totalUpdatedFungibleAssets)
- {
- _state = state;
- TotalUpdatedFungibleAssets = totalUpdatedFungibleAssets;
- }
-
- ///
- public ITrie Trie => throw new NotSupportedException();
-
- public IImmutableDictionary MockTrie => _state.MockTrie;
-
- ///
- public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets { get; }
-
- ///
- [Pure]
- public IValue? GetState(Address address) => _state.GetState(address);
-
- ///
- [Pure]
- public IReadOnlyList GetStates(IReadOnlyList addresses) =>
- _state.GetStates(addresses);
-
- ///
- [Pure]
- public IAccount SetState(Address address, IValue state) => UpdateState(address, state);
-
- ///
- [Pure]
- public IAccount RemoveState(Address address) => UpdateState(address);
-
- ///
- [Pure]
- public FungibleAssetValue GetBalance(Address address, Currency currency) =>
- _state.GetBalance(address, currency);
-
- ///
- [Pure]
- public FungibleAssetValue GetTotalSupply(Currency currency) =>
- _state.GetTotalSupply(currency);
-
- ///
- [Pure]
- public ValidatorSet GetValidatorSet() => _state.GetValidatorSet();
-
- ///
- [Pure]
- public IAccount MintAsset(
- IActionContext context, Address recipient, FungibleAssetValue value)
- {
- if (value.Sign <= 0)
- {
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "The value to mint has to be greater than zero."
- );
- }
-
- Currency currency = value.Currency;
- if (!currency.AllowsToMint(context.Signer))
- {
- throw new CurrencyPermissionException(
- $"The account {context.Signer} has no permission to mint currency {currency}.",
- context.Signer,
- currency
- );
- }
-
- FungibleAssetValue balance = GetBalance(recipient, currency);
- BigInteger rawBalance = (balance + value).RawValue;
-
- if (currency.TotalSupplyTrackable)
- {
- var currentTotalSupply = GetTotalSupply(currency);
- if (currency.MaximumSupply < currentTotalSupply + value)
- {
- var msg = $"The amount {value} attempted to be minted added to the current"
- + $" total supply of {currentTotalSupply} exceeds the"
- + $" maximum allowed supply of {currency.MaximumSupply}.";
- throw new SupplyOverflowException(msg, value);
- }
-
- return UpdateFungibleAssets(
- recipient,
- currency,
- rawBalance,
- (currentTotalSupply + value).RawValue);
- }
- else
- {
- return UpdateFungibleAssets(recipient, currency, rawBalance);
- }
- }
-
- ///
- [Pure]
- public IAccount TransferAsset(
- IActionContext context,
- Address sender,
- Address recipient,
- FungibleAssetValue value,
- bool allowNegativeBalance = false) => context.BlockProtocolVersion > 0
- ? TransferAssetV1(sender, recipient, value, allowNegativeBalance)
- : TransferAssetV0(sender, recipient, value, allowNegativeBalance);
-
- ///
- [Pure]
- public IAccount BurnAsset(
- IActionContext context, Address owner, FungibleAssetValue value)
- {
- string msg;
-
- if (value.Sign <= 0)
- {
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "The value to burn has to be greater than zero."
- );
- }
-
- Currency currency = value.Currency;
- if (!currency.AllowsToMint(context.Signer))
- {
- msg = $"The account {context.Signer} has no permission to burn assets of " +
- $"the currency {currency}.";
- throw new CurrencyPermissionException(msg, context.Signer, currency);
- }
-
- FungibleAssetValue balance = GetBalance(owner, currency);
-
- if (balance < value)
- {
- msg = $"The account {owner}'s balance of {currency} is insufficient to burn: " +
- $"{balance} < {value}.";
- throw new InsufficientBalanceException(msg, owner, balance);
- }
-
- BigInteger rawBalance = (balance - value).RawValue;
- if (currency.TotalSupplyTrackable)
- {
- var currentTotalSupply = GetTotalSupply(currency);
- return UpdateFungibleAssets(
- owner,
- currency,
- rawBalance,
- (currentTotalSupply - value).RawValue);
- }
- else
- {
- return UpdateFungibleAssets(owner, currency, rawBalance);
- }
- }
-
- ///
- [Pure]
- public IAccount SetValidator(Validator validator) =>
- UpdateValidatorSet(GetValidatorSet().Update(validator));
-
- [Pure]
- private MockAccount UpdateState(
- Address address,
- IValue value) =>
- new MockAccount(
- new MockAccountState(
- MockTrie.Add(ToStateKey(address), value)),
- TotalUpdatedFungibleAssets);
-
- [Pure]
- private MockAccount UpdateState(
- Address address) =>
- new MockAccount(
- new MockAccountState(
- MockTrie.Remove(ToStateKey(address))),
- TotalUpdatedFungibleAssets);
-
- [Pure]
- private MockAccount UpdateFungibleAssets(
- Address address,
- Currency currency,
- BigInteger amount,
- BigInteger? supplyAmount = null) => supplyAmount is { } sa
- ? new MockAccount(
- new MockAccountState(
- MockTrie
- .Add(ToFungibleAssetKey(address, currency), new Integer(amount))
- .Add(ToTotalSupplyKey(currency), new Integer(sa))),
- TotalUpdatedFungibleAssets.Add((address, currency)))
- : new MockAccount(
- new MockAccountState(
- MockTrie.Add(ToFungibleAssetKey(address, currency), new Integer(amount))),
- TotalUpdatedFungibleAssets.Add((address, currency)));
-
- [Pure]
- private MockAccount UpdateValidatorSet(ValidatorSet validatorSet) =>
- new MockAccount(
- new MockAccountState(
- MockTrie.Add(ValidatorSetKey, validatorSet.Bencoded)),
- TotalUpdatedFungibleAssets);
-
- [Pure]
- private IAccount TransferAssetV0(
- Address sender,
- Address recipient,
- FungibleAssetValue value,
- bool allowNegativeBalance = false)
- {
- if (value.Sign <= 0)
- {
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "The value to transfer has to be greater than zero."
- );
- }
-
- Currency currency = value.Currency;
- FungibleAssetValue senderBalance = GetBalance(sender, currency);
- FungibleAssetValue recipientBalance = GetBalance(recipient, currency);
-
- if (!allowNegativeBalance && senderBalance < value)
- {
- var msg = $"The account {sender}'s balance of {currency} is insufficient to " +
- $"transfer: {senderBalance} < {value}.";
- throw new InsufficientBalanceException(msg, sender, senderBalance);
- }
-
- return UpdateFungibleAssets(sender, currency, (senderBalance - value).RawValue)
- .UpdateFungibleAssets(recipient, currency, (recipientBalance + value).RawValue);
- }
-
- [Pure]
- private IAccount TransferAssetV1(
- Address sender,
- Address recipient,
- FungibleAssetValue value,
- bool allowNegativeBalance = false)
- {
- if (value.Sign <= 0)
- {
- throw new ArgumentOutOfRangeException(
- nameof(value),
- "The value to transfer has to be greater than zero."
- );
- }
-
- Currency currency = value.Currency;
- FungibleAssetValue senderBalance = GetBalance(sender, currency);
-
- if (!allowNegativeBalance && senderBalance < value)
- {
- var msg = $"The account {sender}'s balance of {currency} is insufficient to " +
- $"transfer: {senderBalance} < {value}.";
- throw new InsufficientBalanceException(msg, sender, senderBalance);
- }
-
- BigInteger senderRawBalance = (senderBalance - value).RawValue;
- MockAccount intermediate = UpdateFungibleAssets(sender, currency, senderRawBalance);
- FungibleAssetValue recipientBalance = intermediate.GetBalance(recipient, currency);
- BigInteger recipientRawBalance = (recipientBalance + value).RawValue;
-
- return intermediate.UpdateFungibleAssets(recipient, currency, recipientRawBalance);
- }
- }
-}
diff --git a/NineChronicles.Headless.Tests/Common/MockAccountState.cs b/NineChronicles.Headless.Tests/Common/MockAccountState.cs
deleted file mode 100644
index 0523cbc94..000000000
--- a/NineChronicles.Headless.Tests/Common/MockAccountState.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-using System.Numerics;
-using Bencodex.Types;
-using Libplanet.Action.State;
-using Libplanet.Crypto;
-using Libplanet.Store.Trie;
-using Libplanet.Types.Assets;
-using Libplanet.Types.Consensus;
-using static NineChronicles.Headless.Tests.Common.KeyConverters;
-
-namespace NineChronicles.Headless.Tests.Common
-{
- ///
- /// A rough replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/AccountState.cs
- /// except this has its constructors exposed as public for testing.
- ///
- public class MockAccountState : IAccountState
- {
- public MockAccountState() : this(ImmutableDictionary.Empty)
- {
- }
-
- public MockAccountState(IImmutableDictionary mockTrie)
- {
- MockTrie = mockTrie;
- }
-
- ///
- public ITrie Trie => throw new NotSupportedException();
-
- ///
- public IImmutableDictionary MockTrie { get; }
-
- ///
- public IValue? GetState(Address address) => MockTrie.TryGetValue(ToStateKey(address), out var v)
- ? v
- : null;
-
- ///
- public IReadOnlyList GetStates(IReadOnlyList addresses) =>
- addresses.Select(address => GetState(address)).ToList();
-
- ///
- public FungibleAssetValue GetBalance(Address address, Currency currency)
- {
- IValue? value = MockTrie.TryGetValue(ToFungibleAssetKey(address, currency), out var v)
- ? v
- : null;
- return value is Integer i
- ? FungibleAssetValue.FromRawValue(currency, i)
- : currency * 0;
- }
-
- ///
- public FungibleAssetValue GetTotalSupply(Currency currency)
- {
- if (!currency.TotalSupplyTrackable)
- {
- throw TotalSupplyNotTrackableException.WithDefaultMessage(currency);
- }
-
- IValue? value = MockTrie.TryGetValue(ToTotalSupplyKey(currency), out var v)
- ? v
- : null;
- return value is Integer i
- ? FungibleAssetValue.FromRawValue(currency, i)
- : currency * 0;
- }
-
- ///
- public ValidatorSet GetValidatorSet()
- {
- IValue? value = MockTrie.TryGetValue(ValidatorSetKey, out var v)
- ? v
- : null;
- return value is List list
- ? new ValidatorSet(list)
- : new ValidatorSet();
- }
-
- // Methods used in unit tests
- public MockAccountState SetState(Address address, IValue state) =>
- new MockAccountState(MockTrie.SetItem(ToStateKey(address), state));
-
- public MockAccountState SetBalance(Address address, FungibleAssetValue amount) =>
- SetBalance((address, amount.Currency), amount.RawValue);
-
- public MockAccountState SetBalance(Address address, Currency currency, BigInteger rawAmount) =>
- SetBalance((address, currency), rawAmount);
-
- public MockAccountState SetBalance((Address Address, Currency Currency) pair, BigInteger rawAmount) =>
- new MockAccountState(MockTrie.SetItem(ToFungibleAssetKey(pair), (Integer)rawAmount));
-
- public MockAccountState AddBalance(Address address, FungibleAssetValue amount) =>
- AddBalance((address, amount.Currency), amount.RawValue);
-
- public MockAccountState AddBalance(Address address, Currency currency, BigInteger rawAmount) =>
- AddBalance((address, currency), rawAmount);
-
- public MockAccountState AddBalance((Address Address, Currency Currency) pair, BigInteger rawAmount)
- {
- var amount = GetBalance(pair.Address, pair.Currency).RawValue + rawAmount;
- return SetBalance(pair, amount);
- }
-
- public MockAccountState SubtractBalance(Address address, FungibleAssetValue amount) =>
- SubtractBalance((address, amount.Currency), amount.RawValue);
-
- public MockAccountState SubtractBalance(Address address, Currency currency, BigInteger rawAmount) =>
- SubtractBalance((address, currency), rawAmount);
-
- public MockAccountState SubtractBalance((Address Address, Currency Currency) pair, BigInteger rawAmount)
- {
- var amount = GetBalance(pair.Address, pair.Currency).RawValue - rawAmount;
- return SetBalance(pair, amount);
- }
-
- public MockAccountState TransferBalance(Address sender, Address recipient, FungibleAssetValue amount) =>
- TransferBalance(sender, recipient, amount.Currency, amount.RawValue);
-
- public MockAccountState TransferBalance(Address sender, Address recipient, Currency currency, BigInteger rawAmount) =>
- SubtractBalance(sender, currency, rawAmount).AddBalance(recipient, currency, rawAmount);
-
- public MockAccountState SetTotalSupply(FungibleAssetValue amount) =>
- SetTotalSupply(amount.Currency, amount.RawValue);
-
- public MockAccountState SetTotalSupply(Currency currency, BigInteger rawAmount) =>
- currency.TotalSupplyTrackable
- ? !(currency.MaximumSupply is { } maximumSupply) || rawAmount <= maximumSupply.RawValue
- ? new MockAccountState(MockTrie.SetItem(ToTotalSupplyKey(currency), (Integer)rawAmount))
- : throw new ArgumentException(
- $"Given {currency}'s total supply is capped at {maximumSupply.RawValue} and " +
- $"cannot be set to {rawAmount}.")
- : throw new ArgumentException(
- $"Given {currency} is not trackable.");
-
- public MockAccountState AddTotalSupply(FungibleAssetValue amount) =>
- AddTotalSupply(amount.Currency, amount.RawValue);
-
- public MockAccountState AddTotalSupply(Currency currency, BigInteger rawAmount)
- {
- var amount = GetTotalSupply(currency).RawValue + rawAmount;
- return SetTotalSupply(currency, amount);
- }
-
- public MockAccountState SubtractTotalSupply(FungibleAssetValue amount) =>
- SubtractTotalSupply(amount.Currency, amount.RawValue);
-
- public MockAccountState SubtractTotalSupply(Currency currency, BigInteger rawAmount)
- {
- var amount = GetTotalSupply(currency).RawValue - rawAmount;
- return SetTotalSupply(currency, amount);
- }
-
- public MockAccountState SetValidator(Validator validator) =>
- new MockAccountState(MockTrie.SetItem(ValidatorSetKey, GetValidatorSet().Update(validator).Bencoded));
- }
-}
diff --git a/NineChronicles.Headless.Tests/Common/MockWorld.cs b/NineChronicles.Headless.Tests/Common/MockWorld.cs
deleted file mode 100644
index c31e6f723..000000000
--- a/NineChronicles.Headless.Tests/Common/MockWorld.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using Libplanet.Store.Trie;
-
-namespace NineChronicles.Headless.Tests.Common
-{
- using System;
- using System.Diagnostics.Contracts;
- using Libplanet.Action.State;
- using Libplanet.Crypto;
-
- ///
- /// A rough replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/World.cs
- /// except this has its constructors exposed as public for testing.
- ///
- [Pure]
- public class MockWorld : IWorld
- {
- private readonly IWorldState _baseState;
-
- public MockWorld(IWorldState baseState)
- : this(baseState, new MockWorldDelta())
- {
- }
-
- public MockWorld(
- IWorldState baseState,
- IWorldDelta delta)
- {
- _baseState = baseState;
- Delta = delta;
- Legacy = baseState.Legacy;
- }
-
- ///
- public IWorldDelta Delta { get; }
-
- ///
- [Pure]
- public ITrie Trie => _baseState.Trie;
-
- ///
- [Pure]
- public bool Legacy { get; private set; }
-
- ///
- [Pure]
- public IAccount GetAccount(Address address)
- {
- if (Delta.Accounts.TryGetValue(address, out IAccount? account))
- {
- return account;
- }
- else
- {
- switch (_baseState.GetAccountState(address))
- {
- case MockAccount mockAccount:
- return mockAccount;
- case MockAccountState mockAccountState:
- return new MockAccount(mockAccountState);
- default:
- throw new InvalidOperationException();
- }
- }
- }
-
- public IAccountState GetAccountState(Address address) => GetAccount(address);
-
- ///
- [Pure]
- public IWorld SetAccount(Address address, IAccount account)
- {
- if (!address.Equals(ReservedAddresses.LegacyAccount)
- && account.TotalUpdatedFungibleAssets.Count > 0)
- {
- return this;
- }
-
- return new MockWorld(
- this,
- Delta.SetAccount(address, account));
- }
- }
-}
diff --git a/NineChronicles.Headless.Tests/Common/MockWorldDelta.cs b/NineChronicles.Headless.Tests/Common/MockWorldDelta.cs
deleted file mode 100644
index e354b0acd..000000000
--- a/NineChronicles.Headless.Tests/Common/MockWorldDelta.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.Linq;
-
-namespace NineChronicles.Headless.Tests.Common
-{
- using System.Collections.Immutable;
- using Libplanet.Action.State;
- using Libplanet.Crypto;
-
- ///
- /// Almost a replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/WorldDelta.cs
- /// except this has its constructors exposed as public for testing.
- ///
- public class MockWorldDelta : IWorldDelta
- {
- private IImmutableDictionary _accounts;
-
- public MockWorldDelta()
- {
- _accounts = ImmutableDictionary.Empty;
- }
-
- private MockWorldDelta(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 MockWorldDelta(_accounts.SetItem(address, new AccountItem(account, false)));
-
- ///
- public IWorldDelta CommitAccount(Address address)
- => _accounts.TryGetValue(address, out AccountItem accountItem)
- ? new MockWorldDelta(
- _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/NineChronicles.Headless.Tests/Common/MockWorldState.cs b/NineChronicles.Headless.Tests/Common/MockWorldState.cs
deleted file mode 100644
index 75713585c..000000000
--- a/NineChronicles.Headless.Tests/Common/MockWorldState.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Libplanet.Store.Trie;
-
-namespace NineChronicles.Headless.Tests.Common
-{
-#nullable enable
-
- using System.Collections.Immutable;
- using Libplanet.Action.State;
- using Libplanet.Crypto;
-
- public class MockWorldState : IWorldState
- {
- private readonly IImmutableDictionary _accounts;
-
- public MockWorldState()
- : this(ImmutableDictionary.Empty)
- {
- }
-
- public MockWorldState(IImmutableDictionary accounts)
- {
- _accounts = accounts;
- }
-
- public ITrie Trie { get; }
- public bool Legacy => true;
-
- public IImmutableDictionary Accounts => _accounts;
-
- public IAccountState GetAccountState(Address address) => _accounts.TryGetValue(address, out IAccount? account)
- ? account
- : new MockAccount(new MockAccountState());
- }
-}
diff --git a/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs
index 7a96f1766..a728e2d1a 100644
--- a/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs
+++ b/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs
@@ -12,6 +12,7 @@
using Libplanet.Action.State;
using Libplanet.Common;
using Libplanet.Crypto;
+using Libplanet.Mocks;
using Libplanet.Types.Assets;
using Nekoyume;
using Nekoyume.Model.Elemental;
@@ -92,10 +93,11 @@ public async Task Garage(
2,
new Address("0x47D082a115c63E7b58B1532d20E631538eaFADde"));
#pragma warning restore CS0618
- MockAccountState mockAccountState = new MockAccountState();
+ MockWorldState mockWorldState = MockWorldState.CreateModern();
+ IAccount account = new Account(mockWorldState.GetAccountState(ReservedAddresses.LegacyAccount));
// NCG
- mockAccountState = mockAccountState
+ account = account
.SetState(
Addresses.GoldCurrency,
new GoldCurrencyState(goldCurrency).Serialize());
@@ -122,7 +124,7 @@ public async Task Garage(
.SetItem("elemental_type", ElementalType.Normal.Serialize())
.SetItem("item_id", HashDigest.FromString(fid).Serialize()));
- mockAccountState = mockAccountState
+ account = account
.SetState(
Addresses.GetGarageAddress(
agentAddr,
@@ -137,7 +139,8 @@ public async Task Garage(
// testing without setting up any balance passes the tests;
// also this is different from the original test setup as there is no way
// to allow state to have "infinite" FAVs with all possible addresses having FAVs
- mockAccountState = mockAccountState
+ mockWorldState = mockWorldState
+ .SetAccount(ReservedAddresses.LegacyAccount, account)
.SetBalance(agentAddr, new FungibleAssetValue(goldCurrency, 99, 99))
.SetBalance(agentAddr, new FungibleAssetValue(Currencies.Crystal, 99, 123456789012345678))
.SetBalance(agentAddr, new FungibleAssetValue(Currencies.Garage, 99, 123456789012345678))
@@ -146,10 +149,7 @@ public async Task Garage(
var queryResult = await ExecuteQueryAsync(
sb.ToString(),
source: new StateContext(
- new MockWorld(new MockWorldState(
- ImmutableDictionary.Empty.Add(
- ReservedAddresses.LegacyAccount,
- new MockAccount(mockAccountState)))),
+ new World(mockWorldState),
0L, new StateMemoryCache()));
Assert.Null(queryResult.Errors);
var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!;
@@ -245,7 +245,7 @@ public async Task CachedSheet(bool cached, string? expected)
var queryResult = await ExecuteQueryAsync(
query,
source: new StateContext(
- new MockWorld(new MockWorldState()),
+ new World(MockWorldState.CreateModern()),
0L, cache));
Assert.Null(queryResult.Errors);
var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!;
diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs
index ec601bf20..be3a97025 100644
--- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs
+++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs
@@ -5,6 +5,7 @@
using GraphQL.Execution;
using Libplanet.Action.State;
using Libplanet.Crypto;
+using Libplanet.Mocks;
using Libplanet.Types.Assets;
using Nekoyume;
using Nekoyume.Action;
@@ -60,21 +61,23 @@ public async Task Query(int goldBalance, string goldDecimalString, int crystalBa
MonsterCollectionState monsterCollectionState = new MonsterCollectionState(monsterCollectionAddress, 7, 0, Fixtures.TableSheetsFX.MonsterCollectionRewardSheet);
Address pledgeAddress = agentState.address.GetPledgeAddress();
- MockAccountState mockAccountState = new MockAccountState()
- .SetState(GoldCurrencyState.Address, new GoldCurrencyState(goldCurrency).Serialize())
- .SetState(monsterCollectionAddress, monsterCollectionState.Serialize())
- .SetState(
- pledgeAddress,
- List.Empty
- .Add(MeadConfig.PatronAddress.Serialize())
- .Add(true.Serialize())
- .Add(4.Serialize()))
+ MockWorldState mockWorldState = MockWorldState.CreateModern();
+ mockWorldState = mockWorldState
+ .SetAccount(
+ ReservedAddresses.LegacyAccount,
+ new Account(mockWorldState.GetAccountState(ReservedAddresses.LegacyAccount))
+ .SetState(GoldCurrencyState.Address, new GoldCurrencyState(goldCurrency).Serialize())
+ .SetState(monsterCollectionAddress, monsterCollectionState.Serialize())
+ .SetState(
+ pledgeAddress,
+ List.Empty
+ .Add(MeadConfig.PatronAddress.Serialize())
+ .Add(true.Serialize())
+ .Add(4.Serialize())))
.SetBalance(agentState.address, CrystalCalculator.CRYSTAL * crystalBalance)
.SetBalance(agentState.address, goldCurrency * goldBalance);
- IWorld mockWorld = new MockWorld(new MockWorldState(ImmutableDictionary.Empty.Add(
- ReservedAddresses.LegacyAccount,
- new MockAccount(mockAccountState))));
- mockWorld = mockWorld.SetAvatarState(
+ IWorld world = new World(mockWorldState);
+ world = world.SetAvatarState(
Fixtures.AvatarAddress,
Fixtures.AvatarStateFX,
true,
@@ -84,7 +87,7 @@ public async Task Query(int goldBalance, string goldDecimalString, int crystalBa
var queryResult = await ExecuteQueryAsync(
query,
- source: new AgentStateType.AgentStateContext(agentState, mockWorld, 0, new StateMemoryCache())
+ source: new AgentStateType.AgentStateContext(agentState, world, 0, new StateMemoryCache())
);
var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!;
var expected = new Dictionary()
diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs
index 0ebc50e03..ddbd69d8d 100644
--- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs
+++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs
@@ -6,6 +6,7 @@
using Lib9c;
using Libplanet.Action.State;
using Libplanet.Crypto;
+using Libplanet.Mocks;
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Model.State;
@@ -30,7 +31,7 @@ public async Task Query(AvatarState avatarState, Dictionary expe
index
inventoryAddress
}";
- IWorld mockWorld = new MockWorld(new MockWorldState());
+ IWorld mockWorld = new World(MockWorldState.CreateModern());
mockWorld = mockWorld.SetAvatarState(
Fixtures.AvatarAddress,
Fixtures.AvatarStateFX,
@@ -65,19 +66,19 @@ public async Task QueryWithCombinationSlotState(AvatarState avatarState, Diction
}
}
";
- IWorld mockWorld = new MockWorld(new MockWorldState());
- mockWorld = mockWorld.SetAvatarState(
+ IWorld world = new World(MockWorldState.CreateModern());
+ world = world.SetAvatarState(
Fixtures.AvatarAddress,
Fixtures.AvatarStateFX,
true,
true,
true,
true);
- mockWorld = mockWorld.SetAgentState(Fixtures.UserAddress, Fixtures.AgentStateFx);
+ world = world.SetAgentState(Fixtures.UserAddress, Fixtures.AgentStateFx);
for (int i = 0; i < Fixtures.AvatarStateFX.combinationSlotAddresses.Count; i++)
{
- mockWorld = mockWorld
+ world = world
.SetLegacyState(
Fixtures.AvatarStateFX.combinationSlotAddresses[i],
Fixtures.CombinationSlotStatesFx[i].Serialize());
@@ -87,7 +88,7 @@ public async Task QueryWithCombinationSlotState(AvatarState avatarState, Diction
query,
source: new AvatarStateType.AvatarStateContext(
avatarState,
- mockWorld,
+ world,
0, new StateMemoryCache()));
var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!;
Assert.Equal(expected, data);
diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs
index 9619249e9..59ebf1cee 100644
--- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs
+++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs
@@ -4,6 +4,7 @@
using GraphQL.Execution;
using Libplanet.Action.State;
using Libplanet.Crypto;
+using Libplanet.Mocks;
using Libplanet.Types.Assets;
using Nekoyume;
using Nekoyume.Model.Stake;
@@ -27,8 +28,12 @@ public async Task Query(StakeStateV2 stakeState, Address stakeStateAddress, long
var goldCurrency = Currency.Legacy("NCG", 2, null);
#pragma warning restore CS0618
- MockAccountState mockAccountState = new MockAccountState()
- .SetState(GoldCurrencyState.Address, new GoldCurrencyState(goldCurrency).Serialize())
+ MockWorldState mockWorldState = MockWorldState.CreateModern();
+ mockWorldState = mockWorldState
+ .SetAccount(
+ ReservedAddresses.LegacyAccount,
+ new Account(mockWorldState.GetAccountState(ReservedAddresses.LegacyAccount))
+ .SetState(GoldCurrencyState.Address, new GoldCurrencyState(goldCurrency).Serialize()))
.SetBalance(Fixtures.StakeStateAddress, goldCurrency, (goldCurrency * deposit).RawValue);
const string query = @"
@@ -45,10 +50,7 @@ public async Task Query(StakeStateV2 stakeState, Address stakeStateAddress, long
source: new StakeStateType.StakeStateContext(
stakeState,
stakeStateAddress,
- new MockWorld(new MockWorldState(
- ImmutableDictionary.Empty.Add(
- ReservedAddresses.LegacyAccount,
- new MockAccount(mockAccountState)))),
+ new World(mockWorldState),
blockIndex, new StateMemoryCache()));
var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!;
Assert.Equal(expected, data);
diff --git a/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs b/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs
index 8078cda5f..f1ad705bb 100644
--- a/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs
+++ b/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs
@@ -7,6 +7,7 @@
using GraphQL.Execution;
using Libplanet.Action.State;
using Libplanet.Crypto;
+using Libplanet.Mocks;
using Libplanet.Types.Assets;
using Nekoyume;
using Nekoyume.Action;
@@ -301,9 +302,10 @@ public async Task RaiderList(bool stateExist, long blockIndex, bool prev)
private IWorldState GetMockState()
{
- return new MockWorld(new MockWorldState(ImmutableDictionary.Empty.Add(
+ MockWorldState mockWorldState = MockWorldState.CreateModern();
+ return new World(mockWorldState.SetAccount(
ReservedAddresses.LegacyAccount,
- new MockAccount(new MockAccountState()
+ new Account(mockWorldState.GetAccountState(ReservedAddresses.LegacyAccount))
.SetState(_raiderStateAddress, _raiderState.Serialize())
.SetState(Addresses.GetSheetAddress(),
@"id,boss_id,started_block_index,ended_block_index,fee,ticket_price,additional_ticket_price,max_purchase_count
@@ -312,7 +314,7 @@ private IWorldState GetMockState()
".Serialize())
.SetState(_worldBossAddress, _worldBossState.Serialize())
.SetState(_worldBossKillRewardRecordAddress, _worldBossKillRewardRecord.Serialize())
- .SetState(_raiderListAddress, List.Empty.Add(_raiderStateAddress.Serialize()))))));
+ .SetState(_raiderListAddress, List.Empty.Add(_raiderStateAddress.Serialize()))));
}
private async Task GetRaidId(long blockIndex, bool prev)
diff --git a/NineChronicles.Headless/BlockChainContext.cs b/NineChronicles.Headless/BlockChainContext.cs
index 0fc1eb096..17a53f10a 100644
--- a/NineChronicles.Headless/BlockChainContext.cs
+++ b/NineChronicles.Headless/BlockChainContext.cs
@@ -16,9 +16,9 @@ public BlockChainContext(StandaloneContext standaloneContext)
}
public bool Preloaded => _standaloneContext.NodeStatus.PreloadEnded;
- public BlockChain? BlockChain => _standaloneContext.BlockChain;
- public IStore? Store => _standaloneContext.Store;
- public Swarm? Swarm => _standaloneContext.Swarm;
+ public BlockChain BlockChain => _standaloneContext.BlockChain;
+ public IStore Store => _standaloneContext.Store;
+ public Swarm Swarm => _standaloneContext.Swarm;
public IBlockChainIndex Index => new RocksDbBlockChainIndex("/tmp/no/no/no/store");
}
}
diff --git a/NineChronicles.Headless/BlockChainService.cs b/NineChronicles.Headless/BlockChainService.cs
index 237c3e9f0..18989f137 100644
--- a/NineChronicles.Headless/BlockChainService.cs
+++ b/NineChronicles.Headless/BlockChainService.cs
@@ -323,7 +323,6 @@ public UnaryResult GetBalanceByBlockHash(
Currency currency = CurrencyExtensions.Deserialize(serializedCurrency);
FungibleAssetValue balance = _blockChain
.GetWorldState(blockHash)
- .GetAccountState(ReservedAddresses.LegacyAccount)
.GetBalance(address, currency);
byte[] encoded = _codec.Encode(
new Bencodex.Types.List(
@@ -348,7 +347,6 @@ public UnaryResult GetBalanceByStateRootHash(
Currency currency = CurrencyExtensions.Deserialize(serializedCurrency);
FungibleAssetValue balance = _blockChain
.GetWorldState(stateRootHash)
- .GetAccountState(ReservedAddresses.LegacyAccount)
.GetBalance(address, currency);
byte[] encoded = _codec.Encode(
new Bencodex.Types.List(
diff --git a/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs b/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs
index 87cb9b00a..8e42ea062 100644
--- a/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs
+++ b/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs
@@ -22,7 +22,10 @@ public AppProtocolVersionType()
resolve: context => context.Source.Signature.ToBuilder().ToArray());
Field(
name: "extra",
- resolve: context => _codec.Encode(context.Source.Extra));
+ resolve: context
+ => context.Source.Extra != null ?
+ _codec.Encode(context.Source.Extra) :
+ null);
}
}
}
diff --git a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs
index afd2f7471..463441863 100644
--- a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs
+++ b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs
@@ -134,13 +134,22 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi
IEnumerable blockTxs = digest.TxIds
.Select(bytes => new TxId(bytes))
- .Select(store.GetTransaction);
+ .Select(txid =>
+ {
+ return store.GetTransaction(txid) ??
+ throw new InvalidOperationException($"Transaction {txid} not found.");
+ });
var filtered = blockTxs
.Where(tx => tx.Actions.Count == 1)
- .Select(tx => (store.GetTxExecution(blockHash, tx.Id), ToAction(tx.Actions[0])))
- .Where(pair => pair.Item1 is { } && pair.Item2 is ITransferAsset)
- .Select(pair => (pair.Item1, (ITransferAsset)pair.Item2))
+ .Select(tx =>
+ (
+ store.GetTxExecution(blockHash, tx.Id) ??
+ throw new InvalidOperationException($"TxExecution {tx.Id} not found."),
+ ToAction(tx.Actions[0])
+ ))
+ .Where(pair => pair.Item2 is ITransferAsset)
+ .Select(pair => (pair.Item1!, (ITransferAsset)pair.Item2))
.Where(pair => !pair.Item1.Fail &&
(!recipient.HasValue || pair.Item2.Recipient == recipient) &&
pair.Item2.Amount.Currency.Ticker == "NCG");
diff --git a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs
index 0b5cb3694..f0d278ecc 100644
--- a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs
+++ b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs
@@ -295,7 +295,8 @@ private IObservable SubscribeTx(IResolveFieldContext context)
{
return null;
}
- var txExecution = store.GetTxExecution(blockHash, transaction.Id);
+ var txExecution = store.GetTxExecution(blockHash, transaction.Id) ??
+ throw new InvalidOperationException("Not found tx execution.");
var txExecutedBlock = chain[blockHash];
return new TxResult(
txExecution.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS,
diff --git a/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs b/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs
index 5121e961b..7d011ae97 100644
--- a/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs
+++ b/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs
@@ -112,7 +112,9 @@ public static LibplanetNodeServiceProperties
Host = swarmHost,
Port = swarmPort,
SwarmPrivateKey = swarmPrivateKey,
- AppProtocolVersion = AppProtocolVersion.FromToken(appProtocolVersionToken),
+ AppProtocolVersion = AppProtocolVersion.FromToken(
+ appProtocolVersionToken ??
+ throw new InvalidOperationException("appProtocolVersionToken cannot be null.")),
TrustedAppProtocolVersionSigners = trustedAppProtocolVersionSigners
?.Select(s => new PublicKey(ByteUtil.ParseHex(s)))
?.ToHashSet(),
diff --git a/NineChronicles.Headless/StandaloneContext.cs b/NineChronicles.Headless/StandaloneContext.cs
index 31e46dc84..8faad9946 100644
--- a/NineChronicles.Headless/StandaloneContext.cs
+++ b/NineChronicles.Headless/StandaloneContext.cs
@@ -15,8 +15,23 @@ namespace NineChronicles.Headless
{
public class StandaloneContext
{
- public BlockChain? BlockChain { get; set; }
- public IKeyStore? KeyStore { get; set; }
+ private BlockChain? _blockChain;
+ private IKeyStore? _keyStore;
+ private IStore? _store;
+ private Swarm? _swarm;
+
+ public BlockChain BlockChain
+ {
+ get => _blockChain ??
+ throw new InvalidOperationException($"{nameof(BlockChain)} property is not set yet.");
+ set => _blockChain = value;
+ }
+ public IKeyStore KeyStore
+ {
+ get => _keyStore ??
+ throw new InvalidOperationException($"{nameof(KeyStore)} property is not set yet.");
+ set => _keyStore = value;
+ }
public bool BootstrapEnded { get; set; }
public bool PreloadEnded { get; set; }
public bool IsMining { get; set; }
@@ -42,9 +57,19 @@ public class StandaloneContext
IsMining = IsMining,
};
- public IStore? Store { get; internal set; }
+ public IStore Store
+ {
+ get => _store ??
+ throw new InvalidOperationException($"{nameof(Store)} property is not set yet.");
+ internal set => _store = value;
+ }
- public Swarm? Swarm { get; internal set; }
+ public Swarm Swarm
+ {
+ get => _swarm ??
+ throw new InvalidOperationException($"{nameof(Swarm)} property is not set yet.");
+ internal set => _swarm = value;
+ }
public CurrencyFactory? CurrencyFactory { get; set; }