コード例 #1
0
        public void VerifyThatColdStakeTransactionCanBeFiltered()
        {
            this.Initialize();
            this.CreateMempoolManager();

            this.coldStakingManager.CreateWallet(walletPassword, walletName1, walletPassphrase, new Mnemonic(walletMnemonic1));

            Wallet.Wallet wallet1 = this.coldStakingManager.GetWalletByName(walletName1);

            Transaction prevTran = this.AddSpendableTransactionToWallet(wallet1);

            IActionResult result = this.coldStakingController.SetupColdStaking(new SetupColdStakingRequest
            {
                HotWalletAddress  = hotWalletAddress1,
                ColdWalletAddress = coldWalletAddress2,
                WalletName        = walletName1,
                WalletAccount     = walletAccount,
                WalletPassword    = walletPassword,
                Amount            = "100",
                Fees = "0.01"
            });

            var jsonResult  = Assert.IsType <JsonResult>(result);
            var response    = Assert.IsType <SetupColdStakingResponse>(jsonResult.Value);
            var transaction = Assert.IsType <PosTransaction>(this.Network.CreateTransaction(response.TransactionHex));

            Assert.Equal("OP_DUP OP_HASH160 OP_ROT OP_IF OP_CHECKCOLDSTAKEVERIFY 90c582cb91d6b6d777c31c891d4943fed4edac3a OP_ELSE 92dfb829d31cefe6a0731f3432dea41596a278d9 OP_ENDIF OP_EQUALVERIFY OP_CHECKSIG", transaction.Outputs[1].ScriptPubKey.ToString());

            // Get the ColdStaking script template if available.
            Dictionary <string, ScriptTemplate> templates = this.coldStakingManager.GetValidStakingTemplates();
            ScriptTemplate coldStakingTemplate            = templates["ColdStaking"];

            Assert.True(coldStakingTemplate.CheckScriptPubKey(transaction.Outputs[1].ScriptPubKey));
        }
