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); }
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; }