diff --git a/NLightning.sln b/NLightning.sln
index 380d2e0..ca50bc9 100644
--- a/NLightning.sln
+++ b/NLightning.sln
@@ -33,11 +33,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BlazorTests", "BlazorTests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLightning.Blazor.Tests", "test\BlazorTests\NLightning.Blazor.Tests\NLightning.Blazor.Tests.csproj", "{C0F21413-47D0-4584-84A5-B730909E7349}"
EndProject
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{29E3DE2A-9C25-4F48-8FCB-0BBAA9699662}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bolt11.Decoder.Blazor", "samples\Bolt11.Decoder.Blazor\Bolt11.Decoder.Blazor.csproj", "{D80680EA-8B09-44BA-A9EE-435C93D298B1}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Fuzztests", "Fuzztests", "{965A5904-F5EC-40EE-822C-3E6912640F80}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NLightning.Bolts.BOLT11", "NLightning.Bolts.BOLT11", "{79B6F41F-3E5A-4ABF-83A4-5860F9ADAC35}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvoiceRoundTripFuzzer", "test\FuzzTests\NLightning.Bolts.BOLT11\InvoiceRoundTripFuzzer\InvoiceRoundTripFuzzer.csproj", "{201CF0D3-AA8A-40D3-843E-4382F9EDEABA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NLightning.Bolts.BOLT8", "NLightning.Bolts.BOLT8", "{6BECD5C6-9978-41E1-BAC0-854E16F0D142}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActOneFuzzer", "test\FuzzTests\NLightning.Bolts.BOLT8\ActOneFuzzer\ActOneFuzzer.csproj", "{A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActTwoFuzzer", "test\FuzzTests\NLightning.Bolts.BOLT8\ActTwoFuzzer\ActTwoFuzzer.csproj", "{8A99EB76-83DA-4E88-A7F8-265224F901D5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|Any CPU = Release|Any CPU
@@ -176,6 +187,42 @@ Global
{D80680EA-8B09-44BA-A9EE-435C93D298B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D80680EA-8B09-44BA-A9EE-435C93D298B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D80680EA-8B09-44BA-A9EE-435C93D298B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Native|Any CPU.ActiveCfg = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Native|Any CPU.Build.0 = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Wasm|Any CPU.Build.0 = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Native|Any CPU.ActiveCfg = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Native|Any CPU.Build.0 = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Wasm|Any CPU.Build.0 = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Native|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Native|Any CPU.Build.0 = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Wasm|Any CPU.Build.0 = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Native|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Native|Any CPU.Build.0 = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Wasm|Any CPU.Build.0 = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Native|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Native|Any CPU.Build.0 = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Wasm|Any CPU.Build.0 = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Native|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Native|Any CPU.Build.0 = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Wasm|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -198,5 +245,11 @@ Global
{99E240CC-6381-40DF-B784-528D2EDBBDFC} = {6F76E9AF-7E6D-42D8-918A-4C5B26BE07C1}
{C0F21413-47D0-4584-84A5-B730909E7349} = {6F76E9AF-7E6D-42D8-918A-4C5B26BE07C1}
{D80680EA-8B09-44BA-A9EE-435C93D298B1} = {29E3DE2A-9C25-4F48-8FCB-0BBAA9699662}
+ {965A5904-F5EC-40EE-822C-3E6912640F80} = {AF4411D4-8EE9-423E-8213-1C9D35E47882}
+ {79B6F41F-3E5A-4ABF-83A4-5860F9ADAC35} = {965A5904-F5EC-40EE-822C-3E6912640F80}
+ {201CF0D3-AA8A-40D3-843E-4382F9EDEABA} = {79B6F41F-3E5A-4ABF-83A4-5860F9ADAC35}
+ {6BECD5C6-9978-41E1-BAC0-854E16F0D142} = {965A5904-F5EC-40EE-822C-3E6912640F80}
+ {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662} = {6BECD5C6-9978-41E1-BAC0-854E16F0D142}
+ {8A99EB76-83DA-4E88-A7F8-265224F901D5} = {6BECD5C6-9978-41E1-BAC0-854E16F0D142}
EndGlobalSection
EndGlobal
diff --git a/src/NLightning.Bolts/AssemblyInfo.cs b/src/NLightning.Bolts/AssemblyInfo.cs
index ad60fd6..d9bfb36 100644
--- a/src/NLightning.Bolts/AssemblyInfo.cs
+++ b/src/NLightning.Bolts/AssemblyInfo.cs
@@ -1,4 +1,6 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("NLightning.Bolts.Tests")]
-[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
+[assembly: InternalsVisibleTo("ActOneFuzzer")]
+[assembly: InternalsVisibleTo("ActTwoFuzzer")]
\ No newline at end of file
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/InvoiceRoundTripFuzzer.csproj b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/InvoiceRoundTripFuzzer.csproj
new file mode 100644
index 0000000..d66d099
--- /dev/null
+++ b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/InvoiceRoundTripFuzzer.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Program.cs b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Program.cs
new file mode 100644
index 0000000..93d0534
--- /dev/null
+++ b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Program.cs
@@ -0,0 +1,33 @@
+using NLightning.Bolts.BOLT11;
+using NLightning.Bolts.Exceptions;
+using SharpFuzz;
+
+namespace InvoiceRoundTripFuzzer;
+
+internal abstract class InvoiceRoundTripFuzzer
+{
+ private static void Main()
+ {
+ Fuzzer.OutOfProcess.Run(stream =>
+ {
+ //pwsh ../../fuzz.ps1 InvoiceRoundTripFuzzer.csproj -i Testcases
+ try
+ {
+ using var reader = new StreamReader(stream);
+
+ var invoiceData = reader.ReadToEnd();
+
+ var invoice = Invoice.Decode(invoiceData);
+
+ var encodedInvoice = invoice.Encode();
+
+ if (!invoiceData.Equals(encodedInvoice, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new Exception("Invoice mismatch");
+ }
+
+ }
+ catch (InvoiceSerializationException) { }
+ });
+ }
+}
\ No newline at end of file
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Testcases/sample1.txt b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Testcases/sample1.txt
new file mode 100644
index 0000000..6e4c257
--- /dev/null
+++ b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Testcases/sample1.txt
@@ -0,0 +1 @@
+lnbc2500n1p0j5lkpp5ujnc3jk8zzxy8ycqzrfpp5f8n5x2c7csthrunm8lzw29tuk3l8qprn5h63qsp5u3u7t
\ No newline at end of file
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/ActOneFuzzer.csproj b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/ActOneFuzzer.csproj
new file mode 100644
index 0000000..e08d49a
--- /dev/null
+++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/ActOneFuzzer.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Program.cs b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Program.cs
new file mode 100644
index 0000000..9873ec5
--- /dev/null
+++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Program.cs
@@ -0,0 +1,40 @@
+using System.Security.Cryptography;
+using NLightning.Bolts.BOLT8.Constants;
+using NLightning.Bolts.BOLT8.States;
+using NLightning.Bolts.Tests.BOLT8.Mock;
+using NLightning.Bolts.Tests.BOLT8.Utils;
+using SharpFuzz;
+
+namespace ActOneFuzzer;
+
+internal abstract class ActOneFuzzer
+{
+ private static void Main()
+ {
+ Fuzzer.OutOfProcess.Run(stream =>
+ {
+ //pwsh ../../fuzz.ps1 ActOneFuzzer.csproj -i Testcases
+ try
+ {
+ using var memory = new MemoryStream();
+ stream.CopyTo(memory);
+ var data = memory.ToArray().AsSpan();
+
+ var responder = new HandshakeState(
+ false,
+ ResponderValidKeysUtil.LocalStaticPrivateKey,
+ ResponderValidKeysUtil.LocalStaticPublicKey,
+ new FakeFixedKeyDh(ResponderValidKeysUtil.EphemeralPrivateKey)
+ );
+
+ var buffer = new byte[ProtocolConstants.MAX_MESSAGE_LENGTH];
+ responder.ReadMessage(data, buffer);
+ }
+ catch (ObjectDisposedException) { }
+ catch (InvalidOperationException) { }
+ catch (ArgumentException) { }
+ catch (CryptographicException) { }
+ catch (FormatException) { } //Crash found: System.FormatException: Invalid public key at NBitcoin.PubKey..ctor(ReadOnlySpan`1 bytes)
+ });
+ }
+}
\ No newline at end of file
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Testcases/sample_input b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Testcases/sample_input
new file mode 100644
index 0000000..16cb829
Binary files /dev/null and b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Testcases/sample_input differ
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/ActTwoFuzzer.csproj b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/ActTwoFuzzer.csproj
new file mode 100644
index 0000000..e08d49a
--- /dev/null
+++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/ActTwoFuzzer.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Program.cs b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Program.cs
new file mode 100644
index 0000000..ba9681d
--- /dev/null
+++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Program.cs
@@ -0,0 +1,45 @@
+using System.Security.Cryptography;
+using System.Text;
+using NLightning.Bolts.BOLT8.Constants;
+using NLightning.Bolts.BOLT8.States;
+using NLightning.Bolts.Tests.BOLT8.Mock;
+using NLightning.Bolts.Tests.BOLT8.Utils;
+using SharpFuzz;
+
+namespace ActTwoFuzzer;
+
+internal abstract class ActTwoFuzzer
+{
+ private static void Main()
+ {
+ Fuzzer.OutOfProcess.Run(stream =>
+ {
+ //pwsh ../../fuzz.ps1 ActTwoFuzzer.csproj -i Testcases
+ try
+ {
+ using var memory = new MemoryStream();
+ stream.CopyTo(memory);
+ var data = memory.ToArray().AsSpan();
+
+ var initiator = new HandshakeState(
+ true,
+ InitiatorValidKeysUtil.LocalStaticPrivateKey,
+ InitiatorValidKeysUtil.RemoteStaticPublicKey,
+ new FakeFixedKeyDh(InitiatorValidKeysUtil.EphemeralPrivateKey)
+ );
+
+ var buffer = new byte[ProtocolConstants.MAX_MESSAGE_LENGTH];
+
+ initiator.WriteMessage(Encoding.ASCII.GetBytes(string.Empty), buffer);
+
+ initiator.ReadMessage(data, buffer);
+ }
+ catch (ObjectDisposedException) { }
+ catch (InvalidOperationException) { }
+ catch (ArgumentException) { }
+ catch (CryptographicException) { }
+ catch (FormatException) { } //Crash found: System.FormatException: Invalid public key at NBitcoin.PubKey..ctor(ReadOnlySpan`1 bytes)
+
+ });
+ }
+}
\ No newline at end of file
diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Testcases/sample_input b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Testcases/sample_input
new file mode 100644
index 0000000..16cb829
Binary files /dev/null and b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Testcases/sample_input differ
diff --git a/test/FuzzTests/fuzz.ps1 b/test/FuzzTests/fuzz.ps1
new file mode 100644
index 0000000..3e5d79b
--- /dev/null
+++ b/test/FuzzTests/fuzz.ps1
@@ -0,0 +1,63 @@
+param (
+ [Parameter(Mandatory = $true)]
+ [string]$project,
+ [Parameter(Mandatory = $true)]
+ [string]$i,
+ [string]$x = $null,
+ [int]$t = 10000,
+ [string]$command = "sharpfuzz"
+)
+
+Set-StrictMode -Version Latest
+
+$outputDir = "bin"
+$findingsDir = "findings"
+
+if (Test-Path $outputDir) {
+ Remove-Item -Recurse -Force $outputDir
+}
+
+if (Test-Path $findingsDir) {
+ Remove-Item -Recurse -Force $findingsDir
+}
+
+dotnet publish $project -c release -o $outputDir
+
+$projectName = (Get-Item $project).BaseName
+$projectDll = "$projectName.dll"
+$project = Join-Path $outputDir $projectDll
+
+$exclusions = @(
+ "dnlib.dll",
+ "SharpFuzz.dll",
+ "SharpFuzz.Common.dll",
+ $projectDll
+)
+
+$fuzzingTargets = Get-ChildItem $outputDir -Filter *.dll `
+| Where-Object { $_.Name -notin $exclusions } `
+| Where-Object { $_.Name -notlike "System.*.dll" }
+
+if (($fuzzingTargets | Measure-Object).Count -eq 0) {
+ Write-Error "No fuzzing targets found"
+ exit 1
+}
+
+foreach ($fuzzingTarget in $fuzzingTargets) {
+ Write-Output "Instrumenting $fuzzingTarget"
+ & $command $fuzzingTarget
+
+ if ($LastExitCode -ne 0) {
+ Write-Error "An error occurred while instrumenting $fuzzingTarget"
+ exit 1
+ }
+}
+
+$env:AFL_SKIP_BIN_CHECK = 1
+
+if ($x) {
+ afl-fuzz -i $i -o $findingsDir -t $t -m none -x $x dotnet $project
+}
+else {
+ afl-fuzz -i $i -o $findingsDir -t $t -m none dotnet $project
+}
\ No newline at end of file
diff --git a/test/FuzzTests/install-fuzz-tools.sh b/test/FuzzTests/install-fuzz-tools.sh
new file mode 100644
index 0000000..2c13203
--- /dev/null
+++ b/test/FuzzTests/install-fuzz-tools.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+set -eux
+
+# Download and extract the latest afl-fuzz source package
+wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
+tar -xvf afl-latest.tgz
+
+rm afl-latest.tgz
+cd afl-2.52b/
+
+# Install afl-fuzz
+sudo make install
+cd ..
+rm -rf afl-2.52b/
+
+# Install SharpFuzz.CommandLine global .NET tool
+dotnet tool install --global SharpFuzz.CommandLine
\ No newline at end of file
diff --git a/test/NLightning.Bolts.Tests/AssemblyInfo.cs b/test/NLightning.Bolts.Tests/AssemblyInfo.cs
index 62ff212..025a0ab 100644
--- a/test/NLightning.Bolts.Tests/AssemblyInfo.cs
+++ b/test/NLightning.Bolts.Tests/AssemblyInfo.cs
@@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;
-[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
+[assembly: InternalsVisibleTo("ActOneFuzzer")]
+[assembly: InternalsVisibleTo("ActTwoFuzzer")]
\ No newline at end of file