示例#1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="coinbase"></param>
        /// <param name="solution"></param>
        /// <param name="runningDistribution"></param>
        /// <returns></returns>
        public VerifyResult VerifyCoinbaseTransaction(Vout coinbase, ulong solution, decimal runningDistribution)
        {
            Guard.Argument(coinbase, nameof(coinbase)).NotNull();
            Guard.Argument(solution, nameof(solution)).NotZero().NotNegative();
            Guard.Argument(runningDistribution, nameof(runningDistribution)).NotZero().NotNegative();
            if (coinbase.Validate().Any())
            {
                return(VerifyResult.UnableToVerify);
            }
            if (coinbase.T != CoinType.Coinbase)
            {
                return(VerifyResult.UnableToVerify);
            }
            var verifyNetworkShare = VerifyNetworkShare(solution, coinbase.A.DivWithNanoTan(), runningDistribution);

            if (verifyNetworkShare == VerifyResult.UnableToVerify)
            {
                return(verifyNetworkShare);
            }
            using var pedersen = new Pedersen();
            var commitSum = pedersen.CommitSum(new List <byte[]> {
                coinbase.C
            }, new List <byte[]> {
                coinbase.C
            });

            return(commitSum == null ? VerifyResult.Succeed : VerifyResult.UnableToVerify);
        }
示例#2
0
 private void clear_butt_Click(object sender, EventArgs e)
 {
     R1.Clear();
     Vin.Clear();
     Vout.Clear();
     MaxPower.Clear();
     R1Val.Clear();
     R2Val.Clear();
 }
        public async Task GetTaskAsync_Verbose_ReturnsTransactionVerboseModelAsync()
        {
            this.chainState.Setup(c => c.ConsensusTip)
            .Returns(this.chain.Tip);
            ChainedHeader block       = this.chain.GetBlock(1);
            Transaction   transaction = this.CreateTransaction();
            var           txId        = new uint256(12142124);

            this.pooledTransaction.Setup(p => p.GetTransaction(txId))
            .ReturnsAsync(transaction);
            var blockStore = new Mock <IBlockStore>();

            blockStore.Setup(b => b.GetTrxBlockIdAsync(txId))
            .ReturnsAsync(block.HashBlock);
            this.fullNode.Setup(f => f.NodeFeature <IBlockStore>(false))
            .Returns(blockStore.Object);
            this.controller = new NodeController(this.fullNode.Object, this.LoggerFactory.Object,
                                                 this.dateTimeProvider.Object, this.chainState.Object, this.nodeSettings,
                                                 this.connectionManager.Object, this.chain, this.network, this.pooledTransaction.Object,
                                                 this.pooledGetUnspentTransaction.Object, this.getUnspentTransaction.Object, this.networkDifficulty.Object);
            string txid    = txId.ToString();
            bool   verbose = true;

            var json = (JsonResult)await this.controller.GetRawTransactionAsync(txid, verbose).ConfigureAwait(false);

            var resultModel = (TransactionVerboseModel)json.Value;

            Assert.NotNull(resultModel);
            var model = Assert.IsType <TransactionVerboseModel>(resultModel);

            Assert.Equal(transaction.GetHash().ToString(), model.TxId);
            Assert.Equal(transaction.GetSerializedSize(), model.Size);
            Assert.Equal(transaction.Version, model.Version);
            Assert.Equal((uint)transaction.LockTime, model.LockTime);
            Assert.Equal(transaction.ToHex(), model.Hex);
            Assert.Equal(block.HashBlock.ToString(), model.BlockHash);
            Assert.Equal(3, model.Confirmations);
            Assert.Equal(Utils.DateTimeToUnixTime(block.Header.BlockTime), model.Time);
            Assert.Equal(Utils.DateTimeToUnixTime(block.Header.BlockTime), model.BlockTime);
            Assert.NotEmpty(model.VIn);
            Vin input         = model.VIn[0];
            var expectedInput = new Vin(transaction.Inputs[0].PrevOut, transaction.Inputs[0].Sequence, transaction.Inputs[0].ScriptSig);

            Assert.Equal(expectedInput.Coinbase, input.Coinbase);
            Assert.Equal(expectedInput.ScriptSig, input.ScriptSig);
            Assert.Equal(expectedInput.Sequence, input.Sequence);
            Assert.Equal(expectedInput.TxId, input.TxId);
            Assert.Equal(expectedInput.VOut, input.VOut);
            Assert.NotEmpty(model.VOut);
            Vout output         = model.VOut[0];
            var  expectedOutput = new Vout(0, transaction.Outputs[0], this.network);

            Assert.Equal(expectedOutput.Value, output.Value);
            Assert.Equal(expectedOutput.N, output.N);
            Assert.Equal(expectedOutput.ScriptPubKey.Hex, output.ScriptPubKey.Hex);
        }
