Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ public bool IsValid()
/// <param name="bitReader">The BitReader to read from</param>
/// <param name="length">The length of the field</param>
/// <param name="bitcoinNetwork">The network type</param>
/// <returns>The FallbackAddressTaggedField</returns>
/// <exception cref="ArgumentException">Thrown when the address is unknown or invalid</exception>
internal static FallbackAddressTaggedField FromBitReader(BitReader bitReader, short length,
BitcoinNetwork bitcoinNetwork)
/// <returns>The FallbackAddressTaggedField, or null if the version is unknown (per BOLT 11, unknown versions should be skipped)</returns>
/// <exception cref="ArgumentException">Thrown when the network is invalid</exception>
internal static FallbackAddressTaggedField? FromBitReader(BitReader bitReader, short length,
BitcoinNetwork bitcoinNetwork)
{
// Get Address Type
// Get Address Type (witness version)
var addressType = bitReader.ReadByteFromBits(5);
var newLength = length - 1;

Expand All @@ -102,7 +102,11 @@ internal static FallbackAddressTaggedField FromBitReader(BitReader bitReader, sh

var network = Network.GetNetwork(bitcoinNetwork) ??
throw new ArgumentException("Network is unknown or invalid.", nameof(bitcoinNetwork));
BitcoinAddress address = addressType switch

// Per BOLT 11: "MUST skip over `f` fields that use an unknown `version`"
// Supported versions: 0 (P2WPKH/P2WSH), 17 (P2PKH), 18 (P2SH)
// Unknown versions (1-16 for future witness versions, 19-31 reserved) should be skipped
BitcoinAddress? address = addressType switch
{
// Witness P2WPKH
0 when data.Length == 20 => new WitKeyId(data).GetAddress(network),
Expand All @@ -112,9 +116,10 @@ internal static FallbackAddressTaggedField FromBitReader(BitReader bitReader, sh
17 => new KeyId(data).GetAddress(network),
// P2SH
18 => new ScriptId(data).GetAddress(network),
_ => throw new ArgumentException("Address is unknown or invalid.", nameof(bitReader))
// Unknown version - skip per BOLT 11 spec
_ => null
};

return new FallbackAddressTaggedField(address);
return address is null ? null : new FallbackAddressTaggedField(address);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public void FromBitReader_CreatesCorrectlyFromBitReader(string expectedAddress,
var taggedField = FallbackAddressTaggedField.FromBitReader(bitReader, bitLength, BitcoinNetwork.Mainnet);

// Assert
Assert.NotNull(taggedField);
Assert.Equal(expectedAddress, taggedField.Value.ToString());
}

Expand All @@ -132,15 +133,45 @@ public void IsValid_ReturnsTrue()
Assert.True(taggedField.IsValid());
}

[Theory]
[InlineData(1)] // Witness v1 (Taproot) - not yet supported
[InlineData(2)] // Future witness version
[InlineData(16)] // Future witness version
[InlineData(19)] // Reserved per BOLT 11
[InlineData(20)] // Reserved per BOLT 11
[InlineData(31)] // Reserved per BOLT 11 (max 5-bit value)
public void FromBitReader_ReturnsNull_ForUnknownVersion(byte version)
{
// Arrange - Create data with the version in the first 5 bits
// Version is stored in 5 bits, followed by 20 bytes (160 bits) of address data
// Total: 5 + 160 = 165 bits = 33 5-bit units
var data = new byte[21]; // (165 bits + 7) / 8 = 21 bytes
data[0] = (byte)(version << 3); // Version in first 5 bits (shifted to align)
// Fill remaining with dummy address data
for (var i = 1; i < data.Length; i++)
data[i] = 0xAB;

var bitReader = new BitReader(data);

// Act
var result = FallbackAddressTaggedField.FromBitReader(bitReader, 33, BitcoinNetwork.Mainnet);

// Assert - Per BOLT 11: "MUST skip over `f` fields that use an unknown `version`"
Assert.Null(result);
}

[Fact]
public void FromBitReader_ThrowsArgumentException_ForInvalidAddressType()
public void FromBitReader_ReturnsNull_ForVersion0WithInvalidDataLength()
{
// Arrange
var invalidData = new byte[] { 255, 0x00, 0x00, 0x00 };
var bitReader = new BitReader(invalidData);
// Arrange - Version 0 but with 15-byte data (not 20 or 32)
// This should return null since it's not a valid P2WPKH (20) or P2WSH (32)
var data = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
var bitReader = new BitReader(data);

// Act & Assert
Assert.Throws<ArgumentException>(() => FallbackAddressTaggedField.FromBitReader(
bitReader, 4, BitcoinNetwork.Mainnet));
// Act
var result = FallbackAddressTaggedField.FromBitReader(bitReader, 13, BitcoinNetwork.Mainnet);

// Assert
Assert.Null(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public void FromBitReader_Reads_From_Beginning_Of_Buffer(BitcoinAddress address)

var parsed = FallbackAddressTaggedField.FromBitReader(reader, length, BitcoinNetwork.Mainnet);

Assert.NotNull(parsed);
Assert.Equal(expected.ToString(), parsed.Value.ToString());
}

Expand All @@ -86,6 +87,7 @@ public void FromBitReader_Reads_From_Middle_Of_Buffer_Unaligned(BitcoinAddress a

var parsed = FallbackAddressTaggedField.FromBitReader(reader, length, BitcoinNetwork.Mainnet);

Assert.NotNull(parsed);
Assert.Equal(expected.ToString(), parsed.Value.ToString());
}

Expand All @@ -99,6 +101,7 @@ public void FromBitReader_Reads_From_Middle_Of_Buffer_Aligned(BitcoinAddress add

var parsed = FallbackAddressTaggedField.FromBitReader(reader, length, BitcoinNetwork.Mainnet);

Assert.NotNull(parsed);
Assert.Equal(expected.ToString(), parsed.Value.ToString());
}

Expand All @@ -112,6 +115,7 @@ public void FromBitReader_Reads_Near_End_Of_Buffer_Unaligned(BitcoinAddress addr

var parsed = FallbackAddressTaggedField.FromBitReader(reader, length, BitcoinNetwork.Mainnet);

Assert.NotNull(parsed);
Assert.Equal(expected.ToString(), parsed.Value.ToString());
}

Expand All @@ -125,6 +129,7 @@ public void FromBitReader_Reads_Near_End_Of_Buffer_Aligned(BitcoinAddress addres

var parsed = FallbackAddressTaggedField.FromBitReader(reader, length, BitcoinNetwork.Mainnet);

Assert.NotNull(parsed);
Assert.Equal(expected.ToString(), parsed.Value.ToString());
}

Expand All @@ -138,6 +143,7 @@ public void FromBitReader_Reads_At_End_Of_Buffer(BitcoinAddress address)

var parsed = FallbackAddressTaggedField.FromBitReader(reader, length, BitcoinNetwork.Mainnet);

Assert.NotNull(parsed);
Assert.Equal(expected.ToString(), parsed.Value.ToString());
}
}
Loading