Beispiel #1
0
        public static CetSigs ParseFromTLV(TLVReader reader)
        {
            CetSigs cet = new CetSigs();

            using (var r = reader.StartReadRecord())
            {
                if (r.Type != AdaptorSigsTLVType)
                {
                    throw new FormatException("Invalid TLV type, expected adaptor sigs");
                }
                List <AdaptorSignature> sigs = new List <AdaptorSignature>();
                while (!r.IsEnd)
                {
                    sigs.Add(AdaptorSignature.ParseFromTLV(reader));
                }
                cet.OutcomeSigs = sigs.ToArray();
            }
            Span <byte> buf = stackalloc byte[64];

            reader.ReadBytes(buf);
            if (!ECDSASignature.TryParseFromCompact(buf, out var sig))
            {
                throw new FormatException("Invalid DER signature");
            }
            cet.RefundSig = sig;
            return(cet);
        }
Beispiel #2
0
        public void SerializeDeserializeCompact(ECDSASignature signature)
        {
            var b = signature.ToCompact();

            ECDSASignature.TryParseFromCompact(b, out var signature2);
            Assert.True(signature.ToDER().SequenceEqual(signature2.ToDER()));
        }
        private BOLT11PaymentRequest(string str, Network network)
        {
            if (str == null)
            {
                throw new ArgumentNullException(nameof(str));
            }
            if (network == null)
            {
                throw new ArgumentNullException(nameof(network));
            }
            _str = str.ToLowerInvariant().Trim();
            if (str.StartsWith("lightning:", StringComparison.OrdinalIgnoreCase))
            {
                str = str.Substring("lightning:".Length);
            }
            var decoded = InternalBech32Encoder.Instance.DecodeData(str);
            var hrp     = decoded.HumanReadablePart.ToLowerInvariant();

            Prefix        = hrp;
            MinimumAmount = LightMoney.Zero;
            if (Prefix.Length == 0)
            {
                throw new FormatException("Invalid BOLT11: No prefix");
            }
            ulong amount           = 0;
            int   firstNumberIndex = -1;

            for (int i = 0; i < Prefix.Length; i++)
            {
                int digit = Array.IndexOf(digits, Prefix[i]);
                if (digit != -1)
                {
                    if (firstNumberIndex == -1)
                    {
                        firstNumberIndex = i;
                    }
                    amount *= 10;
                    amount += (uint)digit;
                }
                else if (firstNumberIndex != -1)
                {
                    break;
                }
            }

            if (firstNumberIndex != -1)
            {
                LightMoneyUnit unit = LightMoneyUnit.BTC;
                switch (Prefix[Prefix.Length - 1])
                {
                case 'm':
                    unit = LightMoneyUnit.MilliBTC;
                    break;

                case 'u':
                    unit = LightMoneyUnit.Micro;
                    break;

                case 'n':
                    unit = LightMoneyUnit.Nano;
                    break;

                case 'p':
                    unit = LightMoneyUnit.MilliSatoshi;
                    if (amount % 10 != 0)
                    {
                        throw new FormatException("Pico BTC denomination which is not a multiple of 10 is not supported by BTCPayServer.Lightning");
                    }
                    amount = amount / 10UL;
                    break;

                default:
                    if (Array.IndexOf(digits, Prefix[Prefix.Length - 1]) == -1)
                    {
                        throw new FormatException("Invalid BOLT11: invalid amount multiplier");
                    }
                    unit = LightMoneyUnit.BTC;
                    break;
                }
                MinimumAmount = LightMoney.FromUnit(amount, unit);
                Prefix        = Prefix.Substring(0, firstNumberIndex);
                if (Prefix.Length == 0)
                {
                    throw new FormatException("Invalid BOLT11: No prefix");
                }
            }

            if (_Prefixes.TryGetValue((network.NetworkSet.CryptoCode, network.ChainName), out var expectedPrefix) &&
                expectedPrefix != Prefix)
            {
                throw new FormatException("Invalid BOLT11: Invalid prefix");
            }

            var bitArray = new BitArray(decoded.Data.Length * 5);

            for (int di = 0; di < decoded.Data.Length; di++)
            {
                bitArray.Set(di * 5 + 0, ((decoded.Data[di] >> 4) & 0x01) == 1);
                bitArray.Set(di * 5 + 1, ((decoded.Data[di] >> 3) & 0x01) == 1);
                bitArray.Set(di * 5 + 2, ((decoded.Data[di] >> 2) & 0x01) == 1);
                bitArray.Set(di * 5 + 3, ((decoded.Data[di] >> 1) & 0x01) == 1);
                bitArray.Set(di * 5 + 4, ((decoded.Data[di] >> 0) & 0x01) == 1);
            }

            var reader = new BitReader(bitArray);

            reader.Position = reader.Count - 520 - 30;
            if (reader.Position < 0)
            {
                throw new FormatException("Invalid BOLT11: Invalid size");
            }

            if (!reader.CanConsume(65))
            {
                throw new FormatException("Invalid BOLT11: Invalid size");
            }
            var rs = reader.ReadBytes(64);

            _Signature = rs;
            if (!ECDSASignature.TryParseFromCompact(rs, out var s) || s is null)
            {
                throw new FormatException("Invalid BOLT11: Invalid ECDSA signature");
            }
            ECDSASignature = s;
            RecoveryId     = reader.ReadBytes(1)[0];

            reader.Position = 0;
            Timestamp       = Utils.UnixTimeToDateTime(reader.ReadULongBE(35));

            void AssertSize(int c)
            {
                if (!reader.CanConsume(c))
                {
                    throw new FormatException("Invalid BOLT11: invalid size");
                }
            }

            ExpiryDate         = Timestamp + TimeSpan.FromHours(1);
            MinFinalCLTVExpiry = 9;
            var fallbackAddresses = new List <BitcoinAddress>();
            var routes            = new List <RouteInformation>();
            var features          = FeatureBits.None;

            while (reader.Position != reader.Count - 520 - 30)
            {
                AssertSize(5 + 10);
                var tag  = (Bolt11FieldType)reader.ReadULongBE(5);
                var size = (int)(reader.ReadULongBE(10) * 5);
                AssertSize(size);
                var afterReadPosition = reader.Position + size;
                switch (tag)
                {
                case Bolt11FieldType.P:
                    if (size != 52 * 5)
                    {
                        break;
                    }
                    if (PaymentHash != null)
                    {
                        throw new FormatException("Invalid BOLT11: Duplicate 'p'");
                    }
                    PaymentHash = new uint256(reader.ReadBytes(32), false);
                    break;

                case Bolt11FieldType.X:
                    ExpiryDate = Timestamp + TimeSpan.FromSeconds(reader.ReadULongBE(size));
                    break;

                case Bolt11FieldType.D:
                    var bytesCount = size / 8;
                    var bytes      = reader.ReadBytes(bytesCount);
                    try
                    {
                        ShortDescription = UTF8NoBOM.GetString(bytes, 0, bytesCount);
                    }
                    catch
                    {
                    }
                    break;

                case Bolt11FieldType.S:
                    if (size != 52 * 5)
                    {
                        break;
                    }
                    if (PaymentSecret != null)
                    {
                        throw new FormatException("Invalid BOLT11: Duplicate 's'");
                    }
                    PaymentSecret = new uint256(reader.ReadBytes(32), false);
                    break;

                case Bolt11FieldType.N:
                    if (size != 53 * 5)
                    {
                        break;
                    }
                    ExplicitPayeePubKey = new PubKey(reader.ReadBytes(33));
                    break;

                case Bolt11FieldType.C:
                    var value = reader.ReadULongBE(size);
                    if (value > int.MaxValue)
                    {
                        break;
                    }
                    MinFinalCLTVExpiry = (int)value;
                    break;

                case Bolt11FieldType.F:
                    if (size < 5)
                    {
                        break;
                    }
                    var version = reader.ReadULongBE(5);
                    switch (version)
                    {
                    case 0:
                        if (size == 5 + (20 * 8))
                        {
                            fallbackAddresses.Add(new BitcoinWitPubKeyAddress(new WitKeyId(reader.ReadBytes(20)), network));
                        }
                        else if (size == 5 + (32 * 8) + 4)
                        {
                            fallbackAddresses.Add(new BitcoinWitScriptAddress(new WitScriptId(reader.ReadBytes(32)), network));
                        }
                        break;

                    case 17:
                        if (size != 5 + (20 * 8))
                        {
                            break;
                        }
                        fallbackAddresses.Add(new BitcoinPubKeyAddress(new KeyId(reader.ReadBytes(20)), network));
                        break;

                    case 18:
                        if (size != 5 + (20 * 8))
                        {
                            break;
                        }
                        fallbackAddresses.Add(new BitcoinScriptAddress(new ScriptId(reader.ReadBytes(20)), network));
                        break;

                    default:
                        break;
                    }
                    break;

                case Bolt11FieldType.H:
                    if (size != 52 * 5)
                    {
                        break;
                    }
                    DescriptionHash = new uint256(reader.ReadBytes(32), false);
                    break;

                case Bolt11FieldType.R:
                    if (size < 264 + 64 + 32 + 32 + 16)
                    {
                        break;
                    }
                    var positionBefore   = reader.Position;
                    var routeInformation = new RouteInformation(reader, size);
                    var readen           = reader.Position - positionBefore;
                    if (size - readen >= 5)
                    {
                        break;
                    }
                    routes.Add(routeInformation);
                    break;

                case Bolt11FieldType.Nine:
                    if (size < 5)
                    {
                        break;
                    }

                    // Read which feature bits are set from right-to-left.
                    for (var i = size - 1; i >= 0; i--)
                    {
                        if (reader.Read())
                        {
                            features |= (FeatureBits)(1 << i);
                        }
                    }
                    break;
                }
                var skip = afterReadPosition - reader.Position;
                if (skip < 0)
                {
                    throw new FormatException("Invalid BOLT11: Invalid size");
                }
                reader.Consume(skip);
            }

            reader = new BitReader(bitArray, bitArray.Count - 520 - 30);
            int byteCount = Math.DivRem(reader.Count, 8, out var remainder);

            if (remainder != 0)
            {
                byteCount++;
            }
            var hashedData = UTF8NoBOM.GetBytes(hrp).Concat(reader.ReadBytes(byteCount)).ToArray();

            Hash              = new uint256(Hashes.SHA256(hashedData));
            Routes            = routes;
            FallbackAddresses = fallbackAddresses;
            FeatureBits       = features;
        }