/// <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));
 }
示例#5
0
        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));
 }
示例#7
0
 public void TrySign(IHDScriptPubKey accountHDScriptPubKey, IHDKey accountKey, RootedKeyPath accountKeyPath, SigHash sigHash = SigHash.All)
 {
     TrySign(accountHDScriptPubKey, accountKey, accountKeyPath, Parent.Normalize(new SigningOptions(sigHash)));
 }
示例#8
0
        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");
                }
            }
        }
示例#9
0
 public override void AddKeyPath(PubKey key, RootedKeyPath rootedKeyPath)
 {
     base.AddKeyPath(key, rootedKeyPath);
     TrySlimUTXO();
 }
示例#10
0
 public TaprootKeyPath(RootedKeyPath rootedKeyPath) : this(rootedKeyPath, null)
 {
 }
 public bool TryGetKeyOrigin(KeyId keyId, [MaybeNullWhen(false)] out RootedKeyPath keyOrigin)
 => KeyOrigins.TryGetValue(keyId, out keyOrigin);