static void SignInput(TxIn txin, Key key, SegWitCoin coin, int index, Transaction transaction) { if (coin.SegWitAddress.AddressType == AddressType.PubKeyHash) { Script scriptCode = GetScriptCode(coin.SegWitAddress.GetScriptPubKey()); uint256 signatureHash = GetHashToSign(transaction, index, scriptCode, coin.UtxoValue); byte[] finalSig = GetSignature(signatureHash, key); txin.WitScript = new WitScript(Op.GetPushOp(finalSig), Op.GetPushOp(key.PubKey.Compress().ToBytes())); } else if (coin.SegWitAddress is ColdStakingAddress coldStakingAddress) { Script scriptCode = coldStakingAddress.GetRedeemScript(); uint256 signatureHash = GetHashToSign(transaction, index, scriptCode, coin.UtxoValue); byte[] finalSig = GetSignature(signatureHash, key); var isColdPubKey = coldStakingAddress.AddressType == AddressType.ColdStakingCold; var publicKey = key.PubKey.Compress(); var scriptSig = new Script(Op.GetPushOp(finalSig), isColdPubKey ? OP_0 : OP_1, Op.GetPushOp(publicKey.ToBytes())); txin.WitScript = scriptSig + new WitScript(Op.GetPushOp(coldStakingAddress.GetRedeemScript().ToBytes(true))); } else if (coin.SegWitAddress is MultiSigAddress multiSigAddress) { var scriptCoin = coin.ToCoin().ToScriptCoin(multiSigAddress.GetRedeemScript()); transaction.Sign(C.Network, new[] { key }, new[] { scriptCoin }); } else { throw new NotSupportedException(); } }
static void CheckTransaction(Transaction tx, SegWitCoin kernelCoin) { var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { TxIn input = tx.Inputs[inputIndex]; TxOut spentOut = new TxOut(kernelCoin.UtxoValue, kernelCoin.SegWitAddress.GetScriptPubKey()); var checker = new TransactionChecker(tx, inputIndex, spentOut, txData); var ctx = new ScriptEvaluationContext { ScriptVerify = ScriptVerify.Mandatory | ScriptVerify.DerSig | ScriptVerify.CheckLockTimeVerify | ScriptVerify.Witness /* todo | ScriptVerify.CheckColdStakeVerify*/ }; bool verifyScriptResult = ctx.VerifyScript(input.ScriptSig, spentOut.ScriptPubKey, checker); if (verifyScriptResult == false) { throw new InvalidOperationException( $"Verify script for transaction '{tx.GetHash()}' input {inputIndex} failed, ScriptSig = '{input.ScriptSig}', ScriptPubKey = '{spentOut.ScriptPubKey}', script evaluation error = '{ctx.Error}'"); } } }
public static Transaction CreateAndSignCoinstakeTransaction(SegWitCoin kernelCoin, long totalReward, uint currentBlockTime, string passphrase, out Key privateKey) { var tx = CreateCoinstakeTransaction(kernelCoin, totalReward, currentBlockTime, passphrase, out privateKey); SigningService.SignInputs(tx, new[] { privateKey }, new[] { kernelCoin }); return(tx); }
public static Coin ToCoin(this SegWitCoin segWitCoin) { var outpoint = new OutPoint(segWitCoin.UtxoTxHash, segWitCoin.UtxoTxN); var txOut = new TxOut(segWitCoin.UtxoValue, segWitCoin.SegWitAddress.GetScriptPubKey()); var coin = new Coin(outpoint, txOut); return(coin); }
public static Transaction CreateCoinstakeTransaction(SegWitCoin kernelCoin, long totalReward, uint currentBlockTime, string passphrase, out Key privateKey) { Transaction tx = C.Network.CreateTransaction(); if (kernelCoin.SegWitAddress is ColdStakingAddress coldStakingAddress && coldStakingAddress.AddressType == AddressType.ColdStakingHot) { privateKey = new Key(coldStakingAddress.StakingKey); }
public static void SignScriptAddress(Transaction tx, Key privateKey, SegWitCoin kernelCoin) { if (kernelCoin.SegWitAddress is MultiSigAddress multiSigAddress) { var sc = kernelCoin.ToCoin().ToScriptCoin(multiSigAddress.GetRedeemScript()); tx.Sign(C.Network, new[] { privateKey }, new[] { sc }); } else if (kernelCoin.SegWitAddress is ColdStakingAddress coldStakingAddress) { var sc = kernelCoin.ToCoin().ToScriptCoin(coldStakingAddress.GetRedeemScript()); tx.Sign(C.Network, new[] { privateKey }, new[] { sc }, new[] { new ColdStakingBuilderExtension(staking: true) }); } }
void PushToCoinsCache(RPCUnspentOutput[] unspentOutputs, RPCFilename dumpFilePath, bool export = false) { this.stopwatch.Restart(); try { var dump = File.ReadAllText(dumpFilePath.filename); File.Delete(dumpFilePath.filename); var lookup = ParseDump(dump); var coins = new List <SegWitCoin>(); foreach (var utxo in unspentOutputs) { if (utxo.confirmations < (export ? 0 : 125)) { continue; } if (lookup.ContainsKey(utxo.address)) { var coin = new SegWitCoin(lookup[utxo.address], uint256.Parse(utxo.txid), utxo.vout, (long)(utxo.amount * C.SatoshisPerCoin), UtxoType.NotSet); coins.Add(coin); } else { this.logger.LogWarning($"For utxo {utxo.txid}-{utxo.vout}, its address {utxo.address} was not found in the lookup."); } } if (export) { Export(coins); } CoinCache.ReplaceCoins(coins); this.logger.LogInformation($"Cached {coins.Count} mature coins for staking - {stopwatch.ElapsedMilliseconds} ms."); } catch (Exception e) { this.logger.LogError($"Error processing wallet dump file {e.Message}"); throw; } }
public static Key GetPrivateKey(this SegWitCoin segWitCoin, string passphrase) { return(new Key(VCL.DecryptWithPassphrase(passphrase, segWitCoin.SegWitAddress.GetEncryptedPrivateKey()))); }
public static Balance GetBalance(ConcurrentDictionary <int, BlockMetadata> blocks, int syncedHeight, HashSet <MemoryPoolEntry> memoryPoolEntries, Func <string, ISegWitAddress> getOwnAddress, string matchAddress = null, AddressType matchAddressType = AddressType.MatchAll) { var balance = new Balance(); var spent = new Dictionary <string, UtxoMetadata>(); // process all confirmed transactions first, oldest to newest foreach (var(height, block) in blocks) { foreach (var tx in block.Transactions) { bool isImmatureForSpending = false; if (tx.TxType.HasCoinbaseMaturity()) { var confirmationsSpending = syncedHeight - height + 1; // if the tip is at 100 and my tx is height 90, it's 11 confirmations isImmatureForSpending = confirmationsSpending < C.Network.Consensus.CoinbaseMaturity; // ok } var confirmationsStaking = syncedHeight - height + 1; // if the tip is at 100 and my tx is height 90, it's 11 confirmations var isImmatureForStaking = confirmationsStaking < C.Network.Consensus.MaxReorgLength; if (tx.Received != null) { foreach (UtxoMetadata utxo in tx.Received.Values) { for (var address = getOwnAddress(utxo.Address).Match(matchAddress, matchAddressType); address != null; address = null) { balance.TotalReceived += utxo.Satoshis; var coin = new SegWitCoin(address, utxo.HashTx, utxo.Index, utxo.Satoshis); if (!isImmatureForSpending) { balance.Spendable += utxo.Satoshis; balance.SpendableCoins.AddSafe(utxo.GetKey(), coin); } if (!isImmatureForStaking) { balance.Stakable += utxo.Satoshis; balance.StakingCoins.AddSafe(utxo.GetKey(), coin); } } } } if (tx.Spent != null) { foreach ((string txIdN, UtxoMetadata utxo) in tx.Spent) { for (var address = getOwnAddress(utxo.Address).Match(matchAddress, matchAddressType); address != null; address = null) { balance.TotalSpent += utxo.Satoshis; spent.AddSafe(txIdN, utxo); } } } } } // unconfirmed transactions - add them last, ordered by time, so that they come last in coin selection // when unconfirmed outputs get spent, to allow the memory pool and network to recognize // the new unspent outputs. foreach (MemoryPoolEntry entry in memoryPoolEntries.OrderBy(x => x.TransactionTime)) { var tx = entry.Transaction; if (tx.Received != null) { foreach (UtxoMetadata utxo in tx.Received.Values) { ISegWitAddress address = getOwnAddress(utxo.Address); balance.TotalReceivedPending += utxo.Satoshis; var coin = new SegWitCoin(address, utxo.HashTx, utxo.Index, utxo.Satoshis); balance.SpendableCoins.AddSafe(utxo.GetKey(), coin); } } if (tx.Spent != null) { foreach (var(txIdN, utxo) in tx.Spent) { for (var address = getOwnAddress(utxo.Address).Match(matchAddress, matchAddressType); address != null; address = null) { balance.TotalSpent += utxo.Satoshis; spent.AddSafe(txIdN, utxo); } } } } // remove what is already spent foreach (var utxoId in spent) { if (balance.SpendableCoins.ContainsKey(utxoId.Key)) { balance.Spendable -= utxoId.Value.Satoshis; balance.SpendableCoins.Remove(utxoId.Key); } if (balance.StakingCoins.ContainsKey(utxoId.Key)) { balance.Stakable -= utxoId.Value.Satoshis; balance.StakingCoins.Remove(utxoId.Key); } } // last balance updates balance.Confirmed = balance.TotalReceived - balance.TotalSpent; balance.Pending = balance.TotalReceivedPending - balance.TotalSpentPending; balance.Total = balance.Confirmed + balance.Pending; return(balance); }