コード例 #2
0
        public GetTransactionModel GetTransaction(string txid)
        {
            if (!uint256.TryParse(txid, out uint256 trxid))
            {
                throw new ArgumentException(nameof(txid));
            }

            WalletAccountReference accountReference = this.GetWalletAccountReference();

            Types.Wallet wallet  = this.walletManager.GetWalletByName(accountReference.WalletName);
            HdAccount    account = this.walletManager.GetAccounts(accountReference.WalletName).Single(a => a.Name == accountReference.AccountName);

            // Get the transaction from the wallet by looking into received and send transactions.
            List <HdAddress>             addresses            = account.GetCombinedAddresses().ToList();
            List <TransactionOutputData> receivedTransactions = addresses.Where(r => !r.IsChangeAddress()).SelectMany(a => wallet.walletStore.GetForAddress(a.Address).Where(t => t.Id == trxid)).ToList();
            List <TransactionOutputData> sendTransactions     = addresses.SelectMany(a => wallet.walletStore.GetForAddress(a.Address).Where(t => t.SpendingDetails != null && t.SpendingDetails.TransactionId == trxid)).ToList();

            if (!receivedTransactions.Any() && !sendTransactions.Any())
            {
                throw new RPCServerException(RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id.");
            }

            // Get the block hash from the transaction in the wallet.
            TransactionOutputData transactionFromWallet = null;
            uint256 blockHash = null;
            int?    blockHeight, blockIndex;

            if (receivedTransactions.Any())
            {
                blockHeight           = receivedTransactions.First().BlockHeight;
                blockIndex            = receivedTransactions.First().BlockIndex;
                blockHash             = receivedTransactions.First().BlockHash;
                transactionFromWallet = receivedTransactions.First();
            }
            else
            {
                blockHeight = sendTransactions.First().SpendingDetails.BlockHeight;
                blockIndex  = sendTransactions.First().SpendingDetails.BlockIndex;
                blockHash   = blockHeight != null?this.ChainIndexer.GetHeader(blockHeight.Value).HashBlock : null;
            }

            // Get the block containing the transaction (if it has  been confirmed).
            ChainedHeaderBlock chainedHeaderBlock = null;

            if (blockHash != null)
            {
                this.ConsensusManager.GetOrDownloadBlocks(new List <uint256> {
                    blockHash
                }, b => { chainedHeaderBlock = b; });
            }

            Block       block = null;
            Transaction transactionFromStore      = null;

            if (chainedHeaderBlock != null)
            {
                block = chainedHeaderBlock.Block;
                transactionFromStore = block.Transactions.Single(t => t.GetHash() == trxid);
            }

            DateTimeOffset transactionTime;
            bool           isGenerated;
            string         hex;

            if (transactionFromStore != null)
            {
                // TODO: Use block header time only. The transaction times will need to be uniformly set to a fixed value when an anti-malleability softfork activates
                if (transactionFromStore is IPosTransactionWithTime posTrx)
                {
                    transactionTime = Utils.UnixTimeToDateTime(posTrx.Time);
                }
                else
                {
                    transactionTime = Utils.UnixTimeToDateTime(chainedHeaderBlock.ChainedHeader.Header.Time);
                }

                isGenerated = transactionFromStore.IsCoinBase || transactionFromStore.IsCoinStake;
                hex         = transactionFromStore.ToHex();
            }
            else if (transactionFromWallet != null)
            {
                transactionTime = transactionFromWallet.CreationTime;
                isGenerated     = transactionFromWallet.IsCoinBase == true || transactionFromWallet.IsCoinStake == true;
                hex             = transactionFromWallet.Hex;
            }
            else
            {
                transactionTime = sendTransactions.First().SpendingDetails.CreationTime;
                isGenerated     = false;
                hex             = null; // TODO get from mempool
            }

            var model = new GetTransactionModel
            {
                Confirmations   = blockHeight != null ? this.ConsensusManager.Tip.Height - blockHeight.Value + 1 : 0,
                Isgenerated     = isGenerated ? true : (bool?)null,
                BlockHash       = blockHash,
                BlockIndex      = blockIndex ?? block?.Transactions.FindIndex(t => t.GetHash() == trxid),
                BlockTime       = block?.Header.BlockTime.ToUnixTimeSeconds(),
                TransactionId   = uint256.Parse(txid),
                TransactionTime = transactionTime.ToUnixTimeSeconds(),
                TimeReceived    = transactionTime.ToUnixTimeSeconds(),
                Details         = new List <GetTransactionDetailsModel>(),
                Hex             = hex
            };

            Money feeSent = Money.Zero;

            if (sendTransactions.Any())
            {
                feeSent = wallet.GetSentTransactionFee(trxid);
            }

            // Send transactions details.
            foreach (PaymentDetails paymentDetail in sendTransactions.Select(s => s.SpendingDetails).SelectMany(sd => sd.Payments))
            {
                // Only a single item should appear per destination address.
                if (model.Details.SingleOrDefault(d => d.Address == paymentDetail.DestinationAddress) == null)
                {
                    model.Details.Add(new GetTransactionDetailsModel
                    {
                        Address     = paymentDetail.DestinationAddress,
                        Category    = GetTransactionDetailsCategoryModel.Send,
                        Amount      = -paymentDetail.Amount.ToDecimal(MoneyUnit.BTC),
                        Fee         = -feeSent.ToDecimal(MoneyUnit.BTC),
                        OutputIndex = paymentDetail.OutputIndex
                    });
                }
            }

            // Get the ColdStaking script template if available.
            Dictionary <string, ScriptTemplate> templates = this.walletManager.GetValidStakingTemplates();
            ScriptTemplate coldStakingTemplate            = templates.ContainsKey("ColdStaking") ? templates["ColdStaking"] : null;

            // Receive transactions details.
            foreach (TransactionOutputData trxInWallet in receivedTransactions)
            {
                // Skip the details if the script pub key is cold staking.
                // TODO: Verify if we actually need this any longer, after changing the internals to recognice account type!
                if (coldStakingTemplate != null && coldStakingTemplate.CheckScriptPubKey(trxInWallet.ScriptPubKey))
                {
                    continue;
                }

                GetTransactionDetailsCategoryModel category;

                if (isGenerated)
                {
                    category = model.Confirmations > this.FullNode.Network.Consensus.CoinbaseMaturity ? GetTransactionDetailsCategoryModel.Generate : GetTransactionDetailsCategoryModel.Immature;
                }
                else
                {
                    category = GetTransactionDetailsCategoryModel.Receive;
                }

                model.Details.Add(new GetTransactionDetailsModel
                {
                    Address     = trxInWallet.Address,
                    Category    = category,
                    Amount      = trxInWallet.Amount.ToDecimal(MoneyUnit.BTC),
                    OutputIndex = trxInWallet.Index
                });
            }

            model.Amount = model.Details.Sum(d => d.Amount);
            model.Fee    = model.Details.FirstOrDefault(d => d.Category == GetTransactionDetailsCategoryModel.Send)?.Fee;

            return(model);
        }
コード例 #3
0
        public GetTransactionModel GetTransaction(string txid)
        {
            if (!uint256.TryParse(txid, out uint256 trxid))
            {
                throw new ArgumentException(nameof(txid));
            }

            WalletAccountReference accountReference = this.GetWalletAccountReference();

            Wallet    hdWallet  = this.walletManager.WalletRepository.GetWallet(accountReference.WalletName);
            HdAccount hdAccount = this.walletManager.WalletRepository.GetAccounts(hdWallet, accountReference.AccountName).First();

            IWalletAddressReadOnlyLookup addressLookup = this.walletManager.WalletRepository.GetWalletAddressLookup(accountReference.WalletName);

            bool IsChangeAddress(Script scriptPubKey)
            {
                return(addressLookup.Contains(scriptPubKey, out AddressIdentifier addressIdentifier) && addressIdentifier.AddressType == 1);
            }

            // Get the transaction from the wallet by looking into received and send transactions.
            List <TransactionData> receivedTransactions = this.walletManager.WalletRepository.GetTransactionOutputs(hdAccount, null, trxid, true)
                                                          .Where(td => !IsChangeAddress(td.ScriptPubKey)).ToList();
            List <TransactionData> sentTransactions = this.walletManager.WalletRepository.GetTransactionInputs(hdAccount, null, trxid, true).ToList();

            TransactionData firstReceivedTransaction = receivedTransactions.FirstOrDefault();
            TransactionData firstSendTransaction     = sentTransactions.FirstOrDefault();

            if (firstReceivedTransaction == null && firstSendTransaction == null)
            {
                throw new RPCServerException(RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id.");
            }

            uint256         blockHash = null;
            int?            blockHeight, blockIndex;
            DateTimeOffset  transactionTime;
            SpendingDetails spendingDetails = firstSendTransaction?.SpendingDetails;

            if (firstReceivedTransaction != null)
            {
                blockHeight     = firstReceivedTransaction.BlockHeight;
                blockIndex      = firstReceivedTransaction.BlockIndex;
                blockHash       = firstReceivedTransaction.BlockHash;
                transactionTime = firstReceivedTransaction.CreationTime;
            }
            else
            {
                blockHeight     = spendingDetails.BlockHeight;
                blockIndex      = spendingDetails.BlockIndex;
                blockHash       = spendingDetails.BlockHash;
                transactionTime = spendingDetails.CreationTime;
            }

            // Get the block containing the transaction (if it has been confirmed).
            ChainedHeaderBlock chainedHeaderBlock = null;

            if (blockHash != null)
            {
                this.ConsensusManager.GetOrDownloadBlocks(new List <uint256> {
                    blockHash
                }, b => { chainedHeaderBlock = b; });
            }

            Block       block = null;
            Transaction transactionFromStore = null;

            if (chainedHeaderBlock != null)
            {
                block = chainedHeaderBlock.Block;
                if (block != null)
                {
                    if (blockIndex == null)
                    {
                        blockIndex = block.Transactions.FindIndex(t => t.GetHash() == trxid);
                    }

                    transactionFromStore = block.Transactions[(int)blockIndex];
                }
            }

            bool   isGenerated;
            string hex;

            if (transactionFromStore != null)
            {
                transactionTime = Utils.UnixTimeToDateTime(chainedHeaderBlock.ChainedHeader.Header.Time);
                isGenerated     = transactionFromStore.IsCoinBase || transactionFromStore.IsCoinStake;
                hex             = transactionFromStore.ToHex();
            }
            else
            {
                isGenerated = false;
                hex         = null; // TODO get from mempool
            }

            var model = new GetTransactionModel
            {
                Confirmations   = blockHeight != null ? this.ConsensusManager.Tip.Height - blockHeight.Value + 1 : 0,
                Isgenerated     = isGenerated ? true : (bool?)null,
                BlockHash       = blockHash,
                BlockIndex      = blockIndex,
                BlockTime       = block?.Header.BlockTime.ToUnixTimeSeconds(),
                TransactionId   = uint256.Parse(txid),
                TransactionTime = transactionTime.ToUnixTimeSeconds(),
                TimeReceived    = transactionTime.ToUnixTimeSeconds(),
                Details         = new List <GetTransactionDetailsModel>(),
                Hex             = hex
            };

            // Send transactions details.
            if (spendingDetails != null)
            {
                Money feeSent = Money.Zero;
                if (firstSendTransaction != null)
                {
                    // Get the change.
                    long change = spendingDetails.Change.Sum(o => o.Amount);

                    Money inputsAmount  = new Money(sentTransactions.Sum(i => i.Amount));
                    Money outputsAmount = new Money(spendingDetails.Payments.Sum(p => p.Amount) + change);

                    feeSent = inputsAmount - outputsAmount;
                }

                var details = spendingDetails.Payments
                              .GroupBy(detail => detail.DestinationAddress)
                              .Select(p => new GetTransactionDetailsModel()
                {
                    Address     = p.Key,
                    Category    = GetTransactionDetailsCategoryModel.Send,
                    OutputIndex = p.First().OutputIndex,
                    Amount      = 0 - p.Sum(detail => detail.Amount.ToDecimal(MoneyUnit.BTC)),
                    Fee         = -feeSent.ToDecimal(MoneyUnit.BTC)
                });

                model.Details.AddRange(details);
            }

            // Get the ColdStaking script template if available.
            Dictionary <string, ScriptTemplate> templates = this.walletManager.GetValidStakingTemplates();
            ScriptTemplate coldStakingTemplate            = templates.ContainsKey("ColdStaking") ? templates["ColdStaking"] : null;

            // Receive transactions details.
            IScriptAddressReader scriptAddressReader = this.FullNode.NodeService <IScriptAddressReader>();

            foreach (TransactionData trxInWallet in receivedTransactions)
            {
                // Skip the details if the script pub key is cold staking.
                // TODO: Verify if we actually need this any longer, after changing the internals to recognize account type
                if (coldStakingTemplate != null && coldStakingTemplate.CheckScriptPubKey(trxInWallet.ScriptPubKey))
                {
                    continue;
                }

                GetTransactionDetailsCategoryModel category;

                if (isGenerated)
                {
                    category = model.Confirmations > this.FullNode.Network.Consensus.CoinbaseMaturity ? GetTransactionDetailsCategoryModel.Generate : GetTransactionDetailsCategoryModel.Immature;
                }
                else
                {
                    category = GetTransactionDetailsCategoryModel.Receive;
                }

                string address = scriptAddressReader.GetAddressFromScriptPubKey(this.FullNode.Network, trxInWallet.ScriptPubKey);

                model.Details.Add(new GetTransactionDetailsModel
                {
                    Address     = address,
                    Category    = category,
                    Amount      = trxInWallet.Amount.ToDecimal(MoneyUnit.BTC),
                    OutputIndex = trxInWallet.Index
                });
            }

            model.Amount = model.Details.Sum(d => d.Amount);
            model.Fee    = model.Details.FirstOrDefault(d => d.Category == GetTransactionDetailsCategoryModel.Send)?.Fee;

            return(model);
        }