示例#4
0
        public async Task GetTaskAsync_Verbose_ReturnsTransactionVerboseModelAsync()
        {
            // Add the 'txindex' setting, otherwise the transactions won't be found.
            this.nodeSettings.ConfigReader.MergeInto(new TextFileConfiguration("-txindex=1"));
            this.chainState.Setup(c => c.ConsensusTip)
            .Returns(this.chain.Tip);
            ChainedHeader block = this.chain.GetHeader(1);

            Transaction transaction = this.CreateTransaction();
            var         txId        = new uint256(12142124);

            this.pooledTransaction.Setup(p => p.GetTransaction(txId))
            .ReturnsAsync(transaction);

            this.blockStore.Setup(b => b.GetBlockIdByTransactionId(txId))
            .Returns(block.HashBlock);

            this.controller = new FullNodeController(this.LoggerFactory.Object, this.pooledTransaction.Object, this.pooledGetUnspentTransaction.Object, this.getUnspentTransaction.Object, this.networkDifficulty.Object,
                                                     this.fullNode.Object, this.nodeSettings, this.network, this.chain, this.chainState.Object, this.connectionManager.Object, this.consensusManager.Object, this.blockStore.Object);

            TransactionModel result = await this.controller.GetRawTransactionAsync(txId.ToString(), true).ConfigureAwait(false);

            Assert.NotNull(result);
            var model = Assert.IsType <TransactionVerboseModel>(result);

            Assert.Equal(transaction.GetHash().ToString(), model.TxId);
            Assert.Equal(transaction.GetSerializedSize(), model.Size);
            Assert.Equal(transaction.Version, model.Version);
            Assert.Equal((uint)transaction.LockTime, model.LockTime);
            Assert.Equal(transaction.ToHex(), model.Hex);

            Assert.Equal(block.HashBlock.ToString(), model.BlockHash);
            Assert.Equal(3, model.Confirmations);
            Assert.Equal(Utils.DateTimeToUnixTime(block.Header.BlockTime), model.Time);
            Assert.Equal(Utils.DateTimeToUnixTime(block.Header.BlockTime), model.BlockTime);

            Assert.NotEmpty(model.VIn);
            Vin input         = model.VIn[0];
            var expectedInput = new Vin(transaction.Inputs[0].PrevOut, transaction.Inputs[0].Sequence, transaction.Inputs[0].ScriptSig);

            Assert.Equal(expectedInput.Coinbase, input.Coinbase);
            Assert.Equal(expectedInput.ScriptSig, input.ScriptSig);
            Assert.Equal(expectedInput.Sequence, input.Sequence);
            Assert.Equal(expectedInput.TxId, input.TxId);
            Assert.Equal(expectedInput.VOut, input.VOut);

            Assert.NotEmpty(model.VOut);
            Vout output         = model.VOut[0];
            var  expectedOutput = new Vout(0, transaction.Outputs[0], this.network);

            Assert.Equal(expectedOutput.Value, output.Value);
            Assert.Equal(expectedOutput.N, output.N);
            Assert.Equal(expectedOutput.ScriptPubKey.Hex, output.ScriptPubKey.Hex);
        }
