diff --git a/Lib9c b/Lib9c index 9c378d993..b0934580d 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 9c378d99369b8c52bc187b563e47a79bdb8cceaa +Subproject commit b0934580d7234d586607dcef34c74b8051d8b3b7 diff --git a/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs b/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs index 6be8af511..8b2d8afec 100644 --- a/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs +++ b/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs @@ -31,7 +31,11 @@ public void Constructor() stateStore); var actionLoader = new SingleActionLoader(typeof(DummyAction)); var actionEvaluator = new ActionEvaluator( - _ => policy.BlockAction, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, actionLoader); var genesisBlock = BlockChain.ProposeGenesisBlock(actionEvaluator); diff --git a/Libplanet.Headless/Hosting/LibplanetNodeService.cs b/Libplanet.Headless/Hosting/LibplanetNodeService.cs index 4e0f063ce..b6eaaadc4 100644 --- a/Libplanet.Headless/Hosting/LibplanetNodeService.cs +++ b/Libplanet.Headless/Hosting/LibplanetNodeService.cs @@ -129,7 +129,11 @@ IActionEvaluator BuildActionEvaluator(IActionEvaluatorConfiguration actionEvalua actionLoader), DefaultActionEvaluatorConfiguration _ => new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), stateStore: StateStore, actionTypeLoader: actionLoader), ForkableActionEvaluatorConfiguration forkableActionEvaluatorConfiguration => diff --git a/NineChronicles.Headless.Executable.Tests/Commands/AccountCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/AccountCommandTest.cs index e21a38c07..79b79ed3b 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/AccountCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/AccountCommandTest.cs @@ -48,7 +48,11 @@ public void Balance(StoreType storeType) IStagePolicy stagePolicy = new VolatileStagePolicy(); IBlockPolicy blockPolicy = new BlockPolicySource().GetPolicy(); ActionEvaluator actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), stateStore, new NCActionLoader()); BlockChain chain = BlockChain.Create( diff --git a/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs index 637bafecc..bce5763dc 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs @@ -58,7 +58,11 @@ public ChainCommandTest() public void Tip(StoreType storeType) { var actionEvaluator = new ActionEvaluator( - _ => new BlockPolicy().BlockAction, + new PolicyActionsRegistry( + _ => new BlockPolicy().BeginBlockActions, + _ => new BlockPolicy().EndBlockActions, + _ => new BlockPolicy().BeginTxActions, + _ => new BlockPolicy().EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); Block genesisBlock = BlockChain.ProposeGenesisBlock(actionEvaluator); @@ -92,7 +96,11 @@ public void Inspect(StoreType storeType) IStagePolicy stagePolicy = new VolatileStagePolicy(); IBlockPolicy blockPolicy = new BlockPolicySource().GetPolicy(); ActionEvaluator actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), stateStore, new NCActionLoader()); Block genesisBlock = BlockChain.ProposeGenesisBlock( @@ -153,7 +161,11 @@ public void Truncate(StoreType storeType) IStagePolicy stagePolicy = new VolatileStagePolicy(); IBlockPolicy blockPolicy = new BlockPolicySource().GetPolicy(); ActionEvaluator actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), stateStore, new NCActionLoader()); Block genesisBlock = BlockChain.ProposeGenesisBlock( @@ -232,7 +244,11 @@ public void PruneState(StoreType storeType) IStagePolicy stagePolicy = new VolatileStagePolicy(); IBlockPolicy blockPolicy = new BlockPolicySource().GetPolicy(); ActionEvaluator actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), stateStore, new NCActionLoader()); BlockChain chain = BlockChain.Create( @@ -284,7 +300,11 @@ public void Snapshot(StoreType storeType) IStagePolicy stagePolicy = new VolatileStagePolicy(); IBlockPolicy blockPolicy = new BlockPolicySource().GetPolicy(); ActionEvaluator actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), stateStore, new NCActionLoader()); BlockChain chain = BlockChain.Create( @@ -445,6 +465,7 @@ private Block MineGenesisBlock() block.Hash, DateTimeOffset.UtcNow, validator.PublicKey, + BigInteger.One, VoteFlag.PreCommit).Sign(validator))) : null; } diff --git a/NineChronicles.Headless.Executable.Tests/Store/StoreExtensionsTest.cs b/NineChronicles.Headless.Executable.Tests/Store/StoreExtensionsTest.cs index d5d77b991..f06ad3b97 100644 --- a/NineChronicles.Headless.Executable.Tests/Store/StoreExtensionsTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Store/StoreExtensionsTest.cs @@ -29,7 +29,11 @@ public void GetGenesisBlock(StoreType storeType) { IStore store = storeType.CreateStore(_storePath); IActionEvaluator actionEvaluator = new ActionEvaluator( - _ => new BlockPolicy().BlockAction, + new PolicyActionsRegistry( + _ => new BlockPolicy().BeginBlockActions, + _ => new BlockPolicy().EndBlockActions, + _ => new BlockPolicy().BeginTxActions, + _ => new BlockPolicy().EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); Block genesisBlock = BlockChain.ProposeGenesisBlock(actionEvaluator); diff --git a/NineChronicles.Headless.Executable/Commands/AccountCommand.cs b/NineChronicles.Headless.Executable/Commands/AccountCommand.cs index 9531c62f4..4e1fb0aec 100644 --- a/NineChronicles.Headless.Executable/Commands/AccountCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/AccountCommand.cs @@ -76,7 +76,7 @@ public void Balance( _console.Error.Flush(); IEnumerable
addrs = digest.TxIds .Select(txId => store.GetTransaction(new TxId(txId.ToArray()))) - .SelectMany(tx => tx.Actions is { } ca + .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..5bbfd1e08 100644 --- a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs @@ -17,15 +17,12 @@ using Libplanet.RocksDBStore; using Libplanet.Store; using Libplanet.Store.Trie; -using Microsoft.Extensions.Configuration; using Nekoyume.Action.Loader; using Nekoyume.Blockchain.Policy; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NineChronicles.Headless.Executable.IO; using NineChronicles.Headless.Executable.Store; -using Serilog; -using Serilog.Core; using static NineChronicles.Headless.NCActionUtils; using CoconaUtils = Libplanet.Extensions.Cocona.Utils; @@ -82,7 +79,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 = store.GetBlock(tipHash)!; _console.Out.WriteLine(CoconaUtils.SerializeHumanReadable(tip.Header)); store.Dispose(); } @@ -129,10 +126,14 @@ public void Inspect( throw new CommandExitedException($"There is no genesis block: {storePath}", -1); } - Block genesisBlock = store.GetBlock(gHash); + Block genesisBlock = store.GetBlock(gHash)!; var blockChainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => blockPolicy.BeginBlockActions, + endBlockActionsGetter: _ => blockPolicy.EndBlockActions, + beginTxActionsGetter: _ => blockPolicy.BeginTxActions, + endTxActionsGetter: _ => blockPolicy.EndTxActions), stateStore, new NCActionLoader()); BlockChain chain = new BlockChain( @@ -165,10 +166,10 @@ public void Inspect( { var block = store.GetBlock(item.value); var previousBlock = store.GetBlock( - block.PreviousHash ?? block.Hash + block!.PreviousHash ?? block.Hash ); - var miningTime = block.Timestamp - previousBlock.Timestamp; + var miningTime = block.Timestamp - previousBlock!.Timestamp; var txCount = 0; var hackAndSlashCount = 0; var rankingBattleCount = 0; @@ -286,11 +287,11 @@ public void Truncate( } snapshotTipHash = hash; - } while (!stateStore.GetStateRoot(store.GetBlock(snapshotTipHash).StateRootHash).Recorded); + } while (!stateStore.GetStateRoot(store.GetBlock(snapshotTipHash)!.StateRootHash).Recorded); var forkedId = Guid.NewGuid(); - Fork(chainId, forkedId, snapshotTipHash, tip, store); + Fork(chainId, forkedId, snapshotTipHash, tip!, store); store.SetCanonicalChainId(forkedId); foreach (var id in store.ListChainIds().Where(id => !id.Equals(forkedId))) @@ -478,17 +479,21 @@ public void Snapshot( new BlockPolicy(); var blockChainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => blockPolicy.BeginBlockActions, + endBlockActionsGetter: _ => blockPolicy.EndBlockActions, + beginTxActionsGetter: _ => blockPolicy.BeginTxActions, + endTxActionsGetter: _ => blockPolicy.EndTxActions), stateStore, new NCActionLoader() ); var originalChain = new BlockChain(blockPolicy, stagePolicy, store, stateStore, store.GetBlock(genesisHash), blockChainStates, actionEvaluator); - var tip = store.GetBlock(tipHash); + Block tip = store.GetBlock(tipHash)!; var potentialSnapshotTipIndex = tipIndex - blockBefore; var potentialSnapshotTipHash = (BlockHash)store.IndexBlockHash(chainId, potentialSnapshotTipIndex)!; - var snapshotTip = store.GetBlock(potentialSnapshotTipHash); + var snapshotTip = store.GetBlock(potentialSnapshotTipHash)!; _console.Out.WriteLine( "Original Store Tip: #{0}\n1. LastCommit: {1}\n2. BlockCommit in Chain: {2}\n3. BlockCommit in Store: {3}", @@ -521,23 +526,27 @@ public void Snapshot( potentialSnapshotTipIndex); blockBefore += 1; potentialSnapshotTipBlockCommit = store - .GetBlock((BlockHash)store.IndexBlockHash(chainId, tip.Index - blockBefore + 1)!).LastCommit; + .GetBlock((BlockHash)store.IndexBlockHash(chainId, tip.Index - blockBefore + 1)!)!.LastCommit; store.PutBlockCommit(tipBlockCommit); store.PutChainBlockCommit(chainId, tipBlockCommit); - store.PutBlockCommit(potentialSnapshotTipBlockCommit); - store.PutChainBlockCommit(chainId, potentialSnapshotTipBlockCommit); + store.PutBlockCommit(potentialSnapshotTipBlockCommit!); + store.PutChainBlockCommit(chainId, potentialSnapshotTipBlockCommit!); } - var blockCommitBlock = store.GetBlock(tipHash); + Block? blockCommitBlock = store.GetBlock(tipHash); + if (blockCommitBlock is not { } blockCommitBlockNotNull) + { + throw new NullReferenceException(nameof(blockChainStates)); + } // 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); - store.PutBlockCommit(blockCommitBlock.LastCommit); - store.PutChainBlockCommit(chainId, blockCommitBlock.LastCommit); - blockCommitBlock = store.GetBlock((BlockHash)blockCommitBlock.PreviousHash!); + _console.Out.WriteLine("Adding block #{0}'s block commit to the store", blockCommitBlockNotNull.Index - 1); + store.PutBlockCommit(blockCommitBlockNotNull.LastCommit!); + store.PutChainBlockCommit(chainId, blockCommitBlockNotNull.LastCommit!); + blockCommitBlock = store.GetBlock((BlockHash)blockCommitBlockNotNull.PreviousHash!); } var snapshotTipIndex = Math.Max(tipIndex - (blockBefore + 1), 0); @@ -555,7 +564,7 @@ public void Snapshot( } snapshotTipHash = hash; - } while (!stateStore.GetStateRoot(store.GetBlock(snapshotTipHash).StateRootHash).Recorded); + } while (!stateStore.GetStateRoot(store.GetBlock(snapshotTipHash)!.StateRootHash).Recorded); var forkedId = Guid.NewGuid(); Fork(chainId, forkedId, snapshotTipHash, tip, store); @@ -1025,7 +1034,7 @@ private void Fork( store.ForkBlockIndexes(src, dest, branchPointHash); if (store.GetBlockCommit(branchPointHash) is { }) { - store.PutChainBlockCommit(dest, store.GetBlockCommit(branchPointHash)); + store.PutChainBlockCommit(dest, store.GetBlockCommit(branchPointHash)!); } store.ForkTxNonces(src, dest); @@ -1033,7 +1042,7 @@ private void Fork( Block block = tip; block.PreviousHash is { } hash && !block.Hash.Equals(branchPointHash); - block = store.GetBlock(hash)) + block = store.GetBlock(hash)!) { IEnumerable<(Address, int)> signers = block .Transactions diff --git a/NineChronicles.Headless.Executable/Commands/GenesisCommand.cs b/NineChronicles.Headless.Executable/Commands/GenesisCommand.cs index 0cac71bde..6d98029ad 100644 --- a/NineChronicles.Headless.Executable/Commands/GenesisCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/GenesisCommand.cs @@ -13,6 +13,7 @@ using Libplanet.Types.Blocks; using Nekoyume; using Nekoyume.Action; +using Nekoyume.Action.DPoS.Misc; using Nekoyume.Model.State; using NineChronicles.Headless.Executable.IO; using CoconaUtils = Libplanet.Extensions.Cocona.Utils; @@ -236,6 +237,28 @@ out List pledgeActions } } + private void ProcessInitialNCGConfigs( + List? configs, + out Dictionary initialNCGs + ) + { + _console.Out.WriteLine("\nProcessing initial NCG..."); + + initialNCGs = new Dictionary(); + if (configs is { }) + { + foreach (NCGConfig config in configs) + { + _console.Out.WriteLine($"Preparing {config.Amount} NCG for {config.Address}..."); + Address agentAddress = new(config.Address); + initialNCGs.Add( + agentAddress, + new FungibleAssetValue(Asset.GovernanceToken, config.Amount, 0) + ); + } + } + } + [Command(Description = "Mine a new genesis block")] public void Mine( [Argument("CONFIG", Description = "JSON config path to mine genesis block")] @@ -263,6 +286,8 @@ public void Mine( ProcessInitialPledgeConfigs(genesisConfig.InitialPledgeConfigs, out var initialPledges); + ProcessInitialNCGConfigs(genesisConfig.InitialNCGConfigs, out var initialNCGs); + // Mine genesis block _console.Out.WriteLine("\nMining genesis block...\n"); Block block = BlockHelper.ProposeGenesisBlock( @@ -276,7 +301,8 @@ public void Mine( item => new PublicKey(ByteUtil.ParseHex(item.PublicKey)), item => new BigInteger(item.Power)), actionBases: adminMeads.Concat(initialMeads).Concat(initialPledges).Concat(GetAdditionalActionBases()), - goldCurrency: currency + goldCurrency: currency, + initialFavs: initialNCGs ); Lib9cUtils.ExportBlock(block, "genesis-block"); @@ -418,6 +444,14 @@ private struct PledgeConfig public int Mead { get; set; } } + [Serializable] + private struct NCGConfig + { + public string Address { get; set; } + + public int Amount { get; set; } + } + /// /// Config to mine new genesis block. /// @@ -454,6 +488,8 @@ private struct GenesisConfig public List? InitialMeadConfigs { get; set; } public List? InitialPledgeConfigs { get; set; } + + public List? InitialNCGConfigs { get; set; } } #pragma warning restore S3459 } diff --git a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs index 2a5154b23..6ea400753 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs @@ -8,6 +8,8 @@ using Libplanet.Crypto; using Libplanet.Action; using Libplanet.Action.State; +using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using Serilog; @@ -28,6 +30,8 @@ public ActionContext( int blockProtocolVersion, IWorld previousState, int randomSeed, + bool isBlockAction, + FungibleAssetValue? maxGasPrice, bool rehearsal = false) { Signer = signer; @@ -38,6 +42,8 @@ public ActionContext( Rehearsal = rehearsal; PreviousState = previousState; RandomSeed = randomSeed; + IsBlockAction = isBlockAction; + MaxGasPrice = maxGasPrice; } public Address Signer { get; } @@ -49,6 +55,8 @@ public ActionContext( public long BlockIndex { get; } public int BlockProtocolVersion { get; } + + public BlockCommit? LastCommit { get; } public bool Rehearsal { get; } @@ -56,7 +64,9 @@ public ActionContext( public int RandomSeed { get; } - public bool BlockAction => TxId is null; + public bool IsBlockAction { get; } + + public FungibleAssetValue? MaxGasPrice { get; } public void UseGas(long gas) { @@ -93,6 +103,8 @@ private static IEnumerable EvaluateActions( Address signer, byte[] signature, IImmutableList actions, + bool isBlockAction, + FungibleAssetValue? maxGasPrice, ILogger? logger = null) { ActionContext CreateActionContext( @@ -106,7 +118,9 @@ ActionContext CreateActionContext( blockIndex: blockIndex, blockProtocolVersion: blockProtocolVersion, previousState: prevState, - randomSeed: randomSeed); + randomSeed: randomSeed, + isBlockAction: isBlockAction, + maxGasPrice: maxGasPrice); } byte[] preEvaluationHashBytes = preEvaluationHash.ToByteArray(); diff --git a/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs b/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs index 9e1db0b20..e7193d0c4 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs @@ -109,8 +109,9 @@ public int Tx( miner: targetBlock.Miner, signer: tx.Signer, signature: tx.Signature, - actions: actions.Cast().ToImmutableList() - ); + actions: actions.Cast().ToImmutableList(), + isBlockAction: false, + maxGasPrice: tx.MaxGasPrice); var actionNum = 1; foreach (var actionEvaluation in actionEvaluations) { @@ -405,7 +406,9 @@ public int RemoteTx( miner: miner, signer: transaction.Signer, signature: transaction.Signature, - actions: actions); + actions: actions, + isBlockAction: true, + maxGasPrice: transaction.MaxGasPrice); actionEvaluations .Select((evaluation, index) => (evaluation, index)) @@ -473,7 +476,11 @@ private static (FileStream? fs, StreamWriter? sw) GetOutputFileStream( var stateStore = new TrieStateStore(stateKeyValueStore); var blockChainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( - _ => policy.BlockAction, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => policy.BeginBlockActions, + endBlockActionsGetter: _ => policy.EndBlockActions, + beginTxActionsGetter: _ => policy.BeginTxActions, + endTxActionsGetter: _ => policy.EndTxActions), stateStore, new NCActionLoader()); return ( @@ -520,7 +527,11 @@ private ActionEvaluator GetActionEvaluator(IStateStore stateStore) var policy = new BlockPolicySource().GetPolicy(); IActionLoader actionLoader = new NCActionLoader(); return new ActionEvaluator( - _ => policy.BlockAction, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => policy.BeginBlockActions, + endBlockActionsGetter: _ => policy.EndBlockActions, + beginTxActionsGetter: _ => policy.BeginTxActions, + endTxActionsGetter: _ => policy.EndTxActions), stateStore: stateStore, actionTypeLoader: actionLoader); } diff --git a/NineChronicles.Headless.Executable/Commands/StateCommand.cs b/NineChronicles.Headless.Executable/Commands/StateCommand.cs index 28a930868..a5dd14942 100644 --- a/NineChronicles.Headless.Executable/Commands/StateCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/StateCommand.cs @@ -109,7 +109,11 @@ IStateStore stateStore new MemoryStore(), sStore); var actionEvaluator = new ActionEvaluator( - _ => policy.BlockAction, + new PolicyActionsRegistry( + beginBlockActionsGetter: _ => policy.BeginBlockActions, + endBlockActionsGetter: _ => policy.EndBlockActions, + beginTxActionsGetter: _ => policy.BeginTxActions, + endTxActionsGetter: _ => policy.EndTxActions), sStore, new NCActionLoader()); @@ -120,8 +124,7 @@ IStateStore stateStore throw new CommandExitedException(1); } - Block block = - store.GetBlock(blockHash); + Block block = store.GetBlock(blockHash)!; 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..9d083f0b2 100644 --- a/NineChronicles.Headless.Executable/Store/StoreExtensions.cs +++ b/NineChronicles.Headless.Executable/Store/StoreExtensions.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.Linq; using Libplanet.Action; @@ -18,7 +17,7 @@ public static Block GetGenesisBlock(this IStore store) } BlockHash genesisBlockHash = store.IterateIndexes(chainId.Value).First(); - Block genesisBlock = store.GetBlock(genesisBlockHash); + Block genesisBlock = store.GetBlock(genesisBlockHash)!; return genesisBlock; } } diff --git a/NineChronicles.Headless.Tests/Common/MockAccount.cs b/NineChronicles.Headless.Tests/Common/MockAccount.cs index ac2d3acdd..705325e29 100644 --- a/NineChronicles.Headless.Tests/Common/MockAccount.cs +++ b/NineChronicles.Headless.Tests/Common/MockAccount.cs @@ -188,6 +188,9 @@ public IAccount BurnAsset( public IAccount SetValidator(Validator validator) => UpdateValidatorSet(GetValidatorSet().Update(validator)); + public IAccount SetValidatorSet(ValidatorSet validatorSet) => + UpdateValidatorSet(validatorSet); + [Pure] private MockAccount UpdateState( Address address, diff --git a/NineChronicles.Headless.Tests/GraphQLTestUtils.cs b/NineChronicles.Headless.Tests/GraphQLTestUtils.cs index 58c7f7eab..fed795418 100644 --- a/NineChronicles.Headless.Tests/GraphQLTestUtils.cs +++ b/NineChronicles.Headless.Tests/GraphQLTestUtils.cs @@ -83,7 +83,11 @@ public static StandaloneContext CreateStandaloneContext() var stateStore = new TrieStateStore(new DefaultKeyValueStore(null)); var policy = new BlockPolicy(); var actionEvaluator = new ActionEvaluator( - _ => policy.BlockAction, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, new NCActionLoader()); var genesisBlock = BlockChain.ProposeGenesisBlock(actionEvaluator); @@ -114,7 +118,11 @@ PrivateKey minerPrivateKey var stateStore = new TrieStateStore(new DefaultKeyValueStore(null)); var policy = new BlockPolicy(); var actionEvaluator = new ActionEvaluator( - _ => policy.BlockAction, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndBlockActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), stateStore, new NCActionLoader()); var genesisBlock = BlockChain.ProposeGenesisBlock( diff --git a/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs b/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs index d7393fbf7..b45544602 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs @@ -35,6 +35,7 @@ using System.Threading.Tasks; using Bencodex.Types; using Libplanet.Types.Tx; +using Nekoyume.Action.DPoS; using Xunit.Abstractions; namespace NineChronicles.Headless.Tests.GraphTypes @@ -55,9 +56,12 @@ public GraphQLTestBase(ITestOutputHelper output) #pragma warning restore CS0618 var sheets = TableSheetsImporter.ImportSheets(); - var blockAction = new RewardGold(); var actionEvaluator = new ActionEvaluator( - _ => blockAction, + new PolicyActionsRegistry( + _ => new DebugPolicy().BeginBlockActions, + _ => new DebugPolicy().EndBlockActions, + _ => new DebugPolicy().BeginTxActions, + _ => new DebugPolicy().EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); var genesisBlock = BlockChain.ProposeGenesisBlock( @@ -250,6 +254,7 @@ protected LibplanetNodeService CreateLibplanetNodeService( hash, DateTimeOffset.UtcNow, validator.PublicKey, + BigInteger.One, VoteFlag.PreCommit).Sign(validator)).ToImmutableArray()) : (BlockCommit?)null; } diff --git a/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs index 1adb11f11..0ea3dc9f0 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs @@ -919,7 +919,11 @@ private Block MakeGenesisBlock( RankingState0? rankingState = null) { var actionEvaluator = new ActionEvaluator( - _ => ServiceBuilder.BlockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => ServiceBuilder.BlockPolicy.BeginBlockActions, + _ => ServiceBuilder.BlockPolicy.EndBlockActions, + _ => ServiceBuilder.BlockPolicy.BeginTxActions, + _ => ServiceBuilder.BlockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); return BlockChain.ProposeGenesisBlock( diff --git a/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs index 4b36b5309..860788f38 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs @@ -15,6 +15,7 @@ using Libplanet.Action.State; using Libplanet.Action.Sys; using Libplanet.Blockchain; +using Libplanet.Blockchain.Policies; using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Headless.Hosting; @@ -119,8 +120,13 @@ public async Task NodeStatus() var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); + var blockPolicy = new BlockPolicy(); var actionEvaluator = new ActionEvaluator( - _ => null, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); var genesisBlock = BlockChain.ProposeGenesisBlock(actionEvaluator); @@ -447,8 +453,13 @@ public async Task ActivationStatus(bool existsActivatedAccounts) { new Libplanet.Types.Consensus.Validator(ProposerPrivateKey.PublicKey, BigInteger.One), }.ToList()); + var blockPolicy = new BlockPolicySource().GetPolicy(); var actionEvaluator = new ActionEvaluator( - _ => null, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); Block genesis = @@ -513,7 +524,6 @@ public async Task ActivationStatus(bool existsActivatedAccounts) ConsensusSeeds = ImmutableList.Empty, ConsensusPeers = ImmutableList.Empty }; - var blockPolicy = new BlockPolicySource().GetPolicy(); var service = new NineChroniclesNodeService( userPrivateKey, properties, blockPolicy, Planet.Odin, StaticActionLoaderSingleton.Instance); @@ -840,8 +850,13 @@ public async Task ActivationKeyNonce(bool trim) { pendingActivation, }; + var blockPolicy = NineChroniclesNodeService.GetBlockPolicy(Planet.Odin, StaticActionLoaderSingleton.Instance); var actionEvaluator = new ActionEvaluator( - _ => null, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); Block genesis = @@ -897,7 +912,6 @@ public async Task ActivationKeyNonce(bool trim) ConsensusPeers = ImmutableList.Empty }; - var blockPolicy = NineChroniclesNodeService.GetBlockPolicy(Planet.Odin, StaticActionLoaderSingleton.Instance); var service = new NineChroniclesNodeService(userPrivateKey, properties, blockPolicy, Planet.Odin, StaticActionLoaderSingleton.Instance); StandaloneContextFx.NineChroniclesNodeService = service; StandaloneContextFx.BlockChain = service.Swarm?.BlockChain; @@ -925,8 +939,13 @@ public async Task ActivationKeyNonce_Throw_ExecutionError(string code, string ms var activatedAccounts = ImmutableHashSet
.Empty; var pendingActivationStates = new List(); + var blockPolicy = new BlockPolicySource().GetPolicy(); var actionEvaluator = new ActionEvaluator( - _ => null, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); Block genesis = @@ -984,7 +1003,6 @@ public async Task ActivationKeyNonce_Throw_ExecutionError(string code, string ms ConsensusSeeds = ImmutableList.Empty, ConsensusPeers = ImmutableList.Empty }; - var blockPolicy = new BlockPolicySource().GetPolicy(); var service = new NineChroniclesNodeService(userPrivateKey, properties, blockPolicy, Planet.Odin, StaticActionLoaderSingleton.Instance); StandaloneContextFx.NineChroniclesNodeService = service; @@ -1006,8 +1024,13 @@ public async Task Balance() var activatedAccounts = ImmutableHashSet
.Empty; var pendingActivationStates = new List(); + var blockPolicy = new BlockPolicySource().GetPolicy(); var actionEvaluator = new ActionEvaluator( - _ => null, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); Block genesis = @@ -1062,7 +1085,6 @@ public async Task Balance() ConsensusSeeds = ImmutableList.Empty, ConsensusPeers = ImmutableList.Empty }; - var blockPolicy = new BlockPolicySource().GetPolicy(); var service = new NineChroniclesNodeService(userPrivateKey, properties, blockPolicy, Planet.Odin, StaticActionLoaderSingleton.Instance); StandaloneContextFx.NineChroniclesNodeService = service; @@ -1112,7 +1134,11 @@ private NineChroniclesNodeService MakeNineChroniclesNodeService(PrivateKey priva new Libplanet.Types.Consensus.Validator(privateKey.PublicKey, BigInteger.One), }.ToList()); var actionEvaluator = new ActionEvaluator( - _ => blockPolicy.BlockAction, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new NCActionLoader()); Block genesis = diff --git a/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs index 8c3fc52ea..097c64ce2 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs @@ -19,6 +19,7 @@ using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Blockchain; +using Libplanet.Blockchain.Policies; using Libplanet.Types.Consensus; using Libplanet.Crypto; using Libplanet.Headless; @@ -133,8 +134,13 @@ public async Task SubscribePreloadProgress() var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); + var blockPolicy = new BlockPolicy(); var actionEvaluator = new ActionEvaluator( - _ => null, + new PolicyActionsRegistry( + _ => blockPolicy.BeginBlockActions, + _ => blockPolicy.EndBlockActions, + _ => blockPolicy.BeginTxActions, + _ => blockPolicy.EndTxActions), new TrieStateStore(new MemoryKeyValueStore()), new SingleActionLoader(typeof(EmptyAction))); var genesisBlock = BlockChain.ProposeGenesisBlock( diff --git a/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs index 62e3d68bf..168457917 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs @@ -45,7 +45,11 @@ public TransactionHeadlessQueryTest() _stateStore = new TrieStateStore(new DefaultKeyValueStore(null)); IBlockPolicy policy = new BlockPolicySource().GetPolicy(); var actionEvaluator = new ActionEvaluator( - _ => policy.BlockAction, + new PolicyActionsRegistry( + _ => policy.BeginBlockActions, + _ => policy.EndTxActions, + _ => policy.BeginTxActions, + _ => policy.EndTxActions), _stateStore, new NCActionLoader()); Block genesisBlock = BlockChain.ProposeGenesisBlock( @@ -428,6 +432,7 @@ private Task ExecuteAsync(string query) hash, DateTimeOffset.UtcNow, validator.PublicKey, + BigInteger.One, VoteFlag.PreCommit).Sign(validator))) : (BlockCommit?)null; } diff --git a/NineChronicles.Headless/ActionEvaluationPublisher.cs b/NineChronicles.Headless/ActionEvaluationPublisher.cs index bba680fd1..152a0cc99 100644 --- a/NineChronicles.Headless/ActionEvaluationPublisher.cs +++ b/NineChronicles.Headless/ActionEvaluationPublisher.cs @@ -20,7 +20,6 @@ using Grpc.Net.Client; using Lib9c.Renderers; using Libplanet.Action.State; -using Libplanet.Blockchain; using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Types.Blocks; @@ -32,6 +31,8 @@ using Microsoft.Extensions.Options; using Nekoyume; using Nekoyume.Action; +using Nekoyume.Action.DPoS; +using Nekoyume.Action.DPoS.Sys; using Nekoyume.Shared.Hubs; using Serilog; @@ -471,9 +472,7 @@ await _hub.BroadcastRenderBlockAsync( { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); - ActionBase? pa = ev.Action is RewardGold - ? null - : ev.Action; + ActionBase pa = ev.Action; var extra = new Dictionary(); var encodeElapsedMilliseconds = stopwatch.ElapsedMilliseconds; diff --git a/NineChronicles.Headless/BlockChainContext.cs b/NineChronicles.Headless/BlockChainContext.cs index 0fc1eb096..0e570a630 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/GraphTypes/ActionMutation.cs b/NineChronicles.Headless/GraphTypes/ActionMutation.cs index 17e9f7e66..8691126f6 100644 --- a/NineChronicles.Headless/GraphTypes/ActionMutation.cs +++ b/NineChronicles.Headless/GraphTypes/ActionMutation.cs @@ -5,11 +5,13 @@ using Libplanet.Explorer.GraphTypes; using Libplanet.Types.Tx; using Nekoyume.Action; -using Nekoyume.Model.State; using Serilog; using System; using System.Collections.Generic; -using Nekoyume.Module; +using System.Numerics; +using Libplanet.Types.Assets; +using Nekoyume.Action.DPoS; +using Nekoyume.Action.DPoS.Misc; namespace NineChronicles.Headless.GraphTypes { @@ -438,6 +440,98 @@ public ActionMutation(NineChroniclesNodeService service) } } ); + + Field>("promoteValidator", + description: "Promote validator.", + arguments: new QueryArguments( + new QueryArgument> + { + Name = "validator", + Description = "Validator public key to promote." + }, + new QueryArgument> + { + Name = "amount", + Description = "Amount of NCG to stake." + } + ), + resolve: context => + { + try + { + Console.WriteLine("[PromoteValidator] PromoteValidator called"); + BlockChain? blockChain = service.BlockChain; + if (blockChain is null) + { + throw new InvalidOperationException($"{nameof(blockChain)} is null."); + } + + Console.WriteLine("[PromoteValidator] BlockChain is not null"); + string validatorString = context.GetArgument("validator"); + PublicKey validator = PublicKey.FromHex(validatorString); + BigInteger amount = context.GetArgument("amount"); + Console.WriteLine($"[PromoteValidator] Validator: {validator}, amount: {amount}"); + var fav = new FungibleAssetValue(Asset.GovernanceToken, amount, 0); + + var action = new PromoteValidator(validator, fav); + Console.WriteLine("[PromoteValidator] PromoteValidator action successfully created"); + + var actions = new[] { action }; + Transaction tx = blockChain.MakeTransaction(service.MinerPrivateKey, actions); + Console.WriteLine("[PromoteValidator] PromoteValidator transaction staged"); + return tx.Id; + } + catch (Exception e) + { + var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}"; + context.Errors.Add(new ExecutionError(msg, e)); + throw; + } + } + ); + + Field>("delegate", + description: "Promote validator.", + arguments: new QueryArguments( + new QueryArgument> + { + Name = "validator", + Description = "Validator address to delegate." + }, + new QueryArgument> + { + Name = "amount", + Description = "Amount of NCG to delegate." + } + ), + resolve: context => + { + try + { + BlockChain? blockChain = service.BlockChain; + if (blockChain is null) + { + throw new InvalidOperationException($"{nameof(blockChain)} is null."); + } + + Address validator = context.GetArgument
("validator"); + BigInteger amount = context.GetArgument("amount"); + var fav = new FungibleAssetValue(Asset.GovernanceToken, amount, 0); + + var action = new Nekoyume.Action.DPoS.Delegate(validator, fav); + + var actions = new[] { action }; + Transaction tx = blockChain.MakeTransaction(service.MinerPrivateKey, actions); + return tx.Id; + } + catch (Exception e) + { + var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}"; + context.Errors.Add(new ExecutionError(msg, e)); + throw; + } + } + ); } } } diff --git a/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs b/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs index 87cb9b00a..2c8b4db0c 100644 --- a/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs +++ b/NineChronicles.Headless/GraphTypes/AppProtocolVersionType.cs @@ -22,7 +22,7 @@ public AppProtocolVersionType() resolve: context => context.Source.Signature.ToBuilder().ToArray()); Field( name: "extra", - resolve: context => _codec.Encode(context.Source.Extra)); + resolve: context => _codec.Encode(context.Source.Extra!)); } } } diff --git a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs index afd2f7471..1a3f3ccbd 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs @@ -134,20 +134,20 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi IEnumerable blockTxs = digest.TxIds .Select(bytes => new TxId(bytes)) - .Select(store.GetTransaction); + .Select(store.GetTransaction)!; 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)) - .Where(pair => !pair.Item1.Fail && + .Where(pair => !pair.Item1!.Fail && (!recipient.HasValue || pair.Item2.Recipient == recipient) && pair.Item2.Amount.Currency.Ticker == "NCG"); var histories = filtered.Select(pair => new TransferNCGHistory( - pair.Item1.BlockHash, + pair.Item1!.BlockHash, pair.Item1.TxId, pair.Item2.Sender, pair.Item2.Recipient, diff --git a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs index 0b5cb3694..f5355c27b 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs @@ -298,7 +298,7 @@ private IObservable SubscribeTx(IResolveFieldContext context) var txExecution = store.GetTxExecution(blockHash, transaction.Id); var txExecutedBlock = chain[blockHash]; return new TxResult( - txExecution.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS, + txExecution!.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS, txExecutedBlock.Index, txExecutedBlock.Hash.ToString(), txExecution.InputState, diff --git a/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs b/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs index 5121e961b..c68f424a5 100644 --- a/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs +++ b/NineChronicles.Headless/Properties/NineChroniclesNodeServiceProperties.cs @@ -112,7 +112,7 @@ public static LibplanetNodeServiceProperties Host = swarmHost, Port = swarmPort, SwarmPrivateKey = swarmPrivateKey, - AppProtocolVersion = AppProtocolVersion.FromToken(appProtocolVersionToken), + AppProtocolVersion = AppProtocolVersion.FromToken(appProtocolVersionToken!), TrustedAppProtocolVersionSigners = trustedAppProtocolVersionSigners ?.Select(s => new PublicKey(ByteUtil.ParseHex(s))) ?.ToHashSet(),