mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-02-21 19:22:53 +00:00
Compare commits
352 Commits
v0.8.1-dev
...
ghostdagRe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16a83f9bac | ||
|
|
7d20ee6b58 | ||
|
|
1ab05c3fbc | ||
|
|
347dd8fc4b | ||
|
|
d2cccd2829 | ||
|
|
7186f83095 | ||
|
|
5c394c2951 | ||
|
|
a786cdc15e | ||
|
|
6dd3d4a9e7 | ||
|
|
73b36f12f0 | ||
|
|
a795a9e619 | ||
|
|
0be1bba408 | ||
|
|
6afc06ce58 | ||
|
|
d01a213f3d | ||
|
|
7ad8ce521c | ||
|
|
86ba80a091 | ||
|
|
088e2114c2 | ||
|
|
2854d91688 | ||
|
|
af10b59181 | ||
|
|
c5b0394bbc | ||
|
|
9266d179a9 | ||
|
|
321792778e | ||
|
|
70f3fa9893 | ||
|
|
4e18031483 | ||
|
|
2abc284e3b | ||
|
|
f1451406f7 | ||
|
|
c12e180873 | ||
|
|
3959bc1e7c | ||
|
|
6ec0a8a559 | ||
|
|
6824be9216 | ||
|
|
d0511c1636 | ||
|
|
7d69b66c7c | ||
|
|
cebcab7f5c | ||
|
|
caf251b7a8 | ||
|
|
1a4161ffc0 | ||
|
|
b84080f3d9 | ||
|
|
cbd0bb6d14 | ||
|
|
d9449a32b8 | ||
|
|
0ee8f2b631 | ||
|
|
ff1c96c149 | ||
|
|
5e335be5ab | ||
|
|
032eda4604 | ||
|
|
e4e3541a30 | ||
|
|
b5933bc4fe | ||
|
|
ec446ac511 | ||
|
|
3d668cc1bd | ||
|
|
1486a6312c | ||
|
|
cd27f2850e | ||
|
|
14cf7f81f3 | ||
|
|
74539f8f0b | ||
|
|
a7299c1b87 | ||
|
|
27c1e4611e | ||
|
|
b8413fcecb | ||
|
|
c084c69771 | ||
|
|
53781eed4d | ||
|
|
837fa65735 | ||
|
|
dd3b2cf7d1 | ||
|
|
3fd324ca28 | ||
|
|
0271858f25 | ||
|
|
7909480757 | ||
|
|
18274c236b | ||
|
|
7829a9fd76 | ||
|
|
1548ed9629 | ||
|
|
32cd643e8b | ||
|
|
05df6e3e4e | ||
|
|
ce326b3c7d | ||
|
|
a4ae263ed5 | ||
|
|
79be1edaa5 | ||
|
|
c1ef5f0c56 | ||
|
|
458e409654 | ||
|
|
df19bdfaf3 | ||
|
|
103edf97d0 | ||
|
|
1f69f9eed9 | ||
|
|
6a12428504 | ||
|
|
63b1d2a05a | ||
|
|
089115c389 | ||
|
|
24a12cf2a1 | ||
|
|
6597f24bab | ||
|
|
dc84913214 | ||
|
|
201646282b | ||
|
|
3f08bf87a8 | ||
|
|
581a12db96 | ||
|
|
fb6c9c8f21 | ||
|
|
2adb4f5d0f | ||
|
|
9ffda2b1da | ||
|
|
bee0893660 | ||
|
|
a7bb1853f9 | ||
|
|
35e555e959 | ||
|
|
a250f697ee | ||
|
|
f66708b3c6 | ||
|
|
8fbea5d239 | ||
|
|
5fa06fe7d7 | ||
|
|
06fd6f1b95 | ||
|
|
d2f4ed660c | ||
|
|
19878aa062 | ||
|
|
6415e525c3 | ||
|
|
995e526dae | ||
|
|
00a023620d | ||
|
|
2908a46441 | ||
|
|
e78cdff3d0 | ||
|
|
2a31074fc4 | ||
|
|
d835f72e74 | ||
|
|
a581dea127 | ||
|
|
7b4b5668e2 | ||
|
|
0e2061d838 | ||
|
|
0a579e7f78 | ||
|
|
1ed6c4c086 | ||
|
|
fea83e5c6c | ||
|
|
7c3beb526e | ||
|
|
171deded4e | ||
|
|
94cdc77481 | ||
|
|
1222a555f2 | ||
|
|
f13fc35b9e | ||
|
|
2d61a67592 | ||
|
|
3a4fa6e0e1 | ||
|
|
2edf6bfd07 | ||
|
|
8225f7fb3c | ||
|
|
3d0a2a47b2 | ||
|
|
4a354cd538 | ||
|
|
1a3b16aaa3 | ||
|
|
5b5a7e60af | ||
|
|
d30f05b250 | ||
|
|
6bc7a4eb85 | ||
|
|
608d1f8ef9 | ||
|
|
a792d4a19e | ||
|
|
8941c518fc | ||
|
|
6f53da18b1 | ||
|
|
44280b9006 | ||
|
|
dbababb978 | ||
|
|
238950cb98 | ||
|
|
ee8fa32ff8 | ||
|
|
e7f9606683 | ||
|
|
97be133cee | ||
|
|
aeb8e9d2cd | ||
|
|
b636ae234e | ||
|
|
a3913dbf80 | ||
|
|
2871a6a527 | ||
|
|
d5a3a96bde | ||
|
|
12c438d389 | ||
|
|
280fa3de46 | ||
|
|
d281dabdb4 | ||
|
|
331042edf1 | ||
|
|
669a9ab4c3 | ||
|
|
65e149b2bb | ||
|
|
7c1495ba65 | ||
|
|
13ffa5093c | ||
|
|
a9a810a2b2 | ||
|
|
c9b591f2d3 | ||
|
|
8d6e71d490 | ||
|
|
2823461fe2 | ||
|
|
2075c585da | ||
|
|
01aee62cb0 | ||
|
|
a6ee871f7e | ||
|
|
6393a8186a | ||
|
|
3916534a7e | ||
|
|
0561347ff1 | ||
|
|
fb11981da1 | ||
|
|
1742e76af7 | ||
|
|
ddfe376388 | ||
|
|
52da65077a | ||
|
|
ed85f09742 | ||
|
|
819ec9f2a7 | ||
|
|
7ea8a72a9e | ||
|
|
ca04c049ab | ||
|
|
9a17198e7d | ||
|
|
756f40c59a | ||
|
|
6a03d31f98 | ||
|
|
319ab6cfcd | ||
|
|
abef96e3de | ||
|
|
2e0bc0f8c4 | ||
|
|
acf5423c63 | ||
|
|
effb545d20 | ||
|
|
ad9c213a06 | ||
|
|
a4adbabf96 | ||
|
|
799eb7515c | ||
|
|
0769705b37 | ||
|
|
189e3b6be9 | ||
|
|
e8dfbc8367 | ||
|
|
d70740331a | ||
|
|
9a81b1328a | ||
|
|
d4f3a252ff | ||
|
|
f14527de4c | ||
|
|
dd57e6abe6 | ||
|
|
67be4d82bf | ||
|
|
a1381d6768 | ||
|
|
10b519a3e2 | ||
|
|
a35f8269ea | ||
|
|
15af6641fc | ||
|
|
1b97cfb302 | ||
|
|
61be80a60c | ||
|
|
83134cc2b5 | ||
|
|
4988817da1 | ||
|
|
68bd8330ac | ||
|
|
192dd2ba8f | ||
|
|
cc49b1826a | ||
|
|
ce348373c6 | ||
|
|
8ad5725421 | ||
|
|
23a2fbf401 | ||
|
|
c1361e5b3e | ||
|
|
53744ceb45 | ||
|
|
bcf2302460 | ||
|
|
6101e6bdb6 | ||
|
|
d9b97afb92 | ||
|
|
b8ca33d91d | ||
|
|
c7deda41c6 | ||
|
|
434cf45112 | ||
|
|
2cc0bf1639 | ||
|
|
0f2d0d45b5 | ||
|
|
09e1a73340 | ||
|
|
c6d20c1f6f | ||
|
|
49e0a2a2e7 | ||
|
|
285ae5cd40 | ||
|
|
541205904e | ||
|
|
256b7f25f1 | ||
|
|
82d95c59a3 | ||
|
|
79c5d4595e | ||
|
|
b195301a99 | ||
|
|
4ad89056c2 | ||
|
|
64f6cd2178 | ||
|
|
26368cd674 | ||
|
|
9ea4c0fa38 | ||
|
|
a04a5462ae | ||
|
|
4577023e44 | ||
|
|
d8293ef635 | ||
|
|
2059d6ba56 | ||
|
|
3ec1cbe236 | ||
|
|
741e0962be | ||
|
|
6279db2bf1 | ||
|
|
e24bc527f3 | ||
|
|
8a309a7d2a | ||
|
|
70d515a5a9 | ||
|
|
72a7ca53e6 | ||
|
|
119e7374e1 | ||
|
|
e509cb1597 | ||
|
|
0fb97a4f37 | ||
|
|
789a7379bd | ||
|
|
778375c4af | ||
|
|
acef311fb4 | ||
|
|
e8cad2b2f3 | ||
|
|
97fddeff4b | ||
|
|
d6fe9a3017 | ||
|
|
1abffd472c | ||
|
|
8c8da3b01f | ||
|
|
51625e7967 | ||
|
|
6fa3aa1dca | ||
|
|
23304a4977 | ||
|
|
b473a09111 | ||
|
|
dd35669861 | ||
|
|
9401b77a4f | ||
|
|
e87368157d | ||
|
|
7dd0188838 | ||
|
|
6172e48adc | ||
|
|
739cffd918 | ||
|
|
bd89ca2125 | ||
|
|
d917a1fc1e | ||
|
|
9b12b9c58a | ||
|
|
533fa8c00e | ||
|
|
0f93189c16 | ||
|
|
52427cb953 | ||
|
|
d72f70fabe | ||
|
|
49b6cc6038 | ||
|
|
c10a087696 | ||
|
|
02d5fb29cf | ||
|
|
48278bd1c0 | ||
|
|
d91afbfe3b | ||
|
|
5f22632836 | ||
|
|
4aafe8a630 | ||
|
|
7e379028f3 | ||
|
|
af1b8c8490 | ||
|
|
c7c8b25c09 | ||
|
|
b0251fe1a6 | ||
|
|
cfe013eca7 | ||
|
|
50e74bf412 | ||
|
|
12f1c3dfab | ||
|
|
a231ec7214 | ||
|
|
8aecf961bc | ||
|
|
0dea766373 | ||
|
|
830ddf4735 | ||
|
|
9d0f513e49 | ||
|
|
bd97075e07 | ||
|
|
05941a76e7 | ||
|
|
7cbda3b018 | ||
|
|
a0b93e1230 | ||
|
|
b749b2db0b | ||
|
|
717914319a | ||
|
|
6ef8eaf133 | ||
|
|
273c271771 | ||
|
|
729e3db145 | ||
|
|
43c00f5e7f | ||
|
|
90d4dbcba1 | ||
|
|
cb9d7e313d | ||
|
|
c2cec2f170 | ||
|
|
e7edfaceb7 | ||
|
|
5632bee49d | ||
|
|
21a459c0f4 | ||
|
|
45edacfbfa | ||
|
|
9f8f0fd747 | ||
|
|
053bb351b5 | ||
|
|
843edc4ba5 | ||
|
|
bd5f4e8c6a | ||
|
|
6b1e691a57 | ||
|
|
bf67c6351e | ||
|
|
99a14c5999 | ||
|
|
b510fc08a7 | ||
|
|
dc3ae4d3ac | ||
|
|
1ebda36b17 | ||
|
|
12379bedb6 | ||
|
|
f90d7d796a | ||
|
|
fddda46d4f | ||
|
|
77adb6c99f | ||
|
|
48e1a2c396 | ||
|
|
6926a7ab81 | ||
|
|
a9e0c33e5c | ||
|
|
2c1688909d | ||
|
|
6714e084e9 | ||
|
|
3354ac67c8 | ||
|
|
0d8f7bba40 | ||
|
|
e04f76b800 | ||
|
|
82fa8e6831 | ||
|
|
b7ca3f4461 | ||
|
|
37bf261da1 | ||
|
|
d90e18ec51 | ||
|
|
9962527793 | ||
|
|
78550d3639 | ||
|
|
7f899b0d09 | ||
|
|
4886425caf | ||
|
|
c3902ed7a8 | ||
|
|
33eaf9edac | ||
|
|
f97b8f7580 | ||
|
|
05979de705 | ||
|
|
32a04d1811 | ||
|
|
a585f32763 | ||
|
|
9866abb75a | ||
|
|
ab3c81c552 | ||
|
|
9756d64f28 | ||
|
|
21fc2d4219 | ||
|
|
f1c6df48c9 | ||
|
|
80c445c78b | ||
|
|
3b6eb73e53 | ||
|
|
f407c44a8d | ||
|
|
a1af992d15 | ||
|
|
048caebda3 | ||
|
|
baa4311a34 | ||
|
|
f6dfce8180 | ||
|
|
a361d62945 | ||
|
|
aac173ed72 | ||
|
|
5f3fb0bf9f | ||
|
|
61f383a713 | ||
|
|
c62bdb2fa1 | ||
|
|
c88869778d | ||
|
|
3fd647b291 | ||
|
|
2f255952b7 |
9
.codecov.yml
Normal file
9
.codecov.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
coverage:
|
||||
status:
|
||||
patch: off
|
||||
|
||||
project:
|
||||
default:
|
||||
informational: true
|
||||
ignore:
|
||||
- "**/*.pb.go" # Ignore all auto generated protobuf structures.
|
||||
196
.github/workflows/SetPageFileSize.ps1
vendored
Normal file
196
.github/workflows/SetPageFileSize.ps1
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
<#
|
||||
# MIT License (MIT) Copyright (c) 2020 Maxim Lobanov and contributors
|
||||
# Source: https://github.com/al-cheb/configure-pagefile-action/blob/master/scripts/SetPageFileSize.ps1
|
||||
.SYNOPSIS
|
||||
Configure Pagefile on Windows machine
|
||||
.NOTES
|
||||
Author: Aleksandr Chebotov
|
||||
|
||||
.EXAMPLE
|
||||
SetPageFileSize.ps1 -MinimumSize 4GB -MaximumSize 8GB -DiskRoot "D:"
|
||||
#>
|
||||
|
||||
param(
|
||||
[System.UInt64] $MinimumSize = 8gb ,
|
||||
[System.UInt64] $MaximumSize = 8gb ,
|
||||
[System.String] $DiskRoot = "D:"
|
||||
)
|
||||
|
||||
# https://referencesource.microsoft.com/#System.IdentityModel/System/IdentityModel/NativeMethods.cs,619688d876febbe1
|
||||
# https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/mm/modwrite/create.htm
|
||||
# https://referencesource.microsoft.com/#mscorlib/microsoft/win32/safehandles/safefilehandle.cs,9b08210f3be75520
|
||||
# https://referencesource.microsoft.com/#mscorlib/system/security/principal/tokenaccesslevels.cs,6eda91f498a38586
|
||||
# https://www.autoitscript.com/forum/topic/117993-api-ntcreatepagingfile/
|
||||
|
||||
$source = @'
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace Util
|
||||
{
|
||||
class NativeMethods
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct LUID
|
||||
{
|
||||
internal uint LowPart;
|
||||
internal uint HighPart;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct LUID_AND_ATTRIBUTES
|
||||
{
|
||||
internal LUID Luid;
|
||||
internal uint Attributes;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct TOKEN_PRIVILEGE
|
||||
{
|
||||
internal uint PrivilegeCount;
|
||||
internal LUID_AND_ATTRIBUTES Privilege;
|
||||
|
||||
internal static readonly uint Size = (uint)Marshal.SizeOf(typeof(TOKEN_PRIVILEGE));
|
||||
}
|
||||
|
||||
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct UNICODE_STRING
|
||||
{
|
||||
internal UInt16 length;
|
||||
internal UInt16 maximumLength;
|
||||
internal string buffer;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
internal static extern IntPtr LocalFree(IntPtr handle);
|
||||
|
||||
[DllImport("advapi32.dll", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)]
|
||||
internal static extern bool LookupPrivilegeValueW(
|
||||
[In] string lpSystemName,
|
||||
[In] string lpName,
|
||||
[Out] out LUID luid
|
||||
);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = false)]
|
||||
internal static extern bool AdjustTokenPrivileges(
|
||||
[In] SafeCloseHandle tokenHandle,
|
||||
[In] bool disableAllPrivileges,
|
||||
[In] ref TOKEN_PRIVILEGE newState,
|
||||
[In] uint bufferLength,
|
||||
[Out] out TOKEN_PRIVILEGE previousState,
|
||||
[Out] out uint returnLength
|
||||
);
|
||||
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = false)]
|
||||
internal static extern bool OpenProcessToken(
|
||||
[In] IntPtr processToken,
|
||||
[In] int desiredAccess,
|
||||
[Out] out SafeCloseHandle tokenHandle
|
||||
);
|
||||
|
||||
[DllImport("ntdll.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
|
||||
internal static extern Int32 NtCreatePagingFile(
|
||||
[In] ref UNICODE_STRING pageFileName,
|
||||
[In] ref Int64 minimumSize,
|
||||
[In] ref Int64 maximumSize,
|
||||
[In] UInt32 flags
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern uint QueryDosDeviceW(
|
||||
string lpDeviceName,
|
||||
StringBuilder lpTargetPath,
|
||||
int ucchMax
|
||||
);
|
||||
}
|
||||
|
||||
public sealed class SafeCloseHandle: SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
|
||||
internal extern static bool CloseHandle(IntPtr handle);
|
||||
|
||||
private SafeCloseHandle() : base(true)
|
||||
{
|
||||
}
|
||||
|
||||
public SafeCloseHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle)
|
||||
{
|
||||
SetHandle(preexistingHandle);
|
||||
}
|
||||
|
||||
override protected bool ReleaseHandle()
|
||||
{
|
||||
return CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public class PageFile
|
||||
{
|
||||
public static void SetPageFileSize(long minimumValue, long maximumValue, string lpDeviceName)
|
||||
{
|
||||
SetPageFilePrivilege();
|
||||
StringBuilder lpTargetPath = new StringBuilder(260);
|
||||
|
||||
UInt32 resultQueryDosDevice = NativeMethods.QueryDosDeviceW(lpDeviceName, lpTargetPath, lpTargetPath.Capacity);
|
||||
if (resultQueryDosDevice == 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
string pageFilePath = lpTargetPath.ToString() + "\\pagefile.sys";
|
||||
|
||||
NativeMethods.UNICODE_STRING pageFileName = new NativeMethods.UNICODE_STRING
|
||||
{
|
||||
length = (ushort)(pageFilePath.Length * 2),
|
||||
maximumLength = (ushort)(2 * (pageFilePath.Length + 1)),
|
||||
buffer = pageFilePath
|
||||
};
|
||||
|
||||
Int32 resultNtCreatePagingFile = NativeMethods.NtCreatePagingFile(ref pageFileName, ref minimumValue, ref maximumValue, 0);
|
||||
if (resultNtCreatePagingFile != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
Console.WriteLine("PageFile: {0} / {1} bytes for {2}", minimumValue, maximumValue, pageFilePath);
|
||||
}
|
||||
|
||||
static void SetPageFilePrivilege()
|
||||
{
|
||||
const int SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||
const int AdjustPrivileges = 0x00000020;
|
||||
const int Query = 0x00000008;
|
||||
|
||||
NativeMethods.LUID luid;
|
||||
NativeMethods.LookupPrivilegeValueW(null, "SeCreatePagefilePrivilege", out luid);
|
||||
|
||||
SafeCloseHandle hToken;
|
||||
NativeMethods.OpenProcessToken(
|
||||
Process.GetCurrentProcess().Handle,
|
||||
AdjustPrivileges | Query,
|
||||
out hToken
|
||||
);
|
||||
|
||||
NativeMethods.TOKEN_PRIVILEGE previousState;
|
||||
NativeMethods.TOKEN_PRIVILEGE newState;
|
||||
uint previousSize = 0;
|
||||
newState.PrivilegeCount = 1;
|
||||
newState.Privilege.Luid = luid;
|
||||
newState.Privilege.Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
NativeMethods.AdjustTokenPrivileges(hToken, false, ref newState, NativeMethods.TOKEN_PRIVILEGE.Size, out previousState, out previousSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
Add-Type -TypeDefinition $source
|
||||
|
||||
# Set SetPageFileSize
|
||||
[Util.PageFile]::SetPageFileSize($minimumSize, $maximumSize, $diskRoot)
|
||||
77
.github/workflows/go-deploy.yml
vendored
Normal file
77
.github/workflows/go-deploy.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Build and Upload assets
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
name: Building For ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Fix windows CRLF
|
||||
run: git config --global core.autocrlf false
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# We need to increase the page size because the tests run out of memory on github CI windows.
|
||||
# Use the powershell script from this github action: https://github.com/al-cheb/configure-pagefile-action/blob/master/scripts/SetPageFileSize.ps1
|
||||
# MIT License (MIT) Copyright (c) 2020 Maxim Lobanov and contributors
|
||||
- name: Increase page size on windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: powershell -command .\.github\workflows\SetPageFileSize.ps1
|
||||
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Build on linux
|
||||
if: runner.os == 'Linux'
|
||||
# `-extldflags=-static` - means static link everything, `-tags netgo,osusergo` means use pure go replacements for "os/user" and "net"
|
||||
# `-s -w` strips the binary to produce smaller size binaries
|
||||
run: |
|
||||
go build -v -ldflags="-s -w -extldflags=-static" -tags netgo,osusergo -o ./bin/ ./...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-linux.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-linux.zip"
|
||||
zip -r "${archive}" ./bin/*
|
||||
echo "archive=${archive}" >> $GITHUB_ENV
|
||||
echo "asset_name=${asset_name}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build on Windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
go build -v -ldflags="-s -w" -o bin/ ./...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-win64.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-win64.zip"
|
||||
powershell "Compress-Archive bin/* \"${archive}\""
|
||||
echo "archive=${archive}" >> $GITHUB_ENV
|
||||
echo "asset_name=${asset_name}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build on MacOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
go build -v -ldflags="-s -w" -o ./bin/ ./...
|
||||
archive="bin/kaspad-${{ github.event.release.tag_name }}-osx.zip"
|
||||
asset_name="kaspad-${{ github.event.release.tag_name }}-osx.zip"
|
||||
zip -r "${archive}" ./bin/*
|
||||
echo "archive=${archive}" >> $GITHUB_ENV
|
||||
echo "asset_name=${asset_name}" >> $GITHUB_ENV
|
||||
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_path: "./${{ env.archive }}"
|
||||
asset_name: "${{ env.asset_name }}"
|
||||
asset_content_type: application/zip
|
||||
49
.github/workflows/go-race.yml
vendored
Normal file
49
.github/workflows/go-race.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: Go-Race
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
race_test:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
branch: [ master, latest ]
|
||||
name: Race detection on ${{ matrix.branch }}
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
|
||||
- name: Set scheduled branch name
|
||||
shell: bash
|
||||
if: github.event_name == 'schedule'
|
||||
run: |
|
||||
if [ "${{ matrix.branch }}" == "master" ]; then
|
||||
echo "run_on=master" >> $GITHUB_ENV
|
||||
fi
|
||||
if [ "${{ matrix.branch }}" == "latest" ]; then
|
||||
branch=$(git branch -r | grep 'v\([0-9]\+\.\)\([0-9]\+\.\)\([0-9]\+\)-dev' | sort -Vr | head -1 | xargs)
|
||||
echo "run_on=${branch}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Set manual branch name
|
||||
shell: bash
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: echo "run_on=${{ github.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Test with race detector
|
||||
shell: bash
|
||||
run: |
|
||||
git checkout "${{ env.run_on }}"
|
||||
git status
|
||||
go test -race ./...
|
||||
73
.github/workflows/go.yml
vendored
Normal file
73
.github/workflows/go.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
name: Go
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
# edtited - "title, body, or the base branch of the PR is modified"
|
||||
# synchronize - "commit(s) pushed to the pull request"
|
||||
types: [opened, synchronize, edited, reopened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-16.04, macos-10.15 ]
|
||||
name: Testing on on ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
- name: Fix windows CRLF
|
||||
run: git config --global core.autocrlf false
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# We need to increase the page size because the tests run out of memory on github CI windows.
|
||||
# Use the powershell script from this github action: https://github.com/al-cheb/configure-pagefile-action/blob/master/scripts/SetPageFileSize.ps1
|
||||
# MIT License (MIT) Copyright (c) 2020 Maxim Lobanov and contributors
|
||||
- name: Increase page size on windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: powershell -command .\.github\workflows\SetPageFileSize.ps1
|
||||
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
|
||||
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
|
||||
- name: Go Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: ./build_and_test.sh -v
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Produce code coverage
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Delete the stability tests from coverage
|
||||
run: rm -r stability-tests
|
||||
|
||||
- name: Create coverage file
|
||||
run: go test -v -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
||||
|
||||
- name: Upload coverage file
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -13,6 +13,21 @@ kaspad.db
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Real binaries, build with `go build .`
|
||||
kaspad
|
||||
cmd/gencerts/gencerts
|
||||
cmd/kaspactl/kaspactl
|
||||
cmd/kasminer/kaspaminer
|
||||
*.exe
|
||||
*.exe~
|
||||
|
||||
# Output of the go coverage tool
|
||||
*.out
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
@@ -31,8 +46,7 @@ _cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
19
CONTRIBUTING.md
Normal file
19
CONTRIBUTING.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Contributing to Kaspad
|
||||
|
||||
Any contribution to Kaspad is very welcome.
|
||||
|
||||
## Getting started
|
||||
|
||||
If you want to start contributing to Kaspad and don't know where to start, you can pick an issue from
|
||||
the [list](https://github.com/kaspanet/kaspad/issues).
|
||||
|
||||
If you want to make a big change it's better to discuss it first by opening an issue or talk about it in
|
||||
[Discord](https://discord.gg/WmGhhzk) to avoid duplicate work.
|
||||
|
||||
## Pull Request process
|
||||
|
||||
Any pull request should be opened against the development branch of the target version. The development branch format is
|
||||
as follows: `vx.y.z-dev`, for example: `v0.8.5-dev`.
|
||||
|
||||
All pull requests should pass the checks written in `build_and_test.sh`, so it's recommended to run this script before
|
||||
submitting your PR.
|
||||
29
README.md
29
README.md
@@ -9,12 +9,16 @@ Warning: This is pre-alpha software. There's no guarantee anything works.
|
||||
|
||||
Kaspad is the reference full node Kaspa implementation written in Go (golang).
|
||||
|
||||
This project is currently under active development and is in a pre-Alpha state.
|
||||
This project is currently under active development and is in a pre-Alpha state.
|
||||
Some things still don't work and APIs are far from finalized. The code is provided for reference only.
|
||||
|
||||
## What is kaspa
|
||||
|
||||
Kaspa is an attempt at a proof-of-work cryptocurrency with instant confirmations and sub-second block times. It is based on [the PHANTOM protocol](https://eprint.iacr.org/2018/104.pdf), a generalization of Nakamoto consensus.
|
||||
|
||||
## Requirements
|
||||
|
||||
Latest version of [Go](http://golang.org) (currently 1.13).
|
||||
Go 1.16 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -27,23 +31,17 @@ Latest version of [Go](http://golang.org) (currently 1.13).
|
||||
|
||||
```bash
|
||||
$ go version
|
||||
$ go env GOROOT GOPATH
|
||||
```
|
||||
|
||||
NOTE: The `GOROOT` and `GOPATH` above must not be the same path. It is
|
||||
recommended that `GOPATH` is set to a directory in your home directory such as
|
||||
`~/dev/go` to avoid write permission issues. It is also recommended to add
|
||||
`$GOPATH/bin` to your `PATH` at this point.
|
||||
|
||||
- Run the following commands to obtain and install kaspad including all dependencies:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/kaspanet/kaspad $GOPATH/src/github.com/kaspanet/kaspad
|
||||
$ cd $GOPATH/src/github.com/kaspanet/kaspad
|
||||
$ git clone https://github.com/kaspanet/kaspad
|
||||
$ cd kaspad
|
||||
$ go install . ./cmd/...
|
||||
```
|
||||
|
||||
- Kaspad (and utilities) should now be installed in `$GOPATH/bin`. If you did
|
||||
- Kaspad (and utilities) should now be installed in `$(go env GOPATH)/bin`. If you did
|
||||
not already add the bin directory to your system path during Go installation,
|
||||
you are encouraged to do so now.
|
||||
|
||||
@@ -53,14 +51,12 @@ $ go install . ./cmd/...
|
||||
Kaspad has several configuration options available to tweak how it runs, but all
|
||||
of the basic operations work with zero configuration.
|
||||
|
||||
#### Linux/BSD/POSIX/Source
|
||||
|
||||
```bash
|
||||
$ ./kaspad
|
||||
$ kaspad
|
||||
```
|
||||
|
||||
## Discord
|
||||
Join our discord server using the following link: https://discord.gg/WmGhhzk
|
||||
Join our discord server using the following link: https://discord.gg/YNYnNN5Pf2
|
||||
|
||||
## Issue Tracker
|
||||
|
||||
@@ -69,9 +65,8 @@ is used for this project.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation is a work-in-progress. It is located in the [docs](https://github.com/kaspanet/kaspad/tree/master/docs) folder.
|
||||
The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
|
||||
|
||||
## License
|
||||
|
||||
Kaspad is licensed under the copyfree [ISC License](https://choosealicense.com/licenses/isc/).
|
||||
|
||||
|
||||
21
app/app.go
21
app/app.go
@@ -7,21 +7,21 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/signal"
|
||||
"github.com/kaspanet/kaspad/util/profiling"
|
||||
"github.com/kaspanet/kaspad/version"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/execenv"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/limits"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/signal"
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/winservice"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
"github.com/kaspanet/kaspad/util/profiling"
|
||||
"github.com/kaspanet/kaspad/version"
|
||||
)
|
||||
|
||||
const leveldbCacheSizeMiB = 256
|
||||
|
||||
var desiredLimits = &limits.DesiredLimits{
|
||||
FileLimitWant: 2048,
|
||||
FileLimitMin: 1024,
|
||||
@@ -49,6 +49,7 @@ func StartApp() error {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return err
|
||||
}
|
||||
defer logger.BackendLog.Close()
|
||||
defer panics.HandlePanic(log, "MAIN", nil)
|
||||
|
||||
app := &kaspadApp{cfg: cfg}
|
||||
@@ -170,7 +171,7 @@ func doUpgrades() error {
|
||||
|
||||
// dbPath returns the path to the block database given a database type.
|
||||
func databasePath(cfg *config.Config) string {
|
||||
return filepath.Join(cfg.DataDir, "db")
|
||||
return filepath.Join(cfg.AppDir, "data")
|
||||
}
|
||||
|
||||
func removeDatabase(cfg *config.Config) error {
|
||||
@@ -181,5 +182,5 @@ func removeDatabase(cfg *config.Config) error {
|
||||
func openDB(cfg *config.Config) (database.Database, error) {
|
||||
dbPath := databasePath(cfg)
|
||||
log.Infof("Loading database from '%s'", dbPath)
|
||||
return ldb.NewLevelDB(dbPath)
|
||||
return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
||||
}
|
||||
|
||||
@@ -5,34 +5,12 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||
"github.com/kaspanet/kaspad/util/binaryserializer"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MaxVarIntPayload is the maximum payload size for a variable length integer.
|
||||
const MaxVarIntPayload = 9
|
||||
|
||||
// MaxInvPerMsg is the maximum number of inventory vectors that can be in any type of kaspa inv message.
|
||||
const MaxInvPerMsg = 1 << 17
|
||||
|
||||
var (
|
||||
// littleEndian is a convenience variable since binary.LittleEndian is
|
||||
// quite long.
|
||||
littleEndian = binary.LittleEndian
|
||||
|
||||
// bigEndian is a convenience variable since binary.BigEndian is quite
|
||||
// long.
|
||||
bigEndian = binary.BigEndian
|
||||
)
|
||||
|
||||
// errNonCanonicalVarInt is the common format string used for non-canonically
|
||||
// encoded variable length integer errors.
|
||||
var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
|
||||
@@ -40,473 +18,3 @@ var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
|
||||
|
||||
// errNoEncodingForType signifies that there's no encoding for the given type.
|
||||
var errNoEncodingForType = errors.New("there's no encoding for this type")
|
||||
|
||||
// int64Time represents a unix timestamp with milliseconds precision encoded with
|
||||
// an int64. It is used as a way to signal the readElement function how to decode
|
||||
// a timestamp into a Go mstime.Time since it is otherwise ambiguous.
|
||||
type int64Time mstime.Time
|
||||
|
||||
// ReadElement reads the next sequence of bytes from r using little endian
|
||||
// depending on the concrete type of element pointed to.
|
||||
func ReadElement(r io.Reader, element interface{}) error {
|
||||
// Attempt to read the element based on the concrete type via fast
|
||||
// type assertions first.
|
||||
switch e := element.(type) {
|
||||
case *int32:
|
||||
rv, err := binaryserializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = int32(rv)
|
||||
return nil
|
||||
|
||||
case *uint32:
|
||||
rv, err := binaryserializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = rv
|
||||
return nil
|
||||
|
||||
case *int64:
|
||||
rv, err := binaryserializer.Uint64(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = int64(rv)
|
||||
return nil
|
||||
|
||||
case *uint64:
|
||||
rv, err := binaryserializer.Uint64(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = rv
|
||||
return nil
|
||||
|
||||
case *uint8:
|
||||
rv, err := binaryserializer.Uint8(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = rv
|
||||
return nil
|
||||
|
||||
case *bool:
|
||||
rv, err := binaryserializer.Uint8(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rv == 0x00 {
|
||||
*e = false
|
||||
} else {
|
||||
*e = true
|
||||
}
|
||||
return nil
|
||||
|
||||
// Unix timestamp encoded as an int64.
|
||||
case *int64Time:
|
||||
rv, err := binaryserializer.Uint64(r, binary.LittleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = int64Time(mstime.UnixMilliseconds(int64(rv)))
|
||||
return nil
|
||||
|
||||
// Message header checksum.
|
||||
case *[4]byte:
|
||||
_, err := io.ReadFull(r, e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// Message header command.
|
||||
case *MessageCommand:
|
||||
rv, err := binaryserializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = MessageCommand(rv)
|
||||
return nil
|
||||
|
||||
// IP address.
|
||||
case *[16]byte:
|
||||
_, err := io.ReadFull(r, e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case *externalapi.DomainHash:
|
||||
_, err := io.ReadFull(r, e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case *id.ID:
|
||||
return e.Deserialize(r)
|
||||
|
||||
case *externalapi.DomainSubnetworkID:
|
||||
_, err := io.ReadFull(r, e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case *ServiceFlag:
|
||||
rv, err := binaryserializer.Uint64(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = ServiceFlag(rv)
|
||||
return nil
|
||||
|
||||
case *KaspaNet:
|
||||
rv, err := binaryserializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = KaspaNet(rv)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Wrapf(errNoEncodingForType, "couldn't find a way to read type %T", element)
|
||||
}
|
||||
|
||||
// readElements reads multiple items from r. It is equivalent to multiple
|
||||
// calls to readElement.
|
||||
func readElements(r io.Reader, elements ...interface{}) error {
|
||||
for _, element := range elements {
|
||||
err := ReadElement(r, element)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteElement writes the little endian representation of element to w.
|
||||
func WriteElement(w io.Writer, element interface{}) error {
|
||||
// Attempt to write the element based on the concrete type via fast
|
||||
// type assertions first.
|
||||
switch e := element.(type) {
|
||||
case int32:
|
||||
err := binaryserializer.PutUint32(w, littleEndian, uint32(e))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case uint32:
|
||||
err := binaryserializer.PutUint32(w, littleEndian, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case int64:
|
||||
err := binaryserializer.PutUint64(w, littleEndian, uint64(e))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case uint64:
|
||||
err := binaryserializer.PutUint64(w, littleEndian, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case uint8:
|
||||
err := binaryserializer.PutUint8(w, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case bool:
|
||||
var err error
|
||||
if e {
|
||||
err = binaryserializer.PutUint8(w, 0x01)
|
||||
} else {
|
||||
err = binaryserializer.PutUint8(w, 0x00)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// Message header checksum.
|
||||
case [4]byte:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// Message header command.
|
||||
case MessageCommand:
|
||||
err := binaryserializer.PutUint32(w, littleEndian, uint32(e))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// IP address.
|
||||
case [16]byte:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case *externalapi.DomainHash:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case *id.ID:
|
||||
return e.Serialize(w)
|
||||
|
||||
case *externalapi.DomainSubnetworkID:
|
||||
_, err := w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case ServiceFlag:
|
||||
err := binaryserializer.PutUint64(w, littleEndian, uint64(e))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case KaspaNet:
|
||||
err := binaryserializer.PutUint32(w, littleEndian, uint32(e))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Wrapf(errNoEncodingForType, "couldn't find a way to write type %T", element)
|
||||
}
|
||||
|
||||
// writeElements writes multiple items to w. It is equivalent to multiple
|
||||
// calls to writeElement.
|
||||
func writeElements(w io.Writer, elements ...interface{}) error {
|
||||
for _, element := range elements {
|
||||
err := WriteElement(w, element)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadVarInt reads a variable length integer from r and returns it as a uint64.
|
||||
func ReadVarInt(r io.Reader) (uint64, error) {
|
||||
discriminant, err := binaryserializer.Uint8(r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var rv uint64
|
||||
switch discriminant {
|
||||
case 0xff:
|
||||
sv, err := binaryserializer.Uint64(r, littleEndian)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
rv = sv
|
||||
|
||||
// The encoding is not canonical if the value could have been
|
||||
// encoded using fewer bytes.
|
||||
min := uint64(0x100000000)
|
||||
if rv < min {
|
||||
return 0, messageError("readVarInt", fmt.Sprintf(
|
||||
errNonCanonicalVarInt, rv, discriminant, min))
|
||||
}
|
||||
|
||||
case 0xfe:
|
||||
sv, err := binaryserializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
rv = uint64(sv)
|
||||
|
||||
// The encoding is not canonical if the value could have been
|
||||
// encoded using fewer bytes.
|
||||
min := uint64(0x10000)
|
||||
if rv < min {
|
||||
return 0, messageError("readVarInt", fmt.Sprintf(
|
||||
errNonCanonicalVarInt, rv, discriminant, min))
|
||||
}
|
||||
|
||||
case 0xfd:
|
||||
sv, err := binaryserializer.Uint16(r, littleEndian)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
rv = uint64(sv)
|
||||
|
||||
// The encoding is not canonical if the value could have been
|
||||
// encoded using fewer bytes.
|
||||
min := uint64(0xfd)
|
||||
if rv < min {
|
||||
return 0, messageError("readVarInt", fmt.Sprintf(
|
||||
errNonCanonicalVarInt, rv, discriminant, min))
|
||||
}
|
||||
|
||||
default:
|
||||
rv = uint64(discriminant)
|
||||
}
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// WriteVarInt serializes val to w using a variable number of bytes depending
|
||||
// on its value.
|
||||
func WriteVarInt(w io.Writer, val uint64) error {
|
||||
if val < 0xfd {
|
||||
_, err := w.Write([]byte{uint8(val)})
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if val <= math.MaxUint16 {
|
||||
var buf [3]byte
|
||||
buf[0] = 0xfd
|
||||
littleEndian.PutUint16(buf[1:], uint16(val))
|
||||
_, err := w.Write(buf[:])
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if val <= math.MaxUint32 {
|
||||
var buf [5]byte
|
||||
buf[0] = 0xfe
|
||||
littleEndian.PutUint32(buf[1:], uint32(val))
|
||||
_, err := w.Write(buf[:])
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
var buf [9]byte
|
||||
buf[0] = 0xff
|
||||
littleEndian.PutUint64(buf[1:], val)
|
||||
_, err := w.Write(buf[:])
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// VarIntSerializeSize returns the number of bytes it would take to serialize
|
||||
// val as a variable length integer.
|
||||
func VarIntSerializeSize(val uint64) int {
|
||||
// The value is small enough to be represented by itself, so it's
|
||||
// just 1 byte.
|
||||
if val < 0xfd {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Discriminant 1 byte plus 2 bytes for the uint16.
|
||||
if val <= math.MaxUint16 {
|
||||
return 3
|
||||
}
|
||||
|
||||
// Discriminant 1 byte plus 4 bytes for the uint32.
|
||||
if val <= math.MaxUint32 {
|
||||
return 5
|
||||
}
|
||||
|
||||
// Discriminant 1 byte plus 8 bytes for the uint64.
|
||||
return 9
|
||||
}
|
||||
|
||||
// ReadVarString reads a variable length string from r and returns it as a Go
|
||||
// string. A variable length string is encoded as a variable length integer
|
||||
// containing the length of the string followed by the bytes that represent the
|
||||
// string itself. An error is returned if the length is greater than the
|
||||
// maximum block payload size since it helps protect against memory exhaustion
|
||||
// attacks and forced panics through malformed messages.
|
||||
func ReadVarString(r io.Reader, pver uint32) (string, error) {
|
||||
count, err := ReadVarInt(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Prevent variable length strings that are larger than the maximum
|
||||
// message size. It would be possible to cause memory exhaustion and
|
||||
// panics without a sane upper bound on this count.
|
||||
if count > MaxMessagePayload {
|
||||
str := fmt.Sprintf("variable length string is too long "+
|
||||
"[count %d, max %d]", count, MaxMessagePayload)
|
||||
return "", messageError("ReadVarString", str)
|
||||
}
|
||||
|
||||
buf := make([]byte, count)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
// WriteVarString serializes str to w as a variable length integer containing
|
||||
// the length of the string followed by the bytes that represent the string
|
||||
// itself.
|
||||
func WriteVarString(w io.Writer, str string) error {
|
||||
err := WriteVarInt(w, uint64(len(str)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(str))
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadVarBytes reads a variable length byte array. A byte array is encoded
|
||||
// as a varInt containing the length of the array followed by the bytes
|
||||
// themselves. An error is returned if the length is greater than the
|
||||
// passed maxAllowed parameter which helps protect against memory exhaustion
|
||||
// attacks and forced panics through malformed messages. The fieldName
|
||||
// parameter is only used for the error message so it provides more context in
|
||||
// the error.
|
||||
func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
|
||||
fieldName string) ([]byte, error) {
|
||||
|
||||
count, err := ReadVarInt(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prevent byte array larger than the max message size. It would
|
||||
// be possible to cause memory exhaustion and panics without a sane
|
||||
// upper bound on this count.
|
||||
if count > uint64(maxAllowed) {
|
||||
str := fmt.Sprintf("%s is larger than the max allowed size "+
|
||||
"[count %d, max %d]", fieldName, count, maxAllowed)
|
||||
return nil, messageError("ReadVarBytes", str)
|
||||
}
|
||||
|
||||
b := make([]byte, count)
|
||||
_, err = io.ReadFull(r, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteVarBytes serializes a variable length byte array to w as a varInt
|
||||
// containing the number of bytes, followed by the bytes themselves.
|
||||
func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
|
||||
slen := uint64(len(bytes))
|
||||
err := WriteVarInt(w, slen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(bytes)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,696 +1,44 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// mainnetGenesisHash is the hash of the first block in the block DAG for the
|
||||
// main network (genesis block).
|
||||
var mainnetGenesisHash = &externalapi.DomainHash{
|
||||
var mainnetGenesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25,
|
||||
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
||||
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
||||
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
||||
}
|
||||
})
|
||||
|
||||
// simnetGenesisHash is the hash of the first block in the block DAG for the
|
||||
// simulation test network.
|
||||
var simnetGenesisHash = &externalapi.DomainHash{
|
||||
var simnetGenesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x9d, 0x89, 0xb0, 0x6e, 0xb3, 0x47, 0xb5, 0x6e,
|
||||
0xcd, 0x6c, 0x63, 0x99, 0x45, 0x91, 0xd5, 0xce,
|
||||
0x9b, 0x43, 0x05, 0xc1, 0xa5, 0x5e, 0x2a, 0xda,
|
||||
0x90, 0x4c, 0xf0, 0x6c, 0x4d, 0x5f, 0xd3, 0x62,
|
||||
}
|
||||
})
|
||||
|
||||
// mainnetGenesisMerkleRoot is the hash of the first transaction in the genesis
|
||||
// block for the main network.
|
||||
var mainnetGenesisMerkleRoot = &externalapi.DomainHash{
|
||||
var mainnetGenesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a,
|
||||
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
||||
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
||||
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
||||
}
|
||||
})
|
||||
|
||||
var exampleAcceptedIDMerkleRoot = &externalapi.DomainHash{
|
||||
var exampleAcceptedIDMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C,
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
}
|
||||
})
|
||||
|
||||
var exampleUTXOCommitment = &externalapi.DomainHash{
|
||||
var exampleUTXOCommitment = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C,
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
}
|
||||
|
||||
// TestElementEncoding tests appmessage encode and decode for various element types. This
|
||||
// is mainly to test the "fast" paths in readElement and writeElement which use
|
||||
// type assertions to avoid reflection when possible.
|
||||
func TestElementEncoding(t *testing.T) {
|
||||
tests := []struct {
|
||||
in interface{} // Value to encode
|
||||
buf []byte // Encoded value
|
||||
}{
|
||||
{int32(1), []byte{0x01, 0x00, 0x00, 0x00}},
|
||||
{uint32(256), []byte{0x00, 0x01, 0x00, 0x00}},
|
||||
{
|
||||
int64(65536),
|
||||
[]byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
uint64(4294967296),
|
||||
[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]byte{0x01},
|
||||
},
|
||||
{
|
||||
false,
|
||||
[]byte{0x00},
|
||||
},
|
||||
{
|
||||
[4]byte{0x01, 0x02, 0x03, 0x04},
|
||||
[]byte{0x01, 0x02, 0x03, 0x04},
|
||||
},
|
||||
{
|
||||
MessageCommand(0x10),
|
||||
[]byte{
|
||||
0x10, 0x00, 0x00, 0x00,
|
||||
},
|
||||
},
|
||||
{
|
||||
[16]byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
},
|
||||
[]byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
},
|
||||
},
|
||||
{
|
||||
(*externalapi.DomainHash)(&[externalapi.DomainHashSize]byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
}),
|
||||
[]byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
},
|
||||
},
|
||||
{
|
||||
ServiceFlag(SFNodeNetwork),
|
||||
[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
KaspaNet(Mainnet),
|
||||
[]byte{0x1d, 0xf7, 0xdc, 0x3d},
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Write to appmessage format.
|
||||
var buf bytes.Buffer
|
||||
err := WriteElement(&buf, test.in)
|
||||
if err != nil {
|
||||
t.Errorf("writeElement #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||
t.Errorf("writeElement #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||
continue
|
||||
}
|
||||
|
||||
// Read from appmessage format.
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
val := test.in
|
||||
if reflect.ValueOf(test.in).Kind() != reflect.Ptr {
|
||||
val = reflect.New(reflect.TypeOf(test.in)).Interface()
|
||||
}
|
||||
err = ReadElement(rbuf, val)
|
||||
if err != nil {
|
||||
t.Errorf("readElement #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
ival := val
|
||||
if reflect.ValueOf(test.in).Kind() != reflect.Ptr {
|
||||
ival = reflect.Indirect(reflect.ValueOf(val)).Interface()
|
||||
}
|
||||
if !reflect.DeepEqual(ival, test.in) {
|
||||
t.Errorf("readElement #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(ival), spew.Sdump(test.in))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestElementEncodingErrors performs negative tests against appmessage encode and decode
|
||||
// of various element types to confirm error paths work correctly.
|
||||
func TestElementEncodingErrors(t *testing.T) {
|
||||
type writeElementReflect int32
|
||||
|
||||
tests := []struct {
|
||||
in interface{} // Value to encode
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
{int32(1), 0, io.ErrShortWrite, io.EOF},
|
||||
{uint32(256), 0, io.ErrShortWrite, io.EOF},
|
||||
{int64(65536), 0, io.ErrShortWrite, io.EOF},
|
||||
{true, 0, io.ErrShortWrite, io.EOF},
|
||||
{[4]byte{0x01, 0x02, 0x03, 0x04}, 0, io.ErrShortWrite, io.EOF},
|
||||
{
|
||||
MessageCommand(10),
|
||||
0, io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
{
|
||||
[16]byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
},
|
||||
0, io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
{
|
||||
(*externalapi.DomainHash)(&[externalapi.DomainHashSize]byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
}),
|
||||
0, io.ErrShortWrite, io.EOF,
|
||||
},
|
||||
{ServiceFlag(SFNodeNetwork), 0, io.ErrShortWrite, io.EOF},
|
||||
{KaspaNet(Mainnet), 0, io.ErrShortWrite, io.EOF},
|
||||
// Type with no supported encoding.
|
||||
{writeElementReflect(0), 0, errNoEncodingForType, errNoEncodingForType},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to appmessage format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := WriteElement(w, test.in)
|
||||
if !errors.Is(err, test.writeErr) {
|
||||
t.Errorf("writeElement #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode from appmessage format.
|
||||
r := newFixedReader(test.max, nil)
|
||||
val := test.in
|
||||
if reflect.ValueOf(test.in).Kind() != reflect.Ptr {
|
||||
val = reflect.New(reflect.TypeOf(test.in)).Interface()
|
||||
}
|
||||
err = ReadElement(r, val)
|
||||
if !errors.Is(err, test.readErr) {
|
||||
t.Errorf("readElement #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarIntEncoding tests appmessage encode and decode for variable length integers.
|
||||
func TestVarIntEncoding(t *testing.T) {
|
||||
tests := []struct {
|
||||
value uint64 // Value to encode
|
||||
buf []byte // Encoded value
|
||||
}{
|
||||
// Latest protocol version.
|
||||
// Single byte
|
||||
{0, []byte{0x00}},
|
||||
// Max single byte
|
||||
{0xfc, []byte{0xfc}},
|
||||
// Min 2-byte
|
||||
{0xfd, []byte{0xfd, 0x0fd, 0x00}},
|
||||
// Max 2-byte
|
||||
{0xffff, []byte{0xfd, 0xff, 0xff}},
|
||||
// Min 4-byte
|
||||
{0x10000, []byte{0xfe, 0x00, 0x00, 0x01, 0x00}},
|
||||
// Max 4-byte
|
||||
{0xffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff}},
|
||||
// Min 8-byte
|
||||
{
|
||||
0x100000000,
|
||||
[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
|
||||
},
|
||||
// Max 8-byte
|
||||
{
|
||||
0xffffffffffffffff,
|
||||
[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to appmessage format.
|
||||
buf := &bytes.Buffer{}
|
||||
err := WriteVarInt(buf, test.value)
|
||||
if err != nil {
|
||||
t.Errorf("WriteVarInt #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||
t.Errorf("WriteVarInt #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode from appmessage format.
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
val, err := ReadVarInt(rbuf)
|
||||
if err != nil {
|
||||
t.Errorf("ReadVarInt #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if val != test.value {
|
||||
t.Errorf("ReadVarInt #%d\n got: %x want: %x", i,
|
||||
val, test.value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarIntEncodingErrors performs negative tests against appmessage encode and decode
|
||||
// of variable length integers to confirm error paths work correctly.
|
||||
func TestVarIntEncodingErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
in uint64 // Value to encode
|
||||
buf []byte // Encoded value
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Force errors on discriminant.
|
||||
{0, []byte{0x00}, 0, io.ErrShortWrite, io.EOF},
|
||||
// Force errors on 2-byte read/write.
|
||||
{0xfd, []byte{0xfd}, 0, io.ErrShortWrite, io.EOF}, // error on writing length
|
||||
{0xfd, []byte{0xfd}, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, // error on writing actual data
|
||||
// Force errors on 4-byte read/write.
|
||||
{0x10000, []byte{0xfe}, 0, io.ErrShortWrite, io.EOF}, // error on writing length
|
||||
{0x10000, []byte{0xfe}, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, // error on writing actual data
|
||||
// Force errors on 8-byte read/write.
|
||||
{0x100000000, []byte{0xff}, 0, io.ErrShortWrite, io.EOF}, // error on writing length
|
||||
{0x100000000, []byte{0xff}, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, // error on writing actual data
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to appmessage format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := WriteVarInt(w, test.in)
|
||||
if !errors.Is(err, test.writeErr) {
|
||||
t.Errorf("WriteVarInt #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode from appmessage format.
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
_, err = ReadVarInt(r)
|
||||
if !errors.Is(err, test.readErr) {
|
||||
t.Errorf("ReadVarInt #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarIntNonCanonical ensures variable length integers that are not encoded
|
||||
// canonically return the expected error.
|
||||
func TestVarIntNonCanonical(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
tests := []struct {
|
||||
name string // Test name for easier identification
|
||||
in []byte // Value to decode
|
||||
pver uint32 // Protocol version for appmessage encoding
|
||||
}{
|
||||
{
|
||||
"0 encoded with 3 bytes", []byte{0xfd, 0x00, 0x00},
|
||||
pver,
|
||||
},
|
||||
{
|
||||
"max single-byte value encoded with 3 bytes",
|
||||
[]byte{0xfd, 0xfc, 0x00}, pver,
|
||||
},
|
||||
{
|
||||
"0 encoded with 5 bytes",
|
||||
[]byte{0xfe, 0x00, 0x00, 0x00, 0x00}, pver,
|
||||
},
|
||||
{
|
||||
"max three-byte value encoded with 5 bytes",
|
||||
[]byte{0xfe, 0xff, 0xff, 0x00, 0x00}, pver,
|
||||
},
|
||||
{
|
||||
"0 encoded with 9 bytes",
|
||||
[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
pver,
|
||||
},
|
||||
{
|
||||
"max five-byte value encoded with 9 bytes",
|
||||
[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00},
|
||||
pver,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Decode from appmessage format.
|
||||
rbuf := bytes.NewReader(test.in)
|
||||
val, err := ReadVarInt(rbuf)
|
||||
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
|
||||
t.Errorf("ReadVarInt #%d (%s) unexpected error %v", i,
|
||||
test.name, err)
|
||||
continue
|
||||
}
|
||||
if val != 0 {
|
||||
t.Errorf("ReadVarInt #%d (%s)\n got: %d want: 0", i,
|
||||
test.name, val)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarIntEncoding tests the serialize size for variable length integers.
|
||||
func TestVarIntSerializeSize(t *testing.T) {
|
||||
tests := []struct {
|
||||
val uint64 // Value to get the serialized size for
|
||||
size int // Expected serialized size
|
||||
}{
|
||||
// Single byte
|
||||
{0, 1},
|
||||
// Max single byte
|
||||
{0xfc, 1},
|
||||
// Min 2-byte
|
||||
{0xfd, 3},
|
||||
// Max 2-byte
|
||||
{0xffff, 3},
|
||||
// Min 4-byte
|
||||
{0x10000, 5},
|
||||
// Max 4-byte
|
||||
{0xffffffff, 5},
|
||||
// Min 8-byte
|
||||
{0x100000000, 9},
|
||||
// Max 8-byte
|
||||
{0xffffffffffffffff, 9},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
serializedSize := VarIntSerializeSize(test.val)
|
||||
if serializedSize != test.size {
|
||||
t.Errorf("VarIntSerializeSize #%d got: %d, want: %d", i,
|
||||
serializedSize, test.size)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarStringEncoding tests appmessage encode and decode for variable length strings.
|
||||
func TestVarStringEncoding(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// str256 is a string that takes a 2-byte varint to encode.
|
||||
str256 := strings.Repeat("test", 64)
|
||||
|
||||
tests := []struct {
|
||||
in string // String to encode
|
||||
out string // String to decoded value
|
||||
buf []byte // Encoded value
|
||||
pver uint32 // Protocol version for appmessage encoding
|
||||
}{
|
||||
// Latest protocol version.
|
||||
// Empty string
|
||||
{"", "", []byte{0x00}, pver},
|
||||
// Single byte varint + string
|
||||
{"Test", "Test", append([]byte{0x04}, []byte("Test")...), pver},
|
||||
// 2-byte varint + string
|
||||
{str256, str256, append([]byte{0xfd, 0x00, 0x01}, []byte(str256)...), pver},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to appmessage format.
|
||||
var buf bytes.Buffer
|
||||
err := WriteVarString(&buf, test.in)
|
||||
if err != nil {
|
||||
t.Errorf("WriteVarString #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||
t.Errorf("WriteVarString #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode from appmessage format.
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
val, err := ReadVarString(rbuf, test.pver)
|
||||
if err != nil {
|
||||
t.Errorf("ReadVarString #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if val != test.out {
|
||||
t.Errorf("ReadVarString #%d\n got: %s want: %s", i,
|
||||
val, test.out)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarStringEncodingErrors performs negative tests against appmessage encode and
|
||||
// decode of variable length strings to confirm error paths work correctly.
|
||||
func TestVarStringEncodingErrors(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// str256 is a string that takes a 2-byte varint to encode.
|
||||
str256 := strings.Repeat("test", 64)
|
||||
|
||||
tests := []struct {
|
||||
in string // Value to encode
|
||||
buf []byte // Encoded value
|
||||
pver uint32 // Protocol version for appmessage encoding
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Latest protocol version with intentional read/write errors.
|
||||
// Force errors on empty string.
|
||||
{"", []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF},
|
||||
// Force error on single byte varint + string.
|
||||
{"Test", []byte{0x04}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
|
||||
// Force errors on 2-byte varint + string.
|
||||
{str256, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to appmessage format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := WriteVarString(w, test.in)
|
||||
if !errors.Is(err, test.writeErr) {
|
||||
t.Errorf("WriteVarString #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode from appmessage format.
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
_, err = ReadVarString(r, test.pver)
|
||||
if !errors.Is(err, test.readErr) {
|
||||
t.Errorf("ReadVarString #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarStringOverflowErrors performs tests to ensure deserializing variable
|
||||
// length strings intentionally crafted to use large values for the string
|
||||
// length are handled properly. This could otherwise potentially be used as an
|
||||
// attack vector.
|
||||
func TestVarStringOverflowErrors(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
tests := []struct {
|
||||
buf []byte // Encoded value
|
||||
pver uint32 // Protocol version for appmessage encoding
|
||||
err error // Expected error
|
||||
}{
|
||||
{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
pver, &MessageError{}},
|
||||
{[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
|
||||
pver, &MessageError{}},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Decode from appmessage format.
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
_, err := ReadVarString(rbuf, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||
t.Errorf("ReadVarString #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, reflect.TypeOf(test.err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestVarBytesEncoding tests appmessage encode and decode for variable length byte array.
|
||||
func TestVarBytesEncoding(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// bytes256 is a byte array that takes a 2-byte varint to encode.
|
||||
bytes256 := bytes.Repeat([]byte{0x01}, 256)
|
||||
|
||||
tests := []struct {
|
||||
in []byte // Byte Array to write
|
||||
buf []byte // Encoded value
|
||||
pver uint32 // Protocol version for appmessage encoding
|
||||
}{
|
||||
// Latest protocol version.
|
||||
// Empty byte array
|
||||
{[]byte{}, []byte{0x00}, pver},
|
||||
// Single byte varint + byte array
|
||||
{[]byte{0x01}, []byte{0x01, 0x01}, pver},
|
||||
// 2-byte varint + byte array
|
||||
{bytes256, append([]byte{0xfd, 0x00, 0x01}, bytes256...), pver},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to appmessage format.
|
||||
var buf bytes.Buffer
|
||||
err := WriteVarBytes(&buf, test.pver, test.in)
|
||||
if err != nil {
|
||||
t.Errorf("WriteVarBytes #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||
t.Errorf("WriteVarBytes #%d\n got: %s want: %s", i,
|
||||
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode from appmessage format.
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
val, err := ReadVarBytes(rbuf, test.pver, MaxMessagePayload,
|
||||
"test payload")
|
||||
if err != nil {
|
||||
t.Errorf("ReadVarBytes #%d error %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||
t.Errorf("ReadVarBytes #%d\n got: %s want: %s", i,
|
||||
val, test.buf)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarBytesEncodingErrors performs negative tests against appmessage encode and
|
||||
// decode of variable length byte arrays to confirm error paths work correctly.
|
||||
func TestVarBytesEncodingErrors(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// bytes256 is a byte array that takes a 2-byte varint to encode.
|
||||
bytes256 := bytes.Repeat([]byte{0x01}, 256)
|
||||
|
||||
tests := []struct {
|
||||
in []byte // Byte Array to write
|
||||
buf []byte // Encoded value
|
||||
pver uint32 // Protocol version for appmessage encoding
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Latest protocol version with intentional read/write errors.
|
||||
// Force errors on empty byte array.
|
||||
{[]byte{}, []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF},
|
||||
// Force error on single byte varint + byte array.
|
||||
{[]byte{0x01, 0x02, 0x03}, []byte{0x04}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
|
||||
// Force errors on 2-byte varint + byte array.
|
||||
{bytes256, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to appmessage format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := WriteVarBytes(w, test.pver, test.in)
|
||||
if !errors.Is(err, test.writeErr) {
|
||||
t.Errorf("WriteVarBytes #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode from appmessage format.
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
_, err = ReadVarBytes(r, test.pver, MaxMessagePayload,
|
||||
"test payload")
|
||||
if !errors.Is(err, test.readErr) {
|
||||
t.Errorf("ReadVarBytes #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestVarBytesOverflowErrors performs tests to ensure deserializing variable
|
||||
// length byte arrays intentionally crafted to use large values for the array
|
||||
// length are handled properly. This could otherwise potentially be used as an
|
||||
// attack vector.
|
||||
func TestVarBytesOverflowErrors(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
tests := []struct {
|
||||
buf []byte // Encoded value
|
||||
pver uint32 // Protocol version for appmessage encoding
|
||||
err error // Expected error
|
||||
}{
|
||||
{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
pver, &MessageError{}},
|
||||
{[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
|
||||
pver, &MessageError{}},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Decode from appmessage format.
|
||||
rbuf := bytes.NewReader(test.buf)
|
||||
_, err := ReadVarBytes(rbuf, test.pver, MaxMessagePayload,
|
||||
"test payload")
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||
t.Errorf("ReadVarBytes #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, reflect.TypeOf(test.err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
)
|
||||
|
||||
@@ -17,17 +24,17 @@ func DomainBlockToMsgBlock(domainBlock *externalapi.DomainBlock) *MsgBlock {
|
||||
}
|
||||
}
|
||||
|
||||
// DomainBlockHeaderToBlockHeader converts an externalapi.DomainBlockHeader to MsgBlockHeader
|
||||
func DomainBlockHeaderToBlockHeader(domainBlockHeader *externalapi.DomainBlockHeader) *MsgBlockHeader {
|
||||
// DomainBlockHeaderToBlockHeader converts an externalapi.BlockHeader to MsgBlockHeader
|
||||
func DomainBlockHeaderToBlockHeader(domainBlockHeader externalapi.BlockHeader) *MsgBlockHeader {
|
||||
return &MsgBlockHeader{
|
||||
Version: domainBlockHeader.Version,
|
||||
ParentHashes: domainBlockHeader.ParentHashes,
|
||||
HashMerkleRoot: &domainBlockHeader.HashMerkleRoot,
|
||||
AcceptedIDMerkleRoot: &domainBlockHeader.AcceptedIDMerkleRoot,
|
||||
UTXOCommitment: &domainBlockHeader.UTXOCommitment,
|
||||
Timestamp: mstime.UnixMilliseconds(domainBlockHeader.TimeInMilliseconds),
|
||||
Bits: domainBlockHeader.Bits,
|
||||
Nonce: domainBlockHeader.Nonce,
|
||||
Version: domainBlockHeader.Version(),
|
||||
ParentHashes: domainBlockHeader.ParentHashes(),
|
||||
HashMerkleRoot: domainBlockHeader.HashMerkleRoot(),
|
||||
AcceptedIDMerkleRoot: domainBlockHeader.AcceptedIDMerkleRoot(),
|
||||
UTXOCommitment: domainBlockHeader.UTXOCommitment(),
|
||||
Timestamp: mstime.UnixMilliseconds(domainBlockHeader.TimeInMilliseconds()),
|
||||
Bits: domainBlockHeader.Bits(),
|
||||
Nonce: domainBlockHeader.Nonce(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,18 +51,18 @@ func MsgBlockToDomainBlock(msgBlock *MsgBlock) *externalapi.DomainBlock {
|
||||
}
|
||||
}
|
||||
|
||||
// BlockHeaderToDomainBlockHeader converts a MsgBlockHeader to externalapi.DomainBlockHeader
|
||||
func BlockHeaderToDomainBlockHeader(blockHeader *MsgBlockHeader) *externalapi.DomainBlockHeader {
|
||||
return &externalapi.DomainBlockHeader{
|
||||
Version: blockHeader.Version,
|
||||
ParentHashes: blockHeader.ParentHashes,
|
||||
HashMerkleRoot: *blockHeader.HashMerkleRoot,
|
||||
AcceptedIDMerkleRoot: *blockHeader.AcceptedIDMerkleRoot,
|
||||
UTXOCommitment: *blockHeader.UTXOCommitment,
|
||||
TimeInMilliseconds: blockHeader.Timestamp.UnixMilliseconds(),
|
||||
Bits: blockHeader.Bits,
|
||||
Nonce: blockHeader.Nonce,
|
||||
}
|
||||
// BlockHeaderToDomainBlockHeader converts a MsgBlockHeader to externalapi.BlockHeader
|
||||
func BlockHeaderToDomainBlockHeader(blockHeader *MsgBlockHeader) externalapi.BlockHeader {
|
||||
return blockheader.NewImmutableBlockHeader(
|
||||
blockHeader.Version,
|
||||
blockHeader.ParentHashes,
|
||||
blockHeader.HashMerkleRoot,
|
||||
blockHeader.AcceptedIDMerkleRoot,
|
||||
blockHeader.UTXOCommitment,
|
||||
blockHeader.Timestamp.UnixMilliseconds(),
|
||||
blockHeader.Bits,
|
||||
blockHeader.Nonce,
|
||||
)
|
||||
}
|
||||
|
||||
// DomainTransactionToMsgTx converts an externalapi.DomainTransaction into an MsgTx
|
||||
@@ -77,7 +84,6 @@ func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction)
|
||||
LockTime: domainTransaction.LockTime,
|
||||
SubnetworkID: domainTransaction.SubnetworkID,
|
||||
Gas: domainTransaction.Gas,
|
||||
PayloadHash: domainTransaction.PayloadHash,
|
||||
Payload: domainTransaction.Payload,
|
||||
}
|
||||
}
|
||||
@@ -127,7 +133,6 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
|
||||
LockTime: msgTx.LockTime,
|
||||
SubnetworkID: msgTx.SubnetworkID,
|
||||
Gas: msgTx.Gas,
|
||||
PayloadHash: msgTx.PayloadHash,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
@@ -153,3 +158,207 @@ func outpointToDomainOutpoint(outpoint *Outpoint) *externalapi.DomainOutpoint {
|
||||
Index: outpoint.Index,
|
||||
}
|
||||
}
|
||||
|
||||
// RPCTransactionToDomainTransaction converts RPCTransactions to DomainTransactions
|
||||
func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externalapi.DomainTransaction, error) {
|
||||
inputs := make([]*externalapi.DomainTransactionInput, len(rpcTransaction.Inputs))
|
||||
for i, input := range rpcTransaction.Inputs {
|
||||
transactionID, err := transactionid.FromString(input.PreviousOutpoint.TransactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
previousOutpoint := &externalapi.DomainOutpoint{
|
||||
TransactionID: *transactionID,
|
||||
Index: input.PreviousOutpoint.Index,
|
||||
}
|
||||
signatureScript, err := hex.DecodeString(input.SignatureScript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inputs[i] = &externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: *previousOutpoint,
|
||||
SignatureScript: signatureScript,
|
||||
Sequence: input.Sequence,
|
||||
}
|
||||
}
|
||||
outputs := make([]*externalapi.DomainTransactionOutput, len(rpcTransaction.Outputs))
|
||||
for i, output := range rpcTransaction.Outputs {
|
||||
scriptPublicKey, err := hex.DecodeString(output.ScriptPublicKey.Script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputs[i] = &externalapi.DomainTransactionOutput{
|
||||
Value: output.Amount,
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{Script: scriptPublicKey, Version: output.ScriptPublicKey.Version},
|
||||
}
|
||||
}
|
||||
|
||||
subnetworkID, err := subnetworks.FromString(rpcTransaction.SubnetworkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload, err := hex.DecodeString(rpcTransaction.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &externalapi.DomainTransaction{
|
||||
Version: rpcTransaction.Version,
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
LockTime: rpcTransaction.LockTime,
|
||||
SubnetworkID: *subnetworkID,
|
||||
Gas: rpcTransaction.LockTime,
|
||||
Payload: payload,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DomainTransactionToRPCTransaction converts DomainTransactions to RPCTransactions
|
||||
func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransaction) *RPCTransaction {
|
||||
inputs := make([]*RPCTransactionInput, len(transaction.Inputs))
|
||||
for i, input := range transaction.Inputs {
|
||||
transactionID := input.PreviousOutpoint.TransactionID.String()
|
||||
previousOutpoint := &RPCOutpoint{
|
||||
TransactionID: transactionID,
|
||||
Index: input.PreviousOutpoint.Index,
|
||||
}
|
||||
signatureScript := hex.EncodeToString(input.SignatureScript)
|
||||
inputs[i] = &RPCTransactionInput{
|
||||
PreviousOutpoint: previousOutpoint,
|
||||
SignatureScript: signatureScript,
|
||||
Sequence: input.Sequence,
|
||||
}
|
||||
}
|
||||
outputs := make([]*RPCTransactionOutput, len(transaction.Outputs))
|
||||
for i, output := range transaction.Outputs {
|
||||
scriptPublicKey := hex.EncodeToString(output.ScriptPublicKey.Script)
|
||||
outputs[i] = &RPCTransactionOutput{
|
||||
Amount: output.Value,
|
||||
ScriptPublicKey: &RPCScriptPublicKey{Script: scriptPublicKey, Version: output.ScriptPublicKey.Version},
|
||||
}
|
||||
}
|
||||
subnetworkID := transaction.SubnetworkID.String()
|
||||
payload := hex.EncodeToString(transaction.Payload)
|
||||
return &RPCTransaction{
|
||||
Version: transaction.Version,
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
LockTime: transaction.LockTime,
|
||||
SubnetworkID: subnetworkID,
|
||||
Gas: transaction.LockTime,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs converts
|
||||
// OutpointAndUTXOEntryPairs to domain OutpointAndUTXOEntryPairs
|
||||
func OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs(
|
||||
outpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair) []*externalapi.OutpointAndUTXOEntryPair {
|
||||
|
||||
domainOutpointAndUTXOEntryPairs := make([]*externalapi.OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
|
||||
for i, outpointAndUTXOEntryPair := range outpointAndUTXOEntryPairs {
|
||||
domainOutpointAndUTXOEntryPairs[i] = &externalapi.OutpointAndUTXOEntryPair{
|
||||
Outpoint: &externalapi.DomainOutpoint{
|
||||
TransactionID: outpointAndUTXOEntryPair.Outpoint.TxID,
|
||||
Index: outpointAndUTXOEntryPair.Outpoint.Index,
|
||||
},
|
||||
UTXOEntry: utxo.NewUTXOEntry(
|
||||
outpointAndUTXOEntryPair.UTXOEntry.Amount,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase,
|
||||
outpointAndUTXOEntryPair.UTXOEntry.BlockDAAScore,
|
||||
),
|
||||
}
|
||||
}
|
||||
return domainOutpointAndUTXOEntryPairs
|
||||
}
|
||||
|
||||
// DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs converts
|
||||
// domain OutpointAndUTXOEntryPairs to OutpointAndUTXOEntryPairs
|
||||
func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
|
||||
outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) []*OutpointAndUTXOEntryPair {
|
||||
|
||||
domainOutpointAndUTXOEntryPairs := make([]*OutpointAndUTXOEntryPair, len(outpointAndUTXOEntryPairs))
|
||||
for i, outpointAndUTXOEntryPair := range outpointAndUTXOEntryPairs {
|
||||
domainOutpointAndUTXOEntryPairs[i] = &OutpointAndUTXOEntryPair{
|
||||
Outpoint: &Outpoint{
|
||||
TxID: outpointAndUTXOEntryPair.Outpoint.TransactionID,
|
||||
Index: outpointAndUTXOEntryPair.Outpoint.Index,
|
||||
},
|
||||
UTXOEntry: &UTXOEntry{
|
||||
Amount: outpointAndUTXOEntryPair.UTXOEntry.Amount(),
|
||||
ScriptPublicKey: outpointAndUTXOEntryPair.UTXOEntry.ScriptPublicKey(),
|
||||
IsCoinbase: outpointAndUTXOEntryPair.UTXOEntry.IsCoinbase(),
|
||||
BlockDAAScore: outpointAndUTXOEntryPair.UTXOEntry.BlockDAAScore(),
|
||||
},
|
||||
}
|
||||
}
|
||||
return domainOutpointAndUTXOEntryPairs
|
||||
}
|
||||
|
||||
// DomainBlockToRPCBlock converts DomainBlocks to RPCBlocks
|
||||
func DomainBlockToRPCBlock(block *externalapi.DomainBlock) *RPCBlock {
|
||||
header := &RPCBlockHeader{
|
||||
Version: uint32(block.Header.Version()),
|
||||
ParentHashes: hashes.ToStrings(block.Header.ParentHashes()),
|
||||
HashMerkleRoot: block.Header.HashMerkleRoot().String(),
|
||||
AcceptedIDMerkleRoot: block.Header.AcceptedIDMerkleRoot().String(),
|
||||
UTXOCommitment: block.Header.UTXOCommitment().String(),
|
||||
Timestamp: block.Header.TimeInMilliseconds(),
|
||||
Bits: block.Header.Bits(),
|
||||
Nonce: block.Header.Nonce(),
|
||||
}
|
||||
transactions := make([]*RPCTransaction, len(block.Transactions))
|
||||
for i, transaction := range block.Transactions {
|
||||
transactions[i] = DomainTransactionToRPCTransaction(transaction)
|
||||
}
|
||||
return &RPCBlock{
|
||||
Header: header,
|
||||
Transactions: transactions,
|
||||
}
|
||||
}
|
||||
|
||||
// RPCBlockToDomainBlock converts `block` into a DomainBlock
|
||||
func RPCBlockToDomainBlock(block *RPCBlock) (*externalapi.DomainBlock, error) {
|
||||
parentHashes := make([]*externalapi.DomainHash, len(block.Header.ParentHashes))
|
||||
for i, parentHash := range block.Header.ParentHashes {
|
||||
domainParentHashes, err := externalapi.NewDomainHashFromString(parentHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentHashes[i] = domainParentHashes
|
||||
}
|
||||
hashMerkleRoot, err := externalapi.NewDomainHashFromString(block.Header.HashMerkleRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acceptedIDMerkleRoot, err := externalapi.NewDomainHashFromString(block.Header.AcceptedIDMerkleRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utxoCommitment, err := externalapi.NewDomainHashFromString(block.Header.UTXOCommitment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := blockheader.NewImmutableBlockHeader(
|
||||
uint16(block.Header.Version),
|
||||
parentHashes,
|
||||
hashMerkleRoot,
|
||||
acceptedIDMerkleRoot,
|
||||
utxoCommitment,
|
||||
block.Header.Timestamp,
|
||||
block.Header.Bits,
|
||||
block.Header.Nonce)
|
||||
transactions := make([]*externalapi.DomainTransaction, len(block.Transactions))
|
||||
for i, transaction := range block.Transactions {
|
||||
domainTransaction, err := RPCTransactionToDomainTransaction(transaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions[i] = domainTransaction
|
||||
}
|
||||
return &externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: transactions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -41,8 +41,6 @@ const (
|
||||
CmdPong
|
||||
CmdRequestBlockLocator
|
||||
CmdBlockLocator
|
||||
CmdSelectedTip
|
||||
CmdRequestSelectedTip
|
||||
CmdInvRelayBlock
|
||||
CmdRequestRelayBlocks
|
||||
CmdInvTransaction
|
||||
@@ -53,10 +51,18 @@ const (
|
||||
CmdReject
|
||||
CmdHeader
|
||||
CmdRequestNextHeaders
|
||||
CmdRequestIBDRootUTXOSetAndBlock
|
||||
CmdIBDRootUTXOSetAndBlock
|
||||
CmdRequestPruningPointUTXOSetAndBlock
|
||||
CmdPruningPointUTXOSetChunk
|
||||
CmdRequestIBDBlocks
|
||||
CmdIBDRootNotFound
|
||||
CmdUnexpectedPruningPoint
|
||||
CmdRequestPruningPointHash
|
||||
CmdPruningPointHash
|
||||
CmdIBDBlockLocator
|
||||
CmdIBDBlockLocatorHighestHash
|
||||
CmdIBDBlockLocatorHighestHashNotFound
|
||||
CmdBlockHeaders
|
||||
CmdRequestNextPruningPointUTXOSetChunk
|
||||
CmdDonePruningPointUTXOSetChunks
|
||||
|
||||
// rpc
|
||||
CmdGetCurrentNetworkRequestMessage
|
||||
@@ -81,15 +87,15 @@ const (
|
||||
CmdAddPeerResponseMessage
|
||||
CmdSubmitTransactionRequestMessage
|
||||
CmdSubmitTransactionResponseMessage
|
||||
CmdNotifyChainChangedRequestMessage
|
||||
CmdNotifyChainChangedResponseMessage
|
||||
CmdChainChangedNotificationMessage
|
||||
CmdNotifyVirtualSelectedParentChainChangedRequestMessage
|
||||
CmdNotifyVirtualSelectedParentChainChangedResponseMessage
|
||||
CmdVirtualSelectedParentChainChangedNotificationMessage
|
||||
CmdGetBlockRequestMessage
|
||||
CmdGetBlockResponseMessage
|
||||
CmdGetSubnetworkRequestMessage
|
||||
CmdGetSubnetworkResponseMessage
|
||||
CmdGetChainFromBlockRequestMessage
|
||||
CmdGetChainFromBlockResponseMessage
|
||||
CmdGetVirtualSelectedParentChainFromBlockRequestMessage
|
||||
CmdGetVirtualSelectedParentChainFromBlockResponseMessage
|
||||
CmdGetBlocksRequestMessage
|
||||
CmdGetBlocksResponseMessage
|
||||
CmdGetBlockCountRequestMessage
|
||||
@@ -108,88 +114,140 @@ const (
|
||||
CmdShutDownResponseMessage
|
||||
CmdGetHeadersRequestMessage
|
||||
CmdGetHeadersResponseMessage
|
||||
CmdNotifyUTXOsChangedRequestMessage
|
||||
CmdNotifyUTXOsChangedResponseMessage
|
||||
CmdUTXOsChangedNotificationMessage
|
||||
CmdStopNotifyingUTXOsChangedRequestMessage
|
||||
CmdStopNotifyingUTXOsChangedResponseMessage
|
||||
CmdGetUTXOsByAddressesRequestMessage
|
||||
CmdGetUTXOsByAddressesResponseMessage
|
||||
CmdGetVirtualSelectedParentBlueScoreRequestMessage
|
||||
CmdGetVirtualSelectedParentBlueScoreResponseMessage
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
|
||||
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
|
||||
CmdBanRequestMessage
|
||||
CmdBanResponseMessage
|
||||
CmdUnbanRequestMessage
|
||||
CmdUnbanResponseMessage
|
||||
CmdGetInfoRequestMessage
|
||||
CmdGetInfoResponseMessage
|
||||
CmdNotifyPruningPointUTXOSetOverrideRequestMessage
|
||||
CmdNotifyPruningPointUTXOSetOverrideResponseMessage
|
||||
CmdPruningPointUTXOSetOverrideNotificationMessage
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
|
||||
)
|
||||
|
||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||
var ProtocolMessageCommandToString = map[MessageCommand]string{
|
||||
CmdVersion: "Version",
|
||||
CmdVerAck: "VerAck",
|
||||
CmdRequestAddresses: "RequestAddresses",
|
||||
CmdAddresses: "Addresses",
|
||||
CmdRequestHeaders: "RequestHeaders",
|
||||
CmdBlock: "Block",
|
||||
CmdTx: "Tx",
|
||||
CmdPing: "Ping",
|
||||
CmdPong: "Pong",
|
||||
CmdRequestBlockLocator: "RequestBlockLocator",
|
||||
CmdBlockLocator: "BlockLocator",
|
||||
CmdSelectedTip: "SelectedTip",
|
||||
CmdRequestSelectedTip: "RequestSelectedTip",
|
||||
CmdInvRelayBlock: "InvRelayBlock",
|
||||
CmdRequestRelayBlocks: "RequestRelayBlocks",
|
||||
CmdInvTransaction: "InvTransaction",
|
||||
CmdRequestTransactions: "RequestTransactions",
|
||||
CmdIBDBlock: "IBDBlock",
|
||||
CmdDoneHeaders: "DoneHeaders",
|
||||
CmdTransactionNotFound: "TransactionNotFound",
|
||||
CmdReject: "Reject",
|
||||
CmdHeader: "Header",
|
||||
CmdRequestNextHeaders: "RequestNextHeaders",
|
||||
CmdRequestIBDRootUTXOSetAndBlock: "RequestPruningUTXOSetAndBlock",
|
||||
CmdIBDRootUTXOSetAndBlock: "IBDRootUTXOSetAndBlock",
|
||||
CmdRequestIBDBlocks: "RequestIBDBlocks",
|
||||
CmdIBDRootNotFound: "IBDRootNotFound",
|
||||
CmdVersion: "Version",
|
||||
CmdVerAck: "VerAck",
|
||||
CmdRequestAddresses: "RequestAddresses",
|
||||
CmdAddresses: "Addresses",
|
||||
CmdRequestHeaders: "RequestHeaders",
|
||||
CmdBlock: "Block",
|
||||
CmdTx: "Tx",
|
||||
CmdPing: "Ping",
|
||||
CmdPong: "Pong",
|
||||
CmdRequestBlockLocator: "RequestBlockLocator",
|
||||
CmdBlockLocator: "BlockLocator",
|
||||
CmdInvRelayBlock: "InvRelayBlock",
|
||||
CmdRequestRelayBlocks: "RequestRelayBlocks",
|
||||
CmdInvTransaction: "InvTransaction",
|
||||
CmdRequestTransactions: "RequestTransactions",
|
||||
CmdIBDBlock: "IBDBlock",
|
||||
CmdDoneHeaders: "DoneHeaders",
|
||||
CmdTransactionNotFound: "TransactionNotFound",
|
||||
CmdReject: "Reject",
|
||||
CmdHeader: "Header",
|
||||
CmdRequestNextHeaders: "RequestNextHeaders",
|
||||
CmdRequestPruningPointUTXOSetAndBlock: "RequestPruningPointUTXOSetAndBlock",
|
||||
CmdPruningPointUTXOSetChunk: "PruningPointUTXOSetChunk",
|
||||
CmdRequestIBDBlocks: "RequestIBDBlocks",
|
||||
CmdUnexpectedPruningPoint: "UnexpectedPruningPoint",
|
||||
CmdRequestPruningPointHash: "RequestPruningPointHashHash",
|
||||
CmdPruningPointHash: "PruningPointHash",
|
||||
CmdIBDBlockLocator: "IBDBlockLocator",
|
||||
CmdIBDBlockLocatorHighestHash: "IBDBlockLocatorHighestHash",
|
||||
CmdIBDBlockLocatorHighestHashNotFound: "IBDBlockLocatorHighestHashNotFound",
|
||||
CmdBlockHeaders: "BlockHeaders",
|
||||
CmdRequestNextPruningPointUTXOSetChunk: "RequestNextPruningPointUTXOSetChunk",
|
||||
CmdDonePruningPointUTXOSetChunks: "DonePruningPointUTXOSetChunks",
|
||||
}
|
||||
|
||||
// RPCMessageCommandToString maps all MessageCommands to their string representation
|
||||
var RPCMessageCommandToString = map[MessageCommand]string{
|
||||
CmdGetCurrentNetworkRequestMessage: "GetCurrentNetworkRequest",
|
||||
CmdGetCurrentNetworkResponseMessage: "GetCurrentNetworkResponse",
|
||||
CmdSubmitBlockRequestMessage: "SubmitBlockRequest",
|
||||
CmdSubmitBlockResponseMessage: "SubmitBlockResponse",
|
||||
CmdGetBlockTemplateRequestMessage: "GetBlockTemplateRequest",
|
||||
CmdGetBlockTemplateResponseMessage: "GetBlockTemplateResponse",
|
||||
CmdGetBlockTemplateTransactionMessage: "CmdGetBlockTemplateTransaction",
|
||||
CmdNotifyBlockAddedRequestMessage: "NotifyBlockAddedRequest",
|
||||
CmdNotifyBlockAddedResponseMessage: "NotifyBlockAddedResponse",
|
||||
CmdBlockAddedNotificationMessage: "BlockAddedNotification",
|
||||
CmdGetPeerAddressesRequestMessage: "GetPeerAddressesRequest",
|
||||
CmdGetPeerAddressesResponseMessage: "GetPeerAddressesResponse",
|
||||
CmdGetSelectedTipHashRequestMessage: "GetSelectedTipHashRequest",
|
||||
CmdGetSelectedTipHashResponseMessage: "GetSelectedTipHashResponse",
|
||||
CmdGetMempoolEntryRequestMessage: "GetMempoolEntryRequest",
|
||||
CmdGetMempoolEntryResponseMessage: "GetMempoolEntryResponse",
|
||||
CmdGetConnectedPeerInfoRequestMessage: "GetConnectedPeerInfoRequest",
|
||||
CmdGetConnectedPeerInfoResponseMessage: "GetConnectedPeerInfoResponse",
|
||||
CmdAddPeerRequestMessage: "AddPeerRequest",
|
||||
CmdAddPeerResponseMessage: "AddPeerResponse",
|
||||
CmdSubmitTransactionRequestMessage: "SubmitTransactionRequest",
|
||||
CmdSubmitTransactionResponseMessage: "SubmitTransactionResponse",
|
||||
CmdNotifyChainChangedRequestMessage: "NotifyChainChangedRequest",
|
||||
CmdNotifyChainChangedResponseMessage: "NotifyChainChangedResponse",
|
||||
CmdChainChangedNotificationMessage: "ChainChangedNotification",
|
||||
CmdGetBlockRequestMessage: "GetBlockRequest",
|
||||
CmdGetBlockResponseMessage: "GetBlockResponse",
|
||||
CmdGetSubnetworkRequestMessage: "GetSubnetworkRequest",
|
||||
CmdGetSubnetworkResponseMessage: "GetSubnetworkResponse",
|
||||
CmdGetChainFromBlockRequestMessage: "GetChainFromBlockRequest",
|
||||
CmdGetChainFromBlockResponseMessage: "GetChainFromBlockResponse",
|
||||
CmdGetBlocksRequestMessage: "GetBlocksRequest",
|
||||
CmdGetBlocksResponseMessage: "GetBlocksResponse",
|
||||
CmdGetBlockCountRequestMessage: "GetBlockCountRequest",
|
||||
CmdGetBlockCountResponseMessage: "GetBlockCountResponse",
|
||||
CmdGetBlockDAGInfoRequestMessage: "GetBlockDAGInfoRequest",
|
||||
CmdGetBlockDAGInfoResponseMessage: "GetBlockDAGInfoResponse",
|
||||
CmdResolveFinalityConflictRequestMessage: "ResolveFinalityConflictRequest",
|
||||
CmdResolveFinalityConflictResponseMessage: "ResolveFinalityConflictResponse",
|
||||
CmdNotifyFinalityConflictsRequestMessage: "NotifyFinalityConflictsRequest",
|
||||
CmdNotifyFinalityConflictsResponseMessage: "NotifyFinalityConflictsResponse",
|
||||
CmdFinalityConflictNotificationMessage: "FinalityConflictNotification",
|
||||
CmdFinalityConflictResolvedNotificationMessage: "FinalityConflictResolvedNotification",
|
||||
CmdGetMempoolEntriesRequestMessage: "GetMempoolEntriesRequestMessage",
|
||||
CmdGetMempoolEntriesResponseMessage: "GetMempoolEntriesResponseMessage",
|
||||
CmdGetHeadersRequestMessage: "GetHeadersRequest",
|
||||
CmdGetHeadersResponseMessage: "GetHeadersResponse",
|
||||
CmdGetCurrentNetworkRequestMessage: "GetCurrentNetworkRequest",
|
||||
CmdGetCurrentNetworkResponseMessage: "GetCurrentNetworkResponse",
|
||||
CmdSubmitBlockRequestMessage: "SubmitBlockRequest",
|
||||
CmdSubmitBlockResponseMessage: "SubmitBlockResponse",
|
||||
CmdGetBlockTemplateRequestMessage: "GetBlockTemplateRequest",
|
||||
CmdGetBlockTemplateResponseMessage: "GetBlockTemplateResponse",
|
||||
CmdGetBlockTemplateTransactionMessage: "CmdGetBlockTemplateTransaction",
|
||||
CmdNotifyBlockAddedRequestMessage: "NotifyBlockAddedRequest",
|
||||
CmdNotifyBlockAddedResponseMessage: "NotifyBlockAddedResponse",
|
||||
CmdBlockAddedNotificationMessage: "BlockAddedNotification",
|
||||
CmdGetPeerAddressesRequestMessage: "GetPeerAddressesRequest",
|
||||
CmdGetPeerAddressesResponseMessage: "GetPeerAddressesResponse",
|
||||
CmdGetSelectedTipHashRequestMessage: "GetSelectedTipHashRequest",
|
||||
CmdGetSelectedTipHashResponseMessage: "GetSelectedTipHashResponse",
|
||||
CmdGetMempoolEntryRequestMessage: "GetMempoolEntryRequest",
|
||||
CmdGetMempoolEntryResponseMessage: "GetMempoolEntryResponse",
|
||||
CmdGetConnectedPeerInfoRequestMessage: "GetConnectedPeerInfoRequest",
|
||||
CmdGetConnectedPeerInfoResponseMessage: "GetConnectedPeerInfoResponse",
|
||||
CmdAddPeerRequestMessage: "AddPeerRequest",
|
||||
CmdAddPeerResponseMessage: "AddPeerResponse",
|
||||
CmdSubmitTransactionRequestMessage: "SubmitTransactionRequest",
|
||||
CmdSubmitTransactionResponseMessage: "SubmitTransactionResponse",
|
||||
CmdNotifyVirtualSelectedParentChainChangedRequestMessage: "NotifyVirtualSelectedParentChainChangedRequest",
|
||||
CmdNotifyVirtualSelectedParentChainChangedResponseMessage: "NotifyVirtualSelectedParentChainChangedResponse",
|
||||
CmdVirtualSelectedParentChainChangedNotificationMessage: "VirtualSelectedParentChainChangedNotification",
|
||||
CmdGetBlockRequestMessage: "GetBlockRequest",
|
||||
CmdGetBlockResponseMessage: "GetBlockResponse",
|
||||
CmdGetSubnetworkRequestMessage: "GetSubnetworkRequest",
|
||||
CmdGetSubnetworkResponseMessage: "GetSubnetworkResponse",
|
||||
CmdGetVirtualSelectedParentChainFromBlockRequestMessage: "GetVirtualSelectedParentChainFromBlockRequest",
|
||||
CmdGetVirtualSelectedParentChainFromBlockResponseMessage: "GetVirtualSelectedParentChainFromBlockResponse",
|
||||
CmdGetBlocksRequestMessage: "GetBlocksRequest",
|
||||
CmdGetBlocksResponseMessage: "GetBlocksResponse",
|
||||
CmdGetBlockCountRequestMessage: "GetBlockCountRequest",
|
||||
CmdGetBlockCountResponseMessage: "GetBlockCountResponse",
|
||||
CmdGetBlockDAGInfoRequestMessage: "GetBlockDAGInfoRequest",
|
||||
CmdGetBlockDAGInfoResponseMessage: "GetBlockDAGInfoResponse",
|
||||
CmdResolveFinalityConflictRequestMessage: "ResolveFinalityConflictRequest",
|
||||
CmdResolveFinalityConflictResponseMessage: "ResolveFinalityConflictResponse",
|
||||
CmdNotifyFinalityConflictsRequestMessage: "NotifyFinalityConflictsRequest",
|
||||
CmdNotifyFinalityConflictsResponseMessage: "NotifyFinalityConflictsResponse",
|
||||
CmdFinalityConflictNotificationMessage: "FinalityConflictNotification",
|
||||
CmdFinalityConflictResolvedNotificationMessage: "FinalityConflictResolvedNotification",
|
||||
CmdGetMempoolEntriesRequestMessage: "GetMempoolEntriesRequest",
|
||||
CmdGetMempoolEntriesResponseMessage: "GetMempoolEntriesResponse",
|
||||
CmdGetHeadersRequestMessage: "GetHeadersRequest",
|
||||
CmdGetHeadersResponseMessage: "GetHeadersResponse",
|
||||
CmdNotifyUTXOsChangedRequestMessage: "NotifyUTXOsChangedRequest",
|
||||
CmdNotifyUTXOsChangedResponseMessage: "NotifyUTXOsChangedResponse",
|
||||
CmdUTXOsChangedNotificationMessage: "UTXOsChangedNotification",
|
||||
CmdStopNotifyingUTXOsChangedRequestMessage: "StopNotifyingUTXOsChangedRequest",
|
||||
CmdStopNotifyingUTXOsChangedResponseMessage: "StopNotifyingUTXOsChangedResponse",
|
||||
CmdGetUTXOsByAddressesRequestMessage: "GetUTXOsByAddressesRequest",
|
||||
CmdGetUTXOsByAddressesResponseMessage: "GetUTXOsByAddressesResponse",
|
||||
CmdGetVirtualSelectedParentBlueScoreRequestMessage: "GetVirtualSelectedParentBlueScoreRequest",
|
||||
CmdGetVirtualSelectedParentBlueScoreResponseMessage: "GetVirtualSelectedParentBlueScoreResponse",
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: "NotifyVirtualSelectedParentBlueScoreChangedRequest",
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage: "NotifyVirtualSelectedParentBlueScoreChangedResponse",
|
||||
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage: "VirtualSelectedParentBlueScoreChangedNotification",
|
||||
CmdBanRequestMessage: "BanRequest",
|
||||
CmdBanResponseMessage: "BanResponse",
|
||||
CmdUnbanRequestMessage: "UnbanRequest",
|
||||
CmdUnbanResponseMessage: "UnbanResponse",
|
||||
CmdGetInfoRequestMessage: "GetInfoRequest",
|
||||
CmdGetInfoResponseMessage: "GeInfoResponse",
|
||||
CmdNotifyPruningPointUTXOSetOverrideRequestMessage: "NotifyPruningPointUTXOSetOverrideRequest",
|
||||
CmdNotifyPruningPointUTXOSetOverrideResponseMessage: "NotifyPruningPointUTXOSetOverrideResponse",
|
||||
CmdPruningPointUTXOSetOverrideNotificationMessage: "PruningPointUTXOSetOverrideNotification",
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: "StopNotifyingPruningPointUTXOSetOverrideRequest",
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage: "StopNotifyingPruningPointUTXOSetOverrideResponse",
|
||||
}
|
||||
|
||||
// Message is an interface that describes a kaspa message. A type that
|
||||
|
||||
19
app/appmessage/p2p_blockheaders.go
Normal file
19
app/appmessage/p2p_blockheaders.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package appmessage
|
||||
|
||||
// BlockHeadersMessage represents a kaspa BlockHeaders message
|
||||
type BlockHeadersMessage struct {
|
||||
baseMessage
|
||||
BlockHeaders []*MsgBlockHeader
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *BlockHeadersMessage) Command() MessageCommand {
|
||||
return CmdBlockHeaders
|
||||
}
|
||||
|
||||
// NewBlockHeadersMessage returns a new kaspa BlockHeaders message
|
||||
func NewBlockHeadersMessage(blockHeaders []*MsgBlockHeader) *BlockHeadersMessage {
|
||||
return &BlockHeadersMessage{
|
||||
BlockHeaders: blockHeaders,
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// MsgIBDRootNotFound implements the Message interface and represents a kaspa
|
||||
// IBDRootNotFound message. It is used to notify the IBD root that was requested
|
||||
// by other peer was not found.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgIBDRootNotFound struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgIBDRootNotFound) Command() MessageCommand {
|
||||
return CmdIBDRootNotFound
|
||||
}
|
||||
|
||||
// NewMsgIBDRootNotFound returns a new kaspa IBDRootNotFound message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgIBDRootNotFound() *MsgDoneHeaders {
|
||||
return &MsgDoneHeaders{}
|
||||
}
|
||||
@@ -15,19 +15,6 @@ import (
|
||||
// backing array multiple times.
|
||||
const defaultTransactionAlloc = 2048
|
||||
|
||||
// MaxMassAcceptedByBlock is the maximum total transaction mass a block may accept.
|
||||
const MaxMassAcceptedByBlock = 10000000
|
||||
|
||||
// MaxMassPerTx is the maximum total mass a transaction may have.
|
||||
const MaxMassPerTx = MaxMassAcceptedByBlock / 2
|
||||
|
||||
// MaxTxPerBlock is the maximum number of transactions that could
|
||||
// possibly fit into a block.
|
||||
const MaxTxPerBlock = (MaxMassAcceptedByBlock / minTxPayload) + 1
|
||||
|
||||
// MaxBlockParents is the maximum allowed number of parents for block.
|
||||
const MaxBlockParents = 10
|
||||
|
||||
// TxLoc holds locator data for the offset and length of where a transaction is
|
||||
// located within a MsgBlock data buffer.
|
||||
type TxLoc struct {
|
||||
@@ -71,7 +58,7 @@ func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||
// Note: this operation modifies the block in place.
|
||||
func (msg *MsgBlock) ConvertToPartial(subnetworkID *externalapi.DomainSubnetworkID) {
|
||||
for _, tx := range msg.Transactions {
|
||||
if tx.SubnetworkID != *subnetworkID {
|
||||
if !tx.SubnetworkID.Equal(subnetworkID) {
|
||||
tx.Payload = []byte{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
)
|
||||
|
||||
// TestBlock tests the MsgBlock API.
|
||||
@@ -110,7 +110,7 @@ func TestConvertToPartial(t *testing.T) {
|
||||
for _, testTransaction := range transactions {
|
||||
var subnetworkTx *MsgTx
|
||||
for _, blockTransaction := range block.Transactions {
|
||||
if blockTransaction.SubnetworkID == *testTransaction.subnetworkID {
|
||||
if blockTransaction.SubnetworkID.Equal(testTransaction.subnetworkID) {
|
||||
subnetworkTx = blockTransaction
|
||||
}
|
||||
}
|
||||
@@ -127,10 +127,10 @@ func TestConvertToPartial(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// blockOne is the first block in the mainnet block DAG.
|
||||
//blockOne is the first block in the mainnet block DAG.
|
||||
var blockOne = MsgBlock{
|
||||
Header: MsgBlockHeader{
|
||||
Version: 1,
|
||||
Version: 0,
|
||||
ParentHashes: []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash},
|
||||
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||
AcceptedIDMerkleRoot: exampleAcceptedIDMerkleRoot,
|
||||
@@ -156,19 +156,21 @@ var blockOne = MsgBlock{
|
||||
[]*TxOut{
|
||||
{
|
||||
Value: 0x12a05f200,
|
||||
ScriptPubKey: []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
|
||||
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
|
||||
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
|
||||
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
|
||||
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
|
||||
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
|
||||
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
|
||||
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
|
||||
0xee, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptPubKey: &externalapi.ScriptPublicKey{
|
||||
Script: []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0x96, 0xb5, 0x38, 0xe8, 0x53, 0x51, 0x9c,
|
||||
0x72, 0x6a, 0x2c, 0x91, 0xe6, 0x1e, 0xc1, 0x16,
|
||||
0x00, 0xae, 0x13, 0x90, 0x81, 0x3a, 0x62, 0x7c,
|
||||
0x66, 0xfb, 0x8b, 0xe7, 0x94, 0x7b, 0xe6, 0x3c,
|
||||
0x52, 0xda, 0x75, 0x89, 0x37, 0x95, 0x15, 0xd4,
|
||||
0xe0, 0xa6, 0x04, 0xf8, 0x14, 0x17, 0x81, 0xe6,
|
||||
0x22, 0x94, 0x72, 0x11, 0x66, 0xbf, 0x62, 0x1e,
|
||||
0x73, 0xa8, 0x2c, 0xbf, 0x23, 0x42, 0xc8, 0x58,
|
||||
0xee, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
Version: 0},
|
||||
},
|
||||
}),
|
||||
},
|
||||
@@ -176,7 +178,7 @@ var blockOne = MsgBlock{
|
||||
|
||||
// Block one serialized bytes.
|
||||
var blockOneBytes = []byte{
|
||||
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||
0x00, 0x00, // Version 0
|
||||
0x02, // NumParentBlocks
|
||||
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, // mainnetGenesisHash
|
||||
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
||||
@@ -202,7 +204,7 @@ var blockOneBytes = []byte{
|
||||
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||
0x01, 0xe3, 0x62, 0x99, 0x00, 0x00, 0x00, 0x00, // Fake Nonce
|
||||
0x01, // TxnCount
|
||||
0x01, 0x00, 0x00, 0x00, // Version
|
||||
0x00, 0x00, 0x00, 0x00, // Version
|
||||
0x01, // Varint for number of transaction inputs
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
@@ -7,7 +7,7 @@ package appmessage
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
@@ -37,7 +37,7 @@ type MsgBlockHeader struct {
|
||||
baseMessage
|
||||
|
||||
// Version of the block. This is not the same as the protocol version.
|
||||
Version int32
|
||||
Version uint16
|
||||
|
||||
// Hashes of the parent block headers in the blockDAG.
|
||||
ParentHashes []*externalapi.DomainHash
|
||||
@@ -73,7 +73,7 @@ func (h *MsgBlockHeader) NumParentBlocks() byte {
|
||||
|
||||
// BlockHash computes the block identifier hash for the given block header.
|
||||
func (h *MsgBlockHeader) BlockHash() *externalapi.DomainHash {
|
||||
return consensusserialization.HeaderHash(BlockHeaderToDomainBlockHeader(h))
|
||||
return consensushashing.HeaderHash(BlockHeaderToDomainBlockHeader(h))
|
||||
}
|
||||
|
||||
// IsGenesis returns true iff this block is a genesis block
|
||||
@@ -90,7 +90,7 @@ func (h *MsgBlockHeader) Command() MessageCommand {
|
||||
// NewBlockHeader returns a new MsgBlockHeader using the provided version, previous
|
||||
// block hash, hash merkle root, accepted ID merkle root, difficulty bits, and nonce used to generate the
|
||||
// block with defaults or calclulated values for the remaining fields.
|
||||
func NewBlockHeader(version int32, parentHashes []*externalapi.DomainHash, hashMerkleRoot *externalapi.DomainHash,
|
||||
func NewBlockHeader(version uint16, parentHashes []*externalapi.DomainHash, hashMerkleRoot *externalapi.DomainHash,
|
||||
acceptedIDMerkleRoot *externalapi.DomainHash, utxoCommitment *externalapi.DomainHash, bits uint32, nonce uint64) *MsgBlockHeader {
|
||||
|
||||
// Limit the timestamp to one millisecond precision since the protocol
|
||||
|
||||
@@ -11,15 +11,11 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"github.com/kaspanet/kaspad/util/random"
|
||||
)
|
||||
|
||||
// TestBlockHeader tests the MsgBlockHeader API.
|
||||
func TestBlockHeader(t *testing.T) {
|
||||
nonce, err := random.Uint64()
|
||||
if err != nil {
|
||||
t.Errorf("random.Uint64: Error generating nonce: %v", err)
|
||||
}
|
||||
nonce := uint64(0xba4d87a69924a93d)
|
||||
|
||||
hashes := []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ package appmessage
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
@@ -13,7 +11,7 @@ import (
|
||||
// TestBlockLocator tests the MsgBlockLocator API.
|
||||
func TestBlockLocator(t *testing.T) {
|
||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||
locatorHash, err := hashes.FromString(hashStr)
|
||||
locatorHash, err := externalapi.NewDomainHashFromString(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
16
app/appmessage/p2p_msgdonepruningpointutxosetchunks.go
Normal file
16
app/appmessage/p2p_msgdonepruningpointutxosetchunks.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package appmessage
|
||||
|
||||
// MsgDonePruningPointUTXOSetChunks represents a kaspa DonePruningPointUTXOSetChunks message
|
||||
type MsgDonePruningPointUTXOSetChunks struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgDonePruningPointUTXOSetChunks) Command() MessageCommand {
|
||||
return CmdDonePruningPointUTXOSetChunks
|
||||
}
|
||||
|
||||
// NewMsgDonePruningPointUTXOSetChunks returns a new MsgDonePruningPointUTXOSetChunks.
|
||||
func NewMsgDonePruningPointUTXOSetChunks() *MsgDonePruningPointUTXOSetChunks {
|
||||
return &MsgDonePruningPointUTXOSetChunks{}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func TestIBDBlock(t *testing.T) {
|
||||
bh := NewBlockHeader(1, parentHashes, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce)
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := MessageCommand(17)
|
||||
wantCmd := MessageCommand(15)
|
||||
msg := NewMsgIBDBlock(NewMsgBlock(bh))
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgIBDBlock: wrong command - got %v want %v",
|
||||
|
||||
27
app/appmessage/p2p_msgibdblocklocator.go
Normal file
27
app/appmessage/p2p_msgibdblocklocator.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgIBDBlockLocator represents a kaspa ibdBlockLocator message
|
||||
type MsgIBDBlockLocator struct {
|
||||
baseMessage
|
||||
TargetHash *externalapi.DomainHash
|
||||
BlockLocatorHashes []*externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgIBDBlockLocator) Command() MessageCommand {
|
||||
return CmdIBDBlockLocator
|
||||
}
|
||||
|
||||
// NewMsgIBDBlockLocator returns a new kaspa ibdBlockLocator message
|
||||
func NewMsgIBDBlockLocator(targetHash *externalapi.DomainHash,
|
||||
blockLocatorHashes []*externalapi.DomainHash) *MsgIBDBlockLocator {
|
||||
|
||||
return &MsgIBDBlockLocator{
|
||||
TargetHash: targetHash,
|
||||
BlockLocatorHashes: blockLocatorHashes,
|
||||
}
|
||||
}
|
||||
23
app/appmessage/p2p_msgibdblocklocatorhighesthash.go
Normal file
23
app/appmessage/p2p_msgibdblocklocatorhighesthash.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgIBDBlockLocatorHighestHash represents a kaspa BlockLocatorHighestHash message
|
||||
type MsgIBDBlockLocatorHighestHash struct {
|
||||
baseMessage
|
||||
HighestHash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgIBDBlockLocatorHighestHash) Command() MessageCommand {
|
||||
return CmdIBDBlockLocatorHighestHash
|
||||
}
|
||||
|
||||
// NewMsgIBDBlockLocatorHighestHash returns a new BlockLocatorHighestHash message
|
||||
func NewMsgIBDBlockLocatorHighestHash(highestHash *externalapi.DomainHash) *MsgIBDBlockLocatorHighestHash {
|
||||
return &MsgIBDBlockLocatorHighestHash{
|
||||
HighestHash: highestHash,
|
||||
}
|
||||
}
|
||||
16
app/appmessage/p2p_msgibdblocklocatorhighesthashnotfound.go
Normal file
16
app/appmessage/p2p_msgibdblocklocatorhighesthashnotfound.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package appmessage
|
||||
|
||||
// MsgIBDBlockLocatorHighestHashNotFound represents a kaspa BlockLocatorHighestHashNotFound message
|
||||
type MsgIBDBlockLocatorHighestHashNotFound struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgIBDBlockLocatorHighestHashNotFound) Command() MessageCommand {
|
||||
return CmdIBDBlockLocatorHighestHashNotFound
|
||||
}
|
||||
|
||||
// NewMsgIBDBlockLocatorHighestHashNotFound returns a new IBDBlockLocatorHighestHashNotFound message
|
||||
func NewMsgIBDBlockLocatorHighestHashNotFound() *MsgIBDBlockLocatorHighestHashNotFound {
|
||||
return &MsgIBDBlockLocatorHighestHashNotFound{}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// MsgIBDRootUTXOSetAndBlock implements the Message interface and represents a kaspa
|
||||
// IBDRootUTXOSetAndBlock message. It is used to answer RequestIBDRootUTXOSetAndBlock messages.
|
||||
type MsgIBDRootUTXOSetAndBlock struct {
|
||||
baseMessage
|
||||
UTXOSet []byte
|
||||
Block *MsgBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgIBDRootUTXOSetAndBlock) Command() MessageCommand {
|
||||
return CmdIBDRootUTXOSetAndBlock
|
||||
}
|
||||
|
||||
// NewMsgIBDRootUTXOSetAndBlock returns a new MsgIBDRootUTXOSetAndBlock.
|
||||
func NewMsgIBDRootUTXOSetAndBlock(utxoSet []byte, block *MsgBlock) *MsgIBDRootUTXOSetAndBlock {
|
||||
return &MsgIBDRootUTXOSetAndBlock{
|
||||
UTXOSet: utxoSet,
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,12 @@ package appmessage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/random"
|
||||
)
|
||||
|
||||
// TestPing tests the MsgPing API against the latest protocol version.
|
||||
func TestPing(t *testing.T) {
|
||||
// Ensure we get the same nonce back out.
|
||||
nonce, err := random.Uint64()
|
||||
if err != nil {
|
||||
t.Errorf("random.Uint64: Error generating nonce: %v", err)
|
||||
}
|
||||
nonce := uint64(0x61c2c5535902862)
|
||||
msg := NewMsgPing(nonce)
|
||||
if msg.Nonce != nonce {
|
||||
t.Errorf("NewMsgPing: wrong nonce - got %v, want %v",
|
||||
|
||||
@@ -6,16 +6,11 @@ package appmessage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/random"
|
||||
)
|
||||
|
||||
// TestPongLatest tests the MsgPong API against the latest protocol version.
|
||||
func TestPongLatest(t *testing.T) {
|
||||
nonce, err := random.Uint64()
|
||||
if err != nil {
|
||||
t.Errorf("random.Uint64: error generating nonce: %v", err)
|
||||
}
|
||||
nonce := uint64(0x1a05b581a5182c)
|
||||
msg := NewMsgPong(nonce)
|
||||
if msg.Nonce != nonce {
|
||||
t.Errorf("NewMsgPong: wrong nonce - got %v, want %v",
|
||||
|
||||
23
app/appmessage/p2p_msgpruningpointhash.go
Normal file
23
app/appmessage/p2p_msgpruningpointhash.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgPruningPointHashMessage represents a kaspa PruningPointHash message
|
||||
type MsgPruningPointHashMessage struct {
|
||||
baseMessage
|
||||
Hash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgPruningPointHashMessage) Command() MessageCommand {
|
||||
return CmdPruningPointHash
|
||||
}
|
||||
|
||||
// NewPruningPointHashMessage returns a new kaspa PruningPointHash message
|
||||
func NewPruningPointHashMessage(hash *externalapi.DomainHash) *MsgPruningPointHashMessage {
|
||||
return &MsgPruningPointHashMessage{
|
||||
Hash: hash,
|
||||
}
|
||||
}
|
||||
36
app/appmessage/p2p_msgpruningpointutxosetchunk.go
Normal file
36
app/appmessage/p2p_msgpruningpointutxosetchunk.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package appmessage
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// MsgPruningPointUTXOSetChunk represents a kaspa PruningPointUTXOSetChunk message
|
||||
type MsgPruningPointUTXOSetChunk struct {
|
||||
baseMessage
|
||||
OutpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgPruningPointUTXOSetChunk) Command() MessageCommand {
|
||||
return CmdPruningPointUTXOSetChunk
|
||||
}
|
||||
|
||||
// NewMsgPruningPointUTXOSetChunk returns a new MsgPruningPointUTXOSetChunk.
|
||||
func NewMsgPruningPointUTXOSetChunk(outpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair) *MsgPruningPointUTXOSetChunk {
|
||||
return &MsgPruningPointUTXOSetChunk{
|
||||
OutpointAndUTXOEntryPairs: outpointAndUTXOEntryPairs,
|
||||
}
|
||||
}
|
||||
|
||||
// OutpointAndUTXOEntryPair is an outpoint along with its
|
||||
// respective UTXO entry
|
||||
type OutpointAndUTXOEntryPair struct {
|
||||
Outpoint *Outpoint
|
||||
UTXOEntry *UTXOEntry
|
||||
}
|
||||
|
||||
// UTXOEntry houses details about an individual transaction output in a UTXO
|
||||
type UTXOEntry struct {
|
||||
Amount uint64
|
||||
ScriptPublicKey *externalapi.ScriptPublicKey
|
||||
BlockDAAScore uint64
|
||||
IsCoinbase bool
|
||||
}
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
)
|
||||
|
||||
// MsgRequestBlockLocator implements the Message interface and represents a kaspa
|
||||
// RequestBlockLocator message. It is used to request a block locator between high
|
||||
// and low hash.
|
||||
// RequestBlockLocator message. It is used to request a block locator between low
|
||||
// and high hash.
|
||||
// The locator is returned via a locator message (MsgBlockLocator).
|
||||
type MsgRequestBlockLocator struct {
|
||||
baseMessage
|
||||
HighHash *externalapi.DomainHash
|
||||
LowHash *externalapi.DomainHash
|
||||
HighHash *externalapi.DomainHash
|
||||
Limit uint32
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
@@ -23,9 +24,10 @@ func (msg *MsgRequestBlockLocator) Command() MessageCommand {
|
||||
// NewMsgRequestBlockLocator returns a new RequestBlockLocator message that conforms to the
|
||||
// Message interface using the passed parameters and defaults for the remaining
|
||||
// fields.
|
||||
func NewMsgRequestBlockLocator(highHash, lowHash *externalapi.DomainHash) *MsgRequestBlockLocator {
|
||||
func NewMsgRequestBlockLocator(lowHash, highHash *externalapi.DomainHash, limit uint32) *MsgRequestBlockLocator {
|
||||
return &MsgRequestBlockLocator{
|
||||
HighHash: highHash,
|
||||
LowHash: lowHash,
|
||||
HighHash: highHash,
|
||||
Limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,21 +4,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
)
|
||||
|
||||
// TestRequestBlockLocator tests the MsgRequestBlockLocator API.
|
||||
func TestRequestBlockLocator(t *testing.T) {
|
||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||
highHash, err := hashes.FromString(hashStr)
|
||||
highHash, err := externalapi.NewDomainHashFromString(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := MessageCommand(9)
|
||||
msg := NewMsgRequestBlockLocator(highHash, &externalapi.DomainHash{})
|
||||
msg := NewMsgRequestBlockLocator(highHash, &externalapi.DomainHash{}, 0)
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgRequestBlockLocator: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
|
||||
@@ -7,26 +7,26 @@ package appmessage
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// TestRequstIBDBlocks tests the MsgRequestHeaders API.
|
||||
func TestRequstIBDBlocks(t *testing.T) {
|
||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||
lowHash, err := hashes.FromString(hashStr)
|
||||
lowHash, err := externalapi.NewDomainHashFromString(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
hashStr = "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||
highHash, err := hashes.FromString(hashStr)
|
||||
highHash, err := externalapi.NewDomainHashFromString(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
msg := NewMsgRequstHeaders(lowHash, highHash)
|
||||
if *msg.HighHash != *highHash {
|
||||
if !msg.HighHash.Equal(highHash) {
|
||||
t.Errorf("NewMsgRequstHeaders: wrong high hash - got %v, want %v",
|
||||
msg.HighHash, highHash)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MaxRequestIBDBlocksHashes is the maximum number of hashes that can
|
||||
// be in a single RequestIBDBlocks message.
|
||||
const MaxRequestIBDBlocksHashes = MaxInvPerMsg
|
||||
|
||||
// MsgRequestIBDBlocks implements the Message interface and represents a kaspa
|
||||
// RequestIBDBlocks message. It is used to request blocks as part of the IBD
|
||||
// protocol.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgRequestIBDRootUTXOSetAndBlock implements the Message interface and represents a kaspa
|
||||
// RequestIBDRootUTXOSetAndBlock message. It is used to request the UTXO set and block body
|
||||
// of the IBD root block.
|
||||
type MsgRequestIBDRootUTXOSetAndBlock struct {
|
||||
baseMessage
|
||||
IBDRoot *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgRequestIBDRootUTXOSetAndBlock) Command() MessageCommand {
|
||||
return CmdRequestIBDRootUTXOSetAndBlock
|
||||
}
|
||||
|
||||
// NewMsgRequestIBDRootUTXOSetAndBlock returns a new MsgRequestIBDRootUTXOSetAndBlock.
|
||||
func NewMsgRequestIBDRootUTXOSetAndBlock(ibdRoot *externalapi.DomainHash) *MsgRequestIBDRootUTXOSetAndBlock {
|
||||
return &MsgRequestIBDRootUTXOSetAndBlock{
|
||||
IBDRoot: ibdRoot,
|
||||
}
|
||||
}
|
||||
16
app/appmessage/p2p_msgrequestnextpruningpointutxosetchunk.go
Normal file
16
app/appmessage/p2p_msgrequestnextpruningpointutxosetchunk.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package appmessage
|
||||
|
||||
// MsgRequestNextPruningPointUTXOSetChunk represents a kaspa RequestNextPruningPointUTXOSetChunk message
|
||||
type MsgRequestNextPruningPointUTXOSetChunk struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgRequestNextPruningPointUTXOSetChunk) Command() MessageCommand {
|
||||
return CmdRequestNextPruningPointUTXOSetChunk
|
||||
}
|
||||
|
||||
// NewMsgRequestNextPruningPointUTXOSetChunk returns a new MsgRequestNextPruningPointUTXOSetChunk.
|
||||
func NewMsgRequestNextPruningPointUTXOSetChunk() *MsgRequestNextPruningPointUTXOSetChunk {
|
||||
return &MsgRequestNextPruningPointUTXOSetChunk{}
|
||||
}
|
||||
23
app/appmessage/p2p_msgrequestpruningpointutxosetandblock.go
Normal file
23
app/appmessage/p2p_msgrequestpruningpointutxosetandblock.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgRequestPruningPointUTXOSetAndBlock represents a kaspa RequestPruningPointUTXOSetAndBlock message
|
||||
type MsgRequestPruningPointUTXOSetAndBlock struct {
|
||||
baseMessage
|
||||
PruningPointHash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgRequestPruningPointUTXOSetAndBlock) Command() MessageCommand {
|
||||
return CmdRequestPruningPointUTXOSetAndBlock
|
||||
}
|
||||
|
||||
// NewMsgRequestPruningPointUTXOSetAndBlock returns a new MsgRequestPruningPointUTXOSetAndBlock
|
||||
func NewMsgRequestPruningPointUTXOSetAndBlock(pruningPointHash *externalapi.DomainHash) *MsgRequestPruningPointUTXOSetAndBlock {
|
||||
return &MsgRequestPruningPointUTXOSetAndBlock{
|
||||
PruningPointHash: pruningPointHash,
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// MsgRequestSelectedTip implements the Message interface and represents a kaspa
|
||||
// RequestSelectedTip message. It is used to request the selected tip of another peer.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgRequestSelectedTip struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgRequestSelectedTip) Command() MessageCommand {
|
||||
return CmdRequestSelectedTip
|
||||
}
|
||||
|
||||
// NewMsgRequestSelectedTip returns a new kaspa RequestSelectedTip message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgRequestSelectedTip() *MsgRequestSelectedTip {
|
||||
return &MsgRequestSelectedTip{}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestRequestSelectedTip tests the MsgRequestSelectedTip API.
|
||||
func TestRequestSelectedTip(t *testing.T) {
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := MessageCommand(12)
|
||||
msg := NewMsgRequestSelectedTip()
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgRequestSelectedTip: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgSelectedTip implements the Message interface and represents a kaspa
|
||||
// selectedtip message. It is used to answer getseltip messages and tell
|
||||
// the asking peer what is the selected tip of this peer.
|
||||
type MsgSelectedTip struct {
|
||||
baseMessage
|
||||
// The selected tip hash of the generator of the message.
|
||||
SelectedTipHash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgSelectedTip) Command() MessageCommand {
|
||||
return CmdSelectedTip
|
||||
}
|
||||
|
||||
// NewMsgSelectedTip returns a new kaspa selectedtip message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgSelectedTip(selectedTipHash *externalapi.DomainHash) *MsgSelectedTip {
|
||||
return &MsgSelectedTip{
|
||||
SelectedTipHash: selectedTipHash,
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// TestSelectedTip tests the MsgSelectedTip API.
|
||||
func TestSelectedTip(t *testing.T) {
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := MessageCommand(11)
|
||||
msg := NewMsgSelectedTip(&externalapi.DomainHash{})
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgSelectedTip: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,7 @@ import (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
|
||||
@@ -41,8 +37,8 @@ const (
|
||||
maxTxInPerMessage = (MaxMessagePayload / minTxInPayload) + 1
|
||||
|
||||
// MinTxOutPayload is the minimum payload size for a transaction output.
|
||||
// Value 8 bytes + Varint for ScriptPubKey length 1 byte.
|
||||
MinTxOutPayload = 9
|
||||
// Value 8 bytes + version 2 bytes + Varint for ScriptPublicKey length 1 byte.
|
||||
MinTxOutPayload = 11
|
||||
|
||||
// maxTxOutPerMessage is the maximum number of transactions outputs that
|
||||
// a transaction which fits into a message could possibly have.
|
||||
@@ -99,23 +95,23 @@ type TxIn struct {
|
||||
// NewTxIn returns a new kaspa transaction input with the provided
|
||||
// previous outpoint point and signature script with a default sequence of
|
||||
// MaxTxInSequenceNum.
|
||||
func NewTxIn(prevOut *Outpoint, signatureScript []byte) *TxIn {
|
||||
func NewTxIn(prevOut *Outpoint, signatureScript []byte, sequence uint64) *TxIn {
|
||||
return &TxIn{
|
||||
PreviousOutpoint: *prevOut,
|
||||
SignatureScript: signatureScript,
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
Sequence: sequence,
|
||||
}
|
||||
}
|
||||
|
||||
// TxOut defines a kaspa transaction output.
|
||||
type TxOut struct {
|
||||
Value uint64
|
||||
ScriptPubKey []byte
|
||||
ScriptPubKey *externalapi.ScriptPublicKey
|
||||
}
|
||||
|
||||
// NewTxOut returns a new kaspa transaction output with the provided
|
||||
// transaction value and public key script.
|
||||
func NewTxOut(value uint64, scriptPubKey []byte) *TxOut {
|
||||
func NewTxOut(value uint64, scriptPubKey *externalapi.ScriptPublicKey) *TxOut {
|
||||
return &TxOut{
|
||||
Value: value,
|
||||
ScriptPubKey: scriptPubKey,
|
||||
@@ -130,13 +126,12 @@ func NewTxOut(value uint64, scriptPubKey []byte) *TxOut {
|
||||
// inputs and outputs.
|
||||
type MsgTx struct {
|
||||
baseMessage
|
||||
Version int32
|
||||
Version uint16
|
||||
TxIn []*TxIn
|
||||
TxOut []*TxOut
|
||||
LockTime uint64
|
||||
SubnetworkID externalapi.DomainSubnetworkID
|
||||
Gas uint64
|
||||
PayloadHash externalapi.DomainHash
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
@@ -162,12 +157,12 @@ func (msg *MsgTx) IsCoinBase() bool {
|
||||
|
||||
// TxHash generates the Hash for the transaction.
|
||||
func (msg *MsgTx) TxHash() *externalapi.DomainHash {
|
||||
return consensusserialization.TransactionHash(MsgTxToDomainTransaction(msg))
|
||||
return consensushashing.TransactionHash(MsgTxToDomainTransaction(msg))
|
||||
}
|
||||
|
||||
// TxID generates the Hash for the transaction without the signature script, gas and payload fields.
|
||||
func (msg *MsgTx) TxID() *externalapi.DomainTransactionID {
|
||||
return consensusserialization.TransactionID(MsgTxToDomainTransaction(msg))
|
||||
return consensushashing.TransactionID(MsgTxToDomainTransaction(msg))
|
||||
}
|
||||
|
||||
// Copy creates a deep copy of a transaction so that the original does not get
|
||||
@@ -182,7 +177,6 @@ func (msg *MsgTx) Copy() *MsgTx {
|
||||
LockTime: msg.LockTime,
|
||||
SubnetworkID: msg.SubnetworkID,
|
||||
Gas: msg.Gas,
|
||||
PayloadHash: msg.PayloadHash,
|
||||
}
|
||||
|
||||
if msg.Payload != nil {
|
||||
@@ -220,20 +214,20 @@ func (msg *MsgTx) Copy() *MsgTx {
|
||||
|
||||
// Deep copy the old TxOut data.
|
||||
for _, oldTxOut := range msg.TxOut {
|
||||
// Deep copy the old ScriptPubKey
|
||||
var newScript []byte
|
||||
// Deep copy the old ScriptPublicKey
|
||||
var newScript externalapi.ScriptPublicKey
|
||||
oldScript := oldTxOut.ScriptPubKey
|
||||
oldScriptLen := len(oldScript)
|
||||
oldScriptLen := len(oldScript.Script)
|
||||
if oldScriptLen > 0 {
|
||||
newScript = make([]byte, oldScriptLen)
|
||||
copy(newScript, oldScript[:oldScriptLen])
|
||||
newScript = externalapi.ScriptPublicKey{Script: make([]byte, oldScriptLen), Version: oldScript.Version}
|
||||
copy(newScript.Script, oldScript.Script[:oldScriptLen])
|
||||
}
|
||||
|
||||
// Create new txOut with the deep copied data and append it to
|
||||
// new Tx.
|
||||
newTxOut := TxOut{
|
||||
Value: oldTxOut.Value,
|
||||
ScriptPubKey: newScript,
|
||||
ScriptPubKey: &newScript,
|
||||
}
|
||||
newTx.TxOut = append(newTx.TxOut, &newTxOut)
|
||||
}
|
||||
@@ -259,8 +253,8 @@ func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
|
||||
// 3. The transaction's subnetwork
|
||||
func (msg *MsgTx) IsSubnetworkCompatible(subnetworkID *externalapi.DomainSubnetworkID) bool {
|
||||
return subnetworkID == nil ||
|
||||
*subnetworkID == subnetworks.SubnetworkIDNative ||
|
||||
*subnetworkID == msg.SubnetworkID
|
||||
subnetworkID.Equal(&subnetworks.SubnetworkIDNative) ||
|
||||
subnetworkID.Equal(&msg.SubnetworkID)
|
||||
}
|
||||
|
||||
// newMsgTx returns a new tx message that conforms to the Message interface.
|
||||
@@ -272,7 +266,7 @@ func (msg *MsgTx) IsSubnetworkCompatible(subnetworkID *externalapi.DomainSubnetw
|
||||
// The payload hash is calculated automatically according to provided payload.
|
||||
// Also, the lock time is set to zero to indicate the transaction is valid
|
||||
// immediately as opposed to some time in future.
|
||||
func newMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
||||
func newMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
||||
gas uint64, payload []byte, lockTime uint64) *MsgTx {
|
||||
|
||||
if txIn == nil {
|
||||
@@ -283,30 +277,24 @@ func newMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, subnetworkID *externa
|
||||
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
|
||||
}
|
||||
|
||||
var payloadHash externalapi.DomainHash
|
||||
if *subnetworkID != subnetworks.SubnetworkIDNative {
|
||||
payloadHash = *hashes.HashData(payload)
|
||||
}
|
||||
|
||||
return &MsgTx{
|
||||
Version: version,
|
||||
TxIn: txIn,
|
||||
TxOut: txOut,
|
||||
SubnetworkID: *subnetworkID,
|
||||
Gas: gas,
|
||||
PayloadHash: payloadHash,
|
||||
Payload: payload,
|
||||
LockTime: lockTime,
|
||||
}
|
||||
}
|
||||
|
||||
// NewNativeMsgTx returns a new tx message in the native subnetwork
|
||||
func NewNativeMsgTx(version int32, txIn []*TxIn, txOut []*TxOut) *MsgTx {
|
||||
func NewNativeMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut) *MsgTx {
|
||||
return newMsgTx(version, txIn, txOut, &subnetworks.SubnetworkIDNative, 0, nil, 0)
|
||||
}
|
||||
|
||||
// NewSubnetworkMsgTx returns a new tx message in the specified subnetwork with specified gas and payload
|
||||
func NewSubnetworkMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
||||
func NewSubnetworkMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
||||
gas uint64, payload []byte) *MsgTx {
|
||||
|
||||
return newMsgTx(version, txIn, txOut, subnetworkID, gas, payload, 0)
|
||||
@@ -315,12 +303,12 @@ func NewSubnetworkMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, subnetworkI
|
||||
// NewNativeMsgTxWithLocktime returns a new tx message in the native subnetwork with a locktime.
|
||||
//
|
||||
// See newMsgTx for further documntation of the parameters
|
||||
func NewNativeMsgTxWithLocktime(version int32, txIn []*TxIn, txOut []*TxOut, locktime uint64) *MsgTx {
|
||||
func NewNativeMsgTxWithLocktime(version uint16, txIn []*TxIn, txOut []*TxOut, locktime uint64) *MsgTx {
|
||||
return newMsgTx(version, txIn, txOut, &subnetworks.SubnetworkIDNative, 0, nil, locktime)
|
||||
}
|
||||
|
||||
// NewRegistryMsgTx creates a new MsgTx that registers a new subnetwork
|
||||
func NewRegistryMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, gasLimit uint64) *MsgTx {
|
||||
func NewRegistryMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, gasLimit uint64) *MsgTx {
|
||||
payload := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(payload, gasLimit)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
@@ -52,7 +52,7 @@ func TestTx(t *testing.T) {
|
||||
// testing package functionality.
|
||||
prevOutIndex := uint32(1)
|
||||
prevOut := NewOutpoint(txID, prevOutIndex)
|
||||
if prevOut.TxID != *txID {
|
||||
if !prevOut.TxID.Equal(txID) {
|
||||
t.Errorf("NewOutpoint: wrong ID - got %v, want %v",
|
||||
spew.Sprint(&prevOut.TxID), spew.Sprint(txID))
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func TestTx(t *testing.T) {
|
||||
|
||||
// Ensure we get the same transaction input back out.
|
||||
sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}
|
||||
txIn := NewTxIn(prevOut, sigScript)
|
||||
txIn := NewTxIn(prevOut, sigScript, constants.MaxTxInSequenceNum)
|
||||
if !reflect.DeepEqual(&txIn.PreviousOutpoint, prevOut) {
|
||||
t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v",
|
||||
spew.Sprint(&txIn.PreviousOutpoint),
|
||||
@@ -82,26 +82,28 @@ func TestTx(t *testing.T) {
|
||||
|
||||
// Ensure we get the same transaction output back out.
|
||||
txValue := uint64(5000000000)
|
||||
scriptPubKey := []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
|
||||
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
|
||||
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
|
||||
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
|
||||
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
|
||||
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
|
||||
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
|
||||
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
|
||||
0xa6, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
}
|
||||
scriptPubKey := &externalapi.ScriptPublicKey{
|
||||
Script: []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
|
||||
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
|
||||
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
|
||||
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
|
||||
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
|
||||
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
|
||||
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
|
||||
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
|
||||
0xa6, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
Version: 0}
|
||||
txOut := NewTxOut(txValue, scriptPubKey)
|
||||
if txOut.Value != txValue {
|
||||
t.Errorf("NewTxOut: wrong scriptPubKey - got %v, want %v",
|
||||
txOut.Value, txValue)
|
||||
|
||||
}
|
||||
if !bytes.Equal(txOut.ScriptPubKey, scriptPubKey) {
|
||||
if !bytes.Equal(txOut.ScriptPubKey.Script, scriptPubKey.Script) {
|
||||
t.Errorf("NewTxOut: wrong scriptPubKey - got %v, want %v",
|
||||
spew.Sdump(txOut.ScriptPubKey),
|
||||
spew.Sdump(scriptPubKey))
|
||||
@@ -131,11 +133,15 @@ func TestTx(t *testing.T) {
|
||||
|
||||
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
||||
func TestTxHashAndID(t *testing.T) {
|
||||
txID1Str := "a3d29c39bfb578235e4813cc8138a9ba10def63acad193a7a880159624840d7f"
|
||||
txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
|
||||
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7"
|
||||
wantTxID1, err := transactionid.FromString(txID1Str)
|
||||
if err != nil {
|
||||
t.Errorf("NewTxIDFromStr: %v", err)
|
||||
return
|
||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
||||
}
|
||||
wantTxHash1, err := transactionid.FromString(txHash1Str)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
||||
}
|
||||
|
||||
// A coinbase transaction
|
||||
@@ -149,7 +155,7 @@ func TestTxHashAndID(t *testing.T) {
|
||||
}
|
||||
txOut := &TxOut{
|
||||
Value: 5000000000,
|
||||
ScriptPubKey: []byte{
|
||||
ScriptPubKey: &externalapi.ScriptPublicKey{Script: []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
|
||||
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
|
||||
@@ -161,32 +167,32 @@ func TestTxHashAndID(t *testing.T) {
|
||||
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
|
||||
0xa6, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
}, Version: 0},
|
||||
}
|
||||
tx1 := NewSubnetworkMsgTx(1, []*TxIn{txIn}, []*TxOut{txOut}, &subnetworks.SubnetworkIDCoinbase, 0, nil)
|
||||
tx1 := NewSubnetworkMsgTx(0, []*TxIn{txIn}, []*TxOut{txOut}, &subnetworks.SubnetworkIDCoinbase, 0, nil)
|
||||
|
||||
// Ensure the hash produced is expected.
|
||||
tx1Hash := tx1.TxHash()
|
||||
if *tx1Hash != (externalapi.DomainHash)(*wantTxID1) {
|
||||
if *tx1Hash != (externalapi.DomainHash)(*wantTxHash1) {
|
||||
t.Errorf("TxHash: wrong hash - got %v, want %v",
|
||||
spew.Sprint(tx1Hash), spew.Sprint(wantTxID1))
|
||||
spew.Sprint(tx1Hash), spew.Sprint(wantTxHash1))
|
||||
}
|
||||
|
||||
// Ensure the TxID for coinbase transaction is the same as TxHash.
|
||||
tx1ID := tx1.TxID()
|
||||
if *tx1ID != *wantTxID1 {
|
||||
if !tx1ID.Equal(wantTxID1) {
|
||||
t.Errorf("TxID: wrong ID - got %v, want %v",
|
||||
spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
|
||||
}
|
||||
|
||||
hash2Str := "c84f3009b337aaa3adeb2ffd41010d5f62dd773ca25b39c908a77da91f87b729"
|
||||
wantHash2, err := hashes.FromString(hash2Str)
|
||||
hash2Str := "8dafd1bec24527d8e3b443ceb0a3b92fffc0d60026317f890b2faf5e9afc177a"
|
||||
wantHash2, err := externalapi.NewDomainHashFromString(hash2Str)
|
||||
if err != nil {
|
||||
t.Errorf("NewTxIDFromStr: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
id2Str := "7c919f676109743a1271a88beeb43849a6f9cc653f6082e59a7266f3df4802b9"
|
||||
id2Str := "89ffb49474637502d9059af38b8a95fc2f0d3baef5c801d7a9b9c8830671b711"
|
||||
wantID2, err := transactionid.FromString(id2Str)
|
||||
if err != nil {
|
||||
t.Errorf("NewTxIDFromStr: %v", err)
|
||||
@@ -196,7 +202,7 @@ func TestTxHashAndID(t *testing.T) {
|
||||
txIns := []*TxIn{{
|
||||
PreviousOutpoint: Outpoint{
|
||||
Index: 0,
|
||||
TxID: externalapi.DomainTransactionID{1, 2, 3},
|
||||
TxID: *externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{1, 2, 3}),
|
||||
},
|
||||
SignatureScript: []byte{
|
||||
0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xDA, 0x0D, 0xC6, 0xAE, 0xCE, 0xFE, 0x1E, 0x06, 0xEF, 0xDF,
|
||||
@@ -214,42 +220,42 @@ func TestTxHashAndID(t *testing.T) {
|
||||
txOuts := []*TxOut{
|
||||
{
|
||||
Value: 244623243,
|
||||
ScriptPubKey: []byte{
|
||||
ScriptPubKey: &externalapi.ScriptPublicKey{Script: []byte{
|
||||
0x76, 0xA9, 0x14, 0xBA, 0xDE, 0xEC, 0xFD, 0xEF, 0x05, 0x07, 0x24, 0x7F, 0xC8, 0xF7, 0x42, 0x41,
|
||||
0xD7, 0x3B, 0xC0, 0x39, 0x97, 0x2D, 0x7B, 0x88, 0xAC,
|
||||
},
|
||||
}, Version: 0},
|
||||
},
|
||||
{
|
||||
Value: 44602432,
|
||||
ScriptPubKey: []byte{
|
||||
ScriptPubKey: &externalapi.ScriptPublicKey{Script: []byte{
|
||||
0x76, 0xA9, 0x14, 0xC1, 0x09, 0x32, 0x48, 0x3F, 0xEC, 0x93, 0xED, 0x51, 0xF5, 0xFE, 0x95, 0xE7,
|
||||
0x25, 0x59, 0xF2, 0xCC, 0x70, 0x43, 0xF9, 0x88, 0xAC,
|
||||
},
|
||||
}, Version: 0},
|
||||
},
|
||||
}
|
||||
tx2 := NewSubnetworkMsgTx(1, txIns, txOuts, &externalapi.DomainSubnetworkID{1, 2, 3}, 0, payload)
|
||||
|
||||
// Ensure the hash produced is expected.
|
||||
tx2Hash := tx2.TxHash()
|
||||
if *tx2Hash != *wantHash2 {
|
||||
if !tx2Hash.Equal(wantHash2) {
|
||||
t.Errorf("TxHash: wrong hash - got %v, want %v",
|
||||
spew.Sprint(tx2Hash), spew.Sprint(wantHash2))
|
||||
}
|
||||
|
||||
// Ensure the TxID for coinbase transaction is the same as TxHash.
|
||||
tx2ID := tx2.TxID()
|
||||
if *tx2ID != *wantID2 {
|
||||
if !tx2ID.Equal(wantID2) {
|
||||
t.Errorf("TxID: wrong ID - got %v, want %v",
|
||||
spew.Sprint(tx2ID), spew.Sprint(wantID2))
|
||||
}
|
||||
|
||||
if *tx2ID == (externalapi.DomainTransactionID)(*tx2Hash) {
|
||||
if tx2ID.Equal((*externalapi.DomainTransactionID)(tx2Hash)) {
|
||||
t.Errorf("tx2ID and tx2Hash shouldn't be the same for non-coinbase transaction with signature and/or payload")
|
||||
}
|
||||
|
||||
tx2.TxIn[0].SignatureScript = []byte{}
|
||||
newTx2Hash := tx2.TxHash()
|
||||
if *tx2ID != (externalapi.DomainTransactionID)(*newTx2Hash) {
|
||||
t.Errorf("tx2ID and newTx2Hash should be the same for transaction with an empty signature")
|
||||
if *tx2ID == (externalapi.DomainTransactionID)(*newTx2Hash) {
|
||||
t.Errorf("tx2ID and newTx2Hash should not be the same even for transaction with an empty signature")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,6 @@ type MsgVersion struct {
|
||||
// on the appmessage. This has a max length of MaxUserAgentLen.
|
||||
UserAgent string
|
||||
|
||||
// The selected tip hash of the generator of the version message.
|
||||
SelectedTipHash *externalapi.DomainHash
|
||||
|
||||
// Don't announce transactions to peer.
|
||||
DisableRelayTx bool
|
||||
|
||||
@@ -85,7 +82,7 @@ func (msg *MsgVersion) Command() MessageCommand {
|
||||
// Message interface using the passed parameters and defaults for the remaining
|
||||
// fields.
|
||||
func NewMsgVersion(addr *NetAddress, id *id.ID, network string,
|
||||
selectedTipHash *externalapi.DomainHash, subnetworkID *externalapi.DomainSubnetworkID) *MsgVersion {
|
||||
subnetworkID *externalapi.DomainSubnetworkID) *MsgVersion {
|
||||
|
||||
// Limit the timestamp to one millisecond precision since the protocol
|
||||
// doesn't support better.
|
||||
@@ -97,7 +94,6 @@ func NewMsgVersion(addr *NetAddress, id *id.ID, network string,
|
||||
Address: addr,
|
||||
ID: id,
|
||||
UserAgent: DefaultUserAgent,
|
||||
SelectedTipHash: selectedTipHash,
|
||||
DisableRelayTx: false,
|
||||
SubnetworkID: subnetworkID,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||
)
|
||||
|
||||
@@ -19,16 +18,15 @@ func TestVersion(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// Create version message data.
|
||||
selectedTipHash := &externalapi.DomainHash{12, 34}
|
||||
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
||||
me := NewNetAddress(tcpAddrMe, SFNodeNetwork)
|
||||
me := NewNetAddress(tcpAddrMe)
|
||||
generatedID, err := id.GenerateID()
|
||||
if err != nil {
|
||||
t.Fatalf("id.GenerateID: %s", err)
|
||||
}
|
||||
|
||||
// Ensure we get the correct data back out.
|
||||
msg := NewMsgVersion(me, generatedID, "mainnet", selectedTipHash, nil)
|
||||
msg := NewMsgVersion(me, generatedID, "mainnet", nil)
|
||||
if msg.ProtocolVersion != pver {
|
||||
t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v",
|
||||
msg.ProtocolVersion, pver)
|
||||
@@ -45,10 +43,6 @@ func TestVersion(t *testing.T) {
|
||||
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
|
||||
msg.UserAgent, DefaultUserAgent)
|
||||
}
|
||||
if *msg.SelectedTipHash != *selectedTipHash {
|
||||
t.Errorf("NewMsgVersion: wrong selected tip hash - got %s, want %s",
|
||||
msg.SelectedTipHash, selectedTipHash)
|
||||
}
|
||||
if msg.DisableRelayTx {
|
||||
t.Errorf("NewMsgVersion: disable relay tx is not false by "+
|
||||
"default - got %v, want %v", msg.DisableRelayTx, false)
|
||||
|
||||
@@ -15,9 +15,6 @@ type NetAddress struct {
|
||||
// Last time the address was seen.
|
||||
Timestamp mstime.Time
|
||||
|
||||
// Bitfield which identifies the services supported by the address.
|
||||
Services ServiceFlag
|
||||
|
||||
// IP address of the peer.
|
||||
IP net.IP
|
||||
|
||||
@@ -26,17 +23,6 @@ type NetAddress struct {
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// HasService returns whether the specified service is supported by the address.
|
||||
func (na *NetAddress) HasService(service ServiceFlag) bool {
|
||||
return na.Services&service == service
|
||||
}
|
||||
|
||||
// AddService adds service as a supported service by the peer generating the
|
||||
// message.
|
||||
func (na *NetAddress) AddService(service ServiceFlag) {
|
||||
na.Services |= service
|
||||
}
|
||||
|
||||
// TCPAddress converts the NetAddress to *net.TCPAddr
|
||||
func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
||||
return &net.TCPAddr{
|
||||
@@ -47,20 +33,19 @@ func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
||||
|
||||
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
|
||||
// supported services with defaults for the remaining fields.
|
||||
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
|
||||
return NewNetAddressTimestamp(mstime.Now(), services, ip, port)
|
||||
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
|
||||
return NewNetAddressTimestamp(mstime.Now(), ip, port)
|
||||
}
|
||||
|
||||
// NewNetAddressTimestamp returns a new NetAddress using the provided
|
||||
// timestamp, IP, port, and supported services. The timestamp is rounded to
|
||||
// single millisecond precision.
|
||||
func NewNetAddressTimestamp(
|
||||
timestamp mstime.Time, services ServiceFlag, ip net.IP, port uint16) *NetAddress {
|
||||
timestamp mstime.Time, ip net.IP, port uint16) *NetAddress {
|
||||
// Limit the timestamp to one millisecond precision since the protocol
|
||||
// doesn't support better.
|
||||
na := NetAddress{
|
||||
Timestamp: timestamp,
|
||||
Services: services,
|
||||
IP: ip,
|
||||
Port: port,
|
||||
}
|
||||
@@ -69,6 +54,6 @@ func NewNetAddressTimestamp(
|
||||
|
||||
// NewNetAddress returns a new NetAddress using the provided TCP address and
|
||||
// supported services with defaults for the remaining fields.
|
||||
func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
|
||||
return NewNetAddressIPPort(addr.IP, uint16(addr.Port), services)
|
||||
func NewNetAddress(addr *net.TCPAddr) *NetAddress {
|
||||
return NewNetAddressIPPort(addr.IP, uint16(addr.Port))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestNetAddress(t *testing.T) {
|
||||
port := 16111
|
||||
|
||||
// Test NewNetAddress.
|
||||
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port}, 0)
|
||||
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port})
|
||||
|
||||
// Ensure we get the same ip, port, and services back out.
|
||||
if !na.IP.Equal(ip) {
|
||||
@@ -25,21 +25,4 @@ func TestNetAddress(t *testing.T) {
|
||||
t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port,
|
||||
port)
|
||||
}
|
||||
if na.Services != 0 {
|
||||
t.Errorf("NetNetAddress: wrong services - got %v, want %v",
|
||||
na.Services, 0)
|
||||
}
|
||||
if na.HasService(SFNodeNetwork) {
|
||||
t.Errorf("HasService: SFNodeNetwork service is set")
|
||||
}
|
||||
|
||||
// Ensure adding the full service node flag works.
|
||||
na.AddService(SFNodeNetwork)
|
||||
if na.Services != SFNodeNetwork {
|
||||
t.Errorf("AddService: wrong services - got %v, want %v",
|
||||
na.Services, SFNodeNetwork)
|
||||
}
|
||||
if !na.HasService(SFNodeNetwork) {
|
||||
t.Errorf("HasService: SFNodeNetwork service not set")
|
||||
}
|
||||
}
|
||||
|
||||
16
app/appmessage/p2p_requestpruningpointhash.go
Normal file
16
app/appmessage/p2p_requestpruningpointhash.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package appmessage
|
||||
|
||||
// MsgRequestPruningPointHashMessage represents a kaspa RequestPruningPointHashMessage message
|
||||
type MsgRequestPruningPointHashMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgRequestPruningPointHashMessage) Command() MessageCommand {
|
||||
return CmdRequestPruningPointHash
|
||||
}
|
||||
|
||||
// NewMsgRequestPruningPointHashMessage returns a new kaspa RequestPruningPointHash message
|
||||
func NewMsgRequestPruningPointHashMessage() *MsgRequestPruningPointHashMessage {
|
||||
return &MsgRequestPruningPointHashMessage{}
|
||||
}
|
||||
16
app/appmessage/p2p_unexpectedpruningpoint.go
Normal file
16
app/appmessage/p2p_unexpectedpruningpoint.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package appmessage
|
||||
|
||||
// MsgUnexpectedPruningPoint represents a kaspa UnexpectedPruningPoint message
|
||||
type MsgUnexpectedPruningPoint struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgUnexpectedPruningPoint) Command() MessageCommand {
|
||||
return CmdUnexpectedPruningPoint
|
||||
}
|
||||
|
||||
// NewMsgUnexpectedPruningPoint returns a new kaspa UnexpectedPruningPoint message
|
||||
func NewMsgUnexpectedPruningPoint() *MsgUnexpectedPruningPoint {
|
||||
return &MsgUnexpectedPruningPoint{}
|
||||
}
|
||||
39
app/appmessage/rpc_ban.go
Normal file
39
app/appmessage/rpc_ban.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package appmessage
|
||||
|
||||
// BanRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type BanRequestMessage struct {
|
||||
baseMessage
|
||||
|
||||
IP string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *BanRequestMessage) Command() MessageCommand {
|
||||
return CmdBanRequestMessage
|
||||
}
|
||||
|
||||
// NewBanRequestMessage returns an instance of the message
|
||||
func NewBanRequestMessage(ip string) *BanRequestMessage {
|
||||
return &BanRequestMessage{
|
||||
IP: ip,
|
||||
}
|
||||
}
|
||||
|
||||
// BanResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type BanResponseMessage struct {
|
||||
baseMessage
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *BanResponseMessage) Command() MessageCommand {
|
||||
return CmdBanResponseMessage
|
||||
}
|
||||
|
||||
// NewBanResponseMessage returns a instance of the message
|
||||
func NewBanResponseMessage() *BanResponseMessage {
|
||||
return &BanResponseMessage{}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ package appmessage
|
||||
type GetBlockRequestMessage struct {
|
||||
baseMessage
|
||||
Hash string
|
||||
SubnetworkID string
|
||||
IncludeTransactionVerboseData bool
|
||||
}
|
||||
|
||||
@@ -15,10 +14,9 @@ func (msg *GetBlockRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlockRequestMessage returns a instance of the message
|
||||
func NewGetBlockRequestMessage(hash string, subnetworkID string, includeTransactionVerboseData bool) *GetBlockRequestMessage {
|
||||
func NewGetBlockRequestMessage(hash string, includeTransactionVerboseData bool) *GetBlockRequestMessage {
|
||||
return &GetBlockRequestMessage{
|
||||
Hash: hash,
|
||||
SubnetworkID: subnetworkID,
|
||||
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||
}
|
||||
}
|
||||
@@ -27,7 +25,7 @@ func NewGetBlockRequestMessage(hash string, subnetworkID string, includeTransact
|
||||
// its respective RPC message
|
||||
type GetBlockResponseMessage struct {
|
||||
baseMessage
|
||||
BlockVerboseData *BlockVerboseData
|
||||
Block *RPCBlock
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -41,69 +39,3 @@ func (msg *GetBlockResponseMessage) Command() MessageCommand {
|
||||
func NewGetBlockResponseMessage() *GetBlockResponseMessage {
|
||||
return &GetBlockResponseMessage{}
|
||||
}
|
||||
|
||||
// BlockVerboseData holds verbose data about a block
|
||||
type BlockVerboseData struct {
|
||||
Hash string
|
||||
Version int32
|
||||
VersionHex string
|
||||
HashMerkleRoot string
|
||||
AcceptedIDMerkleRoot string
|
||||
UTXOCommitment string
|
||||
TxIDs []string
|
||||
TransactionVerboseData []*TransactionVerboseData
|
||||
Time int64
|
||||
Nonce uint64
|
||||
Bits string
|
||||
Difficulty float64
|
||||
ParentHashes []string
|
||||
SelectedParentHash string
|
||||
BlueScore uint64
|
||||
}
|
||||
|
||||
// TransactionVerboseData holds verbose data about a transaction
|
||||
type TransactionVerboseData struct {
|
||||
TxID string
|
||||
Hash string
|
||||
Size uint64
|
||||
Version int32
|
||||
LockTime uint64
|
||||
SubnetworkID string
|
||||
Gas uint64
|
||||
PayloadHash string
|
||||
Payload string
|
||||
TransactionVerboseInputs []*TransactionVerboseInput
|
||||
TransactionVerboseOutputs []*TransactionVerboseOutput
|
||||
BlockHash string
|
||||
Time uint64
|
||||
BlockTime uint64
|
||||
}
|
||||
|
||||
// TransactionVerboseInput holds data about a transaction input
|
||||
type TransactionVerboseInput struct {
|
||||
TxID string
|
||||
OutputIndex uint32
|
||||
ScriptSig *ScriptSig
|
||||
Sequence uint64
|
||||
}
|
||||
|
||||
// ScriptSig holds data about a script signature
|
||||
type ScriptSig struct {
|
||||
Asm string
|
||||
Hex string
|
||||
}
|
||||
|
||||
// TransactionVerboseOutput holds data about a transaction output
|
||||
type TransactionVerboseOutput struct {
|
||||
Value uint64
|
||||
Index uint32
|
||||
ScriptPubKey *ScriptPubKeyResult
|
||||
}
|
||||
|
||||
// ScriptPubKeyResult holds data about a script public key
|
||||
type ScriptPubKeyResult struct {
|
||||
Asm string
|
||||
Hex string
|
||||
Type string
|
||||
Address string
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package appmessage
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// GetBlockCountRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetBlockCountRequestMessage struct {
|
||||
@@ -20,7 +22,8 @@ func NewGetBlockCountRequestMessage() *GetBlockCountRequestMessage {
|
||||
// its respective RPC message
|
||||
type GetBlockCountResponseMessage struct {
|
||||
baseMessage
|
||||
BlockCount uint64
|
||||
BlockCount uint64
|
||||
HeaderCount uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -31,8 +34,9 @@ func (msg *GetBlockCountResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlockCountResponseMessage returns a instance of the message
|
||||
func NewGetBlockCountResponseMessage(blockCount uint64) *GetBlockCountResponseMessage {
|
||||
func NewGetBlockCountResponseMessage(syncInfo *externalapi.SyncInfo) *GetBlockCountResponseMessage {
|
||||
return &GetBlockCountResponseMessage{
|
||||
BlockCount: blockCount,
|
||||
BlockCount: syncInfo.BlockCount,
|
||||
HeaderCount: syncInfo.HeaderCount,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,12 @@ type GetBlockDAGInfoResponseMessage struct {
|
||||
baseMessage
|
||||
NetworkName string
|
||||
BlockCount uint64
|
||||
HeaderCount uint64
|
||||
TipHashes []string
|
||||
VirtualParentHashes []string
|
||||
Difficulty float64
|
||||
PastMedianTime int64
|
||||
PruningPointHash string
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ func NewGetBlockTemplateRequestMessage(payAddress string) *GetBlockTemplateReque
|
||||
// its respective RPC message
|
||||
type GetBlockTemplateResponseMessage struct {
|
||||
baseMessage
|
||||
MsgBlock *MsgBlock
|
||||
Block *RPCBlock
|
||||
IsSynced bool
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -34,6 +35,9 @@ func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlockTemplateResponseMessage returns a instance of the message
|
||||
func NewGetBlockTemplateResponseMessage(msgBlock *MsgBlock) *GetBlockTemplateResponseMessage {
|
||||
return &GetBlockTemplateResponseMessage{MsgBlock: msgBlock}
|
||||
func NewGetBlockTemplateResponseMessage(block *RPCBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
||||
return &GetBlockTemplateResponseMessage{
|
||||
Block: block,
|
||||
IsSynced: isSynced,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type GetBlocksRequestMessage struct {
|
||||
baseMessage
|
||||
LowHash string
|
||||
IncludeBlockHexes bool
|
||||
IncludeBlockVerboseData bool
|
||||
LowHash string
|
||||
IncludeBlocks bool
|
||||
IncludeTransactionVerboseData bool
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -15,11 +15,12 @@ func (msg *GetBlocksRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlocksRequestMessage returns a instance of the message
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeBlockVerboseData bool) *GetBlocksRequestMessage {
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlocks bool,
|
||||
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
|
||||
return &GetBlocksRequestMessage{
|
||||
LowHash: lowHash,
|
||||
IncludeBlockHexes: includeBlockHexes,
|
||||
IncludeBlockVerboseData: includeBlockVerboseData,
|
||||
LowHash: lowHash,
|
||||
IncludeBlocks: includeBlocks,
|
||||
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +28,8 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeB
|
||||
// its respective RPC message
|
||||
type GetBlocksResponseMessage struct {
|
||||
baseMessage
|
||||
BlockHashes []string
|
||||
BlockHexes []string
|
||||
BlockVerboseData []*BlockVerboseData
|
||||
BlockHashes []string
|
||||
Blocks []*RPCBlock
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -40,12 +40,6 @@ func (msg *GetBlocksResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlocksResponseMessage returns a instance of the message
|
||||
func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
|
||||
blockVerboseData []*BlockVerboseData) *GetBlocksResponseMessage {
|
||||
|
||||
return &GetBlocksResponseMessage{
|
||||
BlockHashes: blockHashes,
|
||||
BlockHexes: blockHexes,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
}
|
||||
func NewGetBlocksResponseMessage() *GetBlocksResponseMessage {
|
||||
return &GetBlocksResponseMessage{}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// GetChainFromBlockRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetChainFromBlockRequestMessage struct {
|
||||
baseMessage
|
||||
StartHash string
|
||||
IncludeBlockVerboseData bool
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetChainFromBlockRequestMessage) Command() MessageCommand {
|
||||
return CmdGetChainFromBlockRequestMessage
|
||||
}
|
||||
|
||||
// NewGetChainFromBlockRequestMessage returns a instance of the message
|
||||
func NewGetChainFromBlockRequestMessage(startHash string, includeBlockVerboseData bool) *GetChainFromBlockRequestMessage {
|
||||
return &GetChainFromBlockRequestMessage{
|
||||
StartHash: startHash,
|
||||
IncludeBlockVerboseData: includeBlockVerboseData,
|
||||
}
|
||||
}
|
||||
|
||||
// GetChainFromBlockResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetChainFromBlockResponseMessage struct {
|
||||
baseMessage
|
||||
RemovedChainBlockHashes []string
|
||||
AddedChainBlocks []*ChainBlock
|
||||
BlockVerboseData []*BlockVerboseData
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetChainFromBlockResponseMessage) Command() MessageCommand {
|
||||
return CmdGetChainFromBlockResponseMessage
|
||||
}
|
||||
|
||||
// NewGetChainFromBlockResponseMessage returns a instance of the message
|
||||
func NewGetChainFromBlockResponseMessage(removedChainBlockHashes []string,
|
||||
addedChainBlocks []*ChainBlock, blockVerboseData []*BlockVerboseData) *GetChainFromBlockResponseMessage {
|
||||
|
||||
return &GetChainFromBlockResponseMessage{
|
||||
RemovedChainBlockHashes: removedChainBlockHashes,
|
||||
AddedChainBlocks: addedChainBlocks,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,10 @@ type GetConnectedPeerInfoMessage struct {
|
||||
ID string
|
||||
Address string
|
||||
LastPingDuration int64
|
||||
SelectedTipHash string
|
||||
IsSyncNode bool
|
||||
IsOutbound bool
|
||||
TimeOffset int64
|
||||
UserAgent string
|
||||
AdvertisedProtocolVersion uint32
|
||||
TimeConnected int64
|
||||
IsIBDPeer bool
|
||||
}
|
||||
|
||||
40
app/appmessage/rpc_get_info.go
Normal file
40
app/appmessage/rpc_get_info.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package appmessage
|
||||
|
||||
// GetInfoRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetInfoRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetInfoRequestMessage) Command() MessageCommand {
|
||||
return CmdGetInfoRequestMessage
|
||||
}
|
||||
|
||||
// NewGeInfoRequestMessage returns a instance of the message
|
||||
func NewGeInfoRequestMessage() *GetInfoRequestMessage {
|
||||
return &GetInfoRequestMessage{}
|
||||
}
|
||||
|
||||
// GetInfoResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetInfoResponseMessage struct {
|
||||
baseMessage
|
||||
P2PID string
|
||||
MempoolSize uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetInfoResponseMessage) Command() MessageCommand {
|
||||
return CmdGetInfoResponseMessage
|
||||
}
|
||||
|
||||
// NewGetInfoResponseMessage returns a instance of the message
|
||||
func NewGetInfoResponseMessage(p2pID string, mempoolSize uint64) *GetInfoResponseMessage {
|
||||
return &GetInfoResponseMessage{
|
||||
P2PID: p2pID,
|
||||
MempoolSize: mempoolSize,
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,8 @@ type GetMempoolEntryResponseMessage struct {
|
||||
|
||||
// MempoolEntry represents a transaction in the mempool.
|
||||
type MempoolEntry struct {
|
||||
Fee uint64
|
||||
TransactionVerboseData *TransactionVerboseData
|
||||
Fee uint64
|
||||
Transaction *RPCTransaction
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -38,11 +38,11 @@ func (msg *GetMempoolEntryResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetMempoolEntryResponseMessage returns a instance of the message
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transactionVerboseData *TransactionVerboseData) *GetMempoolEntryResponseMessage {
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transaction *RPCTransaction) *GetMempoolEntryResponseMessage {
|
||||
return &GetMempoolEntryResponseMessage{
|
||||
Entry: &MempoolEntry{
|
||||
Fee: fee,
|
||||
TransactionVerboseData: transactionVerboseData,
|
||||
Fee: fee,
|
||||
Transaction: transaction,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
41
app/appmessage/rpc_get_utxos_by_addresses.go
Normal file
41
app/appmessage/rpc_get_utxos_by_addresses.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package appmessage
|
||||
|
||||
// GetUTXOsByAddressesRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetUTXOsByAddressesRequestMessage struct {
|
||||
baseMessage
|
||||
Addresses []string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetUTXOsByAddressesRequestMessage) Command() MessageCommand {
|
||||
return CmdGetUTXOsByAddressesRequestMessage
|
||||
}
|
||||
|
||||
// NewGetUTXOsByAddressesRequestMessage returns a instance of the message
|
||||
func NewGetUTXOsByAddressesRequestMessage(addresses []string) *GetUTXOsByAddressesRequestMessage {
|
||||
return &GetUTXOsByAddressesRequestMessage{
|
||||
Addresses: addresses,
|
||||
}
|
||||
}
|
||||
|
||||
// GetUTXOsByAddressesResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetUTXOsByAddressesResponseMessage struct {
|
||||
baseMessage
|
||||
Entries []*UTXOsByAddressesEntry
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetUTXOsByAddressesResponseMessage) Command() MessageCommand {
|
||||
return CmdGetUTXOsByAddressesResponseMessage
|
||||
}
|
||||
|
||||
// NewGetUTXOsByAddressesResponseMessage returns a instance of the message
|
||||
func NewGetUTXOsByAddressesResponseMessage(entries []*UTXOsByAddressesEntry) *GetUTXOsByAddressesResponseMessage {
|
||||
return &GetUTXOsByAddressesResponseMessage{
|
||||
Entries: entries,
|
||||
}
|
||||
}
|
||||
38
app/appmessage/rpc_get_virtual_selected_parent_blue_score.go
Normal file
38
app/appmessage/rpc_get_virtual_selected_parent_blue_score.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package appmessage
|
||||
|
||||
// GetVirtualSelectedParentBlueScoreRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetVirtualSelectedParentBlueScoreRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetVirtualSelectedParentBlueScoreRequestMessage) Command() MessageCommand {
|
||||
return CmdGetVirtualSelectedParentBlueScoreRequestMessage
|
||||
}
|
||||
|
||||
// NewGetVirtualSelectedParentBlueScoreRequestMessage returns a instance of the message
|
||||
func NewGetVirtualSelectedParentBlueScoreRequestMessage() *GetVirtualSelectedParentBlueScoreRequestMessage {
|
||||
return &GetVirtualSelectedParentBlueScoreRequestMessage{}
|
||||
}
|
||||
|
||||
// GetVirtualSelectedParentBlueScoreResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetVirtualSelectedParentBlueScoreResponseMessage struct {
|
||||
baseMessage
|
||||
BlueScore uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetVirtualSelectedParentBlueScoreResponseMessage) Command() MessageCommand {
|
||||
return CmdGetVirtualSelectedParentBlueScoreResponseMessage
|
||||
}
|
||||
|
||||
// NewGetVirtualSelectedParentBlueScoreResponseMessage returns a instance of the message
|
||||
func NewGetVirtualSelectedParentBlueScoreResponseMessage(blueScore uint64) *GetVirtualSelectedParentBlueScoreResponseMessage {
|
||||
return &GetVirtualSelectedParentBlueScoreResponseMessage{
|
||||
BlueScore: blueScore,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package appmessage
|
||||
|
||||
// GetVirtualSelectedParentChainFromBlockRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetVirtualSelectedParentChainFromBlockRequestMessage struct {
|
||||
baseMessage
|
||||
StartHash string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetVirtualSelectedParentChainFromBlockRequestMessage) Command() MessageCommand {
|
||||
return CmdGetVirtualSelectedParentChainFromBlockRequestMessage
|
||||
}
|
||||
|
||||
// NewGetVirtualSelectedParentChainFromBlockRequestMessage returns a instance of the message
|
||||
func NewGetVirtualSelectedParentChainFromBlockRequestMessage(startHash string) *GetVirtualSelectedParentChainFromBlockRequestMessage {
|
||||
return &GetVirtualSelectedParentChainFromBlockRequestMessage{
|
||||
StartHash: startHash,
|
||||
}
|
||||
}
|
||||
|
||||
// GetVirtualSelectedParentChainFromBlockResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetVirtualSelectedParentChainFromBlockResponseMessage struct {
|
||||
baseMessage
|
||||
RemovedChainBlockHashes []string
|
||||
AddedChainBlocks []*ChainBlock
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetVirtualSelectedParentChainFromBlockResponseMessage) Command() MessageCommand {
|
||||
return CmdGetVirtualSelectedParentChainFromBlockResponseMessage
|
||||
}
|
||||
|
||||
// NewGetVirtualSelectedParentChainFromBlockResponseMessage returns a instance of the message
|
||||
func NewGetVirtualSelectedParentChainFromBlockResponseMessage(removedChainBlockHashes []string,
|
||||
addedChainBlocks []*ChainBlock) *GetVirtualSelectedParentChainFromBlockResponseMessage {
|
||||
|
||||
return &GetVirtualSelectedParentChainFromBlockResponseMessage{
|
||||
RemovedChainBlockHashes: removedChainBlockHashes,
|
||||
AddedChainBlocks: addedChainBlocks,
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
|
||||
// its respective RPC message
|
||||
type BlockAddedNotificationMessage struct {
|
||||
baseMessage
|
||||
Block *MsgBlock
|
||||
Block *RPCBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -46,7 +46,7 @@ func (msg *BlockAddedNotificationMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewBlockAddedNotificationMessage returns a instance of the message
|
||||
func NewBlockAddedNotificationMessage(block *MsgBlock) *BlockAddedNotificationMessage {
|
||||
func NewBlockAddedNotificationMessage(block *RPCBlock) *BlockAddedNotificationMessage {
|
||||
return &BlockAddedNotificationMessage{
|
||||
Block: block,
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyChainChangedRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyChainChangedRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyChainChangedRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyChainChangedRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyChainChangedRequestMessage returns a instance of the message
|
||||
func NewNotifyChainChangedRequestMessage() *NotifyChainChangedRequestMessage {
|
||||
return &NotifyChainChangedRequestMessage{}
|
||||
}
|
||||
|
||||
// NotifyChainChangedResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyChainChangedResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyChainChangedResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyChainChangedResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyChainChangedResponseMessage returns a instance of the message
|
||||
func NewNotifyChainChangedResponseMessage() *NotifyChainChangedResponseMessage {
|
||||
return &NotifyChainChangedResponseMessage{}
|
||||
}
|
||||
|
||||
// ChainChangedNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type ChainChangedNotificationMessage struct {
|
||||
baseMessage
|
||||
RemovedChainBlockHashes []string
|
||||
AddedChainBlocks []*ChainBlock
|
||||
}
|
||||
|
||||
// ChainBlock represents a DAG chain-block
|
||||
type ChainBlock struct {
|
||||
Hash string
|
||||
AcceptedBlocks []*AcceptedBlock
|
||||
}
|
||||
|
||||
// AcceptedBlock represents a block accepted into the DAG
|
||||
type AcceptedBlock struct {
|
||||
Hash string
|
||||
AcceptedTxIDs []string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *ChainChangedNotificationMessage) Command() MessageCommand {
|
||||
return CmdChainChangedNotificationMessage
|
||||
}
|
||||
|
||||
// NewChainChangedNotificationMessage returns a instance of the message
|
||||
func NewChainChangedNotificationMessage(removedChainBlockHashes []string,
|
||||
addedChainBlocks []*ChainBlock) *ChainChangedNotificationMessage {
|
||||
|
||||
return &ChainChangedNotificationMessage{
|
||||
RemovedChainBlockHashes: removedChainBlockHashes,
|
||||
AddedChainBlocks: addedChainBlocks,
|
||||
}
|
||||
}
|
||||
83
app/appmessage/rpc_notify_pruning_point_utxo_set_override.go
Normal file
83
app/appmessage/rpc_notify_pruning_point_utxo_set_override.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyPruningPointUTXOSetOverrideRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyPruningPointUTXOSetOverrideRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyPruningPointUTXOSetOverrideRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyPruningPointUTXOSetOverrideRequestMessage returns a instance of the message
|
||||
func NewNotifyPruningPointUTXOSetOverrideRequestMessage() *NotifyPruningPointUTXOSetOverrideRequestMessage {
|
||||
return &NotifyPruningPointUTXOSetOverrideRequestMessage{}
|
||||
}
|
||||
|
||||
// NotifyPruningPointUTXOSetOverrideResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyPruningPointUTXOSetOverrideResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyPruningPointUTXOSetOverrideResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyPruningPointUTXOSetOverrideResponseMessage returns a instance of the message
|
||||
func NewNotifyPruningPointUTXOSetOverrideResponseMessage() *NotifyPruningPointUTXOSetOverrideResponseMessage {
|
||||
return &NotifyPruningPointUTXOSetOverrideResponseMessage{}
|
||||
}
|
||||
|
||||
// PruningPointUTXOSetOverrideNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type PruningPointUTXOSetOverrideNotificationMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *PruningPointUTXOSetOverrideNotificationMessage) Command() MessageCommand {
|
||||
return CmdPruningPointUTXOSetOverrideNotificationMessage
|
||||
}
|
||||
|
||||
// NewPruningPointUTXOSetOverrideNotificationMessage returns a instance of the message
|
||||
func NewPruningPointUTXOSetOverrideNotificationMessage() *PruningPointUTXOSetOverrideNotificationMessage {
|
||||
return &PruningPointUTXOSetOverrideNotificationMessage{}
|
||||
}
|
||||
|
||||
// StopNotifyingPruningPointUTXOSetOverrideRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type StopNotifyingPruningPointUTXOSetOverrideRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *StopNotifyingPruningPointUTXOSetOverrideRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideRequestMessage
|
||||
}
|
||||
|
||||
// NewStopNotifyingPruningPointUTXOSetOverrideRequestMessage returns a instance of the message
|
||||
func NewStopNotifyingPruningPointUTXOSetOverrideRequestMessage() *StopNotifyingPruningPointUTXOSetOverrideRequestMessage {
|
||||
return &StopNotifyingPruningPointUTXOSetOverrideRequestMessage{}
|
||||
}
|
||||
|
||||
// StopNotifyingPruningPointUTXOSetOverrideResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type StopNotifyingPruningPointUTXOSetOverrideResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *StopNotifyingPruningPointUTXOSetOverrideResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyPruningPointUTXOSetOverrideResponseMessage
|
||||
}
|
||||
|
||||
// NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage returns a instance of the message
|
||||
func NewStopNotifyingPruningPointUTXOSetOverrideResponseMessage() *StopNotifyingPruningPointUTXOSetOverrideResponseMessage {
|
||||
return &StopNotifyingPruningPointUTXOSetOverrideResponseMessage{}
|
||||
}
|
||||
62
app/appmessage/rpc_notify_utxos_changed.go
Normal file
62
app/appmessage/rpc_notify_utxos_changed.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyUTXOsChangedRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyUTXOsChangedRequestMessage struct {
|
||||
baseMessage
|
||||
Addresses []string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyUTXOsChangedRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyUTXOsChangedRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyUTXOsChangedRequestMessage returns a instance of the message
|
||||
func NewNotifyUTXOsChangedRequestMessage(addresses []string) *NotifyUTXOsChangedRequestMessage {
|
||||
return &NotifyUTXOsChangedRequestMessage{
|
||||
Addresses: addresses,
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyUTXOsChangedResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyUTXOsChangedResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyUTXOsChangedResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyUTXOsChangedResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyUTXOsChangedResponseMessage returns a instance of the message
|
||||
func NewNotifyUTXOsChangedResponseMessage() *NotifyUTXOsChangedResponseMessage {
|
||||
return &NotifyUTXOsChangedResponseMessage{}
|
||||
}
|
||||
|
||||
// UTXOsChangedNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type UTXOsChangedNotificationMessage struct {
|
||||
baseMessage
|
||||
Added []*UTXOsByAddressesEntry
|
||||
Removed []*UTXOsByAddressesEntry
|
||||
}
|
||||
|
||||
// UTXOsByAddressesEntry represents a UTXO of some address
|
||||
type UTXOsByAddressesEntry struct {
|
||||
Address string
|
||||
Outpoint *RPCOutpoint
|
||||
UTXOEntry *RPCUTXOEntry
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *UTXOsChangedNotificationMessage) Command() MessageCommand {
|
||||
return CmdUTXOsChangedNotificationMessage
|
||||
}
|
||||
|
||||
// NewUTXOsChangedNotificationMessage returns a instance of the message
|
||||
func NewUTXOsChangedNotificationMessage() *UTXOsChangedNotificationMessage {
|
||||
return &UTXOsChangedNotificationMessage{}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyVirtualSelectedParentBlueScoreChangedRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualSelectedParentBlueScoreChangedRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualSelectedParentBlueScoreChangedRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualSelectedParentBlueScoreChangedRequestMessage returns a instance of the message
|
||||
func NewNotifyVirtualSelectedParentBlueScoreChangedRequestMessage() *NotifyVirtualSelectedParentBlueScoreChangedRequestMessage {
|
||||
return &NotifyVirtualSelectedParentBlueScoreChangedRequestMessage{}
|
||||
}
|
||||
|
||||
// NotifyVirtualSelectedParentBlueScoreChangedResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualSelectedParentBlueScoreChangedResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualSelectedParentBlueScoreChangedResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualSelectedParentBlueScoreChangedResponseMessage returns a instance of the message
|
||||
func NewNotifyVirtualSelectedParentBlueScoreChangedResponseMessage() *NotifyVirtualSelectedParentBlueScoreChangedResponseMessage {
|
||||
return &NotifyVirtualSelectedParentBlueScoreChangedResponseMessage{}
|
||||
}
|
||||
|
||||
// VirtualSelectedParentBlueScoreChangedNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type VirtualSelectedParentBlueScoreChangedNotificationMessage struct {
|
||||
baseMessage
|
||||
VirtualSelectedParentBlueScore uint64
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *VirtualSelectedParentBlueScoreChangedNotificationMessage) Command() MessageCommand {
|
||||
return CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
|
||||
}
|
||||
|
||||
// NewVirtualSelectedParentBlueScoreChangedNotificationMessage returns a instance of the message
|
||||
func NewVirtualSelectedParentBlueScoreChangedNotificationMessage(
|
||||
virtualSelectedParentBlueScore uint64) *VirtualSelectedParentBlueScoreChangedNotificationMessage {
|
||||
|
||||
return &VirtualSelectedParentBlueScoreChangedNotificationMessage{
|
||||
VirtualSelectedParentBlueScore: virtualSelectedParentBlueScore,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyVirtualSelectedParentChainChangedRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualSelectedParentChainChangedRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualSelectedParentChainChangedRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualSelectedParentChainChangedRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualSelectedParentChainChangedRequestMessage returns a instance of the message
|
||||
func NewNotifyVirtualSelectedParentChainChangedRequestMessage() *NotifyVirtualSelectedParentChainChangedRequestMessage {
|
||||
return &NotifyVirtualSelectedParentChainChangedRequestMessage{}
|
||||
}
|
||||
|
||||
// NotifyVirtualSelectedParentChainChangedResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualSelectedParentChainChangedResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualSelectedParentChainChangedResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualSelectedParentChainChangedResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualSelectedParentChainChangedResponseMessage returns a instance of the message
|
||||
func NewNotifyVirtualSelectedParentChainChangedResponseMessage() *NotifyVirtualSelectedParentChainChangedResponseMessage {
|
||||
return &NotifyVirtualSelectedParentChainChangedResponseMessage{}
|
||||
}
|
||||
|
||||
// VirtualSelectedParentChainChangedNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type VirtualSelectedParentChainChangedNotificationMessage struct {
|
||||
baseMessage
|
||||
RemovedChainBlockHashes []string
|
||||
AddedChainBlocks []*ChainBlock
|
||||
}
|
||||
|
||||
// ChainBlock represents a DAG chain-block
|
||||
type ChainBlock struct {
|
||||
Hash string
|
||||
AcceptedBlocks []*AcceptedBlock
|
||||
}
|
||||
|
||||
// AcceptedBlock represents a block accepted into the DAG
|
||||
type AcceptedBlock struct {
|
||||
Hash string
|
||||
AcceptedTransactionIDs []string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *VirtualSelectedParentChainChangedNotificationMessage) Command() MessageCommand {
|
||||
return CmdVirtualSelectedParentChainChangedNotificationMessage
|
||||
}
|
||||
|
||||
// NewVirtualSelectedParentChainChangedNotificationMessage returns a instance of the message
|
||||
func NewVirtualSelectedParentChainChangedNotificationMessage(removedChainBlockHashes []string,
|
||||
addedChainBlocks []*ChainBlock) *VirtualSelectedParentChainChangedNotificationMessage {
|
||||
|
||||
return &VirtualSelectedParentChainChangedNotificationMessage{
|
||||
RemovedChainBlockHashes: removedChainBlockHashes,
|
||||
AddedChainBlocks: addedChainBlocks,
|
||||
}
|
||||
}
|
||||
37
app/appmessage/rpc_stop_notifying_utxos_changed.go
Normal file
37
app/appmessage/rpc_stop_notifying_utxos_changed.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package appmessage
|
||||
|
||||
// StopNotifyingUTXOsChangedRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type StopNotifyingUTXOsChangedRequestMessage struct {
|
||||
baseMessage
|
||||
Addresses []string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *StopNotifyingUTXOsChangedRequestMessage) Command() MessageCommand {
|
||||
return CmdStopNotifyingUTXOsChangedRequestMessage
|
||||
}
|
||||
|
||||
// NewStopNotifyingUTXOsChangedRequestMessage returns a instance of the message
|
||||
func NewStopNotifyingUTXOsChangedRequestMessage(addresses []string) *StopNotifyingUTXOsChangedRequestMessage {
|
||||
return &StopNotifyingUTXOsChangedRequestMessage{
|
||||
Addresses: addresses,
|
||||
}
|
||||
}
|
||||
|
||||
// StopNotifyingUTXOsChangedResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type StopNotifyingUTXOsChangedResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *StopNotifyingUTXOsChangedResponseMessage) Command() MessageCommand {
|
||||
return CmdStopNotifyingUTXOsChangedResponseMessage
|
||||
}
|
||||
|
||||
// NewStopNotifyingUTXOsChangedResponseMessage returns a instance of the message
|
||||
func NewStopNotifyingUTXOsChangedResponseMessage() *StopNotifyingUTXOsChangedResponseMessage {
|
||||
return &StopNotifyingUTXOsChangedResponseMessage{}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type SubmitBlockRequestMessage struct {
|
||||
baseMessage
|
||||
Block *MsgBlock
|
||||
Block *RPCBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -13,17 +13,39 @@ func (msg *SubmitBlockRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewSubmitBlockRequestMessage returns a instance of the message
|
||||
func NewSubmitBlockRequestMessage(block *MsgBlock) *SubmitBlockRequestMessage {
|
||||
func NewSubmitBlockRequestMessage(block *RPCBlock) *SubmitBlockRequestMessage {
|
||||
return &SubmitBlockRequestMessage{
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
|
||||
// RejectReason describes the reason why a block sent by SubmitBlock was rejected
|
||||
type RejectReason byte
|
||||
|
||||
// RejectReason constants
|
||||
// Not using iota, since in the .proto file those are hardcoded
|
||||
const (
|
||||
RejectReasonNone RejectReason = 0
|
||||
RejectReasonBlockInvalid RejectReason = 1
|
||||
RejectReasonIsInIBD RejectReason = 2
|
||||
)
|
||||
|
||||
var rejectReasonToString = map[RejectReason]string{
|
||||
RejectReasonNone: "None",
|
||||
RejectReasonBlockInvalid: "Block is invalid",
|
||||
RejectReasonIsInIBD: "Node is in IBD",
|
||||
}
|
||||
|
||||
func (rr RejectReason) String() string {
|
||||
return rejectReasonToString[rr]
|
||||
}
|
||||
|
||||
// SubmitBlockResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type SubmitBlockResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
RejectReason RejectReason
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -35,3 +57,35 @@ func (msg *SubmitBlockResponseMessage) Command() MessageCommand {
|
||||
func NewSubmitBlockResponseMessage() *SubmitBlockResponseMessage {
|
||||
return &SubmitBlockResponseMessage{}
|
||||
}
|
||||
|
||||
// RPCBlock is a kaspad block representation meant to be
|
||||
// used over RPC
|
||||
type RPCBlock struct {
|
||||
Header *RPCBlockHeader
|
||||
Transactions []*RPCTransaction
|
||||
VerboseData *RPCBlockVerboseData
|
||||
}
|
||||
|
||||
// RPCBlockHeader is a kaspad block header representation meant to be
|
||||
// used over RPC
|
||||
type RPCBlockHeader struct {
|
||||
Version uint32
|
||||
ParentHashes []string
|
||||
HashMerkleRoot string
|
||||
AcceptedIDMerkleRoot string
|
||||
UTXOCommitment string
|
||||
Timestamp int64
|
||||
Bits uint32
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
// RPCBlockVerboseData holds verbose data about a block
|
||||
type RPCBlockVerboseData struct {
|
||||
Hash string
|
||||
Difficulty float64
|
||||
SelectedParentHash string
|
||||
TransactionIDs []string
|
||||
IsHeaderOnly bool
|
||||
BlueScore uint64
|
||||
ChildrenHashes []string
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type SubmitTransactionRequestMessage struct {
|
||||
baseMessage
|
||||
Transaction *MsgTx
|
||||
Transaction *RPCTransaction
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -13,7 +13,7 @@ func (msg *SubmitTransactionRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewSubmitTransactionRequestMessage returns a instance of the message
|
||||
func NewSubmitTransactionRequestMessage(transaction *MsgTx) *SubmitTransactionRequestMessage {
|
||||
func NewSubmitTransactionRequestMessage(transaction *RPCTransaction) *SubmitTransactionRequestMessage {
|
||||
return &SubmitTransactionRequestMessage{
|
||||
Transaction: transaction,
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func NewSubmitTransactionRequestMessage(transaction *MsgTx) *SubmitTransactionRe
|
||||
// its respective RPC message
|
||||
type SubmitTransactionResponseMessage struct {
|
||||
baseMessage
|
||||
TxID string
|
||||
TransactionID string
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -34,8 +34,79 @@ func (msg *SubmitTransactionResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewSubmitTransactionResponseMessage returns a instance of the message
|
||||
func NewSubmitTransactionResponseMessage(txID string) *SubmitTransactionResponseMessage {
|
||||
func NewSubmitTransactionResponseMessage(transactionID string) *SubmitTransactionResponseMessage {
|
||||
return &SubmitTransactionResponseMessage{
|
||||
TxID: txID,
|
||||
TransactionID: transactionID,
|
||||
}
|
||||
}
|
||||
|
||||
// RPCTransaction is a kaspad transaction representation meant to be
|
||||
// used over RPC
|
||||
type RPCTransaction struct {
|
||||
Version uint16
|
||||
Inputs []*RPCTransactionInput
|
||||
Outputs []*RPCTransactionOutput
|
||||
LockTime uint64
|
||||
SubnetworkID string
|
||||
Gas uint64
|
||||
Payload string
|
||||
VerboseData *RPCTransactionVerboseData
|
||||
}
|
||||
|
||||
// RPCTransactionInput is a kaspad transaction input representation
|
||||
// meant to be used over RPC
|
||||
type RPCTransactionInput struct {
|
||||
PreviousOutpoint *RPCOutpoint
|
||||
SignatureScript string
|
||||
Sequence uint64
|
||||
VerboseData *RPCTransactionInputVerboseData
|
||||
}
|
||||
|
||||
// RPCScriptPublicKey is a kaspad ScriptPublicKey representation
|
||||
type RPCScriptPublicKey struct {
|
||||
Version uint16
|
||||
Script string
|
||||
}
|
||||
|
||||
// RPCTransactionOutput is a kaspad transaction output representation
|
||||
// meant to be used over RPC
|
||||
type RPCTransactionOutput struct {
|
||||
Amount uint64
|
||||
ScriptPublicKey *RPCScriptPublicKey
|
||||
VerboseData *RPCTransactionOutputVerboseData
|
||||
}
|
||||
|
||||
// RPCOutpoint is a kaspad outpoint representation meant to be used
|
||||
// over RPC
|
||||
type RPCOutpoint struct {
|
||||
TransactionID string
|
||||
Index uint32
|
||||
}
|
||||
|
||||
// RPCUTXOEntry is a kaspad utxo entry representation meant to be used
|
||||
// over RPC
|
||||
type RPCUTXOEntry struct {
|
||||
Amount uint64
|
||||
ScriptPublicKey *RPCScriptPublicKey
|
||||
BlockDAAScore uint64
|
||||
IsCoinbase bool
|
||||
}
|
||||
|
||||
// RPCTransactionVerboseData holds verbose data about a transaction
|
||||
type RPCTransactionVerboseData struct {
|
||||
TransactionID string
|
||||
Hash string
|
||||
Size uint64
|
||||
BlockHash string
|
||||
BlockTime uint64
|
||||
}
|
||||
|
||||
// RPCTransactionInputVerboseData holds data about a transaction input
|
||||
type RPCTransactionInputVerboseData struct {
|
||||
}
|
||||
|
||||
// RPCTransactionOutputVerboseData holds data about a transaction output
|
||||
type RPCTransactionOutputVerboseData struct {
|
||||
ScriptPublicKeyType string
|
||||
ScriptPublicKeyAddress string
|
||||
}
|
||||
|
||||
39
app/appmessage/rpc_unban.go
Normal file
39
app/appmessage/rpc_unban.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package appmessage
|
||||
|
||||
// UnbanRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type UnbanRequestMessage struct {
|
||||
baseMessage
|
||||
|
||||
IP string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *UnbanRequestMessage) Command() MessageCommand {
|
||||
return CmdUnbanRequestMessage
|
||||
}
|
||||
|
||||
// NewUnbanRequestMessage returns an instance of the message
|
||||
func NewUnbanRequestMessage(ip string) *UnbanRequestMessage {
|
||||
return &UnbanRequestMessage{
|
||||
IP: ip,
|
||||
}
|
||||
}
|
||||
|
||||
// UnbanResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type UnbanResponseMessage struct {
|
||||
baseMessage
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *UnbanResponseMessage) Command() MessageCommand {
|
||||
return CmdUnbanResponseMessage
|
||||
}
|
||||
|
||||
// NewUnbanResponseMessage returns a instance of the message
|
||||
func NewUnbanResponseMessage() *UnbanResponseMessage {
|
||||
return &UnbanResponseMessage{}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||
|
||||
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
@@ -70,6 +72,8 @@ func (a *ComponentManager) Stop() {
|
||||
log.Errorf("Error stopping the net adapter: %+v", err)
|
||||
}
|
||||
|
||||
a.protocolManager.Close()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,7 +82,7 @@ func (a *ComponentManager) Stop() {
|
||||
func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) (
|
||||
*ComponentManager, error) {
|
||||
|
||||
domain, err := domain.New(cfg.ActiveNetParams, db)
|
||||
domain, err := domain.New(cfg.ActiveNetParams, db, cfg.IsArchivalNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -88,11 +92,21 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg))
|
||||
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg), db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var utxoIndex *utxoindex.UTXOIndex
|
||||
if cfg.UTXOIndex {
|
||||
utxoIndex, err = utxoindex.New(domain.Consensus(), db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("UTXO index started")
|
||||
}
|
||||
|
||||
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -101,7 +115,7 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rpcManager := setupRPC(cfg, domain, netAdapter, protocolManager, connectionManager, addressManager, interrupt)
|
||||
rpcManager := setupRPC(cfg, domain, netAdapter, protocolManager, connectionManager, addressManager, utxoIndex, interrupt)
|
||||
|
||||
return &ComponentManager{
|
||||
cfg: cfg,
|
||||
@@ -121,29 +135,37 @@ func setupRPC(
|
||||
protocolManager *protocol.Manager,
|
||||
connectionManager *connmanager.ConnectionManager,
|
||||
addressManager *addressmanager.AddressManager,
|
||||
utxoIndex *utxoindex.UTXOIndex,
|
||||
shutDownChan chan<- struct{},
|
||||
) *rpc.Manager {
|
||||
|
||||
rpcManager := rpc.NewManager(
|
||||
cfg, domain, netAdapter, protocolManager, connectionManager, addressManager, shutDownChan)
|
||||
cfg,
|
||||
domain,
|
||||
netAdapter,
|
||||
protocolManager,
|
||||
connectionManager,
|
||||
addressManager,
|
||||
utxoIndex,
|
||||
shutDownChan,
|
||||
)
|
||||
protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG)
|
||||
protocolManager.SetOnPruningPointUTXOSetOverrideHandler(rpcManager.NotifyPruningPointUTXOSetOverride)
|
||||
|
||||
return rpcManager
|
||||
}
|
||||
|
||||
func (a *ComponentManager) maybeSeedFromDNS() {
|
||||
if !a.cfg.DisableDNSSeed {
|
||||
dnsseed.SeedFromDNS(a.cfg.NetParams(), a.cfg.DNSSeed, appmessage.SFNodeNetwork, false, nil,
|
||||
dnsseed.SeedFromDNS(a.cfg.NetParams(), a.cfg.DNSSeed, false, nil,
|
||||
a.cfg.Lookup, func(addresses []*appmessage.NetAddress) {
|
||||
// Kaspad uses a lookup of the dns seeder here. Since seeder returns
|
||||
// IPs of nodes and not its own IP, we can not know real IP of
|
||||
// source. So we'll take first returned address as source.
|
||||
a.addressManager.AddAddresses(addresses...)
|
||||
})
|
||||
}
|
||||
|
||||
if a.cfg.GRPCSeed != "" {
|
||||
dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, appmessage.SFNodeNetwork, false, nil,
|
||||
dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, false, nil,
|
||||
func(addresses []*appmessage.NetAddress) {
|
||||
a.addressManager.AddAddresses(addresses...)
|
||||
})
|
||||
|
||||
@@ -7,8 +7,6 @@ package app
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.KASD)
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
var log = logger.RegisterSubSystem("KASD")
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2015-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blocklogger
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
)
|
||||
|
||||
var (
|
||||
receivedLogBlocks int64
|
||||
receivedLogTx int64
|
||||
lastBlockLogTime = mstime.Now()
|
||||
mtx sync.Mutex
|
||||
)
|
||||
|
||||
// LogBlock logs a new block blue score as an information message
|
||||
// to show progress to the user. In order to prevent spam, it limits logging to
|
||||
// one message every 10 seconds with duration and totals included.
|
||||
func LogBlock(block *externalapi.DomainBlock) {
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
|
||||
receivedLogBlocks++
|
||||
receivedLogTx += int64(len(block.Transactions))
|
||||
|
||||
now := mstime.Now()
|
||||
duration := now.Sub(lastBlockLogTime)
|
||||
if duration < time.Second*10 {
|
||||
return
|
||||
}
|
||||
|
||||
// Truncate the duration to 10s of milliseconds.
|
||||
tDuration := duration.Round(10 * time.Millisecond)
|
||||
|
||||
// Log information about new block blue score.
|
||||
blockStr := "blocks"
|
||||
if receivedLogBlocks == 1 {
|
||||
blockStr = "block"
|
||||
}
|
||||
txStr := "transactions"
|
||||
if receivedLogTx == 1 {
|
||||
txStr = "transaction"
|
||||
}
|
||||
|
||||
log.Infof("Processed %d %s in the last %s (%d %s, %s)",
|
||||
receivedLogBlocks, blockStr, tDuration, receivedLogTx,
|
||||
txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds))
|
||||
|
||||
receivedLogBlocks = 0
|
||||
receivedLogTx = 0
|
||||
lastBlockLogTime = now
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/pkg/errors"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||
@@ -16,36 +16,65 @@ import (
|
||||
// OnNewBlock updates the mempool after a new block arrival, and
|
||||
// relays newly unorphaned transactions and possibly rebroadcast
|
||||
// manually added transactions when not in IBD.
|
||||
func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock) error {
|
||||
unorphanedBlocks, err := f.UnorphanBlocks(block)
|
||||
func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||
|
||||
hash := consensushashing.BlockHash(block)
|
||||
log.Debugf("OnNewBlock start for block %s", hash)
|
||||
defer log.Debugf("OnNewBlock end for block %s", hash)
|
||||
|
||||
unorphaningResults, err := f.UnorphanBlocks(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newBlocks := append([]*externalapi.DomainBlock{block}, unorphanedBlocks...)
|
||||
for _, newBlock := range newBlocks {
|
||||
blocklogger.LogBlock(block)
|
||||
log.Debugf("OnNewBlock: block %s unorphaned %d blocks", hash, len(unorphaningResults))
|
||||
|
||||
_ = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
newBlocks := []*externalapi.DomainBlock{block}
|
||||
newBlockInsertionResults := []*externalapi.BlockInsertionResult{blockInsertionResult}
|
||||
for _, unorphaningResult := range unorphaningResults {
|
||||
newBlocks = append(newBlocks, unorphaningResult.block)
|
||||
newBlockInsertionResults = append(newBlockInsertionResults, unorphaningResult.blockInsertionResult)
|
||||
}
|
||||
|
||||
allAcceptedTransactions := make([]*externalapi.DomainTransaction, 0)
|
||||
for i, newBlock := range newBlocks {
|
||||
log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash)
|
||||
acceptedTransactions, err := f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allAcceptedTransactions = append(allAcceptedTransactions, acceptedTransactions...)
|
||||
|
||||
if f.onBlockAddedToDAGHandler != nil {
|
||||
err := f.onBlockAddedToDAGHandler(newBlock)
|
||||
log.Debugf("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
|
||||
blockInsertionResult = newBlockInsertionResults[i]
|
||||
err := f.onBlockAddedToDAGHandler(newBlock, blockInsertionResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f.broadcastTransactionsAfterBlockAdded(newBlocks, allAcceptedTransactions)
|
||||
}
|
||||
|
||||
// OnPruningPointUTXOSetOverride calls the handler function whenever the UTXO set
|
||||
// resets due to pruning point change via IBD.
|
||||
func (f *FlowContext) OnPruningPointUTXOSetOverride() error {
|
||||
if f.onPruningPointUTXOSetOverrideHandler != nil {
|
||||
return f.onPruningPointUTXOSetOverrideHandler()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
|
||||
block *externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
|
||||
addedBlocks []*externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
|
||||
|
||||
f.updateTransactionsToRebroadcast(block)
|
||||
f.updateTransactionsToRebroadcast(addedBlocks)
|
||||
|
||||
// Don't relay transactions when in IBD.
|
||||
if atomic.LoadUint32(&f.isInIBD) != 0 {
|
||||
if f.IsIBDRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -56,7 +85,7 @@ func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
|
||||
|
||||
txIDsToBroadcast := make([]*externalapi.DomainTransactionID, len(transactionsAcceptedToMempool)+len(txIDsToRebroadcast))
|
||||
for i, tx := range transactionsAcceptedToMempool {
|
||||
txIDsToBroadcast[i] = consensusserialization.TransactionID(tx)
|
||||
txIDsToBroadcast[i] = consensushashing.TransactionID(tx)
|
||||
}
|
||||
offset := len(transactionsAcceptedToMempool)
|
||||
for i, txID := range txIDsToRebroadcast {
|
||||
@@ -81,17 +110,65 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
|
||||
|
||||
// AddBlock adds the given block to the DAG and propagates it.
|
||||
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||
err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if len(block.Transactions) == 0 {
|
||||
return protocolerrors.Errorf(false, "cannot add header only block")
|
||||
}
|
||||
|
||||
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Infof("Validation failed for block %s: %s", consensusserialization.BlockHash(block), err)
|
||||
return nil
|
||||
log.Warnf("Validation failed for block %s: %s", consensushashing.BlockHash(block), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = f.OnNewBlock(block)
|
||||
err = f.OnNewBlock(block, blockInsertionResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Broadcast(appmessage.NewMsgInvBlock(consensusserialization.BlockHash(block)))
|
||||
return f.Broadcast(appmessage.NewMsgInvBlock(consensushashing.BlockHash(block)))
|
||||
}
|
||||
|
||||
// IsIBDRunning returns true if IBD is currently marked as running
|
||||
func (f *FlowContext) IsIBDRunning() bool {
|
||||
f.ibdPeerMutex.RLock()
|
||||
defer f.ibdPeerMutex.RUnlock()
|
||||
|
||||
return f.ibdPeer != nil
|
||||
}
|
||||
|
||||
// TrySetIBDRunning attempts to set `isInIBD`. Returns false
|
||||
// if it is already set
|
||||
func (f *FlowContext) TrySetIBDRunning(ibdPeer *peerpkg.Peer) bool {
|
||||
f.ibdPeerMutex.Lock()
|
||||
defer f.ibdPeerMutex.Unlock()
|
||||
|
||||
if f.ibdPeer != nil {
|
||||
return false
|
||||
}
|
||||
f.ibdPeer = ibdPeer
|
||||
log.Infof("IBD started")
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// UnsetIBDRunning unsets isInIBD
|
||||
func (f *FlowContext) UnsetIBDRunning() {
|
||||
f.ibdPeerMutex.Lock()
|
||||
defer f.ibdPeerMutex.Unlock()
|
||||
|
||||
if f.ibdPeer == nil {
|
||||
panic("attempted to unset isInIBD when it was not set to begin with")
|
||||
}
|
||||
|
||||
f.ibdPeer = nil
|
||||
log.Infof("IBD finished")
|
||||
}
|
||||
|
||||
// IBDPeer returns the current IBD peer or null if the node is not
|
||||
// in IBD
|
||||
func (f *FlowContext) IBDPeer() *peerpkg.Peer {
|
||||
f.ibdPeerMutex.RLock()
|
||||
defer f.ibdPeerMutex.RUnlock()
|
||||
|
||||
return f.ibdPeer
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ import (
|
||||
func (*FlowContext) HandleError(err error, flowName string, isStopping *uint32, errChan chan<- error) {
|
||||
isErrRouteClosed := errors.Is(err, router.ErrRouteClosed)
|
||||
if !isErrRouteClosed {
|
||||
if protocolErr := &(protocolerrors.ProtocolError{}); !errors.As(err, &protocolErr) {
|
||||
if protocolErr := (protocolerrors.ProtocolError{}); !errors.As(err, &protocolErr) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Errorf("error from %s: %+v", flowName, err)
|
||||
log.Errorf("error from %s: %s", flowName, err)
|
||||
}
|
||||
|
||||
if atomic.AddUint32(isStopping, 1) == 1 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -9,7 +10,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/relaytransactions"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
@@ -20,7 +21,11 @@ import (
|
||||
|
||||
// OnBlockAddedToDAGHandler is a handler function that's triggered
|
||||
// when a block is added to the DAG
|
||||
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock) error
|
||||
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||
|
||||
// OnPruningPointUTXOSetOverrideHandler is a handle function that's triggered whenever the UTXO set
|
||||
// resets due to pruning point change via IBD.
|
||||
type OnPruningPointUTXOSetOverrideHandler func() error
|
||||
|
||||
// OnTransactionAddedToMempoolHandler is a handler function that's triggered
|
||||
// when a transaction is added to the mempool
|
||||
@@ -35,25 +40,29 @@ type FlowContext struct {
|
||||
addressManager *addressmanager.AddressManager
|
||||
connectionManager *connmanager.ConnectionManager
|
||||
|
||||
onBlockAddedToDAGHandler OnBlockAddedToDAGHandler
|
||||
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler
|
||||
timeStarted int64
|
||||
|
||||
onBlockAddedToDAGHandler OnBlockAddedToDAGHandler
|
||||
onPruningPointUTXOSetOverrideHandler OnPruningPointUTXOSetOverrideHandler
|
||||
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler
|
||||
|
||||
transactionsToRebroadcastLock sync.Mutex
|
||||
transactionsToRebroadcast map[externalapi.DomainTransactionID]*externalapi.DomainTransaction
|
||||
lastRebroadcastTime time.Time
|
||||
sharedRequestedTransactions *relaytransactions.SharedRequestedTransactions
|
||||
sharedRequestedTransactions *transactionrelay.SharedRequestedTransactions
|
||||
|
||||
sharedRequestedBlocks *blockrelay.SharedRequestedBlocks
|
||||
|
||||
isInIBD uint32
|
||||
startIBDMutex sync.Mutex
|
||||
ibdPeer *peerpkg.Peer
|
||||
ibdPeer *peerpkg.Peer
|
||||
ibdPeerMutex sync.RWMutex
|
||||
|
||||
peers map[id.ID]*peerpkg.Peer
|
||||
peersMutex sync.RWMutex
|
||||
|
||||
orphans map[externalapi.DomainHash]*externalapi.DomainBlock
|
||||
orphansMutex sync.RWMutex
|
||||
|
||||
shutdownChan chan struct{}
|
||||
}
|
||||
|
||||
// New returns a new instance of FlowContext.
|
||||
@@ -66,19 +75,37 @@ func New(cfg *config.Config, domain domain.Domain, addressManager *addressmanage
|
||||
domain: domain,
|
||||
addressManager: addressManager,
|
||||
connectionManager: connectionManager,
|
||||
sharedRequestedTransactions: relaytransactions.NewSharedRequestedTransactions(),
|
||||
sharedRequestedTransactions: transactionrelay.NewSharedRequestedTransactions(),
|
||||
sharedRequestedBlocks: blockrelay.NewSharedRequestedBlocks(),
|
||||
peers: make(map[id.ID]*peerpkg.Peer),
|
||||
transactionsToRebroadcast: make(map[externalapi.DomainTransactionID]*externalapi.DomainTransaction),
|
||||
orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
|
||||
timeStarted: mstime.Now().UnixMilliseconds(),
|
||||
shutdownChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Close signals to all flows the the protocol manager is closed.
|
||||
func (f *FlowContext) Close() {
|
||||
close(f.shutdownChan)
|
||||
}
|
||||
|
||||
// ShutdownChan is a chan where flows can subscribe to shutdown
|
||||
// event.
|
||||
func (f *FlowContext) ShutdownChan() <-chan struct{} {
|
||||
return f.shutdownChan
|
||||
}
|
||||
|
||||
// SetOnBlockAddedToDAGHandler sets the onBlockAddedToDAG handler
|
||||
func (f *FlowContext) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler OnBlockAddedToDAGHandler) {
|
||||
f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler
|
||||
}
|
||||
|
||||
// SetOnPruningPointUTXOSetOverrideHandler sets the onPruningPointUTXOSetOverrideHandler handler
|
||||
func (f *FlowContext) SetOnPruningPointUTXOSetOverrideHandler(onPruningPointUTXOSetOverrideHandler OnPruningPointUTXOSetOverrideHandler) {
|
||||
f.onPruningPointUTXOSetOverrideHandler = onPruningPointUTXOSetOverrideHandler
|
||||
}
|
||||
|
||||
// SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler
|
||||
func (f *FlowContext) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler) {
|
||||
f.onTransactionAddedToMempoolHandler = onTransactionAddedToMempoolHandler
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
)
|
||||
|
||||
// StartIBDIfRequired selects a peer and starts IBD against it
|
||||
// if required
|
||||
func (f *FlowContext) StartIBDIfRequired() error {
|
||||
f.startIBDMutex.Lock()
|
||||
defer f.startIBDMutex.Unlock()
|
||||
|
||||
if f.IsInIBD() {
|
||||
return nil
|
||||
}
|
||||
|
||||
syncInfo, err := f.domain.Consensus().GetSyncInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if syncInfo.State == externalapi.SyncStateRelay {
|
||||
return nil
|
||||
}
|
||||
|
||||
peer, err := f.selectPeerForIBD(syncInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if peer == nil {
|
||||
spawn("StartIBDIfRequired-requestSelectedTipsIfRequired", f.requestSelectedTipsIfRequired)
|
||||
return nil
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&f.isInIBD, 1)
|
||||
f.ibdPeer = peer
|
||||
spawn("StartIBDIfRequired-peer.StartIBD", peer.StartIBD)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsInIBD is true if IBD is currently running
|
||||
func (f *FlowContext) IsInIBD() bool {
|
||||
return atomic.LoadUint32(&f.isInIBD) != 0
|
||||
}
|
||||
|
||||
// selectPeerForIBD returns the first peer whose selected tip
|
||||
// hash is not in our DAG
|
||||
func (f *FlowContext) selectPeerForIBD(syncInfo *externalapi.SyncInfo) (*peerpkg.Peer, error) {
|
||||
f.peersMutex.RLock()
|
||||
defer f.peersMutex.RUnlock()
|
||||
|
||||
for _, peer := range f.peers {
|
||||
peerSelectedTipHash := peer.SelectedTipHash()
|
||||
|
||||
if f.IsOrphan(peerSelectedTipHash) {
|
||||
continue
|
||||
}
|
||||
|
||||
blockInfo, err := f.domain.Consensus().GetBlockInfo(peerSelectedTipHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if syncInfo.State == externalapi.SyncStateHeadersFirst {
|
||||
if !blockInfo.Exists {
|
||||
return peer, nil
|
||||
}
|
||||
} else {
|
||||
if blockInfo.Exists && blockInfo.BlockStatus == externalapi.StatusHeaderOnly &&
|
||||
blockInfo.IsBlockInHeaderPruningPointFuture {
|
||||
return peer, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *FlowContext) requestSelectedTipsIfRequired() {
|
||||
dagTimeCurrent, err := f.shouldRequestSelectedTips()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if dagTimeCurrent {
|
||||
return
|
||||
}
|
||||
f.requestSelectedTips()
|
||||
}
|
||||
|
||||
func (f *FlowContext) shouldRequestSelectedTips() (bool, error) {
|
||||
const minDurationToRequestSelectedTips = time.Minute
|
||||
virtualSelectedParent, err := f.domain.Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
virtualSelectedParentTime := mstime.UnixMilliseconds(virtualSelectedParent.Header.TimeInMilliseconds)
|
||||
return mstime.Now().Sub(virtualSelectedParentTime) > minDurationToRequestSelectedTips, nil
|
||||
}
|
||||
|
||||
func (f *FlowContext) requestSelectedTips() {
|
||||
f.peersMutex.RLock()
|
||||
defer f.peersMutex.RUnlock()
|
||||
|
||||
for _, peer := range f.peers {
|
||||
peer.RequestSelectedTipIfRequired()
|
||||
}
|
||||
}
|
||||
|
||||
// FinishIBD finishes the current IBD flow and starts a new one if required.
|
||||
func (f *FlowContext) FinishIBD() error {
|
||||
f.ibdPeer = nil
|
||||
|
||||
atomic.StoreUint32(&f.isInIBD, 0)
|
||||
|
||||
return f.StartIBDIfRequired()
|
||||
}
|
||||
|
||||
// IBDPeer returns the currently active IBD peer.
|
||||
// Returns nil if we aren't currently in IBD
|
||||
func (f *FlowContext) IBDPeer() *peerpkg.Peer {
|
||||
if !f.IsInIBD() {
|
||||
return nil
|
||||
}
|
||||
return f.ibdPeer
|
||||
}
|
||||
@@ -2,8 +2,6 @@ package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
|
||||
@@ -3,21 +3,50 @@ package flowcontext
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// maxOrphans is the maximum amount of orphans allowed in the
|
||||
// orphans collection. This number is an approximation of how
|
||||
// many orphans there can possibly be on average. It is based
|
||||
// on: 2^orphanResolutionRange * PHANTOM K.
|
||||
const maxOrphans = 600
|
||||
|
||||
// UnorphaningResult is the result of unorphaning a block
|
||||
type UnorphaningResult struct {
|
||||
block *externalapi.DomainBlock
|
||||
blockInsertionResult *externalapi.BlockInsertionResult
|
||||
}
|
||||
|
||||
// AddOrphan adds the block to the orphan set
|
||||
func (f *FlowContext) AddOrphan(orphanBlock *externalapi.DomainBlock) {
|
||||
f.orphansMutex.Lock()
|
||||
defer f.orphansMutex.Unlock()
|
||||
|
||||
orphanHash := consensusserialization.BlockHash(orphanBlock)
|
||||
orphanHash := consensushashing.BlockHash(orphanBlock)
|
||||
f.orphans[*orphanHash] = orphanBlock
|
||||
|
||||
if len(f.orphans) > maxOrphans {
|
||||
log.Debugf("Orphan collection size exceeded. Evicting a random orphan")
|
||||
f.evictRandomOrphan()
|
||||
}
|
||||
|
||||
log.Infof("Received a block with missing parents, adding to orphan pool: %s", orphanHash)
|
||||
}
|
||||
|
||||
func (f *FlowContext) evictRandomOrphan() {
|
||||
var toEvict externalapi.DomainHash
|
||||
for hash := range f.orphans {
|
||||
toEvict = hash
|
||||
break
|
||||
}
|
||||
delete(f.orphans, toEvict)
|
||||
log.Debugf("Evicted %s from the orphan collection", toEvict)
|
||||
}
|
||||
|
||||
// IsOrphan returns whether the given blockHash belongs to an orphan block
|
||||
func (f *FlowContext) IsOrphan(blockHash *externalapi.DomainHash) bool {
|
||||
f.orphansMutex.RLock()
|
||||
@@ -28,32 +57,32 @@ func (f *FlowContext) IsOrphan(blockHash *externalapi.DomainHash) bool {
|
||||
}
|
||||
|
||||
// UnorphanBlocks removes the block from the orphan set, and remove all of the blocks that are not orphans anymore.
|
||||
func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*externalapi.DomainBlock, error) {
|
||||
func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*UnorphaningResult, error) {
|
||||
f.orphansMutex.Lock()
|
||||
defer f.orphansMutex.Unlock()
|
||||
|
||||
// Find all the children of rootBlock among the orphans
|
||||
// and add them to the process queue
|
||||
rootBlockHash := consensusserialization.BlockHash(rootBlock)
|
||||
rootBlockHash := consensushashing.BlockHash(rootBlock)
|
||||
processQueue := f.addChildOrphansToProcessQueue(rootBlockHash, []externalapi.DomainHash{})
|
||||
|
||||
var unorphanedBlocks []*externalapi.DomainBlock
|
||||
var unorphaningResults []*UnorphaningResult
|
||||
for len(processQueue) > 0 {
|
||||
var orphanHash externalapi.DomainHash
|
||||
orphanHash, processQueue = processQueue[0], processQueue[1:]
|
||||
orphanBlock := f.orphans[orphanHash]
|
||||
|
||||
log.Tracef("Considering to unorphan block %s with parents %s",
|
||||
orphanHash, orphanBlock.Header.ParentHashes)
|
||||
log.Debugf("Considering to unorphan block %s with parents %s",
|
||||
orphanHash, orphanBlock.Header.ParentHashes())
|
||||
|
||||
canBeUnorphaned := true
|
||||
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
|
||||
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes() {
|
||||
orphanBlockParentInfo, err := f.domain.Consensus().GetBlockInfo(orphanBlockParentHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !orphanBlockParentInfo.Exists {
|
||||
log.Tracef("Cannot unorphan block %s. It's missing at "+
|
||||
if !orphanBlockParentInfo.Exists || orphanBlockParentInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
log.Debugf("Cannot unorphan block %s. It's missing at "+
|
||||
"least the following parent: %s", orphanHash, orphanBlockParentHash)
|
||||
|
||||
canBeUnorphaned = false
|
||||
@@ -61,16 +90,21 @@ func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*ext
|
||||
}
|
||||
}
|
||||
if canBeUnorphaned {
|
||||
err := f.unorphanBlock(orphanHash)
|
||||
blockInsertionResult, unorphaningSucceeded, err := f.unorphanBlock(orphanHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unorphanedBlocks = append(unorphanedBlocks, orphanBlock)
|
||||
processQueue = f.addChildOrphansToProcessQueue(&orphanHash, processQueue)
|
||||
if unorphaningSucceeded {
|
||||
unorphaningResults = append(unorphaningResults, &UnorphaningResult{
|
||||
block: orphanBlock,
|
||||
blockInsertionResult: blockInsertionResult,
|
||||
})
|
||||
processQueue = f.addChildOrphansToProcessQueue(&orphanHash, processQueue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unorphanedBlocks, nil
|
||||
return unorphaningResults, nil
|
||||
}
|
||||
|
||||
// addChildOrphansToProcessQueue finds all child orphans of `blockHash`
|
||||
@@ -99,8 +133,8 @@ func (f *FlowContext) addChildOrphansToProcessQueue(blockHash *externalapi.Domai
|
||||
func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash) []externalapi.DomainHash {
|
||||
var childOrphans []externalapi.DomainHash
|
||||
for orphanHash, orphanBlock := range f.orphans {
|
||||
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
|
||||
if *orphanBlockParentHash == *blockHash {
|
||||
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes() {
|
||||
if orphanBlockParentHash.Equal(blockHash) {
|
||||
childOrphans = append(childOrphans, orphanHash)
|
||||
break
|
||||
}
|
||||
@@ -109,22 +143,71 @@ func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash)
|
||||
return childOrphans
|
||||
}
|
||||
|
||||
func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) error {
|
||||
func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (*externalapi.BlockInsertionResult, bool, error) {
|
||||
orphanBlock, ok := f.orphans[orphanHash]
|
||||
if !ok {
|
||||
return errors.Errorf("attempted to unorphan a non-orphan block %s", orphanHash)
|
||||
return nil, false, errors.Errorf("attempted to unorphan a non-orphan block %s", orphanHash)
|
||||
}
|
||||
delete(f.orphans, orphanHash)
|
||||
|
||||
err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock)
|
||||
blockInsertionResult, err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Infof("Validation failed for orphan block %s: %s", orphanHash, err)
|
||||
return nil
|
||||
log.Warnf("Validation failed for orphan block %s: %s", orphanHash, err)
|
||||
return nil, false, nil
|
||||
}
|
||||
return err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
log.Infof("Unorphaned block %s", orphanHash)
|
||||
return nil
|
||||
return blockInsertionResult, true, nil
|
||||
}
|
||||
|
||||
// GetOrphanRoots returns the roots of the missing ancestors DAG of the given orphan
|
||||
func (f *FlowContext) GetOrphanRoots(orphan *externalapi.DomainHash) ([]*externalapi.DomainHash, bool, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "GetOrphanRoots")
|
||||
defer onEnd()
|
||||
|
||||
f.orphansMutex.RLock()
|
||||
defer f.orphansMutex.RUnlock()
|
||||
|
||||
_, ok := f.orphans[*orphan]
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
queue := []*externalapi.DomainHash{orphan}
|
||||
addedToQueueSet := hashset.New()
|
||||
addedToQueueSet.Add(orphan)
|
||||
|
||||
roots := []*externalapi.DomainHash{}
|
||||
for len(queue) > 0 {
|
||||
var current *externalapi.DomainHash
|
||||
current, queue = queue[0], queue[1:]
|
||||
|
||||
block, ok := f.orphans[*current]
|
||||
if !ok {
|
||||
blockInfo, err := f.domain.Consensus().GetBlockInfo(current)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
roots = append(roots, current)
|
||||
} else {
|
||||
log.Debugf("Block %s was skipped when checking for orphan roots: "+
|
||||
"exists: %t, status: %s", current, blockInfo.Exists, blockInfo.BlockStatus)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for _, parent := range block.Header.ParentHashes() {
|
||||
if !addedToQueueSet.Contains(parent) {
|
||||
queue = append(queue, parent)
|
||||
addedToQueueSet.Add(parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return roots, true, nil
|
||||
}
|
||||
|
||||
43
app/protocol/flowcontext/should_mine.go
Normal file
43
app/protocol/flowcontext/should_mine.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package flowcontext
|
||||
|
||||
import "github.com/kaspanet/kaspad/util/mstime"
|
||||
|
||||
const (
|
||||
maxSelectedParentTimeDiffToAllowMiningInMilliSeconds = 60 * 60 * 1000 // 1 Hour
|
||||
)
|
||||
|
||||
// ShouldMine returns whether it's ok to use block template from this node
|
||||
// for mining purposes.
|
||||
func (f *FlowContext) ShouldMine() (bool, error) {
|
||||
peers := f.Peers()
|
||||
if len(peers) == 0 {
|
||||
log.Debugf("The node is not connected, so ShouldMine returns false")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if f.IsIBDRunning() {
|
||||
log.Debugf("IBD is running, so ShouldMine returns false")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
virtualSelectedParent, err := f.domain.Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
virtualSelectedParentHeader, err := f.domain.Consensus().GetBlockHeader(virtualSelectedParent)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
now := mstime.Now().UnixMilliseconds()
|
||||
if now-virtualSelectedParentHeader.TimeInMilliseconds() < maxSelectedParentTimeDiffToAllowMiningInMilliSeconds {
|
||||
log.Debugf("The selected tip timestamp is recent (%d), so ShouldMine returns true",
|
||||
virtualSelectedParentHeader.TimeInMilliseconds())
|
||||
return true, nil
|
||||
}
|
||||
|
||||
log.Debugf("The selected tip timestamp is old (%d), so ShouldMine returns false",
|
||||
virtualSelectedParentHeader.TimeInMilliseconds())
|
||||
return false, nil
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/relaytransactions"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
)
|
||||
|
||||
// AddTransaction adds transaction to the mempool and propagates it.
|
||||
@@ -19,20 +19,23 @@ func (f *FlowContext) AddTransaction(tx *externalapi.DomainTransaction) error {
|
||||
return err
|
||||
}
|
||||
|
||||
transactionID := consensusserialization.TransactionID(tx)
|
||||
transactionID := consensushashing.TransactionID(tx)
|
||||
f.transactionsToRebroadcast[*transactionID] = tx
|
||||
inv := appmessage.NewMsgInvTransaction([]*externalapi.DomainTransactionID{transactionID})
|
||||
return f.Broadcast(inv)
|
||||
}
|
||||
|
||||
func (f *FlowContext) updateTransactionsToRebroadcast(block *externalapi.DomainBlock) {
|
||||
func (f *FlowContext) updateTransactionsToRebroadcast(addedBlocks []*externalapi.DomainBlock) {
|
||||
f.transactionsToRebroadcastLock.Lock()
|
||||
defer f.transactionsToRebroadcastLock.Unlock()
|
||||
// Note: if the block is red, its transactions won't be rebroadcasted
|
||||
// anymore, although they are not included in the UTXO set.
|
||||
// This is probably ok, since red blocks are quite rare.
|
||||
for _, tx := range block.Transactions {
|
||||
delete(f.transactionsToRebroadcast, *consensusserialization.TransactionID(tx))
|
||||
|
||||
for _, block := range addedBlocks {
|
||||
// Note: if a transaction is included in the DAG but not accepted,
|
||||
// it won't be rebroadcast anymore, although it is not included in
|
||||
// the UTXO set
|
||||
for _, tx := range block.Transactions {
|
||||
delete(f.transactionsToRebroadcast, *consensushashing.TransactionID(tx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,15 +51,15 @@ func (f *FlowContext) txIDsToRebroadcast() []*externalapi.DomainTransactionID {
|
||||
txIDs := make([]*externalapi.DomainTransactionID, len(f.transactionsToRebroadcast))
|
||||
i := 0
|
||||
for _, tx := range f.transactionsToRebroadcast {
|
||||
txIDs[i] = consensusserialization.TransactionID(tx)
|
||||
txIDs[i] = consensushashing.TransactionID(tx)
|
||||
i++
|
||||
}
|
||||
return txIDs
|
||||
}
|
||||
|
||||
// SharedRequestedTransactions returns a *relaytransactions.SharedRequestedTransactions for sharing
|
||||
// SharedRequestedTransactions returns a *transactionrelay.SharedRequestedTransactions for sharing
|
||||
// data about requested transactions between different peers.
|
||||
func (f *FlowContext) SharedRequestedTransactions() *relaytransactions.SharedRequestedTransactions {
|
||||
func (f *FlowContext) SharedRequestedTransactions() *transactionrelay.SharedRequestedTransactions {
|
||||
return f.sharedRequestedTransactions
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,12 @@ import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/common"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// ReceiveAddressesContext is the interface for the context needed for the ReceiveAddresses flow.
|
||||
type ReceiveAddressesContext interface {
|
||||
Config() *config.Config
|
||||
AddressManager() *addressmanager.AddressManager
|
||||
}
|
||||
|
||||
@@ -37,6 +35,5 @@ func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Rou
|
||||
return protocolerrors.Errorf(true, "address count exceeded %d", addressmanager.GetAddressesMax)
|
||||
}
|
||||
|
||||
context.AddressManager().AddAddresses(msgAddresses.AddressList...)
|
||||
return nil
|
||||
return context.AddressManager().AddAddresses(msgAddresses.AddressList...)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package addressexchange
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"math/rand"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
@@ -17,16 +16,11 @@ type SendAddressesContext interface {
|
||||
// SendAddresses sends addresses to a peer that requests it.
|
||||
func SendAddresses(context SendAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
|
||||
for {
|
||||
message, err := incomingRoute.Dequeue()
|
||||
_, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, ok := message.(*appmessage.MsgRequestAddresses)
|
||||
if !ok {
|
||||
return protocolerrors.Errorf(true, "unexpected message. "+
|
||||
"Expected: %s, got: %s", appmessage.CmdRequestAddresses, message.Command())
|
||||
}
|
||||
addresses := context.AddressManager().Addresses()
|
||||
msgAddresses := appmessage.NewMsgAddresses(shuffleAddresses(addresses))
|
||||
|
||||
|
||||
29
app/protocol/flows/blockrelay/block_locator.go
Normal file
29
app/protocol/flows/blockrelay/block_locator.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/common"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
func (flow *handleRelayInvsFlow) sendGetBlockLocator(lowHash *externalapi.DomainHash,
|
||||
highHash *externalapi.DomainHash, limit uint32) error {
|
||||
|
||||
msgGetBlockLocator := appmessage.NewMsgRequestBlockLocator(lowHash, highHash, limit)
|
||||
return flow.outgoingRoute.Enqueue(msgGetBlockLocator)
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) receiveBlockLocator() (blockLocatorHashes []*externalapi.DomainHash, err error) {
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgBlockLocator, ok := message.(*appmessage.MsgBlockLocator)
|
||||
if !ok {
|
||||
return nil,
|
||||
protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdBlockLocator, message.Command())
|
||||
}
|
||||
return msgBlockLocator.BlockLocatorHashes, nil
|
||||
}
|
||||
83
app/protocol/flows/blockrelay/handle_ibd_block_locator.go
Normal file
83
app/protocol/flows/blockrelay/handle_ibd_block_locator.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleIBDBlockLocatorContext is the interface for the context needed for the HandleIBDBlockLocator flow.
|
||||
type HandleIBDBlockLocatorContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
// HandleIBDBlockLocator listens to appmessage.MsgIBDBlockLocator messages and sends
|
||||
// the highest known block that's in the selected parent chain of `targetHash` to the
|
||||
// requesting peer.
|
||||
func HandleIBDBlockLocator(context HandleIBDBlockLocatorContext, incomingRoute *router.Route,
|
||||
outgoingRoute *router.Route, peer *peer.Peer) error {
|
||||
|
||||
for {
|
||||
message, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ibdBlockLocatorMessage := message.(*appmessage.MsgIBDBlockLocator)
|
||||
|
||||
targetHash := ibdBlockLocatorMessage.TargetHash
|
||||
log.Debugf("Received IBDBlockLocator from %s with targetHash %s", peer, targetHash)
|
||||
|
||||
blockInfo, err := context.Domain().Consensus().GetBlockInfo(targetHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.Exists {
|
||||
return protocolerrors.Errorf(true, "received IBDBlockLocator "+
|
||||
"with an unknown targetHash %s", targetHash)
|
||||
}
|
||||
|
||||
foundHighestHashInTheSelectedParentChainOfTargetHash := false
|
||||
for _, blockLocatorHash := range ibdBlockLocatorMessage.BlockLocatorHashes {
|
||||
blockInfo, err := context.Domain().Consensus().GetBlockInfo(blockLocatorHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.Exists {
|
||||
continue
|
||||
}
|
||||
|
||||
isBlockLocatorHashInSelectedParentChainOfHighHash, err :=
|
||||
context.Domain().Consensus().IsInSelectedParentChainOf(blockLocatorHash, targetHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isBlockLocatorHashInSelectedParentChainOfHighHash {
|
||||
continue
|
||||
}
|
||||
|
||||
foundHighestHashInTheSelectedParentChainOfTargetHash = true
|
||||
log.Debugf("Found a known hash %s amongst peer %s's "+
|
||||
"blockLocator that's in the selected parent chain of targetHash %s", blockLocatorHash, peer, targetHash)
|
||||
|
||||
ibdBlockLocatorHighestHashMessage := appmessage.NewMsgIBDBlockLocatorHighestHash(blockLocatorHash)
|
||||
err = outgoingRoute.Enqueue(ibdBlockLocatorHighestHashMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if !foundHighestHashInTheSelectedParentChainOfTargetHash {
|
||||
log.Warnf("no hash was found in the blockLocator "+
|
||||
"that was in the selected parent chain of targetHash %s", targetHash)
|
||||
|
||||
ibdBlockLocatorHighestHashNotFoundMessage := appmessage.NewMsgIBDBlockLocatorHighestHashNotFound()
|
||||
err = outgoingRoute.Enqueue(ibdBlockLocatorHighestHashNotFoundMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package ibd
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -24,13 +25,14 @@ func HandleIBDBlockRequests(context HandleIBDBlockRequestsContext, incomingRoute
|
||||
return err
|
||||
}
|
||||
msgRequestIBDBlocks := message.(*appmessage.MsgRequestIBDBlocks)
|
||||
for _, hash := range msgRequestIBDBlocks.Hashes {
|
||||
log.Debugf("Got request for %d ibd blocks", len(msgRequestIBDBlocks.Hashes))
|
||||
for i, hash := range msgRequestIBDBlocks.Hashes {
|
||||
// Fetch the block from the database.
|
||||
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.Exists {
|
||||
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
return protocolerrors.Errorf(true, "block %s not found", hash)
|
||||
}
|
||||
block, err := context.Domain().Consensus().GetBlock(hash)
|
||||
@@ -46,6 +48,7 @@ func HandleIBDBlockRequests(context HandleIBDBlockRequestsContext, incomingRoute
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("sent %d out of %d", i, len(msgRequestIBDBlocks.Hashes))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandlePruningPointHashRequestsFlowContext is the interface for the context needed for the handlePruningPointHashRequestsFlow flow.
|
||||
type HandlePruningPointHashRequestsFlowContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
type handlePruningPointHashRequestsFlow struct {
|
||||
HandlePruningPointHashRequestsFlowContext
|
||||
incomingRoute, outgoingRoute *router.Route
|
||||
}
|
||||
|
||||
// HandlePruningPointHashRequests listens to appmessage.MsgRequestPruningPointHashMessage messages and sends
|
||||
// the pruning point hash as response.
|
||||
func HandlePruningPointHashRequests(context HandlePruningPointHashRequestsFlowContext, incomingRoute,
|
||||
outgoingRoute *router.Route) error {
|
||||
flow := &handlePruningPointHashRequestsFlow{
|
||||
HandlePruningPointHashRequestsFlowContext: context,
|
||||
incomingRoute: incomingRoute,
|
||||
outgoingRoute: outgoingRoute,
|
||||
}
|
||||
|
||||
return flow.start()
|
||||
}
|
||||
|
||||
func (flow *handlePruningPointHashRequestsFlow) start() error {
|
||||
for {
|
||||
_, err := flow.incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Got request for a pruning point hash")
|
||||
|
||||
pruningPoint, err := flow.Domain().Consensus().PruningPoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewPruningPointHashMessage(pruningPoint))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Sent pruning point hash %s", pruningPoint)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -25,13 +26,14 @@ func HandleRelayBlockRequests(context RelayBlockRequestsContext, incomingRoute *
|
||||
return err
|
||||
}
|
||||
getRelayBlocksMessage := message.(*appmessage.MsgRequestRelayBlocks)
|
||||
log.Debugf("Got request for relay blocks with hashes %s", getRelayBlocksMessage.Hashes)
|
||||
for _, hash := range getRelayBlocksMessage.Hashes {
|
||||
// Fetch the block from the database.
|
||||
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.Exists {
|
||||
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
return protocolerrors.Errorf(true, "block %s not found", hash)
|
||||
}
|
||||
block, err := context.Domain().Consensus().GetBlock(hash)
|
||||
@@ -45,6 +47,7 @@ func HandleRelayBlockRequests(context RelayBlockRequestsContext, incomingRoute *
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Relayed block with hash %s", hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,25 +8,31 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blocks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
mathUtil "github.com/kaspanet/kaspad/util/math"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// orphanResolutionRange is the maximum amount of blockLocator hashes
|
||||
// to search for known blocks. See isBlockInOrphanResolutionRange for
|
||||
// further details
|
||||
var orphanResolutionRange uint32 = 5
|
||||
|
||||
// RelayInvsContext is the interface for the context needed for the HandleRelayInvs flow.
|
||||
type RelayInvsContext interface {
|
||||
Domain() domain.Domain
|
||||
NetAdapter() *netadapter.NetAdapter
|
||||
OnNewBlock(block *externalapi.DomainBlock) error
|
||||
Config() *config.Config
|
||||
OnNewBlock(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||
OnPruningPointUTXOSetOverride() error
|
||||
SharedRequestedBlocks() *SharedRequestedBlocks
|
||||
StartIBDIfRequired() error
|
||||
IsInIBD() bool
|
||||
Broadcast(message appmessage.Message) error
|
||||
AddOrphan(orphanBlock *externalapi.DomainBlock)
|
||||
GetOrphanRoots(orphanHash *externalapi.DomainHash) ([]*externalapi.DomainHash, bool, error)
|
||||
IsOrphan(blockHash *externalapi.DomainHash) bool
|
||||
IsIBDRunning() bool
|
||||
TrySetIBDRunning(ibdPeer *peerpkg.Peer) bool
|
||||
UnsetIBDRunning()
|
||||
}
|
||||
|
||||
type handleRelayInvsFlow struct {
|
||||
@@ -53,6 +59,7 @@ func HandleRelayInvs(context RelayInvsContext, incomingRoute *router.Route, outg
|
||||
|
||||
func (flow *handleRelayInvsFlow) start() error {
|
||||
for {
|
||||
log.Debugf("Waiting for inv")
|
||||
inv, err := flow.readInv()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -64,39 +71,90 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blockInfo.Exists {
|
||||
if blockInfo.Exists && blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
|
||||
if blockInfo.BlockStatus == externalapi.StatusInvalid {
|
||||
return protocolerrors.Errorf(true, "sent inv of an invalid block %s",
|
||||
inv.Hash)
|
||||
}
|
||||
log.Debugf("Block %s already exists. continuing...", inv.Hash)
|
||||
continue
|
||||
}
|
||||
|
||||
if flow.IsOrphan(inv.Hash) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = flow.StartIBDIfRequired()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flow.IsInIBD() {
|
||||
// Block relay is disabled during IBD
|
||||
continue
|
||||
}
|
||||
|
||||
requestQueue := newHashesQueueSet()
|
||||
requestQueue.enqueueIfNotExists(inv.Hash)
|
||||
|
||||
for requestQueue.len() > 0 {
|
||||
err := flow.requestBlocks(requestQueue)
|
||||
log.Debugf("Block %s is a known orphan. Requesting its missing ancestors", inv.Hash)
|
||||
err := flow.AddOrphanRootsToQueue(inv.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Block relay is disabled during IBD
|
||||
if flow.IsIBDRunning() {
|
||||
log.Debugf("Got block %s while in IBD. continuing...", inv.Hash)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Requesting block %s", inv.Hash)
|
||||
block, exists, err := flow.requestBlock(inv.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
log.Debugf("Aborting requesting block %s because it already exists", inv.Hash)
|
||||
continue
|
||||
}
|
||||
|
||||
err = flow.banIfBlockIsHeaderOnly(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Processing block %s", inv.Hash)
|
||||
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrPrunedBlock) {
|
||||
log.Infof("Ignoring pruned block %s", inv.Hash)
|
||||
continue
|
||||
}
|
||||
|
||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
log.Infof("Ignoring duplicate block %s", inv.Hash)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if len(missingParents) > 0 {
|
||||
log.Debugf("Block %s is orphan and has missing parents: %s", inv.Hash, missingParents)
|
||||
err := flow.processOrphan(block, missingParents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Relaying block %s", inv.Hash)
|
||||
err = flow.relayBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Accepted block %s via relay", inv.Hash)
|
||||
err = flow.OnNewBlock(block, blockInsertionResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock) error {
|
||||
if len(block.Transactions) == 0 {
|
||||
return protocolerrors.Errorf(true, "sent header of %s block where expected block with body",
|
||||
consensushashing.BlockHash(block))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
|
||||
if len(flow.invsQueue) > 0 {
|
||||
var inv *appmessage.MsgInvRelayBlock
|
||||
@@ -117,68 +175,34 @@ func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error)
|
||||
return inv, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) requestBlocks(requestQueue *hashesQueueSet) error {
|
||||
numHashesToRequest := mathUtil.MinInt(appmessage.MaxRequestRelayBlocksHashes, requestQueue.len())
|
||||
hashesToRequest := requestQueue.dequeue(numHashesToRequest)
|
||||
|
||||
pendingBlocks := map[externalapi.DomainHash]struct{}{}
|
||||
var filteredHashesToRequest []*externalapi.DomainHash
|
||||
for _, hash := range hashesToRequest {
|
||||
exists := flow.SharedRequestedBlocks().addIfNotExists(hash)
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
// The block can become known from another peer in the process of orphan resolution
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blockInfo.Exists {
|
||||
continue
|
||||
}
|
||||
|
||||
pendingBlocks[*hash] = struct{}{}
|
||||
filteredHashesToRequest = append(filteredHashesToRequest, hash)
|
||||
func (flow *handleRelayInvsFlow) requestBlock(requestHash *externalapi.DomainHash) (*externalapi.DomainBlock, bool, error) {
|
||||
exists := flow.SharedRequestedBlocks().addIfNotExists(requestHash)
|
||||
if exists {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
// Exit early if we've filtered out all the hashes
|
||||
if len(filteredHashesToRequest) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// In case the function returns earlier than expected, we want to make sure requestedBlocks is
|
||||
// In case the function returns earlier than expected, we want to make sure flow.SharedRequestedBlocks() is
|
||||
// clean from any pending blocks.
|
||||
defer flow.SharedRequestedBlocks().removeSet(pendingBlocks)
|
||||
defer flow.SharedRequestedBlocks().remove(requestHash)
|
||||
|
||||
getRelayBlocksMsg := appmessage.NewMsgRequestRelayBlocks(filteredHashesToRequest)
|
||||
getRelayBlocksMsg := appmessage.NewMsgRequestRelayBlocks([]*externalapi.DomainHash{requestHash})
|
||||
err := flow.outgoingRoute.Enqueue(getRelayBlocksMsg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
for len(pendingBlocks) > 0 {
|
||||
msgBlock, err := flow.readMsgBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
block := appmessage.MsgBlockToDomainBlock(msgBlock)
|
||||
blockHash := consensusserialization.BlockHash(block)
|
||||
|
||||
if _, ok := pendingBlocks[*blockHash]; !ok {
|
||||
return protocolerrors.Errorf(true, "got unrequested block %s", blockHash)
|
||||
}
|
||||
|
||||
err = flow.processAndRelayBlock(requestQueue, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(pendingBlocks, *blockHash)
|
||||
flow.SharedRequestedBlocks().remove(blockHash)
|
||||
msgBlock, err := flow.readMsgBlock()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return nil
|
||||
|
||||
block := appmessage.MsgBlockToDomainBlock(msgBlock)
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
if !blockHash.Equal(requestHash) {
|
||||
return nil, false, protocolerrors.Errorf(true, "got unrequested block %s", blockHash)
|
||||
}
|
||||
|
||||
return block, false, nil
|
||||
}
|
||||
|
||||
// readMsgBlock returns the next msgBlock in msgChan, and populates invsQueue with any inv messages that meanwhile arrive.
|
||||
@@ -202,68 +226,104 @@ func (flow *handleRelayInvsFlow) readMsgBlock() (msgBlock *appmessage.MsgBlock,
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) processAndRelayBlock(requestQueue *hashesQueueSet, block *externalapi.DomainBlock) error {
|
||||
blockHash := consensusserialization.BlockHash(block)
|
||||
err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([]*externalapi.DomainHash, *externalapi.BlockInsertionResult, error) {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return errors.Wrapf(err, "failed to process block %s", blockHash)
|
||||
return nil, nil, errors.Wrapf(err, "failed to process block %s", blockHash)
|
||||
}
|
||||
|
||||
missingParentsError := &ruleerrors.ErrMissingParents{}
|
||||
if errors.As(err, missingParentsError) {
|
||||
blueScore, err := blocks.ExtractBlueScore(block)
|
||||
if err != nil {
|
||||
return protocolerrors.Errorf(true, "received an orphan "+
|
||||
"block %s with malformed blue score", blockHash)
|
||||
}
|
||||
|
||||
const maxOrphanBlueScoreDiff = 10000
|
||||
virtualSelectedParent, err := flow.Domain().Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selectedTipBlueScore, err := blocks.ExtractBlueScore(virtualSelectedParent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if blueScore > selectedTipBlueScore+maxOrphanBlueScoreDiff {
|
||||
log.Infof("Orphan block %s has blue score %d and the selected tip blue score is "+
|
||||
"%d. Ignoring orphans with a blue score difference from the selected tip greater than %d",
|
||||
blockHash, blueScore, selectedTipBlueScore, maxOrphanBlueScoreDiff)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add the orphan to the orphan pool
|
||||
flow.AddOrphan(block)
|
||||
|
||||
// Request the parents for the orphan block from the peer that sent it.
|
||||
for _, missingAncestor := range missingParentsError.MissingParentHashes {
|
||||
requestQueue.enqueueIfNotExists(missingAncestor)
|
||||
}
|
||||
return nil
|
||||
return missingParentsError.MissingParentHashes, nil, nil
|
||||
}
|
||||
log.Infof("Rejected block %s from %s: %s", blockHash, flow.peer, err)
|
||||
log.Warnf("Rejected block %s from %s: %s", blockHash, flow.peer, err)
|
||||
return nil, nil, protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
|
||||
}
|
||||
return nil, blockInsertionResult, nil
|
||||
}
|
||||
|
||||
return protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
|
||||
func (flow *handleRelayInvsFlow) relayBlock(block *externalapi.DomainBlock) error {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
return flow.Broadcast(appmessage.NewMsgInvBlock(blockHash))
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) processOrphan(block *externalapi.DomainBlock, missingParents []*externalapi.DomainHash) error {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
|
||||
// Return if the block has been orphaned from elsewhere already
|
||||
if flow.IsOrphan(blockHash) {
|
||||
log.Debugf("Skipping orphan processing for block %s because it is already an orphan", blockHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = flow.Broadcast(appmessage.NewMsgInvBlock(blockHash))
|
||||
// Add the block to the orphan set if it's within orphan resolution range
|
||||
isBlockInOrphanResolutionRange, err := flow.isBlockInOrphanResolutionRange(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isBlockInOrphanResolutionRange {
|
||||
log.Debugf("Block %s is within orphan resolution range. "+
|
||||
"Adding it to the orphan set", blockHash)
|
||||
flow.AddOrphan(block)
|
||||
log.Debugf("Requesting block %s missing ancestors", blockHash)
|
||||
return flow.AddOrphanRootsToQueue(blockHash)
|
||||
}
|
||||
|
||||
// Start IBD unless we already are in IBD
|
||||
log.Debugf("Block %s is out of orphan resolution range. "+
|
||||
"Attempting to start IBD against it.", blockHash)
|
||||
return flow.runIBDIfNotRunning(blockHash)
|
||||
}
|
||||
|
||||
// isBlockInOrphanResolutionRange finds out whether the given blockHash should be
|
||||
// retrieved via the unorphaning mechanism or via IBD. This method sends a
|
||||
// getBlockLocator request to the peer with a limit of orphanResolutionRange.
|
||||
// In the response, if we know none of the hashes, we should retrieve the given
|
||||
// blockHash via IBD. Otherwise, via unorphaning.
|
||||
func (flow *handleRelayInvsFlow) isBlockInOrphanResolutionRange(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
lowHash := flow.Config().ActiveNetParams.GenesisHash
|
||||
err := flow.sendGetBlockLocator(lowHash, blockHash, orphanResolutionRange)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
blockLocatorHashes, err := flow.receiveBlockLocator()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, blockLocatorHash := range blockLocatorHashes {
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(blockLocatorHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if blockInfo.Exists && blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) AddOrphanRootsToQueue(orphan *externalapi.DomainHash) error {
|
||||
orphanRoots, orphanExists, err := flow.GetOrphanRoots(orphan)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Accepted block %s via relay", blockHash)
|
||||
|
||||
err = flow.StartIBDIfRequired()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = flow.OnNewBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
if !orphanExists {
|
||||
log.Infof("Orphan block %s was missing from the orphan pool while requesting for its roots. This "+
|
||||
"probably happened because it was randomly evicted immediately after it was added.", orphan)
|
||||
}
|
||||
|
||||
log.Infof("Block %s has %d missing ancestors. Adding them to the invs queue...", orphan, len(orphanRoots))
|
||||
|
||||
invMessages := make([]*appmessage.MsgInvRelayBlock, len(orphanRoots))
|
||||
for i, root := range orphanRoots {
|
||||
log.Debugf("Adding block %s missing ancestor %s to the invs queue", orphan, root)
|
||||
invMessages[i] = appmessage.NewMsgInvBlock(root)
|
||||
}
|
||||
|
||||
flow.invsQueue = append(invMessages, flow.invsQueue...)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package ibd
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
@@ -32,13 +32,18 @@ func HandleRequestBlockLocator(context RequestBlockLocatorContext, incomingRoute
|
||||
|
||||
func (flow *handleRequestBlockLocatorFlow) start() error {
|
||||
for {
|
||||
lowHash, highHash, err := flow.receiveGetBlockLocator()
|
||||
lowHash, highHash, limit, err := flow.receiveGetBlockLocator()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Received getBlockLocator with lowHash: %s, highHash: %s, limit: %d",
|
||||
lowHash, highHash, limit)
|
||||
|
||||
locator, err := flow.Domain().Consensus().CreateBlockLocator(lowHash, highHash)
|
||||
locator, err := flow.Domain().Consensus().CreateBlockLocator(lowHash, highHash, limit)
|
||||
if err != nil || len(locator) == 0 {
|
||||
if err != nil {
|
||||
log.Debugf("Received error from CreateBlockLocator: %s", err)
|
||||
}
|
||||
return protocolerrors.Errorf(true, "couldn't build a block "+
|
||||
"locator between blocks %s and %s", lowHash, highHash)
|
||||
}
|
||||
@@ -51,15 +56,15 @@ func (flow *handleRequestBlockLocatorFlow) start() error {
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlockLocatorFlow) receiveGetBlockLocator() (lowHash *externalapi.DomainHash,
|
||||
highHash *externalapi.DomainHash, err error) {
|
||||
highHash *externalapi.DomainHash, limit uint32, err error) {
|
||||
|
||||
message, err := flow.incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
msgGetBlockLocator := message.(*appmessage.MsgRequestBlockLocator)
|
||||
|
||||
return msgGetBlockLocator.LowHash, msgGetBlockLocator.HighHash, nil
|
||||
return msgGetBlockLocator.LowHash, msgGetBlockLocator.HighHash, msgGetBlockLocator.Limit, nil
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlockLocatorFlow) sendBlockLocator(locator externalapi.BlockLocator) error {
|
||||
@@ -1,77 +1,89 @@
|
||||
package ibd
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
const ibdBatchSize = router.DefaultMaxMessages
|
||||
const maxHeaders = appmessage.MaxInvPerMsg
|
||||
|
||||
// RequestIBDBlocksContext is the interface for the context needed for the HandleRequestHeaders flow.
|
||||
type RequestIBDBlocksContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
type handleRequestBlocksFlow struct {
|
||||
type handleRequestHeadersFlow struct {
|
||||
RequestIBDBlocksContext
|
||||
incomingRoute, outgoingRoute *router.Route
|
||||
peer *peer.Peer
|
||||
}
|
||||
|
||||
// HandleRequestHeaders handles RequestHeaders messages
|
||||
func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
|
||||
flow := &handleRequestBlocksFlow{
|
||||
func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router.Route,
|
||||
outgoingRoute *router.Route, peer *peer.Peer) error {
|
||||
|
||||
flow := &handleRequestHeadersFlow{
|
||||
RequestIBDBlocksContext: context,
|
||||
incomingRoute: incomingRoute,
|
||||
outgoingRoute: outgoingRoute,
|
||||
peer: peer,
|
||||
}
|
||||
return flow.start()
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlocksFlow) start() error {
|
||||
func (flow *handleRequestHeadersFlow) start() error {
|
||||
for {
|
||||
lowHash, highHash, err := receiveRequestHeaders(flow.incomingRoute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Recieved requestHeaders with lowHash: %s, highHash: %s", lowHash, highHash)
|
||||
|
||||
msgHeaders, err := flow.buildMsgBlockHeaders(lowHash, highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for !lowHash.Equal(highHash) {
|
||||
log.Debugf("Getting block headers between %s and %s to %s", lowHash, highHash, flow.peer)
|
||||
|
||||
for offset := 0; offset < len(msgHeaders); offset += ibdBatchSize {
|
||||
end := offset + ibdBatchSize
|
||||
if end > len(msgHeaders) {
|
||||
end = len(msgHeaders)
|
||||
}
|
||||
|
||||
blocksToSend := msgHeaders[offset:end]
|
||||
err = flow.sendHeaders(blocksToSend)
|
||||
// GetHashesBetween is a relatively heavy operation so we limit it
|
||||
// in order to avoid locking the consensus for too long
|
||||
const maxBlueScoreDifference = 1 << 10
|
||||
blockHashes, _, err := flow.Domain().Consensus().GetHashesBetween(lowHash, highHash, maxBlueScoreDifference)
|
||||
if err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
log.Debugf("Got %d header hashes above lowHash %s", len(blockHashes), lowHash)
|
||||
|
||||
blockHeaders := make([]*appmessage.MsgBlockHeader, len(blockHashes))
|
||||
for i, blockHash := range blockHashes {
|
||||
blockHeader, err := flow.Domain().Consensus().GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(blockHeader)
|
||||
}
|
||||
|
||||
// Exit the loop and don't wait for the GetNextIBDBlocks message if the last batch was
|
||||
// less than ibdBatchSize.
|
||||
if len(blocksToSend) < ibdBatchSize {
|
||||
break
|
||||
blockHeadersMessage := appmessage.NewBlockHeadersMessage(blockHeaders)
|
||||
err = flow.outgoingRoute.Enqueue(blockHeadersMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message, err := flow.incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := message.(*appmessage.MsgRequestNextHeaders); !ok {
|
||||
return protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdRequestNextHeaders, message.Command())
|
||||
}
|
||||
|
||||
// The next lowHash is the last element in blockHashes
|
||||
lowHash = blockHashes[len(blockHashes)-1]
|
||||
}
|
||||
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -90,37 +102,3 @@ func receiveRequestHeaders(incomingRoute *router.Route) (lowHash *externalapi.Do
|
||||
|
||||
return msgRequestIBDBlocks.LowHash, msgRequestIBDBlocks.HighHash, nil
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlocksFlow) buildMsgBlockHeaders(lowHash *externalapi.DomainHash,
|
||||
highHash *externalapi.DomainHash) ([]*appmessage.MsgBlockHeader, error) {
|
||||
|
||||
blockHashes, err := flow.Domain().Consensus().GetHashesBetween(lowHash, highHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(blockHashes) > maxHeaders {
|
||||
blockHashes = blockHashes[:maxHeaders]
|
||||
}
|
||||
|
||||
msgBlockHeaders := make([]*appmessage.MsgBlockHeader, len(blockHashes))
|
||||
for i, blockHash := range blockHashes {
|
||||
header, err := flow.Domain().Consensus().GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgBlockHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(header)
|
||||
}
|
||||
|
||||
return msgBlockHeaders, nil
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlocksFlow) sendHeaders(headers []*appmessage.MsgBlockHeader) error {
|
||||
for _, msgBlockHeader := range headers {
|
||||
err := flow.outgoingRoute.Enqueue(msgBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/common"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleRequestPruningPointUTXOSetAndBlockContext is the interface for the context needed for the HandleRequestPruningPointUTXOSetAndBlock flow.
|
||||
type HandleRequestPruningPointUTXOSetAndBlockContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
type handleRequestPruningPointUTXOSetAndBlockFlow struct {
|
||||
HandleRequestPruningPointUTXOSetAndBlockContext
|
||||
incomingRoute, outgoingRoute *router.Route
|
||||
}
|
||||
|
||||
// HandleRequestPruningPointUTXOSetAndBlock listens to appmessage.MsgRequestPruningPointUTXOSetAndBlock messages and sends
|
||||
// the pruning point UTXO set and block body.
|
||||
func HandleRequestPruningPointUTXOSetAndBlock(context HandleRequestPruningPointUTXOSetAndBlockContext, incomingRoute,
|
||||
outgoingRoute *router.Route) error {
|
||||
flow := &handleRequestPruningPointUTXOSetAndBlockFlow{
|
||||
HandleRequestPruningPointUTXOSetAndBlockContext: context,
|
||||
incomingRoute: incomingRoute,
|
||||
outgoingRoute: outgoingRoute,
|
||||
}
|
||||
|
||||
return flow.start()
|
||||
}
|
||||
|
||||
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) start() error {
|
||||
for {
|
||||
msgRequestPruningPointUTXOSetAndBlock, err := flow.waitForRequestPruningPointUTXOSetAndBlockMessages()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = flow.handleRequestPruningPointUTXOSetAndBlockMessage(msgRequestPruningPointUTXOSetAndBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) handleRequestPruningPointUTXOSetAndBlockMessage(
|
||||
msgRequestPruningPointUTXOSetAndBlock *appmessage.MsgRequestPruningPointUTXOSetAndBlock) error {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "handleRequestPruningPointUTXOSetAndBlockFlow")
|
||||
defer onEnd()
|
||||
|
||||
log.Debugf("Got request for PruningPointHash UTXOSet and Block")
|
||||
|
||||
err := flow.sendPruningPointBlock(msgRequestPruningPointUTXOSetAndBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return flow.sendPruningPointUTXOSet(msgRequestPruningPointUTXOSetAndBlock)
|
||||
}
|
||||
|
||||
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) waitForRequestPruningPointUTXOSetAndBlockMessages() (
|
||||
*appmessage.MsgRequestPruningPointUTXOSetAndBlock, error) {
|
||||
|
||||
message, err := flow.incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgRequestPruningPointUTXOSetAndBlock, ok := message.(*appmessage.MsgRequestPruningPointUTXOSetAndBlock)
|
||||
if !ok {
|
||||
return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdRequestPruningPointUTXOSetAndBlock, message.Command())
|
||||
}
|
||||
return msgRequestPruningPointUTXOSetAndBlock, nil
|
||||
}
|
||||
|
||||
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) sendPruningPointBlock(
|
||||
msgRequestPruningPointUTXOSetAndBlock *appmessage.MsgRequestPruningPointUTXOSetAndBlock) error {
|
||||
|
||||
block, err := flow.Domain().Consensus().GetBlock(msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Retrieved pruning block %s", msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
|
||||
|
||||
return flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDBlock(appmessage.DomainBlockToMsgBlock(block)))
|
||||
}
|
||||
|
||||
func (flow *handleRequestPruningPointUTXOSetAndBlockFlow) sendPruningPointUTXOSet(
|
||||
msgRequestPruningPointUTXOSetAndBlock *appmessage.MsgRequestPruningPointUTXOSetAndBlock) error {
|
||||
|
||||
// Send the UTXO set in `step`-sized chunks
|
||||
const step = 1000
|
||||
var fromOutpoint *externalapi.DomainOutpoint
|
||||
chunksSent := 0
|
||||
for {
|
||||
pruningPointUTXOs, err := flow.Domain().Consensus().GetPruningPointUTXOs(
|
||||
msgRequestPruningPointUTXOSetAndBlock.PruningPointHash, fromOutpoint, step)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrWrongPruningPointHash) {
|
||||
return flow.outgoingRoute.Enqueue(appmessage.NewMsgUnexpectedPruningPoint())
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Retrieved %d UTXOs for pruning block %s",
|
||||
len(pruningPointUTXOs), msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
|
||||
|
||||
outpointAndUTXOEntryPairs :=
|
||||
appmessage.DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(pruningPointUTXOs)
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgPruningPointUTXOSetChunk(outpointAndUTXOEntryPairs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pruningPointUTXOs) < step {
|
||||
log.Debugf("Finished sending UTXOs for pruning block %s",
|
||||
msgRequestPruningPointUTXOSetAndBlock.PruningPointHash)
|
||||
|
||||
return flow.outgoingRoute.Enqueue(appmessage.NewMsgDonePruningPointUTXOSetChunks())
|
||||
}
|
||||
|
||||
fromOutpoint = pruningPointUTXOs[len(pruningPointUTXOs)-1].Outpoint
|
||||
chunksSent++
|
||||
|
||||
// Wait for the peer to request more chunks every `ibdBatchSize` chunks
|
||||
if chunksSent%ibdBatchSize == 0 {
|
||||
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, ok := message.(*appmessage.MsgRequestNextPruningPointUTXOSetChunk)
|
||||
if !ok {
|
||||
return protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdRequestNextPruningPointUTXOSetChunk, message.Command())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
type hashesQueueSet struct {
|
||||
queue []*externalapi.DomainHash
|
||||
set map[externalapi.DomainHash]struct{}
|
||||
}
|
||||
|
||||
func (r *hashesQueueSet) enqueueIfNotExists(hash *externalapi.DomainHash) {
|
||||
if _, ok := r.set[*hash]; ok {
|
||||
return
|
||||
}
|
||||
r.queue = append(r.queue, hash)
|
||||
r.set[*hash] = struct{}{}
|
||||
}
|
||||
|
||||
func (r *hashesQueueSet) dequeue(numItems int) []*externalapi.DomainHash {
|
||||
var hashes []*externalapi.DomainHash
|
||||
hashes, r.queue = r.queue[:numItems], r.queue[numItems:]
|
||||
for _, hash := range hashes {
|
||||
delete(r.set, *hash)
|
||||
}
|
||||
return hashes
|
||||
}
|
||||
|
||||
func (r *hashesQueueSet) len() int {
|
||||
return len(r.queue)
|
||||
}
|
||||
|
||||
func newHashesQueueSet() *hashesQueueSet {
|
||||
return &hashesQueueSet{
|
||||
set: make(map[externalapi.DomainHash]struct{}),
|
||||
}
|
||||
}
|
||||
577
app/protocol/flows/blockrelay/ibd.go
Normal file
577
app/protocol/flows/blockrelay/ibd.go
Normal file
@@ -0,0 +1,577 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/common"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.DomainHash) error {
|
||||
wasIBDNotRunning := flow.TrySetIBDRunning(flow.peer)
|
||||
if !wasIBDNotRunning {
|
||||
log.Debugf("IBD is already running")
|
||||
return nil
|
||||
}
|
||||
defer flow.UnsetIBDRunning()
|
||||
|
||||
log.Debugf("IBD started with peer %s and highHash %s", flow.peer, highHash)
|
||||
|
||||
log.Debugf("Syncing headers up to %s", highHash)
|
||||
headersSynced, err := flow.syncHeaders(highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !headersSynced {
|
||||
log.Debugf("Aborting IBD because the headers failed to sync")
|
||||
return nil
|
||||
}
|
||||
log.Debugf("Finished syncing headers up to %s", highHash)
|
||||
|
||||
log.Debugf("Syncing the current pruning point UTXO set")
|
||||
syncedPruningPointUTXOSetSuccessfully, err := flow.syncPruningPointUTXOSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !syncedPruningPointUTXOSetSuccessfully {
|
||||
log.Debugf("Aborting IBD because the pruning point UTXO set failed to sync")
|
||||
return nil
|
||||
}
|
||||
log.Debugf("Finished syncing the current pruning point UTXO set")
|
||||
|
||||
log.Debugf("Downloading block bodies up to %s", highHash)
|
||||
err = flow.syncMissingBlockBodies(highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Finished downloading block bodies up to %s", highHash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncHeaders attempts to sync headers from the peer. This method may fail
|
||||
// because the peer and us have conflicting pruning points. In that case we
|
||||
// return (false, nil) so that we may stop IBD gracefully.
|
||||
func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) (bool, error) {
|
||||
log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash)
|
||||
highestSharedBlockHash, highestSharedBlockFound, err := flow.findHighestSharedBlockHash(highHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !highestSharedBlockFound {
|
||||
return false, nil
|
||||
}
|
||||
log.Debugf("Found highest shared chain block %s with peer %s", highestSharedBlockHash, flow.peer)
|
||||
|
||||
err = flow.downloadHeaders(highestSharedBlockHash, highHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// If the highHash has not been received, the peer is misbehaving
|
||||
highHashBlockInfo, err := flow.Domain().Consensus().GetBlockInfo(highHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !highHashBlockInfo.Exists {
|
||||
return false, protocolerrors.Errorf(true, "did not receive "+
|
||||
"highHash header %s from peer %s during header download", highHash, flow.peer)
|
||||
}
|
||||
log.Debugf("Headers downloaded from peer %s", flow.peer)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// findHighestSharedBlock attempts to find the highest shared block between the peer
|
||||
// and this node. This method may fail because the peer and us have conflicting pruning
|
||||
// points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
|
||||
func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(
|
||||
targetHash *externalapi.DomainHash) (*externalapi.DomainHash, bool, error) {
|
||||
|
||||
log.Debugf("Sending a blockLocator to %s between pruning point and headers selected tip", flow.peer)
|
||||
blockLocator, err := flow.Domain().Consensus().CreateFullHeadersSelectedChainBlockLocator()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
for {
|
||||
highestHash, highestHashFound, err := flow.fetchHighestHash(targetHash, blockLocator)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if !highestHashFound {
|
||||
return nil, false, nil
|
||||
}
|
||||
highestHashIndex, err := flow.findHighestHashIndex(highestHash, blockLocator)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if highestHashIndex == 0 ||
|
||||
// If the block locator contains only two adjacent chain blocks, the
|
||||
// syncer will always find the same highest chain block, so to avoid
|
||||
// an endless loop, we explicitly stop the loop in such situation.
|
||||
(len(blockLocator) == 2 && highestHashIndex == 1) {
|
||||
|
||||
return highestHash, true, nil
|
||||
}
|
||||
|
||||
locatorHashAboveHighestHash := highestHash
|
||||
if highestHashIndex > 0 {
|
||||
locatorHashAboveHighestHash = blockLocator[highestHashIndex-1]
|
||||
}
|
||||
|
||||
blockLocator, err = flow.nextBlockLocator(highestHash, locatorHashAboveHighestHash)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) nextBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {
|
||||
log.Debugf("Sending a blockLocator to %s between %s and %s", flow.peer, lowHash, highHash)
|
||||
blockLocator, err := flow.Domain().Consensus().CreateHeadersSelectedChainBlockLocator(lowHash, highHash)
|
||||
if err != nil {
|
||||
if errors.Is(model.ErrBlockNotInSelectedParentChain, err) {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Headers selected parent chain moved since findHighestSharedBlockHash - " +
|
||||
"restarting with full block locator")
|
||||
blockLocator, err = flow.Domain().Consensus().CreateFullHeadersSelectedChainBlockLocator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return blockLocator, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) findHighestHashIndex(
|
||||
highestHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (int, error) {
|
||||
|
||||
highestHashIndex := 0
|
||||
highestHashIndexFound := false
|
||||
for i, blockLocatorHash := range blockLocator {
|
||||
if highestHash.Equal(blockLocatorHash) {
|
||||
highestHashIndex = i
|
||||
highestHashIndexFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !highestHashIndexFound {
|
||||
return 0, protocolerrors.Errorf(true, "highest hash %s "+
|
||||
"returned from peer %s is not in the original blockLocator", highestHash, flow.peer)
|
||||
}
|
||||
log.Debugf("The index of the highest hash in the original "+
|
||||
"blockLocator sent to %s is %d", flow.peer, highestHashIndex)
|
||||
|
||||
return highestHashIndex, nil
|
||||
}
|
||||
|
||||
// fetchHighestHash attempts to fetch the highest hash the peer knows amongst the given
|
||||
// blockLocator. This method may fail because the peer and us have conflicting pruning
|
||||
// points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
|
||||
func (flow *handleRelayInvsFlow) fetchHighestHash(
|
||||
targetHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (*externalapi.DomainHash, bool, error) {
|
||||
|
||||
ibdBlockLocatorMessage := appmessage.NewMsgIBDBlockLocator(targetHash, blockLocator)
|
||||
err := flow.outgoingRoute.Enqueue(ibdBlockLocatorMessage)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case *appmessage.MsgIBDBlockLocatorHighestHash:
|
||||
highestHash := message.HighestHash
|
||||
log.Debugf("The highest hash the peer %s knows is %s", flow.peer, highestHash)
|
||||
|
||||
return highestHash, true, nil
|
||||
case *appmessage.MsgIBDBlockLocatorHighestHashNotFound:
|
||||
log.Debugf("Peer %s does not know any block within our blockLocator. "+
|
||||
"This should only happen if there's a DAG split deeper than the pruning point.", flow.peer)
|
||||
return nil, false, nil
|
||||
default:
|
||||
return nil, false, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDBlockLocatorHighestHash, message.Command())
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externalapi.DomainHash,
|
||||
highHash *externalapi.DomainHash) error {
|
||||
|
||||
err := flow.sendRequestHeaders(highestSharedBlockHash, highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Keep a short queue of blockHeadersMessages so that there's
|
||||
// never a moment when the node is not validating and inserting
|
||||
// headers
|
||||
blockHeadersMessageChan := make(chan *appmessage.BlockHeadersMessage, 2)
|
||||
errChan := make(chan error)
|
||||
spawn("handleRelayInvsFlow-downloadHeaders", func() {
|
||||
for {
|
||||
blockHeadersMessage, doneIBD, err := flow.receiveHeaders()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
if doneIBD {
|
||||
close(blockHeadersMessageChan)
|
||||
return
|
||||
}
|
||||
|
||||
blockHeadersMessageChan <- blockHeadersMessage
|
||||
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestNextHeaders())
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
for {
|
||||
select {
|
||||
case blockHeadersMessage, ok := <-blockHeadersMessageChan:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
for _, header := range blockHeadersMessage.BlockHeaders {
|
||||
err = flow.processHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case err := <-errChan:
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) sendRequestHeaders(highestSharedBlockHash *externalapi.DomainHash,
|
||||
peerSelectedTipHash *externalapi.DomainHash) error {
|
||||
|
||||
msgGetBlockInvs := appmessage.NewMsgRequstHeaders(highestSharedBlockHash, peerSelectedTipHash)
|
||||
return flow.outgoingRoute.Enqueue(msgGetBlockInvs)
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) receiveHeaders() (msgIBDBlock *appmessage.BlockHeadersMessage, doneIBD bool, err error) {
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case *appmessage.BlockHeadersMessage:
|
||||
return message, false, nil
|
||||
case *appmessage.MsgDoneHeaders:
|
||||
return nil, true, nil
|
||||
default:
|
||||
return nil, false,
|
||||
protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s or %s, got: %s", appmessage.CmdHeader, appmessage.CmdDoneHeaders, message.Command())
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) processHeader(msgBlockHeader *appmessage.MsgBlockHeader) error {
|
||||
header := appmessage.BlockHeaderToDomainBlockHeader(msgBlockHeader)
|
||||
block := &externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: nil,
|
||||
}
|
||||
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blockInfo.Exists {
|
||||
log.Debugf("Block header %s is already in the DAG. Skipping...", blockHash)
|
||||
return nil
|
||||
}
|
||||
_, err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return errors.Wrapf(err, "failed to process header %s during IBD", blockHash)
|
||||
}
|
||||
|
||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
log.Debugf("Skipping block header %s as it is a duplicate", blockHash)
|
||||
} else {
|
||||
log.Infof("Rejected block header %s from %s during IBD: %s", blockHash, flow.peer, err)
|
||||
return protocolerrors.Wrapf(true, err, "got invalid block header %s during IBD", blockHash)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) syncPruningPointUTXOSet() (bool, error) {
|
||||
log.Debugf("Checking if a new pruning point is available")
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointHashMessage())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
msgPruningPointHash, ok := message.(*appmessage.MsgPruningPointHashMessage)
|
||||
if !ok {
|
||||
return false, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdPruningPointHash, message.Command())
|
||||
}
|
||||
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(msgPruningPointHash.Hash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !blockInfo.Exists {
|
||||
return false, errors.Errorf("The pruning point header is missing")
|
||||
}
|
||||
|
||||
if blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
|
||||
log.Debugf("Already has the block data of the new suggested pruning point %s", msgPruningPointHash.Hash)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
log.Infof("Checking if the suggested pruning point %s is compatible to the node DAG", msgPruningPointHash.Hash)
|
||||
isValid, err := flow.Domain().Consensus().IsValidPruningPoint(msgPruningPointHash.Hash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
log.Infof("The suggested pruning point %s is incompatible to this node DAG, so stopping IBD with this"+
|
||||
" peer", msgPruningPointHash.Hash)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
log.Info("Fetching the pruning point UTXO set")
|
||||
succeed, err := flow.fetchMissingUTXOSet(msgPruningPointHash.Hash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !succeed {
|
||||
log.Infof("Couldn't successfully fetch the pruning point UTXO set. Stopping IBD.")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
log.Info("Fetched the new pruning point UTXO set")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(pruningPointHash *externalapi.DomainHash) (succeed bool, err error) {
|
||||
defer func() {
|
||||
err := flow.Domain().Consensus().ClearImportedPruningPointData()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to clear imported pruning point data: %s", err))
|
||||
}
|
||||
}()
|
||||
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointUTXOSetAndBlock(pruningPointHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
block, err := flow.receivePruningPointBlock()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
receivedAll, err := flow.receiveAndInsertPruningPointUTXOSet(pruningPointHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !receivedAll {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = flow.Domain().Consensus().ValidateAndInsertImportedPruningPoint(block)
|
||||
if err != nil {
|
||||
// TODO: Find a better way to deal with finality conflicts.
|
||||
if errors.Is(err, ruleerrors.ErrSuggestedPruningViolatesFinality) {
|
||||
return false, nil
|
||||
}
|
||||
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with pruning point UTXO set")
|
||||
}
|
||||
|
||||
err = flow.OnPruningPointUTXOSetOverride()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) receivePruningPointBlock() (*externalapi.DomainBlock, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "receivePruningPointBlock")
|
||||
defer onEnd()
|
||||
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ibdBlockMessage, ok := message.(*appmessage.MsgIBDBlock)
|
||||
if !ok {
|
||||
return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDBlock, message.Command())
|
||||
}
|
||||
block := appmessage.MsgBlockToDomainBlock(ibdBlockMessage.MsgBlock)
|
||||
|
||||
log.Debugf("Received pruning point block %s", consensushashing.BlockHash(block))
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) receiveAndInsertPruningPointUTXOSet(
|
||||
pruningPointHash *externalapi.DomainHash) (bool, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "receiveAndInsertPruningPointUTXOSet")
|
||||
defer onEnd()
|
||||
|
||||
receivedChunkCount := 0
|
||||
receivedUTXOCount := 0
|
||||
for {
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch message := message.(type) {
|
||||
case *appmessage.MsgPruningPointUTXOSetChunk:
|
||||
receivedUTXOCount += len(message.OutpointAndUTXOEntryPairs)
|
||||
domainOutpointAndUTXOEntryPairs :=
|
||||
appmessage.OutpointAndUTXOEntryPairsToDomainOutpointAndUTXOEntryPairs(message.OutpointAndUTXOEntryPairs)
|
||||
|
||||
err := flow.Domain().Consensus().AppendImportedPruningPointUTXOs(domainOutpointAndUTXOEntryPairs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
receivedChunkCount++
|
||||
if receivedChunkCount%ibdBatchSize == 0 {
|
||||
log.Debugf("Received %d UTXO set chunks so far, totaling in %d UTXOs",
|
||||
receivedChunkCount, receivedUTXOCount)
|
||||
|
||||
requestNextPruningPointUTXOSetChunkMessage := appmessage.NewMsgRequestNextPruningPointUTXOSetChunk()
|
||||
err := flow.outgoingRoute.Enqueue(requestNextPruningPointUTXOSetChunkMessage)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
case *appmessage.MsgDonePruningPointUTXOSetChunks:
|
||||
log.Infof("Finished receiving the UTXO set. Total UTXOs: %d", receivedUTXOCount)
|
||||
return true, nil
|
||||
|
||||
case *appmessage.MsgUnexpectedPruningPoint:
|
||||
log.Infof("Could not receive the next UTXO chunk because the pruning point %s "+
|
||||
"is no longer the pruning point of peer %s", pruningPointHash, flow.peer)
|
||||
return false, nil
|
||||
|
||||
default:
|
||||
return false, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s or %s or %s, got: %s", appmessage.CmdPruningPointUTXOSetChunk,
|
||||
appmessage.CmdDonePruningPointUTXOSetChunks, appmessage.CmdUnexpectedPruningPoint, message.Command(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.DomainHash) error {
|
||||
hashes, err := flow.Domain().Consensus().GetMissingBlockBodyHashes(highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(hashes) == 0 {
|
||||
// Blocks can be inserted inside the DAG during IBD if those were requested before IBD started.
|
||||
// In rare cases, all the IBD blocks might be already inserted by the time we reach this point.
|
||||
// In these cases - GetMissingBlockBodyHashes would return an empty array.
|
||||
log.Debugf("No missing block body hashes found.")
|
||||
return nil
|
||||
}
|
||||
|
||||
for offset := 0; offset < len(hashes); offset += ibdBatchSize {
|
||||
var hashesToRequest []*externalapi.DomainHash
|
||||
if offset+ibdBatchSize < len(hashes) {
|
||||
hashesToRequest = hashes[offset : offset+ibdBatchSize]
|
||||
} else {
|
||||
hashesToRequest = hashes[offset:]
|
||||
}
|
||||
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDBlocks(hashesToRequest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, expectedHash := range hashesToRequest {
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgIBDBlock, ok := message.(*appmessage.MsgIBDBlock)
|
||||
if !ok {
|
||||
return protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDBlock, message.Command())
|
||||
}
|
||||
|
||||
block := appmessage.MsgBlockToDomainBlock(msgIBDBlock.MsgBlock)
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
if !expectedHash.Equal(blockHash) {
|
||||
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
||||
}
|
||||
|
||||
err = flow.banIfBlockIsHeaderOnly(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
log.Debugf("Skipping IBD Block %s as it has already been added to the DAG", blockHash)
|
||||
continue
|
||||
}
|
||||
return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash)
|
||||
}
|
||||
err = flow.OnNewBlock(block, blockInsertionResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dequeueIncomingMessageAndSkipInvs is a convenience method to be used during
|
||||
// IBD. Inv messages are expected to arrive at any given moment, but should be
|
||||
// ignored while we're in IBD
|
||||
func (flow *handleRelayInvsFlow) dequeueIncomingMessageAndSkipInvs(timeout time.Duration) (appmessage.Message, error) {
|
||||
for {
|
||||
message, err := flow.incomingRoute.DequeueWithTimeout(timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := message.(*appmessage.MsgInvRelayBlock); !ok {
|
||||
return message, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// SendVirtualSelectedParentInvContext is the interface for the context needed for the SendVirtualSelectedParentInv flow.
|
||||
type SendVirtualSelectedParentInvContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
// SendVirtualSelectedParentInv sends a peer the selected parent hash of the virtual
|
||||
func SendVirtualSelectedParentInv(context SendVirtualSelectedParentInvContext,
|
||||
outgoingRoute *router.Route, peer *peerpkg.Peer) error {
|
||||
|
||||
virtualSelectedParent, err := context.Domain().Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Sending virtual selected parent hash %s to peer %s", virtualSelectedParent, peer)
|
||||
|
||||
virtualSelectedParentInv := appmessage.NewMsgInvBlock(virtualSelectedParent)
|
||||
return outgoingRoute.Enqueue(virtualSelectedParentInv)
|
||||
}
|
||||
@@ -24,7 +24,6 @@ type HandleHandshakeContext interface {
|
||||
NetAdapter() *netadapter.NetAdapter
|
||||
Domain() domain.Domain
|
||||
AddressManager() *addressmanager.AddressManager
|
||||
StartIBDIfRequired() error
|
||||
AddToPeers(peer *peerpkg.Peer) error
|
||||
HandleError(err error, flowName string, isStopping *uint32, errChan chan<- error)
|
||||
}
|
||||
@@ -83,21 +82,18 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
|
||||
|
||||
err := context.AddToPeers(peer)
|
||||
if err != nil {
|
||||
if errors.As(err, &common.ErrPeerWithSameIDExists) {
|
||||
if errors.Is(err, common.ErrPeerWithSameIDExists) {
|
||||
return nil, protocolerrors.Wrap(false, err, "peer already exists")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if peerAddress != nil {
|
||||
context.AddressManager().AddAddresses(peerAddress)
|
||||
err := context.AddressManager().AddAddresses(peerAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = context.StartIBDIfRequired()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return peer, nil
|
||||
}
|
||||
|
||||
@@ -111,7 +107,7 @@ func handleError(err error, flowName string, isStopping *uint32, errChan chan er
|
||||
return
|
||||
}
|
||||
|
||||
if protocolErr := &(protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
|
||||
if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
|
||||
log.Errorf("Handshake protocol error from %s: %s", flowName, err)
|
||||
if atomic.AddUint32(isStopping, 1) == 1 {
|
||||
errChan <- err
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
var log = logger.RegisterSubSystem("PROT")
|
||||
var spawn = panics.GoroutineWrapperFunc(log)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user