示例#5
0
 public override int GetHashCode()
 {
     unchecked
     {
         var hashCode = (TransactionId != null ? TransactionId.GetHashCode() : 0);
         hashCode = (hashCode * 397) ^ Sequence.GetHashCode();
         hashCode = (hashCode * 397) ^ Vout.GetHashCode();
         hashCode = (hashCode * 397) ^ (ScriptSig != null ? ScriptSig.GetHashCode() : 0);
         hashCode = (hashCode * 397) ^ (Addresses != null ? Addresses.GetHashCode() : 0);
         hashCode = (hashCode * 397) ^ (Value != null ? Value.GetHashCode() : 0);
         return(hashCode);
     }
 }
        public async Task SendingATransactionWithAnOpReturn()
        {
            int sendingAccountBalanceOnStart   = 98000596;
            int receivingAccountBalanceOnStart = 0;

            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                // Arrange.
                // Create a sending and a receiving node.
                CoreNode sendingNode   = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Miner).Start();
                CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Listener).Start();
                TestHelper.ConnectAndSync(sendingNode, receivingNode);

                // Check balances.
                WalletBalanceModel sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api"
                                                         .AppendPathSegment("wallet/balance")
                                                         .SetQueryParams(new { walletName = "mywallet" })
                                                         .GetJsonAsync <WalletBalanceModel>();

                AccountBalanceModel sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single();
                (sendingAccountBalance.AmountConfirmed + sendingAccountBalance.AmountUnconfirmed).Should().Be(new Money(sendingAccountBalanceOnStart, MoneyUnit.BTC));

                WalletBalanceModel receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api"
                                                           .AppendPathSegment("wallet/balance")
                                                           .SetQueryParams(new { walletName = "mywallet" })
                                                           .GetJsonAsync <WalletBalanceModel>();

                AccountBalanceModel receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single();
                (receivingAccountBalance.AmountConfirmed + receivingAccountBalance.AmountUnconfirmed).Should().Be(new Money(receivingAccountBalanceOnStart));

                // Act.
                // Get an address to send to.
                IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api"
                                                       .AppendPathSegment("wallet/unusedAddresses")
                                                       .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 1 })
                                                       .GetJsonAsync <IEnumerable <string> >();

                // Build and send the transaction with an Op_Return.
                WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api"
                                                                    .AppendPathSegment("wallet/build-transaction")
                                                                    .PostJsonAsync(new BuildTransactionRequest
                {
                    WalletName       = "mywallet",
                    AccountName      = "account 0",
                    FeeType          = "low",
                    Password         = "******",
                    ShuffleOutputs   = true,
                    AllowUnconfirmed = true,
                    Recipients       = unusedaddresses.Select(address => new RecipientModel
                    {
                        DestinationAddress = address,
                        Amount             = "1"
                    }).ToList(),
                    OpReturnData   = "some data to send",
                    OpReturnAmount = "1"
                })
                                                                    .ReceiveJson <WalletBuildTransactionModel>();

                await $"http://localhost:{sendingNode.ApiPort}/api"
                .AppendPathSegment("wallet/send-transaction")
                .PostJsonAsync(new SendTransactionRequest
                {
                    Hex = buildTransactionModel.Hex
                })
                .ReceiveJson <WalletSendTransactionModel>();

                // Assert.
                // Mine and sync so that we make sure the receiving node is up to date.
                TestHelper.MineBlocks(sendingNode, 1);
                TestHelper.WaitForNodeToSync(sendingNode, receivingNode);

                // The receiving node should have coins.
                receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api"
                                        .AppendPathSegment("wallet/balance")
                                        .SetQueryParams(new { walletName = "mywallet" })
                                        .GetJsonAsync <WalletBalanceModel>();

                receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single();
                (receivingAccountBalance.AmountConfirmed).Should().Be(new Money(receivingAccountBalanceOnStart + 1, MoneyUnit.BTC));

                // The sending node should have fewer coins.
                sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api"
                                      .AppendPathSegment("wallet/balance")
                                      .SetQueryParams(new { walletName = "mywallet" })
                                      .GetJsonAsync <WalletBalanceModel>();

                sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single();
                (sendingAccountBalance.AmountConfirmed).Should().Be(new Money(sendingAccountBalanceOnStart + 4 - 2, MoneyUnit.BTC));

                // Check the transaction.
                string lastBlockHash = await $"http://localhost:{receivingNode.ApiPort}/api"
                                       .AppendPathSegment("consensus/getbestblockhash")
                                       .GetJsonAsync <string>();

                BlockTransactionDetailsModel block = await $"http://localhost:{receivingNode.ApiPort}/api"
                                                     .AppendPathSegment("blockstore/block")
                                                     .SetQueryParams(new { hash = lastBlockHash, showTransactionDetails = true, outputJson = true })
                                                     .GetJsonAsync <BlockTransactionDetailsModel>();

                TransactionVerboseModel trx = block.Transactions.SingleOrDefault(t => t.TxId == buildTransactionModel.TransactionId.ToString());
                trx.Should().NotBeNull();

                Vout opReturnOutputFromBlock = trx.VOut.Single(t => t.ScriptPubKey.Type == "nulldata");
                opReturnOutputFromBlock.Value.Should().Be(1);
                var      script = opReturnOutputFromBlock.ScriptPubKey.Asm;
                string[] ops    = script.Split(" ");
                ops[0].Should().Be("OP_RETURN");
                Encoders.Hex.DecodeData(ops[1]).Should().BeEquivalentTo(System.Text.Encoding.UTF8.GetBytes("some data to send"));
            }
        }
