コード例 #1
0
        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
                );
        }
コード例 #2
0
 private byte[] TxExecutionKey(TxExecution txExecution) =>
 TxExecutionKey(txExecution.BlockHash, txExecution.TxId);
コード例 #3
0
        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));
                }
            }