/// <summary> /// Get the balance change if you were signing this transaction. /// </summary> /// <param name="accountHDScriptPubKey">The hdScriptPubKey used to generate addresses</param> /// <param name="accountKey">The account key that will be used to sign (ie. 49'/0'/0')</param> /// <param name="accountKeyPath">The account key path</param> /// <returns>The balance change</returns> public Money GetBalance(IHDScriptPubKey accountHDScriptPubKey, IHDKey accountKey, RootedKeyPath accountKeyPath = null) { if (accountHDScriptPubKey == null) { throw new ArgumentNullException(nameof(accountHDScriptPubKey)); } Money total = Money.Zero; foreach (var o in CoinsFor(accountHDScriptPubKey, accountKey, accountKeyPath)) { var amount = o.GetCoin()?.Amount; if (amount == null) { continue; } total += o is PSBTInput ? -amount : amount; } return(total); }
/// <summary> /// Filter the coins which contains the <paramref name="accountKey"/> and <paramref name="accountKeyPath"/> in the HDKeys and derive /// the same scriptPubKeys as <paramref name="accountHDScriptPubKey"/>. /// </summary> /// <param name="accountHDScriptPubKey">The hdScriptPubKey used to generate addresses</param> /// <param name="accountKey">The account key that will be used to sign (ie. 49'/0'/0')</param> /// <param name="accountKeyPath">The account key path</param> /// <returns>Inputs with HD keys matching masterFingerprint and account key</returns> public IEnumerable <PSBTCoin> CoinsFor(IHDScriptPubKey accountHDScriptPubKey, IHDKey accountKey, RootedKeyPath accountKeyPath = null) { if (accountKey == null) { throw new ArgumentNullException(nameof(accountKey)); } if (accountHDScriptPubKey == null) { throw new ArgumentNullException(nameof(accountHDScriptPubKey)); } accountHDScriptPubKey = accountHDScriptPubKey.AsHDKeyCache(); accountKey = accountKey.AsHDKeyCache(); return(Inputs.CoinsFor(accountHDScriptPubKey, accountKey, accountKeyPath).OfType <PSBTCoin>().Concat(Outputs.CoinsFor(accountHDScriptPubKey, accountKey, accountKeyPath).OfType <PSBTCoin>())); }
/// <summary> /// Sign all inputs which derive addresses from <paramref name="accountHDScriptPubKey"/> and that need to be signed by <paramref name="accountKey"/>. /// </summary> /// <param name="accountHDScriptPubKey">The address generator</param> /// <param name="accountKey">The account key with which to sign</param> /// <param name="accountKeyPath">The account key path (eg. [masterFP]/49'/0'/0')</param> /// <param name="sigHash">The SigHash</param> /// <returns>This PSBT</returns> public PSBT SignAll(IHDScriptPubKey accountHDScriptPubKey, IHDKey accountKey, RootedKeyPath accountKeyPath, SigHash sigHash = SigHash.All) { if (accountKey == null) { throw new ArgumentNullException(nameof(accountKey)); } if (accountHDScriptPubKey == null) { throw new ArgumentNullException(nameof(accountHDScriptPubKey)); } accountHDScriptPubKey = accountHDScriptPubKey.AsHDKeyCache(); accountKey = accountKey.AsHDKeyCache(); Money total = Money.Zero; foreach (var o in Inputs.CoinsFor(accountHDScriptPubKey, accountKey, accountKeyPath)) { o.TrySign(accountHDScriptPubKey, accountKey, accountKeyPath, sigHash); } return(this); }
/// <summary> /// Get the balance change if you were signing this transaction. /// </summary> /// <param name="accountHDScriptPubKey">The hdScriptPubKey used to generate addresses</param> /// <param name="accountKey">The account key that will be used to sign (ie. 49'/0'/0')</param> /// <param name="accountKeyPath">The account key path</param> /// <returns>The balance change</returns> public Money GetBalance(ScriptPubKeyType scriptPubKeyType, IHDKey accountKey, RootedKeyPath accountKeyPath = null) { if (accountKey == null) { throw new ArgumentNullException(nameof(accountKey)); } return(GetBalance(new HDKeyScriptPubKey(accountKey, scriptPubKeyType), accountKey, accountKeyPath)); }
internal PSBT(BitcoinStream stream, Network network) { if (network == null) { throw new ArgumentNullException(nameof(network)); } Network = network; Inputs = new PSBTInputList(); Outputs = new PSBTOutputList(); var magicBytes = stream.Inner.ReadBytes(PSBT_MAGIC_BYTES.Length); if (!magicBytes.SequenceEqual(PSBT_MAGIC_BYTES)) { throw new FormatException("Invalid PSBT magic bytes"); } // It will be reassigned in `ReadWriteAsVarString` so no worry to assign 0 length array here. byte[] k = new byte[0]; byte[] v = new byte[0]; var txFound = false; stream.ReadWriteAsVarString(ref k); while (k.Length != 0) { switch (k[0]) { case PSBTConstants.PSBT_GLOBAL_UNSIGNED_TX: if (k.Length != 1) { throw new FormatException("Invalid PSBT. Contains illegal value in key global tx"); } if (tx != null) { throw new FormatException("Duplicate Key, unsigned tx already provided"); } tx = stream.ConsensusFactory.CreateTransaction(); uint size = 0; stream.ReadWriteAsVarInt(ref size); var pos = stream.Counter.ReadenBytes; tx.ReadWrite(stream); if (stream.Counter.ReadenBytes - pos != size) { throw new FormatException("Malformed global tx. Unexpected size."); } if (tx.Inputs.Any(txin => txin.ScriptSig != Script.Empty || txin.WitScript != WitScript.Empty)) { throw new FormatException("Malformed global tx. It should not contain any scriptsig or witness by itself"); } txFound = true; break; case PSBTConstants.PSBT_GLOBAL_XPUB when XPubVersionBytes != null: if (k.Length != 1 + XPubVersionBytes.Length + 74) { throw new FormatException("Malformed global xpub."); } for (int ii = 0; ii < XPubVersionBytes.Length; ii++) { if (k[1 + ii] != XPubVersionBytes[ii]) { throw new FormatException("Malformed global xpub."); } } stream.ReadWriteAsVarString(ref v); KeyPath path = KeyPath.FromBytes(v.Skip(4).ToArray()); var rootedKeyPath = new RootedKeyPath(new HDFingerprint(v.Take(4).ToArray()), path); GlobalXPubs.Add(new ExtPubKey(k, 1 + XPubVersionBytes.Length, 74).GetWif(Network), rootedKeyPath); break; default: if (unknown.ContainsKey(k)) { throw new FormatException("Invalid PSBTInput, duplicate key for unknown value"); } stream.ReadWriteAsVarString(ref v); unknown.Add(k, v); break; } stream.ReadWriteAsVarString(ref k); } if (!txFound) { throw new FormatException("Invalid PSBT. No global TX"); } int i = 0; while (stream.Inner.CanRead && i < tx.Inputs.Count) { var psbtin = new PSBTInput(stream, this, (uint)i, tx.Inputs[i]); Inputs.Add(psbtin); i++; } if (i != tx.Inputs.Count) { throw new FormatException("Invalid PSBT. Number of input does not match to the global tx"); } i = 0; while (stream.Inner.CanRead && i < tx.Outputs.Count) { var psbtout = new PSBTOutput(stream, this, (uint)i, tx.Outputs[i]); Outputs.Add(psbtout); i++; } if (i != tx.Outputs.Count) { throw new FormatException("Invalid PSBT. Number of outputs does not match to the global tx"); } }
/// <summary> /// Add keypath information to this PSBT for each input or output involving it /// </summary> /// <param name="pubkey">The public key which need to sign</param> /// <param name="rootedKeyPath">The keypath to this public key</param> /// <returns>This PSBT</returns> public PSBT AddKeyPath(PubKey pubkey, RootedKeyPath rootedKeyPath) { return(AddKeyPath(pubkey, rootedKeyPath, null)); }
public void TrySign(IHDScriptPubKey accountHDScriptPubKey, IHDKey accountKey, RootedKeyPath accountKeyPath, SigHash sigHash = SigHash.All) { TrySign(accountHDScriptPubKey, accountKey, accountKeyPath, Parent.Normalize(new SigningOptions(sigHash))); }
public void TrySign(IHDScriptPubKey accountHDScriptPubKey, IHDKey accountKey, RootedKeyPath accountKeyPath, SigningOptions signingOptions) { if (accountKey == null) { throw new ArgumentNullException(nameof(accountKey)); } if (accountHDScriptPubKey == null) { throw new ArgumentNullException(nameof(accountHDScriptPubKey)); } if (IsFinalized()) { return; } var cache = accountKey.AsHDKeyCache(); accountHDScriptPubKey = accountHDScriptPubKey.AsHDKeyCache(); foreach (var hdk in this.HDKeysFor(accountHDScriptPubKey, cache, accountKeyPath)) { if (((HDKeyCache)cache.Derive(hdk.AddressKeyPath)).Inner is ISecret k) { Sign(k.PrivateKey, signingOptions); } else { throw new ArgumentException(paramName: nameof(accountKey), message: "This should be a private key"); } } }
public override void AddKeyPath(PubKey key, RootedKeyPath rootedKeyPath) { base.AddKeyPath(key, rootedKeyPath); TrySlimUTXO(); }
public TaprootKeyPath(RootedKeyPath rootedKeyPath) : this(rootedKeyPath, null) { }
public bool TryGetKeyOrigin(KeyId keyId, [MaybeNullWhen(false)] out RootedKeyPath keyOrigin) => KeyOrigins.TryGetValue(keyId, out keyOrigin);