public void Append(bool getTxExecutionViaStore) { Func <BlockHash, TxId, TxExecution> getTxExecution = getTxExecutionViaStore ? (Func <BlockHash, TxId, TxExecution>)_blockChain.Store.GetTxExecution : _blockChain.GetTxExecution; PrivateKey[] keys = Enumerable.Repeat(0, 5).Select(_ => new PrivateKey()).ToArray(); (Address[] addresses, Transaction <DumbAction>[] txs) = MakeFixturesForAppendTests(keys: keys); var genesis = _blockChain.Genesis; Assert.Equal(1, _blockChain.Count); Assert.Empty(_renderer.ActionRecords); Assert.Empty(_renderer.BlockRecords); var block1 = TestUtils.MineNext( genesis, _blockChain.Policy.GetHashAlgorithm, miner: keys[4].PublicKey, difficulty: _blockChain.Policy.GetNextBlockDifficulty(_blockChain), blockInterval: TimeSpan.FromSeconds(10) ).Evaluate(keys[4], _blockChain); _blockChain.Append(block1); Block <DumbAction> block2 = TestUtils.MineNext( block1, _blockChain.Policy.GetHashAlgorithm, txs, miner: keys[4].PublicKey, difficulty: _blockChain.Policy.GetNextBlockDifficulty(_blockChain), blockInterval: TimeSpan.FromSeconds(10) ).Evaluate(keys[4], _blockChain); foreach (Transaction <DumbAction> tx in txs) { Assert.Null(getTxExecution(genesis.Hash, tx.Id)); Assert.Null(getTxExecution(block1.Hash, tx.Id)); Assert.Null(getTxExecution(block2.Hash, tx.Id)); } foreach (var tx in txs) { Assert.Null(_fx.Store.GetFirstTxIdBlockHashIndex(tx.Id)); } _blockChain.Append(block2); foreach (var tx in txs) { Assert.True(_fx.Store.GetFirstTxIdBlockHashIndex(tx.Id)?.Equals(block2.Hash)); } Assert.True(_blockChain.ContainsBlock(block2.Hash)); RenderRecord <DumbAction> .ActionSuccess[] renders = _renderer.ActionSuccessRecords .Where(r => r.Action is DumbAction) .ToArray(); DumbAction[] actions = renders.Select(r => (DumbAction)r.Action).ToArray(); Assert.Equal(4, renders.Length); Assert.True(renders.All(r => r.Render)); Assert.Equal("foo", actions[0].Item); Assert.Equal(2, renders[0].Context.BlockIndex); Assert.Equal( new IValue[] { null, null, null, null, (Integer)1 }, addresses.Select(renders[0].Context.PreviousStates.GetState) ); Assert.Equal( new IValue[] { (Text)"foo", null, null, null, (Integer)1 }, addresses.Select(renders[0].NextStates.GetState) ); Assert.Equal("bar", actions[1].Item); Assert.Equal(2, renders[1].Context.BlockIndex); Assert.Equal( addresses.Select(renders[0].NextStates.GetState), addresses.Select(renders[1].Context.PreviousStates.GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", null, null, (Integer)1 }, addresses.Select(renders[1].NextStates.GetState) ); Assert.Equal("baz", actions[2].Item); Assert.Equal(2, renders[2].Context.BlockIndex); Assert.Equal( addresses.Select(renders[1].NextStates.GetState), addresses.Select(renders[2].Context.PreviousStates.GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", (Text)"baz", null, (Integer)1 }, addresses.Select(renders[2].NextStates.GetState) ); Assert.Equal("qux", actions[3].Item); Assert.Equal(2, renders[3].Context.BlockIndex); Assert.Equal( addresses.Select(renders[2].NextStates.GetState), addresses.Select(renders[3].Context.PreviousStates.GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", (Text)"baz", (Text)"qux", (Integer)1, }, addresses.Select(renders[3].NextStates.GetState) ); Address minerAddress = addresses[4]; RenderRecord <DumbAction> .ActionSuccess[] blockRenders = _renderer.ActionSuccessRecords .Where(r => r.Action is MinerReward) .ToArray(); Assert.Equal((Integer)2, (Integer)_blockChain.GetState(minerAddress)); Assert.Equal(2, blockRenders.Length); Assert.True(blockRenders.All(r => r.Render)); Assert.Equal(1, blockRenders[0].Context.BlockIndex); Assert.Equal(2, blockRenders[1].Context.BlockIndex); Assert.Equal( (Integer)1, (Integer)blockRenders[0].NextStates.GetState(minerAddress) ); Assert.Equal( (Integer)1, (Integer)blockRenders[1].Context.PreviousStates.GetState(minerAddress) ); Assert.Equal( (Integer)2, (Integer)blockRenders[1].NextStates.GetState(minerAddress) ); foreach (Transaction <DumbAction> tx in txs) { Assert.Null(getTxExecution(genesis.Hash, tx.Id)); Assert.Null(getTxExecution(block1.Hash, tx.Id)); TxExecution e = getTxExecution(block2.Hash, tx.Id); Assert.IsType <TxSuccess>(e); var s = (TxSuccess)e; Assert.Equal(block2.Hash, s.BlockHash); Assert.Equal(tx.Id, s.TxId); Assert.Equal(tx.UpdatedAddresses, s.UpdatedAddresses); Assert.Equal( tx.UpdatedAddresses.ToImmutableDictionary( address => address, address => _blockChain.GetState(address) ), s.UpdatedStates ); Assert.Empty(s.FungibleAssetsDelta); Assert.Empty(s.UpdatedFungibleAssets); } var pk = new PrivateKey(); Transaction <DumbAction> tx1Transfer = _fx.MakeTransaction( new[] { new DumbAction(pk.ToAddress(), "foo", pk.ToAddress(), addresses[1], 10), new DumbAction(addresses[0], "bar", pk.ToAddress(), addresses[2], 20), }, nonce: 0, privateKey: pk ); Transaction <DumbAction> tx2Error = _fx.MakeTransaction( new[] { // As it tries to transfer a negative value, it throws // ArgumentOutOfRangeException: new DumbAction(pk.ToAddress(), "foo", addresses[0], addresses[1], -5), }, nonce: 1, privateKey: pk ); Transaction <DumbAction> tx3Transfer = _fx.MakeTransaction( new[] { new DumbAction(pk.ToAddress(), "foo", pk.ToAddress(), addresses[1], 5), }, nonce: 2, privateKey: pk ); Block <DumbAction> block3 = TestUtils.MineNext( block2, _blockChain.Policy.GetHashAlgorithm, new[] { tx1Transfer, tx2Error, tx3Transfer }, miner: keys[4].PublicKey, difficulty: _blockChain.Policy.GetNextBlockDifficulty(_blockChain) ).Evaluate(keys[4], _blockChain); _blockChain.Append(block3); var txExecution1 = getTxExecution(block3.Hash, tx1Transfer.Id); _logger.Verbose(nameof(txExecution1) + " = {@TxExecution}", txExecution1); Assert.IsType <TxSuccess>(txExecution1); var txSuccess1 = (TxSuccess)txExecution1; Assert.Equal( addresses.Take(3).Append(pk.ToAddress()).ToImmutableHashSet(), txSuccess1.UpdatedAddresses ); Assert.Equal( ImmutableDictionary <Address, IValue> .Empty .Add(pk.ToAddress(), (Text)"foo") .Add(addresses[0], (Text)"foo,bar"), txSuccess1.UpdatedStates ); Assert.Equal( ImmutableDictionary <Address, IImmutableDictionary <Currency, FAV> > .Empty .Add( pk.ToAddress(), ImmutableDictionary <Currency, FAV> .Empty .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * -30) ) .Add( addresses[1], ImmutableDictionary <Currency, FAV> .Empty .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * 10) ) .Add( addresses[2], ImmutableDictionary <Currency, FAV> .Empty .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * 20) ), txSuccess1.UpdatedFungibleAssets ); Assert.Equal( txSuccess1.FungibleAssetsDelta, txSuccess1.UpdatedFungibleAssets ); var txExecution2 = getTxExecution(block3.Hash, tx2Error.Id); _logger.Verbose(nameof(txExecution2) + " = {@TxExecution}", txExecution2); Assert.IsType <TxFailure>(txExecution2); var txFailure = (TxFailure)txExecution2; Assert.Equal(block3.Hash, txFailure.BlockHash); Assert.Equal(tx2Error.Id, txFailure.TxId); Assert.Equal( $"{nameof(System)}.{nameof(ArgumentOutOfRangeException)}", txFailure.ExceptionName ); Assert.Equal( Dictionary.Empty.Add("parameterName", "value"), txFailure.ExceptionMetadata ); var txExecution3 = getTxExecution(block3.Hash, tx3Transfer.Id); _logger.Verbose(nameof(txExecution3) + " = {@TxExecution}", txExecution3); Assert.IsType <TxSuccess>(txExecution3); var txSuccess3 = (TxSuccess)txExecution3; Assert.Equal( ImmutableDictionary <Address, IImmutableDictionary <Currency, FAV> > .Empty .Add( pk.ToAddress(), ImmutableDictionary <Currency, FAV> .Empty .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * -5) ) .Add( addresses[1], ImmutableDictionary <Currency, FAV> .Empty .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * 5) ), txSuccess3.FungibleAssetsDelta ); Assert.Equal( ImmutableDictionary <Address, IImmutableDictionary <Currency, FAV> > .Empty .Add( pk.ToAddress(), ImmutableDictionary <Currency, FAV> .Empty .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * -35) ) .Add( addresses[1], ImmutableDictionary <Currency, FAV> .Empty .Add(DumbAction.DumbCurrency, DumbAction.DumbCurrency * 15) ), txSuccess3.UpdatedFungibleAssets ); }
private byte[] TxExecutionKey(TxExecution txExecution) => TxExecutionKey(txExecution.BlockHash, txExecution.TxId);
public TransactionHeadlessQuery(StandaloneContext standaloneContext) { Field <NonNullGraphType <LongGraphType> >( name: "nextTxNonce", arguments: new QueryArguments( new QueryArgument <NonNullGraphType <AddressType> > { Name = "address", Description = "Target address to query" } ), resolve: context => { if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain)) { throw new ExecutionError( $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!"); } Address address = context.GetArgument <Address>("address"); return(blockChain.GetNextTxNonce(address)); } ); Field <TransactionType <NCAction> >( name: "getTx", arguments: new QueryArguments( new QueryArgument <NonNullGraphType <TxIdType> > { Name = "txId", Description = "transaction id." } ), resolve: context => { if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain)) { throw new ExecutionError( $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!"); } var txId = context.GetArgument <TxId>("txId"); return(blockChain.GetTransaction(txId)); } ); Field <NonNullGraphType <StringGraphType> >( name: "createUnsignedTx", arguments: new QueryArguments( new QueryArgument <NonNullGraphType <StringGraphType> > { Name = "publicKey", Description = "The base64-encoded public key for Transaction.", }, new QueryArgument <NonNullGraphType <StringGraphType> > { Name = "plainValue", Description = "The base64-encoded plain value of action for Transaction.", } ), resolve: context => { if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain)) { throw new ExecutionError( $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!"); } string plainValueString = context.GetArgument <string>("plainValue"); var plainValue = new Bencodex.Codec().Decode(System.Convert.FromBase64String(plainValueString)); #pragma warning disable 612 var action = new NCAction(); #pragma warning restore 612 action.LoadPlainValue(plainValue); var publicKey = new PublicKey(Convert.FromBase64String(context.GetArgument <string>("publicKey"))); Address signer = publicKey.ToAddress(); long nonce = blockChain.GetNextTxNonce(signer); Transaction <NCAction> unsignedTransaction = Transaction <NCAction> .CreateUnsigned(nonce, publicKey, blockChain.Genesis.Hash, new[] { action }); return(Convert.ToBase64String(unsignedTransaction.Serialize(false))); }); Field <NonNullGraphType <StringGraphType> >( name: "attachSignature", arguments: new QueryArguments( new QueryArgument <NonNullGraphType <StringGraphType> > { Name = "unsignedTransaction", Description = "The base64-encoded unsigned transaction to attach the given signature." }, new QueryArgument <NonNullGraphType <StringGraphType> > { Name = "signature", Description = "The base64-encoded signature of the given unsigned transaction." } ), resolve: context => { byte[] signature = Convert.FromBase64String(context.GetArgument <string>("signature")); Transaction <NCAction> unsignedTransaction = Transaction <NCAction> .Deserialize( Convert.FromBase64String(context.GetArgument <string>("unsignedTransaction")), false); Transaction <NCAction> signedTransaction = new Transaction <NCAction>( unsignedTransaction.Nonce, unsignedTransaction.Signer, unsignedTransaction.PublicKey, unsignedTransaction.GenesisHash, unsignedTransaction.UpdatedAddresses, unsignedTransaction.Timestamp, unsignedTransaction.Actions, signature); return(Convert.ToBase64String(signedTransaction.Serialize(true))); }); Field <NonNullGraphType <TxResultType> >( name: "transactionResult", arguments: new QueryArguments( new QueryArgument <NonNullGraphType <TxIdType> > { Name = "txId", Description = "transaction id." } ), resolve: context => { if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain)) { throw new ExecutionError( $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!"); } if (!(standaloneContext.Store is IStore store)) { throw new ExecutionError( $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.Store)} was not set yet!"); } TxId txId = context.GetArgument <TxId>("txId"); if (!(store.GetFirstTxIdBlockHashIndex(txId) is { } txExecutedBlockHash)) { return(blockChain.GetStagedTransactionIds().Contains(txId) ? new TxResult(TxStatus.STAGING, null, null) : new TxResult(TxStatus.INVALID, null, null)); } try { TxExecution execution = blockChain.GetTxExecution(txExecutedBlockHash, txId); Block <PolymorphicAction <ActionBase> > txExecutedBlock = blockChain[txExecutedBlockHash]; return(execution switch { TxSuccess txSuccess => new TxResult(TxStatus.SUCCESS, txExecutedBlock.Index, txExecutedBlock.Hash.ToString()), TxFailure txFailure => new TxResult(TxStatus.FAILURE, txExecutedBlock.Index, txExecutedBlock.Hash.ToString()), _ => throw new NotImplementedException( $"{nameof(execution)} is not expected concrete class.") }); } catch (Exception) { return(new TxResult(TxStatus.INVALID, null, null)); } }