示例#7
0
        private async Task ProcessBlockForSwapVoteTransactionsAsync(BlockTransactionDetailsModel block, int blockHeight)
        {
            // Inspect each transaction
            foreach (TransactionVerboseModel transaction in block.Transactions)
            {
                // Find the first the OP_RETURN output.
                Vout opReturnOutput = transaction.VOut.FirstOrDefault(v => v.ScriptPubKey.Type == "nulldata");
                if (opReturnOutput == null)
                {
                    continue;
                }

                IList <Op> ops           = new NBitcoin.Script(opReturnOutput.ScriptPubKey.Asm).ToOps();
                var        potentialVote = Encoding.ASCII.GetString(ops.Last().PushData);
                try
                {
                    var isVote = potentialVote.Substring(0, 1);
                    if (isVote != "V")
                    {
                        continue;
                    }

                    var isVoteValue = potentialVote.Substring(1, 1);
                    if (isVoteValue == "1" || isVoteValue == "0")
                    {
                        // Verify the sender address is a valid Strat address
                        var potentialStratAddress       = potentialVote.Substring(2);
                        ValidatedAddress validateResult = await $"http://localhost:{this.StratisNetworkApiPort}/api"
                                                          .AppendPathSegment("node/validateaddress")
                                                          .SetQueryParams(new { address = potentialStratAddress })
                                                          .GetJsonAsync <ValidatedAddress>();

                        if (!validateResult.IsValid)
                        {
                            Console.WriteLine($"Invalid STRAT address: '{potentialStratAddress}'");
                            continue;
                        }

                        AddressBalancesResult balance = await $"http://localhost:{this.StratisNetworkApiPort}/api"
                                                        .AppendPathSegment($"blockstore/{BlockStoreRouteEndPoint.GetAddressesBalances}")
                                                        .SetQueryParams(new { addresses = potentialStratAddress, minConfirmations = 0 })
                                                        .GetJsonAsync <AddressBalancesResult>();

                        if (isVoteValue == "0")
                        {
                            this.castVotes.Add(new CastVote()
                            {
                                Address = potentialStratAddress, Balance = balance.Balances[0].Balance, InFavour = false, BlockHeight = blockHeight
                            });
                            Console.WriteLine($"'No' vote found at height {blockHeight}.");
                        }

                        if (isVoteValue == "1")
                        {
                            this.castVotes.Add(new CastVote()
                            {
                                Address = potentialStratAddress, Balance = balance.Balances[0].Balance, InFavour = true, BlockHeight = blockHeight
                            });
                            Console.WriteLine($"'Yes' vote found at height {blockHeight}.");
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
        }
示例#8
0
        private async Task ProcessBlockForCollateralVoteTransactionsAsync(BlockTransactionDetailsModel block, int blockHeight)
        {
            // Inspect each transaction
            foreach (TransactionVerboseModel transaction in block.Transactions)
            {
                // Find the first the OP_RETURN output.
                Vout opReturnOutput = transaction.VOut.FirstOrDefault(v => v.ScriptPubKey.Type == "nulldata");
                if (opReturnOutput == null)
                {
                    continue;
                }

                // Before checking if it's a vote, check if it's a swap transaction as we now know it has an OP_RETURN.
                // Ignore any transactions that have inconsequential OP_RETURN values.
                if (opReturnOutput.Value >= 1.0m)
                {
                    // For the purposes of speeding up this search, it doesn't matter per se whether the burn transaction has a valid destination address in it.

                    TransactionModel tx = this.blockExplorerClient.GetTransaction(transaction.TxId);

                    // Check the inputs of the transaction to see if it was funded by one of the vote addresses.
                    string address = null;
                    foreach (In input in tx._in)
                    {
                        if (input.hash == null)
                        {
                            continue;
                        }

                        // The block explorer calls this 'hash' but it is in fact the address that funded the input.
                        // It is possible that several voting addresses consolidated together in one swap transaction, so just take the first one.
                        if (this.collateralVotes.ContainsKey(input.hash))
                        {
                            if (address == null)
                            {
                                // We will assign the entire burn amount to the first found voting address.
                                address = input.hash;
                            }
                            else
                            {
                                // However, we have to recompute the balance of all the vote addresses present in the inputs that we are not going to assign the burn to.
                                // This is because they will not necessarily be revisited later if there are no further transactions affecting them.
                                // We presume that since they are participating in a burn subsequent to their initial vote, their balance will drop.
                                if (!address.Equals(input.hash))
                                {
                                    AddressBalancesResult balance = await $"http://localhost:{this.StratisNetworkApiPort}/api"
                                                                    .AppendPathSegment($"blockstore/{BlockStoreRouteEndPoint.GetAddressesBalances}")
                                                                    .SetQueryParams(new { addresses = input.hash, minConfirmations = 0 })
                                                                    .GetJsonAsync <AddressBalancesResult>();

                                    Console.WriteLine($"Reset balance for '{input.hash}' to {balance.Balances[0].Balance} due to burn transaction {transaction.TxId} at height {blockHeight}");

                                    this.collateralVotes[input.hash].Balance = balance.Balances[0].Balance;
                                }
                            }
                        }
                    }

                    if (address != null)
                    {
                        this.collateralVotes[address].BlockHeight = blockHeight;
                        this.collateralVotes[address].Balance     = Money.Coins(opReturnOutput.Value);

                        Console.WriteLine($"Detected that address '{address}' burnt {opReturnOutput.Value} via transaction {transaction.TxId} at height {blockHeight}");

                        // We can now skip checking if this output was a vote.
                        continue;
                    }
                }

                IList <Op> ops           = new NBitcoin.Script(opReturnOutput.ScriptPubKey.Asm).ToOps();
                var        potentialVote = Encoding.ASCII.GetString(ops.Last().PushData);
                try
                {
                    var isVote = potentialVote.Substring(0, 1);
                    if (isVote != "V")
                    {
                        continue;
                    }

                    var isCollateralVote = potentialVote.Substring(1, 1);
                    if (isCollateralVote != "C")
                    {
                        continue;
                    }

                    var collateralVote = potentialVote.Substring(2, 1);
                    if (!new[] { "A", "B", "C", "D", "E" }.Contains(collateralVote))
                    {
                        Console.WriteLine($"Invalid vote found '{collateralVote}'; height {blockHeight}.");
                        continue;
                    }

                    // Verify the sender address is a valid Strat address
                    var potentialStratAddress       = potentialVote.Substring(3);
                    ValidatedAddress validateResult = await $"http://localhost:{this.StratisNetworkApiPort}/api"
                                                      .AppendPathSegment("node/validateaddress")
                                                      .SetQueryParams(new { address = potentialStratAddress })
                                                      .GetJsonAsync <ValidatedAddress>();

                    if (!validateResult.IsValid)
                    {
                        Console.WriteLine($"Invalid STRAT address: '{potentialStratAddress}'");
                        continue;
                    }

                    AddressBalancesResult balance = await $"http://localhost:{this.StratisNetworkApiPort}/api"
                                                    .AppendPathSegment($"blockstore/{BlockStoreRouteEndPoint.GetAddressesBalances}")
                                                    .SetQueryParams(new { addresses = potentialStratAddress, minConfirmations = 0 })
                                                    .GetJsonAsync <AddressBalancesResult>();

                    Money determinedBalance = balance.Balances[0].Balance;

                    if (!this.collateralVotes.ContainsKey(potentialStratAddress))
                    {
                        Console.WriteLine($"Collateral vote found for {potentialStratAddress} at height {blockHeight}; Selection '{collateralVote}'; Balance {determinedBalance}");

                        this.collateralVotes.Add(potentialStratAddress, new CollateralVote()
                        {
                            Address = potentialStratAddress, Balance = determinedBalance, Selection = collateralVote, BlockHeight = blockHeight
                        });
                    }
                    else
                    {
                        Console.WriteLine($"Updating existing vote for {potentialStratAddress} at height {blockHeight}; Selection '{collateralVote}'; Balance {determinedBalance}");

                        this.collateralVotes[potentialStratAddress] = new CollateralVote()
                        {
                            Address = potentialStratAddress, Balance = determinedBalance, Selection = collateralVote, BlockHeight = blockHeight
                        };
                    }
                }
                catch (Exception)
                {
                }
            }
        }
示例#9
0
        public Transaction(JObject json, Block block, List <Vin> _vin) //details
        {
            txid   = json["result"]["txid"].ToString();
            hash   = json["result"]["hash"].ToString();
            size   = Int32.Parse(json["result"]["size"].ToString());
            weight = Int32.Parse(json["result"]["weight"].ToString());


            if (block != null)
            {
                time            = block.time;
                status          = "Confirmed";
                includedInBlock = "Yes";
                blockh          = block.height;
                conf            = block.confirmations;
            }
            else
            {
                status          = "Unconfirmed";
                includedInBlock = "No";
                conf            = 0;
            }
            int     br            = json["result"]["vout"].Count();
            decimal ukupnoIzlaztr = 0;

            vout = new List <Vout>();

            foreach (int i in Enumerable.Range(0, br))
            {
                var     s     = json["result"]["vout"][i]["value"].ToString();
                decimal value = Decimal.Round(decimal.Parse(json["result"]["vout"][i]["value"].ToString(), System.Globalization.NumberStyles.Any), 8, MidpointRounding.AwayFromZero);



                if (value > 0)
                {
                    ukupnoIzlaztr += value;

                    int           bradresa = json["result"]["vout"][i]["scriptPubKey"]["addresses"].Count();
                    List <string> a        = new List <string>();
                    foreach (int j in Enumerable.Range(0, bradresa))
                    {
                        string address = json["result"]["vout"][i]["scriptPubKey"]["addresses"][j].ToString();
                        a.Add(address);
                    }

                    Vout v = new Vout();
                    v.value = value;
                    ScriptPubKey spk = new ScriptPubKey();
                    spk.addresses = new List <string>();
                    foreach (string add in a)
                    {
                        spk.addresses.Add(add);
                    }
                    v.scriptPubKey = spk;
                    v.n            = Int32.Parse(json["result"]["vout"][i]["n"].ToString());

                    vout.Add(v);
                }
            }
            ukupnoIzlaz = ukupnoIzlaztr;


            //vin
            decimal ukupnoUlaztr = 0;

            vin = _vin;
            int br2 = _vin.Count();

            foreach (int i in Enumerable.Range(0, br2))
            {
                ukupnoUlaztr += _vin[i].value;
            }

            ukupnoUlaz = ukupnoUlaztr;

            if (_vin[0].coinbase == true)
            {
                fee = 0;
            }
            else
            {
                fee = ukupnoUlaz - ukupnoIzlaz;
            }
        }
示例#10
0
        public Transaction(JObject json, List <Vin> _vin)//summary
        {
            txid = json["result"]["txid"].ToString();
            hash = json["result"]["hash"].ToString();

            int     br            = json["result"]["vout"].Count();
            decimal ukupnoIzlaztr = 0;

            vout = new List <Vout>();

            foreach (int i in Enumerable.Range(0, br))
            {
                var     s     = json["result"]["vout"][i]["value"].ToString();
                decimal value = Decimal.Round(decimal.Parse(json["result"]["vout"][i]["value"].ToString(), System.Globalization.NumberStyles.Any), 8, MidpointRounding.AwayFromZero);



                if (value > 0)
                {
                    ukupnoIzlaztr += value;

                    int           bradresa = json["result"]["vout"][i]["scriptPubKey"]["addresses"].Count();
                    List <string> a        = new List <string>();
                    foreach (int j in Enumerable.Range(0, bradresa))
                    {
                        string address = json["result"]["vout"][i]["scriptPubKey"]["addresses"][j].ToString();
                        a.Add(address);
                    }


                    Vout v = new Vout();
                    v.value = value;
                    ScriptPubKey spk = new ScriptPubKey();
                    spk.addresses = new List <string>();
                    foreach (string add in a)
                    {
                        spk.addresses.Add(add);
                    }
                    v.scriptPubKey = spk;

                    vout.Add(v);
                }
            }
            ukupnoIzlaz = ukupnoIzlaztr;


            decimal ukupnoUlaztr = 0;

            vin = _vin;
            int br2 = _vin.Count();

            foreach (int i in Enumerable.Range(0, br2))
            {
                ukupnoUlaztr += _vin[i].value;
            }

            ukupnoUlaz = ukupnoUlaztr;

            if (_vin[0].coinbase == true)
            {
                fee = 0;
            }
            else
            {
                fee = ukupnoUlaz - ukupnoIzlaz;
            }
        }