mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-02-26 13:15:47 +00:00
Compare commits
1 Commits
patch5
...
bignet-deb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
917fa11706 |
@@ -1,9 +0,0 @@
|
|||||||
coverage:
|
|
||||||
status:
|
|
||||||
patch: off
|
|
||||||
|
|
||||||
project:
|
|
||||||
default:
|
|
||||||
informational: true
|
|
||||||
ignore:
|
|
||||||
- "**/*.pb.go" # Ignore all auto generated protobuf structures.
|
|
||||||
196
.github/workflows/SetPageFileSize.ps1
vendored
196
.github/workflows/SetPageFileSize.ps1
vendored
@@ -1,196 +0,0 @@
|
|||||||
<#
|
|
||||||
# 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 = 16gb ,
|
|
||||||
[System.UInt64] $MaximumSize = 16gb ,
|
|
||||||
[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)
|
|
||||||
76
.github/workflows/deploy.yaml
vendored
76
.github/workflows/deploy.yaml
vendored
@@ -1,76 +0,0 @@
|
|||||||
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, ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- name: Fix CRLF on Windows
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: git config --global core.autocrlf false
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# Increase the pagefile size on Windows to aviod running out of memory
|
|
||||||
- name: Increase pagefile size on Windows
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: powershell -command .github\workflows\SetPageFileSize.ps1
|
|
||||||
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
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/ . ./cmd/...
|
|
||||||
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/ . ./cmd/...
|
|
||||||
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/ . ./cmd/...
|
|
||||||
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/race.yaml
vendored
49
.github/workflows/race.yaml
vendored
@@ -1,49 +0,0 @@
|
|||||||
name: Race
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
race_test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
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: Setup Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: 1.16
|
|
||||||
|
|
||||||
- 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 -timeout 20m -race ./...
|
|
||||||
98
.github/workflows/tests.yaml
vendored
98
.github/workflows/tests.yaml
vendored
@@ -1,98 +0,0 @@
|
|||||||
name: Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
# edtited - because base branch can be modified
|
|
||||||
# synchronize - update commits on PR
|
|
||||||
types: [opened, synchronize, edited]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ ubuntu-latest, macos-latest ]
|
|
||||||
name: Tests, ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Fix CRLF on Windows
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: git config --global core.autocrlf false
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# Increase the pagefile size on Windows to aviod running out of memory
|
|
||||||
- name: Increase pagefile size on Windows
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: powershell -command .github\workflows\SetPageFileSize.ps1
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
stability-test-fast:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Fast stability tests, ${{ github.head_ref }}
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: 1.16
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install kaspad
|
|
||||||
run: go install ./...
|
|
||||||
|
|
||||||
- name: Install golint
|
|
||||||
run: go get -u golang.org/x/lint/golint
|
|
||||||
|
|
||||||
- name: Run fast stability tests
|
|
||||||
working-directory: stability-tests
|
|
||||||
run: ./install_and_test.sh
|
|
||||||
|
|
||||||
|
|
||||||
coverage:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Produce code coverage
|
|
||||||
steps:
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
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,21 +13,6 @@ kaspad.db
|
|||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
*.so
|
*.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
|
# Folders
|
||||||
_obj
|
_obj
|
||||||
@@ -46,7 +31,8 @@ _cgo_export.*
|
|||||||
|
|
||||||
_testmain.go
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
# 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 `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.
|
|
||||||
34
README.md
34
README.md
@@ -1,21 +1,20 @@
|
|||||||
|
|
||||||
Kaspad
|
Kaspad
|
||||||
====
|
====
|
||||||
|
Warning: This is pre-alpha software. There's no guarantee anything works.
|
||||||
|
====
|
||||||
|
|
||||||
[](https://choosealicense.com/licenses/isc/)
|
[](https://choosealicense.com/licenses/isc/)
|
||||||
[](http://godoc.org/github.com/kaspanet/kaspad)
|
[](http://godoc.org/github.com/kaspanet/kaspad)
|
||||||
|
|
||||||
Kaspad is the reference full node Kaspa implementation written in Go (golang).
|
Kaspad is the reference full node Kaspa implementation written in Go (golang).
|
||||||
|
|
||||||
This project is currently under active development and is in Beta 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
|
## Requirements
|
||||||
|
|
||||||
Go 1.16 or later.
|
Latest version of [Go](http://golang.org) (currently 1.13).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -28,17 +27,23 @@ Go 1.16 or later.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go version
|
$ 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:
|
- Run the following commands to obtain and install kaspad including all dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/kaspanet/kaspad
|
$ git clone https://github.com/kaspanet/kaspad $GOPATH/src/github.com/kaspanet/kaspad
|
||||||
$ cd kaspad
|
$ cd $GOPATH/src/github.com/kaspanet/kaspad
|
||||||
$ go install . ./cmd/...
|
$ go install . ./cmd/...
|
||||||
```
|
```
|
||||||
|
|
||||||
- Kaspad (and utilities) should now be installed in `$(go env GOPATH)/bin`. If you did
|
- Kaspad (and utilities) should now be installed in `$GOPATH/bin`. If you did
|
||||||
not already add the bin directory to your system path during Go installation,
|
not already add the bin directory to your system path during Go installation,
|
||||||
you are encouraged to do so now.
|
you are encouraged to do so now.
|
||||||
|
|
||||||
@@ -48,24 +53,25 @@ $ go install . ./cmd/...
|
|||||||
Kaspad has several configuration options available to tweak how it runs, but all
|
Kaspad has several configuration options available to tweak how it runs, but all
|
||||||
of the basic operations work with zero configuration.
|
of the basic operations work with zero configuration.
|
||||||
|
|
||||||
|
#### Linux/BSD/POSIX/Source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ kaspad
|
$ ./kaspad
|
||||||
```
|
```
|
||||||
|
|
||||||
## Discord
|
## Discord
|
||||||
Join our discord server using the following link: https://discord.gg/YNYnNN5Pf2
|
Join our discord server using the following link: https://discord.gg/WmGhhzk
|
||||||
|
|
||||||
## Issue Tracker
|
## Issue Tracker
|
||||||
|
|
||||||
The [integrated github issue tracker](https://github.com/kaspanet/kaspad/issues)
|
The [integrated github issue tracker](https://github.com/kaspanet/kaspad/issues)
|
||||||
is used for this project.
|
is used for this project.
|
||||||
|
|
||||||
Issue priorities may be seen at https://github.com/orgs/kaspanet/projects/4
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
|
The documentation is a work-in-progress. It is located in the [docs](https://github.com/kaspanet/kaspad/tree/master/docs) folder.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Kaspad is licensed under the copyfree [ISC License](https://choosealicense.com/licenses/isc/).
|
Kaspad is licensed under the copyfree [ISC License](https://choosealicense.com/licenses/isc/).
|
||||||
|
|
||||||
|
|||||||
345
app/app.go
345
app/app.go
@@ -2,188 +2,247 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"sync/atomic"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/app/protocol"
|
||||||
|
"github.com/kaspanet/kaspad/domain/blockdag"
|
||||||
|
"github.com/kaspanet/kaspad/domain/blockdag/indexers"
|
||||||
|
"github.com/kaspanet/kaspad/domain/mempool"
|
||||||
|
"github.com/kaspanet/kaspad/domain/mining"
|
||||||
|
"github.com/kaspanet/kaspad/domain/txscript"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
"github.com/kaspanet/kaspad/infrastructure/network/dnsseed"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/os/execenv"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/os/limits"
|
"github.com/kaspanet/kaspad/infrastructure/network/rpc"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/os/signal"
|
"github.com/kaspanet/kaspad/infrastructure/os/signal"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/os/winservice"
|
"github.com/kaspanet/kaspad/util"
|
||||||
"github.com/kaspanet/kaspad/util/panics"
|
"github.com/kaspanet/kaspad/util/panics"
|
||||||
"github.com/kaspanet/kaspad/util/profiling"
|
|
||||||
"github.com/kaspanet/kaspad/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// App is a wrapper for all the kaspad services
|
||||||
leveldbCacheSizeMiB = 256
|
type App struct {
|
||||||
defaultDataDirname = "datadir2"
|
cfg *config.Config
|
||||||
)
|
rpcServer *rpc.Server
|
||||||
|
addressManager *addressmanager.AddressManager
|
||||||
|
protocolManager *protocol.Manager
|
||||||
|
connectionManager *connmanager.ConnectionManager
|
||||||
|
netAdapter *netadapter.NetAdapter
|
||||||
|
|
||||||
var desiredLimits = &limits.DesiredLimits{
|
started, shutdown int32
|
||||||
FileLimitWant: 2048,
|
|
||||||
FileLimitMin: 1024,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var serviceDescription = &winservice.ServiceDescription{
|
// Start launches all the kaspad services.
|
||||||
Name: "kaspadsvc",
|
func (a *App) Start() {
|
||||||
DisplayName: "Kaspad Service",
|
// Already started?
|
||||||
Description: "Downloads and stays synchronized with the Kaspa blockDAG and " +
|
if atomic.AddInt32(&a.started, 1) != 1 {
|
||||||
"provides DAG services to applications.",
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type kaspadApp struct {
|
log.Trace("Starting kaspad")
|
||||||
cfg *config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartApp starts the kaspad app, and blocks until it finishes running
|
err := a.protocolManager.Start()
|
||||||
func StartApp() error {
|
|
||||||
execenv.Initialize(desiredLimits)
|
|
||||||
|
|
||||||
// Load configuration and parse command line. This function also
|
|
||||||
// initializes logging and configures it accordingly.
|
|
||||||
cfg, err := config.LoadConfig()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
panics.Exit(log, fmt.Sprintf("Error starting the p2p protocol: %+v", err))
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer logger.BackendLog.Close()
|
|
||||||
defer panics.HandlePanic(log, "MAIN", nil)
|
|
||||||
|
|
||||||
app := &kaspadApp{cfg: cfg}
|
|
||||||
|
|
||||||
// Call serviceMain on Windows to handle running as a service. When
|
|
||||||
// the return isService flag is true, exit now since we ran as a
|
|
||||||
// service. Otherwise, just fall through to normal operation.
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
isService, err := winservice.WinServiceMain(app.main, serviceDescription, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if isService {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.main(nil)
|
a.maybeSeedFromDNS()
|
||||||
|
|
||||||
|
a.connectionManager.Start()
|
||||||
|
|
||||||
|
if !a.cfg.DisableRPC {
|
||||||
|
a.rpcServer.Start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
// Stop gracefully shuts down all the kaspad services.
|
||||||
// Get a channel that will be closed when a shutdown signal has been
|
func (a *App) Stop() {
|
||||||
// triggered either from an OS signal such as SIGINT (Ctrl+C) or from
|
// Make sure this only happens once.
|
||||||
// another subsystem such as the RPC server.
|
if atomic.AddInt32(&a.shutdown, 1) != 1 {
|
||||||
interrupt := signal.InterruptListener()
|
log.Infof("Kaspad is already in the process of shutting down")
|
||||||
defer log.Info("Shutdown complete")
|
return
|
||||||
|
|
||||||
// Show version at startup.
|
|
||||||
log.Infof("Version %s", version.Version())
|
|
||||||
|
|
||||||
// Enable http profiling server if requested.
|
|
||||||
if app.cfg.Profile != "" {
|
|
||||||
profiling.Start(app.cfg.Profile, log)
|
|
||||||
}
|
|
||||||
profiling.TrackHeap(app.cfg.AppDir, log)
|
|
||||||
|
|
||||||
// Return now if an interrupt signal was triggered.
|
|
||||||
if signal.InterruptRequested(interrupt) {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.cfg.ResetDatabase {
|
log.Warnf("Kaspad shutting down")
|
||||||
err := removeDatabase(app.cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the database
|
a.connectionManager.Stop()
|
||||||
databaseContext, err := openDB(app.cfg)
|
|
||||||
|
err := a.protocolManager.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Loading database failed: %+v", err)
|
log.Errorf("Error stopping the p2p protocol: %+v", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
// Shutdown the RPC server if it's not disabled.
|
||||||
log.Infof("Gracefully shutting down the database...")
|
if !a.cfg.DisableRPC {
|
||||||
err := databaseContext.Close()
|
err := a.rpcServer.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to close the database: %s", err)
|
log.Errorf("Error stopping rpcServer: %+v", err)
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
// Return now if an interrupt signal was triggered.
|
|
||||||
if signal.InterruptRequested(interrupt) {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create componentManager and start it.
|
err = a.addressManager.Stop()
|
||||||
componentManager, err := NewComponentManager(app.cfg, databaseContext, interrupt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to start kaspad: %+v", err)
|
log.Errorf("Error stopping address manager: %s", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
return
|
||||||
log.Infof("Gracefully shutting down kaspad...")
|
|
||||||
|
|
||||||
shutdownDone := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
componentManager.Stop()
|
|
||||||
shutdownDone <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
const shutdownTimeout = 2 * time.Minute
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-shutdownDone:
|
|
||||||
case <-time.After(shutdownTimeout):
|
|
||||||
log.Criticalf("Graceful shutdown timed out %s. Terminating...", shutdownTimeout)
|
|
||||||
}
|
|
||||||
log.Infof("Kaspad shutdown complete")
|
|
||||||
}()
|
|
||||||
|
|
||||||
componentManager.Start()
|
|
||||||
|
|
||||||
if startedChan != nil {
|
|
||||||
startedChan <- struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until the interrupt signal is received from an OS signal or
|
|
||||||
// shutdown is requested through one of the subsystems such as the RPC
|
|
||||||
// server.
|
|
||||||
<-interrupt
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dbPath returns the path to the block database given a database type.
|
// New returns a new App instance configured to listen on addr for the
|
||||||
func databasePath(cfg *config.Config) string {
|
// kaspa network type specified by dagParams. Use start to begin accepting
|
||||||
return filepath.Join(cfg.AppDir, defaultDataDirname)
|
// connections from peers.
|
||||||
}
|
func New(cfg *config.Config, databaseContext *dbaccess.DatabaseContext, interrupt <-chan struct{}) (*App, error) {
|
||||||
|
indexManager, acceptanceIndex := setupIndexes(cfg)
|
||||||
|
|
||||||
func removeDatabase(cfg *config.Config) error {
|
sigCache := txscript.NewSigCache(cfg.SigCacheMaxSize)
|
||||||
dbPath := databasePath(cfg)
|
|
||||||
return os.RemoveAll(dbPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openDB(cfg *config.Config) (database.Database, error) {
|
// Create a new block DAG instance with the appropriate configuration.
|
||||||
dbPath := databasePath(cfg)
|
dag, err := setupDAG(cfg, databaseContext, interrupt, sigCache, indexManager)
|
||||||
|
|
||||||
err := checkDatabaseVersion(dbPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Loading database from '%s'", dbPath)
|
txMempool := setupMempool(cfg, dag, sigCache)
|
||||||
db, err := ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
|
||||||
|
netAdapter, err := netadapter.NewNetAdapter(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addressManager, err := addressmanager.New(cfg, databaseContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
protocolManager, err := protocol.NewManager(cfg, dag, netAdapter, addressManager, txMempool, connectionManager)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rpcServer, err := setupRPC(
|
||||||
|
cfg, dag, txMempool, sigCache, acceptanceIndex, connectionManager, addressManager, protocolManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db, nil
|
return &App{
|
||||||
|
cfg: cfg,
|
||||||
|
rpcServer: rpcServer,
|
||||||
|
protocolManager: protocolManager,
|
||||||
|
connectionManager: connectionManager,
|
||||||
|
netAdapter: netAdapter,
|
||||||
|
addressManager: addressManager,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) maybeSeedFromDNS() {
|
||||||
|
if !a.cfg.DisableDNSSeed {
|
||||||
|
dnsseed.SeedFromDNS(a.cfg.NetParams(), a.cfg.DNSSeed, appmessage.SFNodeNetwork, 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, addresses[0], nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func setupDAG(cfg *config.Config, databaseContext *dbaccess.DatabaseContext, interrupt <-chan struct{},
|
||||||
|
sigCache *txscript.SigCache, indexManager blockdag.IndexManager) (*blockdag.BlockDAG, error) {
|
||||||
|
|
||||||
|
dag, err := blockdag.New(&blockdag.Config{
|
||||||
|
Interrupt: interrupt,
|
||||||
|
DatabaseContext: databaseContext,
|
||||||
|
DAGParams: cfg.NetParams(),
|
||||||
|
TimeSource: blockdag.NewTimeSource(),
|
||||||
|
SigCache: sigCache,
|
||||||
|
IndexManager: indexManager,
|
||||||
|
SubnetworkID: cfg.SubnetworkID,
|
||||||
|
})
|
||||||
|
return dag, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupIndexes(cfg *config.Config) (blockdag.IndexManager, *indexers.AcceptanceIndex) {
|
||||||
|
// Create indexes if needed.
|
||||||
|
var indexes []indexers.Indexer
|
||||||
|
var acceptanceIndex *indexers.AcceptanceIndex
|
||||||
|
if cfg.AcceptanceIndex {
|
||||||
|
log.Info("acceptance index is enabled")
|
||||||
|
acceptanceIndex = indexers.NewAcceptanceIndex()
|
||||||
|
indexes = append(indexes, acceptanceIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an index manager if any of the optional indexes are enabled.
|
||||||
|
if len(indexes) < 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
indexManager := indexers.NewManager(indexes)
|
||||||
|
return indexManager, acceptanceIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupMempool(cfg *config.Config, dag *blockdag.BlockDAG, sigCache *txscript.SigCache) *mempool.TxPool {
|
||||||
|
mempoolConfig := mempool.Config{
|
||||||
|
Policy: mempool.Policy{
|
||||||
|
AcceptNonStd: cfg.RelayNonStd,
|
||||||
|
MaxOrphanTxs: cfg.MaxOrphanTxs,
|
||||||
|
MaxOrphanTxSize: config.DefaultMaxOrphanTxSize,
|
||||||
|
MinRelayTxFee: cfg.MinRelayTxFee,
|
||||||
|
MaxTxVersion: 1,
|
||||||
|
},
|
||||||
|
CalcSequenceLockNoLock: func(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) {
|
||||||
|
return dag.CalcSequenceLockNoLock(tx, utxoSet)
|
||||||
|
},
|
||||||
|
SigCache: sigCache,
|
||||||
|
DAG: dag,
|
||||||
|
}
|
||||||
|
|
||||||
|
return mempool.New(&mempoolConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRPC(cfg *config.Config,
|
||||||
|
dag *blockdag.BlockDAG,
|
||||||
|
txMempool *mempool.TxPool,
|
||||||
|
sigCache *txscript.SigCache,
|
||||||
|
acceptanceIndex *indexers.AcceptanceIndex,
|
||||||
|
connectionManager *connmanager.ConnectionManager,
|
||||||
|
addressManager *addressmanager.AddressManager,
|
||||||
|
protocolManager *protocol.Manager) (*rpc.Server, error) {
|
||||||
|
|
||||||
|
if !cfg.DisableRPC {
|
||||||
|
policy := mining.Policy{
|
||||||
|
BlockMaxMass: cfg.BlockMaxMass,
|
||||||
|
}
|
||||||
|
blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, txMempool, dag, sigCache)
|
||||||
|
|
||||||
|
rpcServer, err := rpc.NewRPCServer(cfg, dag, txMempool, acceptanceIndex, blockTemplateGenerator,
|
||||||
|
connectionManager, addressManager, protocolManager)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal process shutdown when the RPC server requests it.
|
||||||
|
spawn("setupRPC-handleShutdownRequest", func() {
|
||||||
|
<-rpcServer.RequestedProcessShutdown()
|
||||||
|
signal.ShutdownRequestChannel <- struct{}{}
|
||||||
|
})
|
||||||
|
|
||||||
|
return rpcServer, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2PNodeID returns the network ID associated with this App
|
||||||
|
func (a *App) P2PNodeID() *id.ID {
|
||||||
|
return a.netAdapter.ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressManager returns the AddressManager associated with this App
|
||||||
|
func (a *App) AddressManager() *addressmanager.AddressManager {
|
||||||
|
return a.addressManager
|
||||||
}
|
}
|
||||||
|
|||||||
431
app/appmessage/bench_test.go
Normal file
431
app/appmessage/bench_test.go
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
// 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"
|
||||||
|
"compress/bzip2"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
|
||||||
|
// the main network and test network.
|
||||||
|
var genesisCoinbaseTxIns = []*TxIn{
|
||||||
|
{
|
||||||
|
PreviousOutpoint: Outpoint{
|
||||||
|
TxID: daghash.TxID{},
|
||||||
|
Index: 0xffffffff,
|
||||||
|
},
|
||||||
|
SignatureScript: []byte{
|
||||||
|
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, /* |.......E| */
|
||||||
|
0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, /* |The Time| */
|
||||||
|
0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, /* |s 03/Jan| */
|
||||||
|
0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, /* |/2009 Ch| */
|
||||||
|
0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x72, /* |ancellor| */
|
||||||
|
0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, 0x6e, /* | on brin| */
|
||||||
|
0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x63, /* |k of sec|*/
|
||||||
|
0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6c, /* |ond bail| */
|
||||||
|
0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, /* |out for |*/
|
||||||
|
0x62, 0x61, 0x6e, 0x6b, 0x73, /* |banks| */
|
||||||
|
},
|
||||||
|
Sequence: math.MaxUint64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var genesisCoinbaseTxOuts = []*TxOut{
|
||||||
|
{
|
||||||
|
Value: 0x12a05f200,
|
||||||
|
ScriptPubKey: []byte{
|
||||||
|
0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */
|
||||||
|
0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */
|
||||||
|
0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */
|
||||||
|
0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */
|
||||||
|
0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */
|
||||||
|
0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */
|
||||||
|
0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */
|
||||||
|
0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */
|
||||||
|
0x1d, 0x5f, 0xac, /* |._.| */
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var genesisCoinbaseTx = NewNativeMsgTx(1, genesisCoinbaseTxIns, genesisCoinbaseTxOuts)
|
||||||
|
|
||||||
|
// BenchmarkWriteVarInt1 performs a benchmark on how long it takes to write
|
||||||
|
// a single byte variable length integer.
|
||||||
|
func BenchmarkWriteVarInt1(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
WriteVarInt(ioutil.Discard, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteVarInt3 performs a benchmark on how long it takes to write
|
||||||
|
// a three byte variable length integer.
|
||||||
|
func BenchmarkWriteVarInt3(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
WriteVarInt(ioutil.Discard, 65535)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteVarInt5 performs a benchmark on how long it takes to write
|
||||||
|
// a five byte variable length integer.
|
||||||
|
func BenchmarkWriteVarInt5(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
WriteVarInt(ioutil.Discard, 4294967295)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteVarInt9 performs a benchmark on how long it takes to write
|
||||||
|
// a nine byte variable length integer.
|
||||||
|
func BenchmarkWriteVarInt9(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
WriteVarInt(ioutil.Discard, 18446744073709551615)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadVarInt1 performs a benchmark on how long it takes to read
|
||||||
|
// a single byte variable length integer.
|
||||||
|
func BenchmarkReadVarInt1(b *testing.B) {
|
||||||
|
buf := []byte{0x01}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
ReadVarInt(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadVarInt3 performs a benchmark on how long it takes to read
|
||||||
|
// a three byte variable length integer.
|
||||||
|
func BenchmarkReadVarInt3(b *testing.B) {
|
||||||
|
buf := []byte{0x0fd, 0xff, 0xff}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
ReadVarInt(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadVarInt5 performs a benchmark on how long it takes to read
|
||||||
|
// a five byte variable length integer.
|
||||||
|
func BenchmarkReadVarInt5(b *testing.B) {
|
||||||
|
buf := []byte{0xfe, 0xff, 0xff, 0xff, 0xff}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
ReadVarInt(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadVarInt9 performs a benchmark on how long it takes to read
|
||||||
|
// a nine byte variable length integer.
|
||||||
|
func BenchmarkReadVarInt9(b *testing.B) {
|
||||||
|
buf := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
ReadVarInt(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadVarStr4 performs a benchmark on how long it takes to read a
|
||||||
|
// four byte variable length string.
|
||||||
|
func BenchmarkReadVarStr4(b *testing.B) {
|
||||||
|
buf := []byte{0x04, 't', 'e', 's', 't'}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
ReadVarString(r, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadVarStr10 performs a benchmark on how long it takes to read a
|
||||||
|
// ten byte variable length string.
|
||||||
|
func BenchmarkReadVarStr10(b *testing.B) {
|
||||||
|
buf := []byte{0x0a, 't', 'e', 's', 't', '0', '1', '2', '3', '4', '5'}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
ReadVarString(r, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteVarStr4 performs a benchmark on how long it takes to write a
|
||||||
|
// four byte variable length string.
|
||||||
|
func BenchmarkWriteVarStr4(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
WriteVarString(ioutil.Discard, "test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteVarStr10 performs a benchmark on how long it takes to write a
|
||||||
|
// ten byte variable length string.
|
||||||
|
func BenchmarkWriteVarStr10(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
WriteVarString(ioutil.Discard, "test012345")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadOutpoint performs a benchmark on how long it takes to read a
|
||||||
|
// transaction outpoint.
|
||||||
|
func BenchmarkReadOutpoint(b *testing.B) {
|
||||||
|
buf := []byte{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
|
||||||
|
0xff, 0xff, 0xff, 0xff, // Previous output index
|
||||||
|
}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
var op Outpoint
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
readOutpoint(r, 0, 0, &op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteOutpoint performs a benchmark on how long it takes to write a
|
||||||
|
// transaction outpoint.
|
||||||
|
func BenchmarkWriteOutpoint(b *testing.B) {
|
||||||
|
op := &Outpoint{
|
||||||
|
TxID: daghash.TxID{},
|
||||||
|
Index: 0,
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
writeOutpoint(ioutil.Discard, 0, 0, op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadTxOut performs a benchmark on how long it takes to read a
|
||||||
|
// transaction output.
|
||||||
|
func BenchmarkReadTxOut(b *testing.B) {
|
||||||
|
buf := []byte{
|
||||||
|
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
|
||||||
|
0x43, // Varint for length of scriptPubKey
|
||||||
|
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
|
||||||
|
}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
var txOut TxOut
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
readTxOut(r, 0, 0, &txOut)
|
||||||
|
scriptPool.Return(txOut.ScriptPubKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteTxOut performs a benchmark on how long it takes to write
|
||||||
|
// a transaction output.
|
||||||
|
func BenchmarkWriteTxOut(b *testing.B) {
|
||||||
|
txOut := blockOne.Transactions[0].TxOut[0]
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
WriteTxOut(ioutil.Discard, 0, 0, txOut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadTxIn performs a benchmark on how long it takes to read a
|
||||||
|
// transaction input.
|
||||||
|
func BenchmarkReadTxIn(b *testing.B) {
|
||||||
|
buf := []byte{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
|
||||||
|
0xff, 0xff, 0xff, 0xff, // Previous output index
|
||||||
|
0x07, // Varint for length of signature script
|
||||||
|
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Sequence
|
||||||
|
}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
var txIn TxIn
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
readTxIn(r, 0, 0, &txIn)
|
||||||
|
scriptPool.Return(txIn.SignatureScript)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteTxIn performs a benchmark on how long it takes to write
|
||||||
|
// a transaction input.
|
||||||
|
func BenchmarkWriteTxIn(b *testing.B) {
|
||||||
|
txIn := blockOne.Transactions[0].TxIn[0]
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
writeTxIn(ioutil.Discard, 0, 0, txIn, txEncodingFull)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDeserializeTx performs a benchmark on how long it takes to
|
||||||
|
// deserialize a small transaction.
|
||||||
|
func BenchmarkDeserializeTxSmall(b *testing.B) {
|
||||||
|
buf := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version
|
||||||
|
0x01, // Varint for number of input transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // // Previous output hash
|
||||||
|
0xff, 0xff, 0xff, 0xff, // Prevous output index
|
||||||
|
0x07, // Varint for length of signature script
|
||||||
|
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Sequence
|
||||||
|
0x01, // Varint for number of output transactions
|
||||||
|
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
|
||||||
|
0x43, // Varint for length of scriptPubKey
|
||||||
|
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
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
||||||
|
}
|
||||||
|
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
var tx MsgTx
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
tx.Deserialize(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDeserializeTxLarge performs a benchmark on how long it takes to
|
||||||
|
// deserialize a very large transaction.
|
||||||
|
func BenchmarkDeserializeTxLarge(b *testing.B) {
|
||||||
|
fi, err := os.Open("testdata/megatx.bin.bz2")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Failed to read transaction data: %v", err)
|
||||||
|
}
|
||||||
|
defer fi.Close()
|
||||||
|
buf, err := ioutil.ReadAll(bzip2.NewReader(fi))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Failed to read transaction data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
var tx MsgTx
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
tx.Deserialize(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkSerializeTx performs a benchmark on how long it takes to serialize
|
||||||
|
// a transaction.
|
||||||
|
func BenchmarkSerializeTx(b *testing.B) {
|
||||||
|
tx := blockOne.Transactions[0]
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tx.Serialize(ioutil.Discard)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkReadBlockHeader performs a benchmark on how long it takes to
|
||||||
|
// deserialize a block header.
|
||||||
|
func BenchmarkReadBlockHeader(b *testing.B) {
|
||||||
|
buf := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||||
|
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
|
||||||
|
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
||||||
|
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
||||||
|
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock
|
||||||
|
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
|
||||||
|
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
|
||||||
|
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
|
||||||
|
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, // MerkleRoot
|
||||||
|
0x29, 0xab, 0x5f, 0x49, 0x00, 0x00, 0x00, 0x00, // Timestamp
|
||||||
|
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||||
|
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Fake Nonce
|
||||||
|
0x00, // TxnCount Varint
|
||||||
|
}
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
var header BlockHeader
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
readBlockHeader(r, 0, &header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkWriteBlockHeader performs a benchmark on how long it takes to
|
||||||
|
// serialize a block header.
|
||||||
|
func BenchmarkWriteBlockHeader(b *testing.B) {
|
||||||
|
header := blockOne.Header
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
writeBlockHeader(ioutil.Discard, 0, &header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkTxHash performs a benchmark on how long it takes to hash a
|
||||||
|
// transaction.
|
||||||
|
func BenchmarkTxHash(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
genesisCoinbaseTx.TxHash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDoubleHashB performs a benchmark on how long it takes to perform a
|
||||||
|
// double hash returning a byte slice.
|
||||||
|
func BenchmarkDoubleHashB(b *testing.B) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
|
||||||
|
b.Errorf("Serialize: unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txBytes := buf.Bytes()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = daghash.DoubleHashB(txBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDoubleHashH performs a benchmark on how long it takes to perform
|
||||||
|
// a double hash returning a daghash.Hash.
|
||||||
|
func BenchmarkDoubleHashH(b *testing.B) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
|
||||||
|
b.Errorf("Serialize: unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txBytes := buf.Bytes()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = daghash.DoubleHashH(txBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDoubleHashWriter performs a benchmark on how long it takes to perform
|
||||||
|
// a double hash via the writer returning a daghash.Hash.
|
||||||
|
func BenchmarkDoubleHashWriter(b *testing.B) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := genesisCoinbaseTx.Serialize(&buf)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Serialize: unexpected error: %+v", err)
|
||||||
|
}
|
||||||
|
txBytes := buf.Bytes()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
writer := daghash.NewDoubleHashWriter()
|
||||||
|
_, _ = writer.Write(txBytes)
|
||||||
|
writer.Finalize()
|
||||||
|
}
|
||||||
|
}
|
||||||
190
app/appmessage/blockheader.go
Normal file
190
app/appmessage/blockheader.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseBlockHeaderPayload is the base number of bytes a block header can be,
|
||||||
|
// not including the list of parent block headers.
|
||||||
|
// Version 4 bytes + Timestamp 8 bytes + Bits 4 bytes + Nonce 8 bytes +
|
||||||
|
// + NumParentBlocks 1 byte + HashMerkleRoot hash +
|
||||||
|
// + AcceptedIDMerkleRoot hash + UTXOCommitment hash.
|
||||||
|
// To get total size of block header len(ParentHashes) * daghash.HashSize should be
|
||||||
|
// added to this value
|
||||||
|
const BaseBlockHeaderPayload = 25 + 3*(daghash.HashSize)
|
||||||
|
|
||||||
|
// MaxNumParentBlocks is the maximum number of parent blocks a block can reference.
|
||||||
|
// Currently set to 255 as the maximum number NumParentBlocks can be due to it being a byte
|
||||||
|
const MaxNumParentBlocks = 255
|
||||||
|
|
||||||
|
// MaxBlockHeaderPayload is the maximum number of bytes a block header can be.
|
||||||
|
// BaseBlockHeaderPayload + up to MaxNumParentBlocks hashes of parent blocks
|
||||||
|
const MaxBlockHeaderPayload = BaseBlockHeaderPayload + (MaxNumParentBlocks * daghash.HashSize)
|
||||||
|
|
||||||
|
// BlockHeader defines information about a block and is used in the kaspa
|
||||||
|
// block (MsgBlock) and headers (MsgHeader) messages.
|
||||||
|
type BlockHeader struct {
|
||||||
|
// Version of the block. This is not the same as the protocol version.
|
||||||
|
Version int32
|
||||||
|
|
||||||
|
// Hashes of the parent block headers in the blockDAG.
|
||||||
|
ParentHashes []*daghash.Hash
|
||||||
|
|
||||||
|
// HashMerkleRoot is the merkle tree reference to hash of all transactions for the block.
|
||||||
|
HashMerkleRoot *daghash.Hash
|
||||||
|
|
||||||
|
// AcceptedIDMerkleRoot is merkle tree reference to hash all transactions
|
||||||
|
// accepted form the block.Blues
|
||||||
|
AcceptedIDMerkleRoot *daghash.Hash
|
||||||
|
|
||||||
|
// UTXOCommitment is an ECMH UTXO commitment to the block UTXO.
|
||||||
|
UTXOCommitment *daghash.Hash
|
||||||
|
|
||||||
|
// Time the block was created.
|
||||||
|
Timestamp mstime.Time
|
||||||
|
|
||||||
|
// Difficulty target for the block.
|
||||||
|
Bits uint32
|
||||||
|
|
||||||
|
// Nonce used to generate the block.
|
||||||
|
Nonce uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumParentBlocks return the number of entries in ParentHashes
|
||||||
|
func (h *BlockHeader) NumParentBlocks() byte {
|
||||||
|
return byte(len(h.ParentHashes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockHash computes the block identifier hash for the given block header.
|
||||||
|
func (h *BlockHeader) BlockHash() *daghash.Hash {
|
||||||
|
// Encode the header and double sha256 everything prior to the number of
|
||||||
|
// transactions.
|
||||||
|
writer := daghash.NewDoubleHashWriter()
|
||||||
|
err := writeBlockHeader(writer, 0, h)
|
||||||
|
if err != nil {
|
||||||
|
// It seems like this could only happen if the writer returned an error.
|
||||||
|
// and this writer should never return an error (no allocations or possible failures)
|
||||||
|
// the only non-writer error path here is unknown types in `WriteElement`
|
||||||
|
panic(fmt.Sprintf("BlockHash() failed. this should never fail unless BlockHeader was changed. err: %+v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
res := writer.Finalize()
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGenesis returns true iff this block is a genesis block
|
||||||
|
func (h *BlockHeader) IsGenesis() bool {
|
||||||
|
return h.NumParentBlocks() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Deserialize for decoding block headers stored to disk, such as in a
|
||||||
|
// database, as opposed to decoding block headers from the appmessage.
|
||||||
|
func (h *BlockHeader) KaspaDecode(r io.Reader, pver uint32) error {
|
||||||
|
return readBlockHeader(r, pver, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Serialize for encoding block headers to be stored to disk, such as in a
|
||||||
|
// database, as opposed to encoding block headers for the appmessage.
|
||||||
|
func (h *BlockHeader) KaspaEncode(w io.Writer, pver uint32) error {
|
||||||
|
return writeBlockHeader(w, pver, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes a block header from r into the receiver using a format
|
||||||
|
// that is suitable for long-term storage such as a database while respecting
|
||||||
|
// the Version field.
|
||||||
|
func (h *BlockHeader) Deserialize(r io.Reader) error {
|
||||||
|
// At the current time, there is no difference between the appmessage encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of readBlockHeader.
|
||||||
|
return readBlockHeader(r, 0, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize encodes a block header from r into the receiver using a format
|
||||||
|
// that is suitable for long-term storage such as a database while respecting
|
||||||
|
// the Version field.
|
||||||
|
func (h *BlockHeader) Serialize(w io.Writer) error {
|
||||||
|
// At the current time, there is no difference between the appmessage encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of writeBlockHeader.
|
||||||
|
return writeBlockHeader(w, 0, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeSize returns the number of bytes it would take to serialize the
|
||||||
|
// block header.
|
||||||
|
func (h *BlockHeader) SerializeSize() int {
|
||||||
|
return BaseBlockHeaderPayload + int(h.NumParentBlocks())*daghash.HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockHeader returns a new BlockHeader 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 []*daghash.Hash, hashMerkleRoot *daghash.Hash,
|
||||||
|
acceptedIDMerkleRoot *daghash.Hash, utxoCommitment *daghash.Hash, bits uint32, nonce uint64) *BlockHeader {
|
||||||
|
|
||||||
|
// Limit the timestamp to one millisecond precision since the protocol
|
||||||
|
// doesn't support better.
|
||||||
|
return &BlockHeader{
|
||||||
|
Version: version,
|
||||||
|
ParentHashes: parentHashes,
|
||||||
|
HashMerkleRoot: hashMerkleRoot,
|
||||||
|
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
|
||||||
|
UTXOCommitment: utxoCommitment,
|
||||||
|
Timestamp: mstime.Now(),
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readBlockHeader reads a kaspa block header from r. See Deserialize for
|
||||||
|
// decoding block headers stored to disk, such as in a database, as opposed to
|
||||||
|
// decoding from the appmessage.
|
||||||
|
func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error {
|
||||||
|
var numParentBlocks byte
|
||||||
|
err := readElements(r, &bh.Version, &numParentBlocks)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bh.ParentHashes = make([]*daghash.Hash, numParentBlocks)
|
||||||
|
for i := byte(0); i < numParentBlocks; i++ {
|
||||||
|
hash := &daghash.Hash{}
|
||||||
|
err := ReadElement(r, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bh.ParentHashes[i] = hash
|
||||||
|
}
|
||||||
|
bh.HashMerkleRoot = &daghash.Hash{}
|
||||||
|
bh.AcceptedIDMerkleRoot = &daghash.Hash{}
|
||||||
|
bh.UTXOCommitment = &daghash.Hash{}
|
||||||
|
return readElements(r, bh.HashMerkleRoot, bh.AcceptedIDMerkleRoot, bh.UTXOCommitment,
|
||||||
|
(*int64Time)(&bh.Timestamp), &bh.Bits, &bh.Nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBlockHeader writes a kaspa block header to w. See Serialize for
|
||||||
|
// encoding block headers to be stored to disk, such as in a database, as
|
||||||
|
// opposed to encoding for the appmessage.
|
||||||
|
func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error {
|
||||||
|
timestamp := bh.Timestamp.UnixMilliseconds()
|
||||||
|
if err := writeElements(w, bh.Version, bh.NumParentBlocks()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, hash := range bh.ParentHashes {
|
||||||
|
if err := WriteElement(w, hash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writeElements(w, bh.HashMerkleRoot, bh.AcceptedIDMerkleRoot, bh.UTXOCommitment, timestamp, bh.Bits, bh.Nonce)
|
||||||
|
}
|
||||||
345
app/appmessage/blockheader_test.go
Normal file
345
app/appmessage/blockheader_test.go
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
// 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"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
|
"github.com/kaspanet/kaspad/util/random"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestBlockHeader tests the BlockHeader API.
|
||||||
|
func TestBlockHeader(t *testing.T) {
|
||||||
|
nonce, err := random.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("random.Uint64: Error generating nonce: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashes := []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash}
|
||||||
|
|
||||||
|
merkleHash := mainnetGenesisMerkleRoot
|
||||||
|
acceptedIDMerkleRoot := exampleAcceptedIDMerkleRoot
|
||||||
|
bits := uint32(0x1d00ffff)
|
||||||
|
bh := NewBlockHeader(1, hashes, merkleHash, acceptedIDMerkleRoot, exampleUTXOCommitment, bits, nonce)
|
||||||
|
|
||||||
|
// Ensure we get the same data back out.
|
||||||
|
if !reflect.DeepEqual(bh.ParentHashes, hashes) {
|
||||||
|
t.Errorf("NewBlockHeader: wrong prev hashes - got %v, want %v",
|
||||||
|
spew.Sprint(bh.ParentHashes), spew.Sprint(hashes))
|
||||||
|
}
|
||||||
|
if !bh.HashMerkleRoot.IsEqual(merkleHash) {
|
||||||
|
t.Errorf("NewBlockHeader: wrong merkle root - got %v, want %v",
|
||||||
|
spew.Sprint(bh.HashMerkleRoot), spew.Sprint(merkleHash))
|
||||||
|
}
|
||||||
|
if bh.Bits != bits {
|
||||||
|
t.Errorf("NewBlockHeader: wrong bits - got %v, want %v",
|
||||||
|
bh.Bits, bits)
|
||||||
|
}
|
||||||
|
if bh.Nonce != nonce {
|
||||||
|
t.Errorf("NewBlockHeader: wrong nonce - got %v, want %v",
|
||||||
|
bh.Nonce, nonce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockHeaderEncoding tests the BlockHeader appmessage encode and decode for various
|
||||||
|
// protocol versions.
|
||||||
|
func TestBlockHeaderEncoding(t *testing.T) {
|
||||||
|
nonce := uint64(123123) // 0x000000000001e0f3
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
// baseBlockHdr is used in the various tests as a baseline BlockHeader.
|
||||||
|
bits := uint32(0x1d00ffff)
|
||||||
|
baseBlockHdr := &BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
ParentHashes: []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash},
|
||||||
|
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||||
|
AcceptedIDMerkleRoot: exampleAcceptedIDMerkleRoot,
|
||||||
|
UTXOCommitment: exampleUTXOCommitment,
|
||||||
|
Timestamp: mstime.UnixMilliseconds(0x17315ed0f99),
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
// baseBlockHdrEncoded is the appmessage encoded bytes of baseBlockHdr.
|
||||||
|
baseBlockHdrEncoded := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||||
|
0x02, // NumParentBlocks
|
||||||
|
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, // mainnetGenesisHash
|
||||||
|
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
||||||
|
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
||||||
|
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
||||||
|
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
||||||
|
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||||
|
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||||
|
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||||
|
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a, // HashMerkleRoot
|
||||||
|
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
||||||
|
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
||||||
|
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
||||||
|
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x99, 0x0f, 0xed, 0x15, 0x73, 0x01, 0x00, 0x00, // Timestamp
|
||||||
|
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||||
|
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Fake Nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *BlockHeader // Data to encode
|
||||||
|
out *BlockHeader // Expected decoded data
|
||||||
|
buf []byte // Encoded data
|
||||||
|
pver uint32 // Protocol version for appmessage encoding
|
||||||
|
}{
|
||||||
|
// Latest protocol version.
|
||||||
|
{
|
||||||
|
baseBlockHdr,
|
||||||
|
baseBlockHdr,
|
||||||
|
baseBlockHdrEncoded,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode to appmessage format.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := writeBlockHeader(&buf, test.pver, test.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("writeBlockHeader #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("writeBlockHeader #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
err = test.in.KaspaEncode(&buf, pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaEncode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("KaspaEncode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the block header from appmessage format.
|
||||||
|
var bh BlockHeader
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = readBlockHeader(rbuf, test.pver, &bh)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("readBlockHeader #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&bh, test.out) {
|
||||||
|
t.Errorf("readBlockHeader #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&bh), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rbuf = bytes.NewReader(test.buf)
|
||||||
|
err = bh.KaspaDecode(rbuf, pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaDecode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&bh, test.out) {
|
||||||
|
t.Errorf("KaspaDecode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&bh), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockHeaderSerialize tests BlockHeader serialize and deserialize.
|
||||||
|
func TestBlockHeaderSerialize(t *testing.T) {
|
||||||
|
nonce := uint64(123123) // 0x01e0f3
|
||||||
|
|
||||||
|
// baseBlockHdr is used in the various tests as a baseline BlockHeader.
|
||||||
|
bits := uint32(0x1d00ffff)
|
||||||
|
baseBlockHdr := &BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
ParentHashes: []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash},
|
||||||
|
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||||
|
AcceptedIDMerkleRoot: exampleAcceptedIDMerkleRoot,
|
||||||
|
UTXOCommitment: exampleUTXOCommitment,
|
||||||
|
Timestamp: mstime.UnixMilliseconds(0x17315ed0f99),
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
// baseBlockHdrEncoded is the appmessage encoded bytes of baseBlockHdr.
|
||||||
|
baseBlockHdrEncoded := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||||
|
0x02, // NumParentBlocks
|
||||||
|
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, // mainnetGenesisHash
|
||||||
|
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
||||||
|
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
||||||
|
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
||||||
|
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
||||||
|
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||||
|
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||||
|
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||||
|
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a, // HashMerkleRoot
|
||||||
|
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
||||||
|
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
||||||
|
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
||||||
|
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x99, 0x0f, 0xed, 0x15, 0x73, 0x01, 0x00, 0x00, // Timestamp
|
||||||
|
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||||
|
0xf3, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Fake Nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *BlockHeader // Data to encode
|
||||||
|
out *BlockHeader // Expected decoded data
|
||||||
|
buf []byte // Serialized data
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
baseBlockHdr,
|
||||||
|
baseBlockHdr,
|
||||||
|
baseBlockHdrEncoded,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Serialize the block header.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.Serialize(&buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Serialize #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("Serialize #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the block header.
|
||||||
|
var bh BlockHeader
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = bh.Deserialize(rbuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Deserialize #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&bh, test.out) {
|
||||||
|
t.Errorf("Deserialize #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&bh), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockHeaderSerializeSize performs tests to ensure the serialize size for
|
||||||
|
// various block headers is accurate.
|
||||||
|
func TestBlockHeaderSerializeSize(t *testing.T) {
|
||||||
|
nonce := uint64(123123) // 0x1e0f3
|
||||||
|
bits := uint32(0x1d00ffff)
|
||||||
|
timestamp := mstime.UnixMilliseconds(0x495fab29000)
|
||||||
|
baseBlockHdr := &BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
ParentHashes: []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash},
|
||||||
|
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||||
|
AcceptedIDMerkleRoot: &daghash.ZeroHash,
|
||||||
|
UTXOCommitment: &daghash.ZeroHash,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisBlockHdr := &BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
ParentHashes: []*daghash.Hash{},
|
||||||
|
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||||
|
AcceptedIDMerkleRoot: &daghash.ZeroHash,
|
||||||
|
UTXOCommitment: &daghash.ZeroHash,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
in *BlockHeader // Block header to encode
|
||||||
|
size int // Expected serialized size
|
||||||
|
}{
|
||||||
|
// Block with no transactions.
|
||||||
|
{genesisBlockHdr, 121},
|
||||||
|
|
||||||
|
// First block in the mainnet block DAG.
|
||||||
|
{baseBlockHdr, 185},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
serializedSize := test.in.SerializeSize()
|
||||||
|
if serializedSize != test.size {
|
||||||
|
t.Errorf("BlockHeader.SerializeSize: #%d got: %d, want: "+
|
||||||
|
"%d", i, serializedSize, test.size)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsGenesis(t *testing.T) {
|
||||||
|
nonce := uint64(123123) // 0x1e0f3
|
||||||
|
bits := uint32(0x1d00ffff)
|
||||||
|
timestamp := mstime.UnixMilliseconds(0x495fab29000)
|
||||||
|
|
||||||
|
baseBlockHdr := &BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
ParentHashes: []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash},
|
||||||
|
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
genesisBlockHdr := &BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
ParentHashes: []*daghash.Hash{},
|
||||||
|
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *BlockHeader // Block header to encode
|
||||||
|
isGenesis bool // Expected result for call of .IsGenesis
|
||||||
|
}{
|
||||||
|
{genesisBlockHdr, true},
|
||||||
|
{baseBlockHdr, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
isGenesis := test.in.IsGenesis()
|
||||||
|
if isGenesis != test.isGenesis {
|
||||||
|
t.Errorf("BlockHeader.IsGenesis: #%d got: %t, want: %t",
|
||||||
|
i, isGenesis, test.isGenesis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,34 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||||
|
"github.com/kaspanet/kaspad/util/binaryserializer"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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.
|
// MaxInvPerMsg is the maximum number of inventory vectors that can be in any type of kaspa inv message.
|
||||||
const MaxInvPerMsg = 1 << 17
|
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
|
// errNonCanonicalVarInt is the common format string used for non-canonically
|
||||||
// encoded variable length integer errors.
|
// encoded variable length integer errors.
|
||||||
var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
|
var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
|
||||||
@@ -18,3 +40,472 @@ var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
|
|||||||
|
|
||||||
// errNoEncodingForType signifies that there's no encoding for the given type.
|
// errNoEncodingForType signifies that there's no encoding for the given type.
|
||||||
var errNoEncodingForType = errors.New("there's no encoding for this 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 *daghash.Hash:
|
||||||
|
_, err := io.ReadFull(r, e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *id.ID:
|
||||||
|
return e.Deserialize(r)
|
||||||
|
|
||||||
|
case *subnetworkid.SubnetworkID:
|
||||||
|
_, 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 *daghash.Hash:
|
||||||
|
_, err := w.Write(e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *id.ID:
|
||||||
|
return e.Serialize(w)
|
||||||
|
|
||||||
|
case *subnetworkid.SubnetworkID:
|
||||||
|
_, 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 {
|
||||||
|
return binaryserializer.PutUint8(w, uint8(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
if val <= math.MaxUint16 {
|
||||||
|
err := binaryserializer.PutUint8(w, 0xfd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binaryserializer.PutUint16(w, littleEndian, uint16(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
if val <= math.MaxUint32 {
|
||||||
|
err := binaryserializer.PutUint8(w, 0xfe)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binaryserializer.PutUint32(w, littleEndian, uint32(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := binaryserializer.PutUint8(w, 0xff)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binaryserializer.PutUint64(w, littleEndian, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,44 +1,695 @@
|
|||||||
|
// 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
|
package appmessage
|
||||||
|
|
||||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
// mainnetGenesisHash is the hash of the first block in the block DAG for the
|
// mainnetGenesisHash is the hash of the first block in the block DAG for the
|
||||||
// main network (genesis block).
|
// main network (genesis block).
|
||||||
var mainnetGenesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
var mainnetGenesisHash = &daghash.Hash{
|
||||||
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25,
|
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25,
|
||||||
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
||||||
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
||||||
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
||||||
})
|
}
|
||||||
|
|
||||||
// simnetGenesisHash is the hash of the first block in the block DAG for the
|
// simnetGenesisHash is the hash of the first block in the block DAG for the
|
||||||
// simulation test network.
|
// simulation test network.
|
||||||
var simnetGenesisHash = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
var simnetGenesisHash = &daghash.Hash{
|
||||||
0x9d, 0x89, 0xb0, 0x6e, 0xb3, 0x47, 0xb5, 0x6e,
|
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a,
|
||||||
0xcd, 0x6c, 0x63, 0x99, 0x45, 0x91, 0xd5, 0xce,
|
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||||
0x9b, 0x43, 0x05, 0xc1, 0xa5, 0x5e, 0x2a, 0xda,
|
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||||
0x90, 0x4c, 0xf0, 0x6c, 0x4d, 0x5f, 0xd3, 0x62,
|
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||||
})
|
}
|
||||||
|
|
||||||
// mainnetGenesisMerkleRoot is the hash of the first transaction in the genesis
|
// mainnetGenesisMerkleRoot is the hash of the first transaction in the genesis
|
||||||
// block for the main network.
|
// block for the main network.
|
||||||
var mainnetGenesisMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
var mainnetGenesisMerkleRoot = &daghash.Hash{
|
||||||
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a,
|
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a,
|
||||||
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
||||||
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
||||||
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
||||||
})
|
}
|
||||||
|
|
||||||
var exampleAcceptedIDMerkleRoot = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
var exampleAcceptedIDMerkleRoot = &daghash.Hash{
|
||||||
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C,
|
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C,
|
||||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
})
|
}
|
||||||
|
|
||||||
var exampleUTXOCommitment = externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
var exampleUTXOCommitment = &daghash.Hash{
|
||||||
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C,
|
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C,
|
||||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
(*daghash.Hash)(&[daghash.HashSize]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,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
(*daghash.Hash)(&[daghash.HashSize]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,601 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DomainBlockToMsgBlock converts an externalapi.DomainBlock to MsgBlock
|
|
||||||
func DomainBlockToMsgBlock(domainBlock *externalapi.DomainBlock) *MsgBlock {
|
|
||||||
msgTxs := make([]*MsgTx, 0, len(domainBlock.Transactions))
|
|
||||||
for _, domainTransaction := range domainBlock.Transactions {
|
|
||||||
msgTxs = append(msgTxs, DomainTransactionToMsgTx(domainTransaction))
|
|
||||||
}
|
|
||||||
return &MsgBlock{
|
|
||||||
Header: *DomainBlockHeaderToBlockHeader(domainBlock.Header),
|
|
||||||
Transactions: msgTxs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainBlockHeaderToBlockHeader converts an externalapi.BlockHeader to MsgBlockHeader
|
|
||||||
func DomainBlockHeaderToBlockHeader(domainBlockHeader externalapi.BlockHeader) *MsgBlockHeader {
|
|
||||||
return &MsgBlockHeader{
|
|
||||||
Version: domainBlockHeader.Version(),
|
|
||||||
Parents: domainBlockHeader.Parents(),
|
|
||||||
HashMerkleRoot: domainBlockHeader.HashMerkleRoot(),
|
|
||||||
AcceptedIDMerkleRoot: domainBlockHeader.AcceptedIDMerkleRoot(),
|
|
||||||
UTXOCommitment: domainBlockHeader.UTXOCommitment(),
|
|
||||||
Timestamp: mstime.UnixMilliseconds(domainBlockHeader.TimeInMilliseconds()),
|
|
||||||
Bits: domainBlockHeader.Bits(),
|
|
||||||
Nonce: domainBlockHeader.Nonce(),
|
|
||||||
BlueScore: domainBlockHeader.BlueScore(),
|
|
||||||
DAAScore: domainBlockHeader.DAAScore(),
|
|
||||||
BlueWork: domainBlockHeader.BlueWork(),
|
|
||||||
PruningPoint: domainBlockHeader.PruningPoint(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgBlockToDomainBlock converts a MsgBlock to externalapi.DomainBlock
|
|
||||||
func MsgBlockToDomainBlock(msgBlock *MsgBlock) *externalapi.DomainBlock {
|
|
||||||
transactions := make([]*externalapi.DomainTransaction, 0, len(msgBlock.Transactions))
|
|
||||||
for _, msgTx := range msgBlock.Transactions {
|
|
||||||
transactions = append(transactions, MsgTxToDomainTransaction(msgTx))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &externalapi.DomainBlock{
|
|
||||||
Header: BlockHeaderToDomainBlockHeader(&msgBlock.Header),
|
|
||||||
Transactions: transactions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockHeaderToDomainBlockHeader converts a MsgBlockHeader to externalapi.BlockHeader
|
|
||||||
func BlockHeaderToDomainBlockHeader(blockHeader *MsgBlockHeader) externalapi.BlockHeader {
|
|
||||||
return blockheader.NewImmutableBlockHeader(
|
|
||||||
blockHeader.Version,
|
|
||||||
blockHeader.Parents,
|
|
||||||
blockHeader.HashMerkleRoot,
|
|
||||||
blockHeader.AcceptedIDMerkleRoot,
|
|
||||||
blockHeader.UTXOCommitment,
|
|
||||||
blockHeader.Timestamp.UnixMilliseconds(),
|
|
||||||
blockHeader.Bits,
|
|
||||||
blockHeader.Nonce,
|
|
||||||
blockHeader.DAAScore,
|
|
||||||
blockHeader.BlueScore,
|
|
||||||
blockHeader.BlueWork,
|
|
||||||
blockHeader.PruningPoint,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainTransactionToMsgTx converts an externalapi.DomainTransaction into an MsgTx
|
|
||||||
func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction) *MsgTx {
|
|
||||||
txIns := make([]*TxIn, 0, len(domainTransaction.Inputs))
|
|
||||||
for _, input := range domainTransaction.Inputs {
|
|
||||||
txIns = append(txIns, domainTransactionInputToTxIn(input))
|
|
||||||
}
|
|
||||||
|
|
||||||
txOuts := make([]*TxOut, 0, len(domainTransaction.Outputs))
|
|
||||||
for _, output := range domainTransaction.Outputs {
|
|
||||||
txOuts = append(txOuts, domainTransactionOutputToTxOut(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MsgTx{
|
|
||||||
Version: domainTransaction.Version,
|
|
||||||
TxIn: txIns,
|
|
||||||
TxOut: txOuts,
|
|
||||||
LockTime: domainTransaction.LockTime,
|
|
||||||
SubnetworkID: domainTransaction.SubnetworkID,
|
|
||||||
Gas: domainTransaction.Gas,
|
|
||||||
Payload: domainTransaction.Payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func domainTransactionOutputToTxOut(domainTransactionOutput *externalapi.DomainTransactionOutput) *TxOut {
|
|
||||||
return &TxOut{
|
|
||||||
Value: domainTransactionOutput.Value,
|
|
||||||
ScriptPubKey: domainTransactionOutput.ScriptPublicKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func domainTransactionInputToTxIn(domainTransactionInput *externalapi.DomainTransactionInput) *TxIn {
|
|
||||||
return &TxIn{
|
|
||||||
PreviousOutpoint: *domainOutpointToOutpoint(domainTransactionInput.PreviousOutpoint),
|
|
||||||
SignatureScript: domainTransactionInput.SignatureScript,
|
|
||||||
Sequence: domainTransactionInput.Sequence,
|
|
||||||
SigOpCount: domainTransactionInput.SigOpCount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func domainOutpointToOutpoint(domainOutpoint externalapi.DomainOutpoint) *Outpoint {
|
|
||||||
return NewOutpoint(
|
|
||||||
&domainOutpoint.TransactionID,
|
|
||||||
domainOutpoint.Index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgTxToDomainTransaction converts an MsgTx into externalapi.DomainTransaction
|
|
||||||
func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
|
|
||||||
transactionInputs := make([]*externalapi.DomainTransactionInput, 0, len(msgTx.TxIn))
|
|
||||||
for _, txIn := range msgTx.TxIn {
|
|
||||||
transactionInputs = append(transactionInputs, txInToDomainTransactionInput(txIn))
|
|
||||||
}
|
|
||||||
|
|
||||||
transactionOutputs := make([]*externalapi.DomainTransactionOutput, 0, len(msgTx.TxOut))
|
|
||||||
for _, txOut := range msgTx.TxOut {
|
|
||||||
transactionOutputs = append(transactionOutputs, txOutToDomainTransactionOutput(txOut))
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := make([]byte, 0)
|
|
||||||
if msgTx.Payload != nil {
|
|
||||||
payload = msgTx.Payload
|
|
||||||
}
|
|
||||||
|
|
||||||
return &externalapi.DomainTransaction{
|
|
||||||
Version: msgTx.Version,
|
|
||||||
Inputs: transactionInputs,
|
|
||||||
Outputs: transactionOutputs,
|
|
||||||
LockTime: msgTx.LockTime,
|
|
||||||
SubnetworkID: msgTx.SubnetworkID,
|
|
||||||
Gas: msgTx.Gas,
|
|
||||||
Payload: payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func txOutToDomainTransactionOutput(txOut *TxOut) *externalapi.DomainTransactionOutput {
|
|
||||||
return &externalapi.DomainTransactionOutput{
|
|
||||||
Value: txOut.Value,
|
|
||||||
ScriptPublicKey: txOut.ScriptPubKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func txInToDomainTransactionInput(txIn *TxIn) *externalapi.DomainTransactionInput {
|
|
||||||
return &externalapi.DomainTransactionInput{
|
|
||||||
PreviousOutpoint: *outpointToDomainOutpoint(&txIn.PreviousOutpoint), //TODO
|
|
||||||
SignatureScript: txIn.SignatureScript,
|
|
||||||
SigOpCount: txIn.SigOpCount,
|
|
||||||
Sequence: txIn.Sequence,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func outpointToDomainOutpoint(outpoint *Outpoint) *externalapi.DomainOutpoint {
|
|
||||||
return &externalapi.DomainOutpoint{
|
|
||||||
TransactionID: outpoint.TxID,
|
|
||||||
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 {
|
|
||||||
previousOutpoint, err := RPCOutpointToDomainOutpoint(input.PreviousOutpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
signatureScript, err := hex.DecodeString(input.SignatureScript)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
inputs[i] = &externalapi.DomainTransactionInput{
|
|
||||||
PreviousOutpoint: *previousOutpoint,
|
|
||||||
SignatureScript: signatureScript,
|
|
||||||
Sequence: input.Sequence,
|
|
||||||
SigOpCount: input.SigOpCount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCOutpointToDomainOutpoint converts RPCOutpoint to DomainOutpoint
|
|
||||||
func RPCOutpointToDomainOutpoint(outpoint *RPCOutpoint) (*externalapi.DomainOutpoint, error) {
|
|
||||||
transactionID, err := transactionid.FromString(outpoint.TransactionID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &externalapi.DomainOutpoint{
|
|
||||||
TransactionID: *transactionID,
|
|
||||||
Index: outpoint.Index,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCUTXOEntryToUTXOEntry converts RPCUTXOEntry to UTXOEntry
|
|
||||||
func RPCUTXOEntryToUTXOEntry(entry *RPCUTXOEntry) (externalapi.UTXOEntry, error) {
|
|
||||||
script, err := hex.DecodeString(entry.ScriptPublicKey.Script)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utxo.NewUTXOEntry(
|
|
||||||
entry.Amount,
|
|
||||||
&externalapi.ScriptPublicKey{
|
|
||||||
Script: script,
|
|
||||||
Version: entry.ScriptPublicKey.Version,
|
|
||||||
},
|
|
||||||
entry.IsCoinbase,
|
|
||||||
entry.BlockDAAScore,
|
|
||||||
), 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,
|
|
||||||
SigOpCount: input.SigOpCount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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] = outpointAndUTXOEntryPairToDomainOutpointAndUTXOEntryPair(outpointAndUTXOEntryPair)
|
|
||||||
}
|
|
||||||
return domainOutpointAndUTXOEntryPairs
|
|
||||||
}
|
|
||||||
|
|
||||||
func outpointAndUTXOEntryPairToDomainOutpointAndUTXOEntryPair(
|
|
||||||
outpointAndUTXOEntryPair *OutpointAndUTXOEntryPair) *externalapi.OutpointAndUTXOEntryPair {
|
|
||||||
return &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,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
parents := make([]*RPCBlockLevelParents, len(block.Header.Parents()))
|
|
||||||
for i, blockLevelParents := range block.Header.Parents() {
|
|
||||||
parents[i] = &RPCBlockLevelParents{
|
|
||||||
ParentHashes: hashes.ToStrings(blockLevelParents),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
header := &RPCBlockHeader{
|
|
||||||
Version: uint32(block.Header.Version()),
|
|
||||||
Parents: parents,
|
|
||||||
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(),
|
|
||||||
DAAScore: block.Header.DAAScore(),
|
|
||||||
BlueScore: block.Header.BlueScore(),
|
|
||||||
BlueWork: block.Header.BlueWork().Text(16),
|
|
||||||
PruningPoint: block.Header.PruningPoint().String(),
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
parents := make([]externalapi.BlockLevelParents, len(block.Header.Parents))
|
|
||||||
for i, blockLevelParents := range block.Header.Parents {
|
|
||||||
parents[i] = make(externalapi.BlockLevelParents, len(blockLevelParents.ParentHashes))
|
|
||||||
for j, parentHash := range blockLevelParents.ParentHashes {
|
|
||||||
var err error
|
|
||||||
parents[i][j], err = externalapi.NewDomainHashFromString(parentHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
blueWork, success := new(big.Int).SetString(block.Header.BlueWork, 16)
|
|
||||||
if !success {
|
|
||||||
return nil, errors.Errorf("failed to parse blue work: %s", block.Header.BlueWork)
|
|
||||||
}
|
|
||||||
pruningPoint, err := externalapi.NewDomainHashFromString(block.Header.PruningPoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
header := blockheader.NewImmutableBlockHeader(
|
|
||||||
uint16(block.Header.Version),
|
|
||||||
parents,
|
|
||||||
hashMerkleRoot,
|
|
||||||
acceptedIDMerkleRoot,
|
|
||||||
utxoCommitment,
|
|
||||||
block.Header.Timestamp,
|
|
||||||
block.Header.Bits,
|
|
||||||
block.Header.Nonce,
|
|
||||||
block.Header.DAAScore,
|
|
||||||
block.Header.BlueScore,
|
|
||||||
blueWork,
|
|
||||||
pruningPoint)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockWithTrustedDataToDomainBlockWithTrustedData converts *MsgBlockWithTrustedData to *externalapi.BlockWithTrustedData
|
|
||||||
func BlockWithTrustedDataToDomainBlockWithTrustedData(block *MsgBlockWithTrustedData) *externalapi.BlockWithTrustedData {
|
|
||||||
daaWindow := make([]*externalapi.TrustedDataDataDAAHeader, len(block.DAAWindow))
|
|
||||||
for i, daaBlock := range block.DAAWindow {
|
|
||||||
daaWindow[i] = &externalapi.TrustedDataDataDAAHeader{
|
|
||||||
Header: BlockHeaderToDomainBlockHeader(&daaBlock.Block.Header),
|
|
||||||
GHOSTDAGData: ghostdagDataToDomainGHOSTDAGData(daaBlock.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ghostdagData := make([]*externalapi.BlockGHOSTDAGDataHashPair, len(block.GHOSTDAGData))
|
|
||||||
for i, datum := range block.GHOSTDAGData {
|
|
||||||
ghostdagData[i] = &externalapi.BlockGHOSTDAGDataHashPair{
|
|
||||||
Hash: datum.Hash,
|
|
||||||
GHOSTDAGData: ghostdagDataToDomainGHOSTDAGData(datum.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &externalapi.BlockWithTrustedData{
|
|
||||||
Block: MsgBlockToDomainBlock(block.Block),
|
|
||||||
DAAWindow: daaWindow,
|
|
||||||
GHOSTDAGData: ghostdagData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrustedDataDataDAABlockV4ToTrustedDataDataDAAHeader converts *TrustedDataDAAHeader to *externalapi.TrustedDataDataDAAHeader
|
|
||||||
func TrustedDataDataDAABlockV4ToTrustedDataDataDAAHeader(daaBlock *TrustedDataDAAHeader) *externalapi.TrustedDataDataDAAHeader {
|
|
||||||
return &externalapi.TrustedDataDataDAAHeader{
|
|
||||||
Header: BlockHeaderToDomainBlockHeader(daaBlock.Header),
|
|
||||||
GHOSTDAGData: ghostdagDataToDomainGHOSTDAGData(daaBlock.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GHOSTDAGHashPairToDomainGHOSTDAGHashPair converts *BlockGHOSTDAGDataHashPair to *externalapi.BlockGHOSTDAGDataHashPair
|
|
||||||
func GHOSTDAGHashPairToDomainGHOSTDAGHashPair(datum *BlockGHOSTDAGDataHashPair) *externalapi.BlockGHOSTDAGDataHashPair {
|
|
||||||
return &externalapi.BlockGHOSTDAGDataHashPair{
|
|
||||||
Hash: datum.Hash,
|
|
||||||
GHOSTDAGData: ghostdagDataToDomainGHOSTDAGData(datum.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ghostdagDataToDomainGHOSTDAGData(data *BlockGHOSTDAGData) *externalapi.BlockGHOSTDAGData {
|
|
||||||
bluesAnticoneSizes := make(map[externalapi.DomainHash]externalapi.KType, len(data.BluesAnticoneSizes))
|
|
||||||
for _, pair := range data.BluesAnticoneSizes {
|
|
||||||
bluesAnticoneSizes[*pair.BlueHash] = pair.AnticoneSize
|
|
||||||
}
|
|
||||||
return externalapi.NewBlockGHOSTDAGData(
|
|
||||||
data.BlueScore,
|
|
||||||
data.BlueWork,
|
|
||||||
data.SelectedParent,
|
|
||||||
data.MergeSetBlues,
|
|
||||||
data.MergeSetReds,
|
|
||||||
bluesAnticoneSizes,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func domainGHOSTDAGDataGHOSTDAGData(data *externalapi.BlockGHOSTDAGData) *BlockGHOSTDAGData {
|
|
||||||
bluesAnticoneSizes := make([]*BluesAnticoneSizes, 0, len(data.BluesAnticoneSizes()))
|
|
||||||
for blueHash, anticoneSize := range data.BluesAnticoneSizes() {
|
|
||||||
blueHashCopy := blueHash
|
|
||||||
bluesAnticoneSizes = append(bluesAnticoneSizes, &BluesAnticoneSizes{
|
|
||||||
BlueHash: &blueHashCopy,
|
|
||||||
AnticoneSize: anticoneSize,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return &BlockGHOSTDAGData{
|
|
||||||
BlueScore: data.BlueScore(),
|
|
||||||
BlueWork: data.BlueWork(),
|
|
||||||
SelectedParent: data.SelectedParent(),
|
|
||||||
MergeSetBlues: data.MergeSetBlues(),
|
|
||||||
MergeSetReds: data.MergeSetReds(),
|
|
||||||
BluesAnticoneSizes: bluesAnticoneSizes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainBlockWithTrustedDataToBlockWithTrustedData converts *externalapi.BlockWithTrustedData to *MsgBlockWithTrustedData
|
|
||||||
func DomainBlockWithTrustedDataToBlockWithTrustedData(block *externalapi.BlockWithTrustedData) *MsgBlockWithTrustedData {
|
|
||||||
daaWindow := make([]*TrustedDataDataDAABlock, len(block.DAAWindow))
|
|
||||||
for i, daaBlock := range block.DAAWindow {
|
|
||||||
daaWindow[i] = &TrustedDataDataDAABlock{
|
|
||||||
Block: &MsgBlock{
|
|
||||||
Header: *DomainBlockHeaderToBlockHeader(daaBlock.Header),
|
|
||||||
},
|
|
||||||
GHOSTDAGData: domainGHOSTDAGDataGHOSTDAGData(daaBlock.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ghostdagData := make([]*BlockGHOSTDAGDataHashPair, len(block.GHOSTDAGData))
|
|
||||||
for i, datum := range block.GHOSTDAGData {
|
|
||||||
ghostdagData[i] = &BlockGHOSTDAGDataHashPair{
|
|
||||||
Hash: datum.Hash,
|
|
||||||
GHOSTDAGData: domainGHOSTDAGDataGHOSTDAGData(datum.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MsgBlockWithTrustedData{
|
|
||||||
Block: DomainBlockToMsgBlock(block.Block),
|
|
||||||
DAAScore: block.Block.Header.DAAScore(),
|
|
||||||
DAAWindow: daaWindow,
|
|
||||||
GHOSTDAGData: ghostdagData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainBlockWithTrustedDataToBlockWithTrustedDataV4 converts a set of *externalapi.DomainBlock, daa window indices and ghostdag data indices
|
|
||||||
// to *MsgBlockWithTrustedDataV4
|
|
||||||
func DomainBlockWithTrustedDataToBlockWithTrustedDataV4(block *externalapi.DomainBlock, daaWindowIndices, ghostdagDataIndices []uint64) *MsgBlockWithTrustedDataV4 {
|
|
||||||
return &MsgBlockWithTrustedDataV4{
|
|
||||||
Block: DomainBlockToMsgBlock(block),
|
|
||||||
DAAWindowIndices: daaWindowIndices,
|
|
||||||
GHOSTDAGDataIndices: ghostdagDataIndices,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainTrustedDataToTrustedData converts *externalapi.BlockWithTrustedData to *MsgBlockWithTrustedData
|
|
||||||
func DomainTrustedDataToTrustedData(domainDAAWindow []*externalapi.TrustedDataDataDAAHeader, domainGHOSTDAGData []*externalapi.BlockGHOSTDAGDataHashPair) *MsgTrustedData {
|
|
||||||
daaWindow := make([]*TrustedDataDAAHeader, len(domainDAAWindow))
|
|
||||||
for i, daaBlock := range domainDAAWindow {
|
|
||||||
daaWindow[i] = &TrustedDataDAAHeader{
|
|
||||||
Header: DomainBlockHeaderToBlockHeader(daaBlock.Header),
|
|
||||||
GHOSTDAGData: domainGHOSTDAGDataGHOSTDAGData(daaBlock.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ghostdagData := make([]*BlockGHOSTDAGDataHashPair, len(domainGHOSTDAGData))
|
|
||||||
for i, datum := range domainGHOSTDAGData {
|
|
||||||
ghostdagData[i] = &BlockGHOSTDAGDataHashPair{
|
|
||||||
Hash: datum.Hash,
|
|
||||||
GHOSTDAGData: domainGHOSTDAGDataGHOSTDAGData(datum.GHOSTDAGData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MsgTrustedData{
|
|
||||||
DAAWindow: daaWindow,
|
|
||||||
GHOSTDAGData: ghostdagData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgPruningPointProofToDomainPruningPointProof converts *MsgPruningPointProof to *externalapi.PruningPointProof
|
|
||||||
func MsgPruningPointProofToDomainPruningPointProof(pruningPointProofMessage *MsgPruningPointProof) *externalapi.PruningPointProof {
|
|
||||||
headers := make([][]externalapi.BlockHeader, len(pruningPointProofMessage.Headers))
|
|
||||||
for blockLevel, blockLevelParents := range pruningPointProofMessage.Headers {
|
|
||||||
headers[blockLevel] = make([]externalapi.BlockHeader, len(blockLevelParents))
|
|
||||||
for i, header := range blockLevelParents {
|
|
||||||
headers[blockLevel][i] = BlockHeaderToDomainBlockHeader(header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &externalapi.PruningPointProof{
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainPruningPointProofToMsgPruningPointProof converts *externalapi.PruningPointProof to *MsgPruningPointProof
|
|
||||||
func DomainPruningPointProofToMsgPruningPointProof(pruningPointProof *externalapi.PruningPointProof) *MsgPruningPointProof {
|
|
||||||
headers := make([][]*MsgBlockHeader, len(pruningPointProof.Headers))
|
|
||||||
for blockLevel, blockLevelParents := range pruningPointProof.Headers {
|
|
||||||
headers[blockLevel] = make([]*MsgBlockHeader, len(blockLevelParents))
|
|
||||||
for i, header := range blockLevelParents {
|
|
||||||
headers[blockLevel][i] = DomainBlockHeaderToBlockHeader(header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &MsgPruningPointProof{
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,20 +32,3 @@ func (e *MessageError) Error() string {
|
|||||||
func messageError(f string, desc string) *MessageError {
|
func messageError(f string, desc string) *MessageError {
|
||||||
return &MessageError{Func: f, Description: desc}
|
return &MessageError{Func: f, Description: desc}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPCError represents an error arriving from the RPC
|
|
||||||
type RPCError struct {
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err RPCError) Error() string {
|
|
||||||
return err.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCErrorf formats according to a format specifier and returns the string
|
|
||||||
// as an RPCError.
|
|
||||||
func RPCErrorf(format string, args ...interface{}) *RPCError {
|
|
||||||
return &RPCError{
|
|
||||||
Message: fmt.Sprintf(format, args...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
56
app/appmessage/fakemessage_test.go
Normal file
56
app/appmessage/fakemessage_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// 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 "io"
|
||||||
|
|
||||||
|
// fakeMessage implements the Message interface and is used to force encode
|
||||||
|
// errors in messages.
|
||||||
|
type fakeMessage struct {
|
||||||
|
command MessageCommand
|
||||||
|
payload []byte
|
||||||
|
forceEncodeErr bool
|
||||||
|
forceLenErr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaDecode doesn't do anything. It just satisfies the appmessage.Message
|
||||||
|
// interface.
|
||||||
|
func (msg *fakeMessage) KaspaDecode(r io.Reader, pver uint32) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaEncode writes the payload field of the fake message or forces an error
|
||||||
|
// if the forceEncodeErr flag of the fake message is set. It also satisfies the
|
||||||
|
// appmessage.Message interface.
|
||||||
|
func (msg *fakeMessage) KaspaEncode(w io.Writer, pver uint32) error {
|
||||||
|
if msg.forceEncodeErr {
|
||||||
|
err := &MessageError{
|
||||||
|
Func: "fakeMessage.KaspaEncode",
|
||||||
|
Description: "intentional error",
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := w.Write(msg.payload)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the command field of the fake message and satisfies the
|
||||||
|
// Message interface.
|
||||||
|
func (msg *fakeMessage) Command() MessageCommand {
|
||||||
|
return msg.command
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the length of the payload field of fake message
|
||||||
|
// or a smaller value if the forceLenErr flag of the fake message is set. It
|
||||||
|
// satisfies the Message interface.
|
||||||
|
func (msg *fakeMessage) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
lenp := uint32(len(msg.payload))
|
||||||
|
if msg.forceLenErr {
|
||||||
|
return lenp - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return lenp
|
||||||
|
}
|
||||||
@@ -11,16 +11,13 @@ import (
|
|||||||
|
|
||||||
// MaxMessagePayload is the maximum bytes a message can be regardless of other
|
// MaxMessagePayload is the maximum bytes a message can be regardless of other
|
||||||
// individual limits imposed by messages themselves.
|
// individual limits imposed by messages themselves.
|
||||||
const MaxMessagePayload = 1024 * 1024 * 32 // 32MB
|
const MaxMessagePayload = (1024 * 1024 * 32) // 32MB
|
||||||
|
|
||||||
// MessageCommand is a number in the header of a message that represents its type.
|
// MessageCommand is a number in the header of a message that represents its type.
|
||||||
type MessageCommand uint32
|
type MessageCommand uint32
|
||||||
|
|
||||||
func (cmd MessageCommand) String() string {
|
func (cmd MessageCommand) String() string {
|
||||||
cmdString, ok := ProtocolMessageCommandToString[cmd]
|
cmdString, ok := MessageCommandToString[cmd]
|
||||||
if !ok {
|
|
||||||
cmdString, ok = RPCMessageCommandToString[cmd]
|
|
||||||
}
|
|
||||||
if !ok {
|
if !ok {
|
||||||
cmdString = "unknown command"
|
cmdString = "unknown command"
|
||||||
}
|
}
|
||||||
@@ -29,255 +26,54 @@ func (cmd MessageCommand) String() string {
|
|||||||
|
|
||||||
// Commands used in kaspa message headers which describe the type of message.
|
// Commands used in kaspa message headers which describe the type of message.
|
||||||
const (
|
const (
|
||||||
// protocol
|
|
||||||
CmdVersion MessageCommand = iota
|
CmdVersion MessageCommand = iota
|
||||||
CmdVerAck
|
CmdVerAck
|
||||||
CmdRequestAddresses
|
CmdRequestAddresses
|
||||||
CmdAddresses
|
CmdAddresses
|
||||||
CmdRequestHeaders
|
CmdRequestIBDBlocks
|
||||||
CmdBlock
|
CmdBlock
|
||||||
CmdTx
|
CmdTx
|
||||||
CmdPing
|
CmdPing
|
||||||
CmdPong
|
CmdPong
|
||||||
CmdRequestBlockLocator
|
CmdRequestBlockLocator
|
||||||
CmdBlockLocator
|
CmdBlockLocator
|
||||||
|
CmdSelectedTip
|
||||||
|
CmdRequestSelectedTip
|
||||||
CmdInvRelayBlock
|
CmdInvRelayBlock
|
||||||
CmdRequestRelayBlocks
|
CmdRequestRelayBlocks
|
||||||
CmdInvTransaction
|
CmdInvTransaction
|
||||||
CmdRequestTransactions
|
CmdRequestTransactions
|
||||||
CmdDoneHeaders
|
CmdIBDBlock
|
||||||
|
CmdRequestNextIBDBlocks
|
||||||
|
CmdDoneIBDBlocks
|
||||||
CmdTransactionNotFound
|
CmdTransactionNotFound
|
||||||
CmdReject
|
CmdReject
|
||||||
CmdRequestNextHeaders
|
|
||||||
CmdRequestPruningPointUTXOSet
|
|
||||||
CmdPruningPointUTXOSetChunk
|
|
||||||
CmdUnexpectedPruningPoint
|
|
||||||
CmdIBDBlockLocator
|
|
||||||
CmdIBDBlockLocatorHighestHash
|
|
||||||
CmdIBDBlockLocatorHighestHashNotFound
|
|
||||||
CmdBlockHeaders
|
|
||||||
CmdRequestNextPruningPointUTXOSetChunk
|
|
||||||
CmdDonePruningPointUTXOSetChunks
|
|
||||||
CmdBlockWithTrustedData
|
|
||||||
CmdDoneBlocksWithTrustedData
|
|
||||||
CmdRequestPruningPointAndItsAnticone
|
|
||||||
CmdIBDBlock
|
|
||||||
CmdRequestIBDBlocks
|
|
||||||
CmdPruningPoints
|
|
||||||
CmdRequestPruningPointProof
|
|
||||||
CmdPruningPointProof
|
|
||||||
CmdReady
|
|
||||||
CmdTrustedData
|
|
||||||
CmdBlockWithTrustedDataV4
|
|
||||||
|
|
||||||
// rpc
|
|
||||||
CmdGetCurrentNetworkRequestMessage
|
|
||||||
CmdGetCurrentNetworkResponseMessage
|
|
||||||
CmdSubmitBlockRequestMessage
|
|
||||||
CmdSubmitBlockResponseMessage
|
|
||||||
CmdGetBlockTemplateRequestMessage
|
|
||||||
CmdGetBlockTemplateResponseMessage
|
|
||||||
CmdGetBlockTemplateTransactionMessage
|
|
||||||
CmdNotifyBlockAddedRequestMessage
|
|
||||||
CmdNotifyBlockAddedResponseMessage
|
|
||||||
CmdBlockAddedNotificationMessage
|
|
||||||
CmdGetPeerAddressesRequestMessage
|
|
||||||
CmdGetPeerAddressesResponseMessage
|
|
||||||
CmdGetSelectedTipHashRequestMessage
|
|
||||||
CmdGetSelectedTipHashResponseMessage
|
|
||||||
CmdGetMempoolEntryRequestMessage
|
|
||||||
CmdGetMempoolEntryResponseMessage
|
|
||||||
CmdGetConnectedPeerInfoRequestMessage
|
|
||||||
CmdGetConnectedPeerInfoResponseMessage
|
|
||||||
CmdAddPeerRequestMessage
|
|
||||||
CmdAddPeerResponseMessage
|
|
||||||
CmdSubmitTransactionRequestMessage
|
|
||||||
CmdSubmitTransactionResponseMessage
|
|
||||||
CmdNotifyVirtualSelectedParentChainChangedRequestMessage
|
|
||||||
CmdNotifyVirtualSelectedParentChainChangedResponseMessage
|
|
||||||
CmdVirtualSelectedParentChainChangedNotificationMessage
|
|
||||||
CmdGetBlockRequestMessage
|
|
||||||
CmdGetBlockResponseMessage
|
|
||||||
CmdGetSubnetworkRequestMessage
|
|
||||||
CmdGetSubnetworkResponseMessage
|
|
||||||
CmdGetVirtualSelectedParentChainFromBlockRequestMessage
|
|
||||||
CmdGetVirtualSelectedParentChainFromBlockResponseMessage
|
|
||||||
CmdGetBlocksRequestMessage
|
|
||||||
CmdGetBlocksResponseMessage
|
|
||||||
CmdGetBlockCountRequestMessage
|
|
||||||
CmdGetBlockCountResponseMessage
|
|
||||||
CmdGetBlockDAGInfoRequestMessage
|
|
||||||
CmdGetBlockDAGInfoResponseMessage
|
|
||||||
CmdResolveFinalityConflictRequestMessage
|
|
||||||
CmdResolveFinalityConflictResponseMessage
|
|
||||||
CmdNotifyFinalityConflictsRequestMessage
|
|
||||||
CmdNotifyFinalityConflictsResponseMessage
|
|
||||||
CmdFinalityConflictNotificationMessage
|
|
||||||
CmdFinalityConflictResolvedNotificationMessage
|
|
||||||
CmdGetMempoolEntriesRequestMessage
|
|
||||||
CmdGetMempoolEntriesResponseMessage
|
|
||||||
CmdShutDownRequestMessage
|
|
||||||
CmdShutDownResponseMessage
|
|
||||||
CmdGetHeadersRequestMessage
|
|
||||||
CmdGetHeadersResponseMessage
|
|
||||||
CmdNotifyUTXOsChangedRequestMessage
|
|
||||||
CmdNotifyUTXOsChangedResponseMessage
|
|
||||||
CmdUTXOsChangedNotificationMessage
|
|
||||||
CmdStopNotifyingUTXOsChangedRequestMessage
|
|
||||||
CmdStopNotifyingUTXOsChangedResponseMessage
|
|
||||||
CmdGetUTXOsByAddressesRequestMessage
|
|
||||||
CmdGetUTXOsByAddressesResponseMessage
|
|
||||||
CmdGetBalanceByAddressRequestMessage
|
|
||||||
CmdGetBalanceByAddressResponseMessage
|
|
||||||
CmdGetVirtualSelectedParentBlueScoreRequestMessage
|
|
||||||
CmdGetVirtualSelectedParentBlueScoreResponseMessage
|
|
||||||
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
|
|
||||||
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
|
|
||||||
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
|
|
||||||
CmdBanRequestMessage
|
|
||||||
CmdBanResponseMessage
|
|
||||||
CmdUnbanRequestMessage
|
|
||||||
CmdUnbanResponseMessage
|
|
||||||
CmdGetInfoRequestMessage
|
|
||||||
CmdGetInfoResponseMessage
|
|
||||||
CmdNotifyPruningPointUTXOSetOverrideRequestMessage
|
|
||||||
CmdNotifyPruningPointUTXOSetOverrideResponseMessage
|
|
||||||
CmdPruningPointUTXOSetOverrideNotificationMessage
|
|
||||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
|
|
||||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
|
|
||||||
CmdEstimateNetworkHashesPerSecondRequestMessage
|
|
||||||
CmdEstimateNetworkHashesPerSecondResponseMessage
|
|
||||||
CmdNotifyVirtualDaaScoreChangedRequestMessage
|
|
||||||
CmdNotifyVirtualDaaScoreChangedResponseMessage
|
|
||||||
CmdVirtualDaaScoreChangedNotificationMessage
|
|
||||||
CmdGetBalancesByAddressesRequestMessage
|
|
||||||
CmdGetBalancesByAddressesResponseMessage
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
// MessageCommandToString maps all MessageCommands to their string representation
|
||||||
var ProtocolMessageCommandToString = map[MessageCommand]string{
|
var MessageCommandToString = map[MessageCommand]string{
|
||||||
CmdVersion: "Version",
|
CmdVersion: "Version",
|
||||||
CmdVerAck: "VerAck",
|
CmdVerAck: "VerAck",
|
||||||
CmdRequestAddresses: "RequestAddresses",
|
CmdRequestAddresses: "RequestAddresses",
|
||||||
CmdAddresses: "Addresses",
|
CmdAddresses: "Addresses",
|
||||||
CmdRequestHeaders: "CmdRequestHeaders",
|
CmdRequestIBDBlocks: "RequestBlocks",
|
||||||
CmdBlock: "Block",
|
CmdBlock: "Block",
|
||||||
CmdTx: "Tx",
|
CmdTx: "Tx",
|
||||||
CmdPing: "Ping",
|
CmdPing: "Ping",
|
||||||
CmdPong: "Pong",
|
CmdPong: "Pong",
|
||||||
CmdRequestBlockLocator: "RequestBlockLocator",
|
CmdRequestBlockLocator: "RequestBlockLocator",
|
||||||
CmdBlockLocator: "BlockLocator",
|
CmdBlockLocator: "BlockLocator",
|
||||||
CmdInvRelayBlock: "InvRelayBlock",
|
CmdSelectedTip: "SelectedTip",
|
||||||
CmdRequestRelayBlocks: "RequestRelayBlocks",
|
CmdRequestSelectedTip: "RequestSelectedTip",
|
||||||
CmdInvTransaction: "InvTransaction",
|
CmdInvRelayBlock: "InvRelayBlock",
|
||||||
CmdRequestTransactions: "RequestTransactions",
|
CmdRequestRelayBlocks: "RequestRelayBlocks",
|
||||||
CmdDoneHeaders: "DoneHeaders",
|
CmdInvTransaction: "InvTransaction",
|
||||||
CmdTransactionNotFound: "TransactionNotFound",
|
CmdRequestTransactions: "RequestTransactions",
|
||||||
CmdReject: "Reject",
|
CmdIBDBlock: "IBDBlock",
|
||||||
CmdRequestNextHeaders: "RequestNextHeaders",
|
CmdRequestNextIBDBlocks: "RequestNextIBDBlocks",
|
||||||
CmdRequestPruningPointUTXOSet: "RequestPruningPointUTXOSet",
|
CmdDoneIBDBlocks: "DoneIBDBlocks",
|
||||||
CmdPruningPointUTXOSetChunk: "PruningPointUTXOSetChunk",
|
CmdTransactionNotFound: "TransactionNotFound",
|
||||||
CmdUnexpectedPruningPoint: "UnexpectedPruningPoint",
|
CmdReject: "Reject",
|
||||||
CmdIBDBlockLocator: "IBDBlockLocator",
|
|
||||||
CmdIBDBlockLocatorHighestHash: "IBDBlockLocatorHighestHash",
|
|
||||||
CmdIBDBlockLocatorHighestHashNotFound: "IBDBlockLocatorHighestHashNotFound",
|
|
||||||
CmdBlockHeaders: "BlockHeaders",
|
|
||||||
CmdRequestNextPruningPointUTXOSetChunk: "RequestNextPruningPointUTXOSetChunk",
|
|
||||||
CmdDonePruningPointUTXOSetChunks: "DonePruningPointUTXOSetChunks",
|
|
||||||
CmdBlockWithTrustedData: "BlockWithTrustedData",
|
|
||||||
CmdDoneBlocksWithTrustedData: "DoneBlocksWithTrustedData",
|
|
||||||
CmdRequestPruningPointAndItsAnticone: "RequestPruningPointAndItsAnticoneHeaders",
|
|
||||||
CmdIBDBlock: "IBDBlock",
|
|
||||||
CmdRequestIBDBlocks: "RequestIBDBlocks",
|
|
||||||
CmdPruningPoints: "PruningPoints",
|
|
||||||
CmdRequestPruningPointProof: "RequestPruningPointProof",
|
|
||||||
CmdPruningPointProof: "PruningPointProof",
|
|
||||||
CmdReady: "Ready",
|
|
||||||
CmdTrustedData: "TrustedData",
|
|
||||||
CmdBlockWithTrustedDataV4: "BlockWithTrustedDataV4",
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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",
|
|
||||||
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",
|
|
||||||
CmdGetBalanceByAddressRequestMessage: "GetBalanceByAddressRequest",
|
|
||||||
CmdGetBalanceByAddressResponseMessage: "GetBalancesByAddressResponse",
|
|
||||||
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",
|
|
||||||
CmdEstimateNetworkHashesPerSecondRequestMessage: "EstimateNetworkHashesPerSecondRequest",
|
|
||||||
CmdEstimateNetworkHashesPerSecondResponseMessage: "EstimateNetworkHashesPerSecondResponse",
|
|
||||||
CmdNotifyVirtualDaaScoreChangedRequestMessage: "NotifyVirtualDaaScoreChangedRequest",
|
|
||||||
CmdNotifyVirtualDaaScoreChangedResponseMessage: "NotifyVirtualDaaScoreChangedResponse",
|
|
||||||
CmdVirtualDaaScoreChangedNotificationMessage: "VirtualDaaScoreChangedNotification",
|
|
||||||
CmdGetBalancesByAddressesRequestMessage: "GetBalancesByAddressesRequest",
|
|
||||||
CmdGetBalancesByAddressesResponseMessage: "GetBalancesByAddressesResponse",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is an interface that describes a kaspa message. A type that
|
// Message is an interface that describes a kaspa message. A type that
|
||||||
|
|||||||
75
app/appmessage/msgaddresses.go
Normal file
75
app/appmessage/msgaddresses.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright (c) 2013-2015 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 (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxAddressesPerMsg is the maximum number of addresses that can be in a single
|
||||||
|
// kaspa Addresses message (MsgAddresses).
|
||||||
|
const MaxAddressesPerMsg = 1000
|
||||||
|
|
||||||
|
// MsgAddresses implements the Message interface and represents a kaspa
|
||||||
|
// Addresses message. It is used to provide a list of known active peers on the
|
||||||
|
// network. An active peer is considered one that has transmitted a message
|
||||||
|
// within the last 3 hours. Nodes which have not transmitted in that time
|
||||||
|
// frame should be forgotten. Each message is limited to a maximum number of
|
||||||
|
// addresses, which is currently 1000. As a result, multiple messages must
|
||||||
|
// be used to relay the full list.
|
||||||
|
//
|
||||||
|
// Use the AddAddress function to build up the list of known addresses when
|
||||||
|
// sending an Addresses message to another peer.
|
||||||
|
type MsgAddresses struct {
|
||||||
|
baseMessage
|
||||||
|
IncludeAllSubnetworks bool
|
||||||
|
SubnetworkID *subnetworkid.SubnetworkID
|
||||||
|
AddrList []*NetAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAddress adds a known active peer to the message.
|
||||||
|
func (msg *MsgAddresses) AddAddress(na *NetAddress) error {
|
||||||
|
if len(msg.AddrList)+1 > MaxAddressesPerMsg {
|
||||||
|
str := fmt.Sprintf("too many addresses in message [max %d]",
|
||||||
|
MaxAddressesPerMsg)
|
||||||
|
return messageError("MsgAddresses.AddAddress", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.AddrList = append(msg.AddrList, na)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAddresses adds multiple known active peers to the message.
|
||||||
|
func (msg *MsgAddresses) AddAddresses(netAddrs ...*NetAddress) error {
|
||||||
|
for _, na := range netAddrs {
|
||||||
|
err := msg.AddAddress(na)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAddresses removes all addresses from the message.
|
||||||
|
func (msg *MsgAddresses) ClearAddresses() {
|
||||||
|
msg.AddrList = []*NetAddress{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgAddresses) Command() MessageCommand {
|
||||||
|
return CmdAddresses
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgAddresses returns a new kaspa Addresses message that conforms to the
|
||||||
|
// Message interface. See MsgAddresses for details.
|
||||||
|
func NewMsgAddresses(includeAllSubnetworks bool, subnetworkID *subnetworkid.SubnetworkID) *MsgAddresses {
|
||||||
|
return &MsgAddresses{
|
||||||
|
IncludeAllSubnetworks: includeAllSubnetworks,
|
||||||
|
SubnetworkID: subnetworkID,
|
||||||
|
AddrList: make([]*NetAddress, 0, MaxAddressesPerMsg),
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/appmessage/msgaddresses_test.go
Normal file
58
app/appmessage/msgaddresses_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// 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 (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestAddresses tests the MsgAddresses API.
|
||||||
|
func TestAddresses(t *testing.T) {
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := MessageCommand(3)
|
||||||
|
msg := NewMsgAddresses(false, nil)
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgAddresses: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure NetAddresses are added properly.
|
||||||
|
tcpAddr := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
||||||
|
na := NewNetAddress(tcpAddr, SFNodeNetwork)
|
||||||
|
err := msg.AddAddress(na)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("AddAddress: %v", err)
|
||||||
|
}
|
||||||
|
if msg.AddrList[0] != na {
|
||||||
|
t.Errorf("AddAddress: wrong address added - got %v, want %v",
|
||||||
|
spew.Sprint(msg.AddrList[0]), spew.Sprint(na))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the address list is cleared properly.
|
||||||
|
msg.ClearAddresses()
|
||||||
|
if len(msg.AddrList) != 0 {
|
||||||
|
t.Errorf("ClearAddresses: address list is not empty - "+
|
||||||
|
"got %v [%v], want %v", len(msg.AddrList),
|
||||||
|
spew.Sprint(msg.AddrList[0]), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding more than the max allowed addresses per message returns
|
||||||
|
// error.
|
||||||
|
for i := 0; i < MaxAddressesPerMsg+1; i++ {
|
||||||
|
err = msg.AddAddress(na)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("AddAddress: expected error on too many addresses " +
|
||||||
|
"not received")
|
||||||
|
}
|
||||||
|
err = msg.AddAddresses(na)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("AddAddresses: expected error on too many addresses " +
|
||||||
|
"not received")
|
||||||
|
}
|
||||||
|
}
|
||||||
250
app/appmessage/msgblock.go
Normal file
250
app/appmessage/msgblock.go
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultTransactionAlloc is the default size used for the backing array
|
||||||
|
// for transactions. The transaction array will dynamically grow as needed, but
|
||||||
|
// this figure is intended to provide enough space for the number of
|
||||||
|
// transactions in the vast majority of blocks without needing to grow the
|
||||||
|
// backing array multiple times.
|
||||||
|
const defaultTransactionAlloc = 2048
|
||||||
|
|
||||||
|
// MaxMassPerBlock is the maximum total transaction mass a block may have.
|
||||||
|
const MaxMassPerBlock = 10000000
|
||||||
|
|
||||||
|
// MaxMassPerTx is the maximum total mass a transaction may have.
|
||||||
|
const MaxMassPerTx = MaxMassPerBlock / 2
|
||||||
|
|
||||||
|
// MaxTxPerBlock is the maximum number of transactions that could
|
||||||
|
// possibly fit into a block.
|
||||||
|
const MaxTxPerBlock = (MaxMassPerBlock / minTxPayload) + 1
|
||||||
|
|
||||||
|
// TxLoc holds locator data for the offset and length of where a transaction is
|
||||||
|
// located within a MsgBlock data buffer.
|
||||||
|
type TxLoc struct {
|
||||||
|
TxStart int
|
||||||
|
TxLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgBlock implements the Message interface and represents a kaspa
|
||||||
|
// block message. It is used to deliver block and transaction information in
|
||||||
|
// response to a getdata message (MsgGetData) for a given block hash.
|
||||||
|
type MsgBlock struct {
|
||||||
|
baseMessage
|
||||||
|
Header BlockHeader
|
||||||
|
Transactions []*MsgTx
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransaction adds a transaction to the message.
|
||||||
|
func (msg *MsgBlock) AddTransaction(tx *MsgTx) {
|
||||||
|
msg.Transactions = append(msg.Transactions, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearTransactions removes all transactions from the message.
|
||||||
|
func (msg *MsgBlock) ClearTransactions() {
|
||||||
|
msg.Transactions = make([]*MsgTx, 0, defaultTransactionAlloc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Deserialize for decoding blocks stored to disk, such as in a database, as
|
||||||
|
// opposed to decoding blocks from the appmessage.
|
||||||
|
func (msg *MsgBlock) KaspaDecode(r io.Reader, pver uint32) error {
|
||||||
|
err := readBlockHeader(r, pver, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
txCount, err := ReadVarInt(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent more transactions than could possibly fit into a block.
|
||||||
|
// It would be possible to cause memory exhaustion and panics without
|
||||||
|
// a sane upper bound on this count.
|
||||||
|
if txCount > MaxTxPerBlock {
|
||||||
|
str := fmt.Sprintf("too many transactions to fit into a block "+
|
||||||
|
"[count %d, max %d]", txCount, MaxTxPerBlock)
|
||||||
|
return messageError("MsgBlock.KaspaDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Transactions = make([]*MsgTx, 0, txCount)
|
||||||
|
for i := uint64(0); i < txCount; i++ {
|
||||||
|
tx := MsgTx{}
|
||||||
|
err := tx.KaspaDecode(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Transactions = append(msg.Transactions, &tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes a block from r into the receiver using a format that is
|
||||||
|
// suitable for long-term storage such as a database while respecting the
|
||||||
|
// Version field in the block. This function differs from KaspaDecode in that
|
||||||
|
// KaspaDecode decodes from the kaspa appmessage protocol as it was sent across the
|
||||||
|
// network. The appmessage encoding can technically differ depending on the protocol
|
||||||
|
// version and doesn't even really need to match the format of a stored block at
|
||||||
|
// all. As of the time this comment was written, the encoded block is the same
|
||||||
|
// in both instances, but there is a distinct difference and separating the two
|
||||||
|
// allows the API to be flexible enough to deal with changes.
|
||||||
|
func (msg *MsgBlock) Deserialize(r io.Reader) error {
|
||||||
|
// At the current time, there is no difference between the appmessage encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of KaspaDecode.
|
||||||
|
return msg.KaspaDecode(r, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeserializeTxLoc decodes r in the same manner Deserialize does, but it takes
|
||||||
|
// a byte buffer instead of a generic reader and returns a slice containing the
|
||||||
|
// start and length of each transaction within the raw data that is being
|
||||||
|
// deserialized.
|
||||||
|
func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
|
||||||
|
fullLen := r.Len()
|
||||||
|
|
||||||
|
// At the current time, there is no difference between the appmessage encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of existing appmessage protocol functions.
|
||||||
|
err := readBlockHeader(r, 0, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
txCount, err := ReadVarInt(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent more transactions than could possibly fit into a block.
|
||||||
|
// It would be possible to cause memory exhaustion and panics without
|
||||||
|
// a sane upper bound on this count.
|
||||||
|
if txCount > MaxTxPerBlock {
|
||||||
|
str := fmt.Sprintf("too many transactions to fit into a block "+
|
||||||
|
"[count %d, max %d]", txCount, MaxTxPerBlock)
|
||||||
|
return nil, messageError("MsgBlock.DeserializeTxLoc", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize each transaction while keeping track of its location
|
||||||
|
// within the byte stream.
|
||||||
|
msg.Transactions = make([]*MsgTx, 0, txCount)
|
||||||
|
txLocs := make([]TxLoc, txCount)
|
||||||
|
for i := uint64(0); i < txCount; i++ {
|
||||||
|
txLocs[i].TxStart = fullLen - r.Len()
|
||||||
|
tx := MsgTx{}
|
||||||
|
err := tx.Deserialize(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Transactions = append(msg.Transactions, &tx)
|
||||||
|
txLocs[i].TxLen = (fullLen - r.Len()) - txLocs[i].TxStart
|
||||||
|
}
|
||||||
|
|
||||||
|
return txLocs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Serialize for encoding blocks to be stored to disk, such as in a
|
||||||
|
// database, as opposed to encoding blocks for the appmessage.
|
||||||
|
func (msg *MsgBlock) KaspaEncode(w io.Writer, pver uint32) error {
|
||||||
|
err := writeBlockHeader(w, pver, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteVarInt(w, uint64(len(msg.Transactions)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tx := range msg.Transactions {
|
||||||
|
err = tx.KaspaEncode(w, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize encodes the block to w using a format that suitable for long-term
|
||||||
|
// storage such as a database while respecting the Version field in the block.
|
||||||
|
// This function differs from KaspaEncode in that KaspaEncode encodes the block to
|
||||||
|
// the kaspa appmessage protocol in order to be sent across the network. The appmessage
|
||||||
|
// encoding can technically differ depending on the protocol version and doesn't
|
||||||
|
// even really need to match the format of a stored block at all. As of the
|
||||||
|
// time this comment was written, the encoded block is the same in both
|
||||||
|
// instances, but there is a distinct difference and separating the two allows
|
||||||
|
// the API to be flexible enough to deal with changes.
|
||||||
|
func (msg *MsgBlock) Serialize(w io.Writer) error {
|
||||||
|
// At the current time, there is no difference between the appmessage encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of KaspaEncode.
|
||||||
|
return msg.KaspaEncode(w, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeSize returns the number of bytes it would take to serialize the
|
||||||
|
// block.
|
||||||
|
func (msg *MsgBlock) SerializeSize() int {
|
||||||
|
// Block header bytes + Serialized varint size for the number of
|
||||||
|
// transactions.
|
||||||
|
n := msg.Header.SerializeSize() + VarIntSerializeSize(uint64(len(msg.Transactions)))
|
||||||
|
|
||||||
|
for _, tx := range msg.Transactions {
|
||||||
|
n += tx.SerializeSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgBlock) Command() MessageCommand {
|
||||||
|
return CmdBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return MaxMessagePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockHash computes the block identifier hash for this block.
|
||||||
|
func (msg *MsgBlock) BlockHash() *daghash.Hash {
|
||||||
|
return msg.Header.BlockHash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertToPartial clears out all the payloads of the subnetworks that are
|
||||||
|
// incompatible with the given subnetwork ID.
|
||||||
|
// Note: this operation modifies the block in place.
|
||||||
|
func (msg *MsgBlock) ConvertToPartial(subnetworkID *subnetworkid.SubnetworkID) {
|
||||||
|
for _, tx := range msg.Transactions {
|
||||||
|
if !tx.SubnetworkID.IsEqual(subnetworkID) {
|
||||||
|
tx.Payload = []byte{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgBlock returns a new kaspa block message that conforms to the
|
||||||
|
// Message interface. See MsgBlock for details.
|
||||||
|
func NewMsgBlock(blockHeader *BlockHeader) *MsgBlock {
|
||||||
|
return &MsgBlock{
|
||||||
|
Header: *blockHeader,
|
||||||
|
Transactions: make([]*MsgTx, 0, defaultTransactionAlloc),
|
||||||
|
}
|
||||||
|
}
|
||||||
610
app/appmessage/msgblock_test.go
Normal file
610
app/appmessage/msgblock_test.go
Normal file
@@ -0,0 +1,610 @@
|
|||||||
|
// 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"
|
||||||
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestBlock tests the MsgBlock API.
|
||||||
|
func TestBlock(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
// Block 1 header.
|
||||||
|
parentHashes := blockOne.Header.ParentHashes
|
||||||
|
hashMerkleRoot := blockOne.Header.HashMerkleRoot
|
||||||
|
acceptedIDMerkleRoot := blockOne.Header.AcceptedIDMerkleRoot
|
||||||
|
utxoCommitment := blockOne.Header.UTXOCommitment
|
||||||
|
bits := blockOne.Header.Bits
|
||||||
|
nonce := blockOne.Header.Nonce
|
||||||
|
bh := NewBlockHeader(1, parentHashes, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce)
|
||||||
|
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := MessageCommand(5)
|
||||||
|
msg := NewMsgBlock(bh)
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgBlock: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
|
wantPayload := uint32(1024 * 1024 * 32)
|
||||||
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
|
if maxPayload != wantPayload {
|
||||||
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
"protocol version %d - got %v, want %v", pver,
|
||||||
|
maxPayload, wantPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we get the same block header data back out.
|
||||||
|
if !reflect.DeepEqual(&msg.Header, bh) {
|
||||||
|
t.Errorf("NewMsgBlock: wrong block header - got %v, want %v",
|
||||||
|
spew.Sdump(&msg.Header), spew.Sdump(bh))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transactions are added properly.
|
||||||
|
tx := blockOne.Transactions[0].Copy()
|
||||||
|
msg.AddTransaction(tx)
|
||||||
|
if !reflect.DeepEqual(msg.Transactions, blockOne.Transactions) {
|
||||||
|
t.Errorf("AddTransaction: wrong transactions - got %v, want %v",
|
||||||
|
spew.Sdump(msg.Transactions),
|
||||||
|
spew.Sdump(blockOne.Transactions))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transactions are properly cleared.
|
||||||
|
msg.ClearTransactions()
|
||||||
|
if len(msg.Transactions) != 0 {
|
||||||
|
t.Errorf("ClearTransactions: wrong transactions - got %v, want %v",
|
||||||
|
len(msg.Transactions), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockHash tests the ability to generate the hash of a block accurately.
|
||||||
|
func TestBlockHash(t *testing.T) {
|
||||||
|
// Block 1 hash.
|
||||||
|
hashStr := "55d71bd49a8233bc9f0edbcbd0ad5d3eaebffe1fc6a6443a1c1f310fd02c11a5"
|
||||||
|
wantHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the hash produced is expected.
|
||||||
|
blockHash := blockOne.BlockHash()
|
||||||
|
if !blockHash.IsEqual(wantHash) {
|
||||||
|
t.Errorf("BlockHash: wrong hash - got %v, want %v",
|
||||||
|
spew.Sprint(blockHash), spew.Sprint(wantHash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertToPartial(t *testing.T) {
|
||||||
|
transactions := []struct {
|
||||||
|
subnetworkID *subnetworkid.SubnetworkID
|
||||||
|
payload []byte
|
||||||
|
expectedPayloadLength int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
subnetworkID: subnetworkid.SubnetworkIDNative,
|
||||||
|
payload: []byte{},
|
||||||
|
expectedPayloadLength: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subnetworkID: subnetworkid.SubnetworkIDRegistry,
|
||||||
|
payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
|
||||||
|
expectedPayloadLength: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subnetworkID: &subnetworkid.SubnetworkID{123},
|
||||||
|
payload: []byte{0x01},
|
||||||
|
expectedPayloadLength: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subnetworkID: &subnetworkid.SubnetworkID{234},
|
||||||
|
payload: []byte{0x02},
|
||||||
|
expectedPayloadLength: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
block := MsgBlock{}
|
||||||
|
payload := []byte{1}
|
||||||
|
for _, transaction := range transactions {
|
||||||
|
block.Transactions = append(block.Transactions, NewSubnetworkMsgTx(1, nil, nil, transaction.subnetworkID, 0, payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
block.ConvertToPartial(&subnetworkid.SubnetworkID{123})
|
||||||
|
|
||||||
|
for _, transaction := range transactions {
|
||||||
|
var subnetworkTx *MsgTx
|
||||||
|
for _, tx := range block.Transactions {
|
||||||
|
if tx.SubnetworkID.IsEqual(transaction.subnetworkID) {
|
||||||
|
subnetworkTx = tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if subnetworkTx == nil {
|
||||||
|
t.Errorf("ConvertToPartial: subnetworkID '%s' not found in block!", transaction.subnetworkID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLength := len(subnetworkTx.Payload)
|
||||||
|
if payloadLength != transaction.expectedPayloadLength {
|
||||||
|
t.Errorf("ConvertToPartial: unexpected payload length for subnetwork '%s': expected: %d, got: %d",
|
||||||
|
transaction.subnetworkID, transaction.expectedPayloadLength, payloadLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockEncoding tests the MsgBlock appmessage encode and decode for various numbers
|
||||||
|
// of transaction inputs and outputs and protocol versions.
|
||||||
|
func TestBlockEncoding(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgBlock // Message to encode
|
||||||
|
out *MsgBlock // Expected decoded message
|
||||||
|
buf []byte // Encoded value
|
||||||
|
txLocs []TxLoc // Expected transaction locations
|
||||||
|
pver uint32 // Protocol version for appmessage encoding
|
||||||
|
}{
|
||||||
|
// Latest protocol version.
|
||||||
|
{
|
||||||
|
&blockOne,
|
||||||
|
&blockOne,
|
||||||
|
blockOneBytes,
|
||||||
|
blockOneTxLocs,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode the message to appmessage format.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.KaspaEncode(&buf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaEncode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("KaspaEncode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the message from appmessage format.
|
||||||
|
var msg MsgBlock
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = msg.KaspaDecode(rbuf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaDecode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&msg, test.out) {
|
||||||
|
t.Errorf("KaspaDecode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&msg), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockEncodingErrors performs negative tests against appmessage encode and decode
|
||||||
|
// of MsgBlock to confirm error paths work correctly.
|
||||||
|
func TestBlockEncodingErrors(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgBlock // 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
|
||||||
|
}{
|
||||||
|
// Force error in version.
|
||||||
|
{&blockOne, blockOneBytes, pver, 0, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in num block hashes.
|
||||||
|
{&blockOne, blockOneBytes, pver, 4, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in prev block hash #1.
|
||||||
|
{&blockOne, blockOneBytes, pver, 5, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in prev block hash #2.
|
||||||
|
{&blockOne, blockOneBytes, pver, 37, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in hash merkle root.
|
||||||
|
{&blockOne, blockOneBytes, pver, 69, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in accepted ID merkle root.
|
||||||
|
{&blockOne, blockOneBytes, pver, 101, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in utxo commitment.
|
||||||
|
{&blockOne, blockOneBytes, pver, 133, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in timestamp.
|
||||||
|
{&blockOne, blockOneBytes, pver, 165, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in difficulty bits.
|
||||||
|
{&blockOne, blockOneBytes, pver, 173, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in header nonce.
|
||||||
|
{&blockOne, blockOneBytes, pver, 177, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction count.
|
||||||
|
{&blockOne, blockOneBytes, pver, 185, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transactions.
|
||||||
|
{&blockOne, blockOneBytes, pver, 186, io.ErrShortWrite, io.EOF},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode to appmessage format.
|
||||||
|
w := newFixedWriter(test.max)
|
||||||
|
err := test.in.KaspaEncode(w, test.pver)
|
||||||
|
if !errors.Is(err, test.writeErr) {
|
||||||
|
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode from appmessage format.
|
||||||
|
var msg MsgBlock
|
||||||
|
r := newFixedReader(test.max, test.buf)
|
||||||
|
err = msg.KaspaDecode(r, test.pver)
|
||||||
|
if !errors.Is(err, test.readErr) {
|
||||||
|
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockSerialize tests MsgBlock serialize and deserialize.
|
||||||
|
func TestBlockSerialize(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgBlock // Message to encode
|
||||||
|
out *MsgBlock // Expected decoded message
|
||||||
|
buf []byte // Serialized data
|
||||||
|
txLocs []TxLoc // Expected transaction locations
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
&blockOne,
|
||||||
|
&blockOne,
|
||||||
|
blockOneBytes,
|
||||||
|
blockOneTxLocs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Serialize the block.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.Serialize(&buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Serialize #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("Serialize #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the block.
|
||||||
|
var block MsgBlock
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = block.Deserialize(rbuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Deserialize #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&block, test.out) {
|
||||||
|
t.Errorf("Deserialize #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&block), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the block while gathering transaction location
|
||||||
|
// information.
|
||||||
|
var txLocBlock MsgBlock
|
||||||
|
br := bytes.NewBuffer(test.buf)
|
||||||
|
txLocs, err := txLocBlock.DeserializeTxLoc(br)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("DeserializeTxLoc #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&txLocBlock, test.out) {
|
||||||
|
t.Errorf("DeserializeTxLoc #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&txLocBlock), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(txLocs, test.txLocs) {
|
||||||
|
t.Errorf("DeserializeTxLoc #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(txLocs), spew.Sdump(test.txLocs))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockSerializeErrors performs negative tests against appmessage encode and
|
||||||
|
// decode of MsgBlock to confirm error paths work correctly.
|
||||||
|
func TestBlockSerializeErrors(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgBlock // Value to encode
|
||||||
|
buf []byte // Serialized data
|
||||||
|
max int // Max size of fixed buffer to induce errors
|
||||||
|
writeErr error // Expected write error
|
||||||
|
readErr error // Expected read error
|
||||||
|
}{
|
||||||
|
// Force error in version.
|
||||||
|
{&blockOne, blockOneBytes, 0, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in numParentBlocks.
|
||||||
|
{&blockOne, blockOneBytes, 4, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in prev block hash #1.
|
||||||
|
{&blockOne, blockOneBytes, 5, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in prev block hash #2.
|
||||||
|
{&blockOne, blockOneBytes, 37, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in hash merkle root.
|
||||||
|
{&blockOne, blockOneBytes, 69, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in accepted ID merkle root.
|
||||||
|
{&blockOne, blockOneBytes, 101, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in utxo commitment.
|
||||||
|
{&blockOne, blockOneBytes, 133, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in timestamp.
|
||||||
|
{&blockOne, blockOneBytes, 165, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in difficulty bits.
|
||||||
|
{&blockOne, blockOneBytes, 173, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in header nonce.
|
||||||
|
{&blockOne, blockOneBytes, 177, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction count.
|
||||||
|
{&blockOne, blockOneBytes, 185, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transactions.
|
||||||
|
{&blockOne, blockOneBytes, 186, io.ErrShortWrite, io.EOF},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Serialize the block.
|
||||||
|
w := newFixedWriter(test.max)
|
||||||
|
err := test.in.Serialize(w)
|
||||||
|
if !errors.Is(err, test.writeErr) {
|
||||||
|
t.Errorf("Serialize #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the block.
|
||||||
|
var block MsgBlock
|
||||||
|
r := newFixedReader(test.max, test.buf)
|
||||||
|
err = block.Deserialize(r)
|
||||||
|
if !errors.Is(err, test.readErr) {
|
||||||
|
t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var txLocBlock MsgBlock
|
||||||
|
br := bytes.NewBuffer(test.buf[0:test.max])
|
||||||
|
_, err = txLocBlock.DeserializeTxLoc(br)
|
||||||
|
if !errors.Is(err, test.readErr) {
|
||||||
|
t.Errorf("DeserializeTxLoc #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockOverflowErrors performs tests to ensure deserializing blocks which
|
||||||
|
// are intentionally crafted to use large values for the number of transactions
|
||||||
|
// are handled properly. This could otherwise potentially be used as an attack
|
||||||
|
// vector.
|
||||||
|
func TestBlockOverflowErrors(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
buf []byte // Encoded value
|
||||||
|
pver uint32 // Protocol version for appmessage encoding
|
||||||
|
err error // Expected error
|
||||||
|
}{
|
||||||
|
// Block that claims to have ~uint64(0) transactions.
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||||
|
0x02, // NumParentBlocks
|
||||||
|
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, // mainnetGenesisHash
|
||||||
|
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
||||||
|
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
||||||
|
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
||||||
|
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||||
|
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||||
|
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||||
|
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, // HashMerkleRoot
|
||||||
|
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
|
||||||
|
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
|
||||||
|
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a,
|
||||||
|
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x61, 0xbc, 0x66, 0x49, 0x00, 0x00, 0x00, 0x00, // Timestamp
|
||||||
|
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||||
|
0x01, 0xe3, 0x62, 0x99, 0x00, 0x00, 0x00, 0x00, // Fake Nonce
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, // TxnCount
|
||||||
|
}, pver, &MessageError{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Decode from appmessage format.
|
||||||
|
var msg MsgBlock
|
||||||
|
r := bytes.NewReader(test.buf)
|
||||||
|
err := msg.KaspaDecode(r, test.pver)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||||
|
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, reflect.TypeOf(test.err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize from appmessage format.
|
||||||
|
r = bytes.NewReader(test.buf)
|
||||||
|
err = msg.Deserialize(r)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||||
|
t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, reflect.TypeOf(test.err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize with transaction location info from appmessage format.
|
||||||
|
br := bytes.NewBuffer(test.buf)
|
||||||
|
_, err = msg.DeserializeTxLoc(br)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||||
|
t.Errorf("DeserializeTxLoc #%d wrong error got: %v, "+
|
||||||
|
"want: %v", i, err, reflect.TypeOf(test.err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlockSerializeSize performs tests to ensure the serialize size for
|
||||||
|
// various blocks is accurate.
|
||||||
|
func TestBlockSerializeSize(t *testing.T) {
|
||||||
|
// Block with no transactions.
|
||||||
|
noTxBlock := NewMsgBlock(&blockOne.Header)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgBlock // Block to encode
|
||||||
|
size int // Expected serialized size
|
||||||
|
}{
|
||||||
|
// Block with no transactions.
|
||||||
|
{noTxBlock, 186},
|
||||||
|
|
||||||
|
// First block in the mainnet block DAG.
|
||||||
|
{&blockOne, len(blockOneBytes)},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
serializedSize := test.in.SerializeSize()
|
||||||
|
if serializedSize != test.size {
|
||||||
|
t.Errorf("MsgBlock.SerializeSize: #%d got: %d, want: "+
|
||||||
|
"%d", i, serializedSize, test.size)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockOne is the first block in the mainnet block DAG.
|
||||||
|
var blockOne = MsgBlock{
|
||||||
|
Header: BlockHeader{
|
||||||
|
Version: 1,
|
||||||
|
ParentHashes: []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash},
|
||||||
|
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||||
|
AcceptedIDMerkleRoot: exampleAcceptedIDMerkleRoot,
|
||||||
|
UTXOCommitment: exampleUTXOCommitment,
|
||||||
|
Timestamp: mstime.UnixMilliseconds(0x17315ed0f99),
|
||||||
|
Bits: 0x1d00ffff, // 486604799
|
||||||
|
Nonce: 0x9962e301, // 2573394689
|
||||||
|
},
|
||||||
|
Transactions: []*MsgTx{
|
||||||
|
NewNativeMsgTx(1,
|
||||||
|
[]*TxIn{
|
||||||
|
{
|
||||||
|
PreviousOutpoint: Outpoint{
|
||||||
|
TxID: daghash.TxID{},
|
||||||
|
Index: 0xffffffff,
|
||||||
|
},
|
||||||
|
SignatureScript: []byte{
|
||||||
|
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04,
|
||||||
|
},
|
||||||
|
Sequence: math.MaxUint64,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]*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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block one serialized bytes.
|
||||||
|
var blockOneBytes = []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||||
|
0x02, // NumParentBlocks
|
||||||
|
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, // mainnetGenesisHash
|
||||||
|
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
||||||
|
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
||||||
|
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
||||||
|
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
||||||
|
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||||
|
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||||
|
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||||
|
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a, // HashMerkleRoot
|
||||||
|
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
||||||
|
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
||||||
|
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
||||||
|
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
||||||
|
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||||
|
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||||
|
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||||
|
0x99, 0x0f, 0xed, 0x15, 0x73, 0x01, 0x00, 0x00, // Timestamp
|
||||||
|
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||||
|
0x01, 0xe3, 0x62, 0x99, 0x00, 0x00, 0x00, 0x00, // Fake Nonce
|
||||||
|
0x01, // TxnCount
|
||||||
|
0x01, 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,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
|
||||||
|
0xff, 0xff, 0xff, 0xff, // Prevous output index
|
||||||
|
0x07, // Varint for length of signature script
|
||||||
|
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script (coinbase)
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Sequence
|
||||||
|
0x01, // Varint for number of transaction outputs
|
||||||
|
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
|
||||||
|
0x43, // Varint for length of scriptPubKey
|
||||||
|
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 uncompressed public key
|
||||||
|
0xac, // OP_CHECKSIG
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // SubnetworkID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction location information for block one transactions.
|
||||||
|
var blockOneTxLocs = []TxLoc{
|
||||||
|
{TxStart: 186, TxLen: 162},
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
|
// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
|
||||||
@@ -13,7 +13,7 @@ const MaxBlockLocatorsPerMsg = 500
|
|||||||
// syncing with you.
|
// syncing with you.
|
||||||
type MsgBlockLocator struct {
|
type MsgBlockLocator struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
BlockLocatorHashes []*externalapi.DomainHash
|
BlockLocatorHashes []*daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -24,7 +24,7 @@ func (msg *MsgBlockLocator) Command() MessageCommand {
|
|||||||
|
|
||||||
// NewMsgBlockLocator returns a new kaspa locator message that conforms to
|
// NewMsgBlockLocator returns a new kaspa locator message that conforms to
|
||||||
// the Message interface. See MsgBlockLocator for details.
|
// the Message interface. See MsgBlockLocator for details.
|
||||||
func NewMsgBlockLocator(locatorHashes []*externalapi.DomainHash) *MsgBlockLocator {
|
func NewMsgBlockLocator(locatorHashes []*daghash.Hash) *MsgBlockLocator {
|
||||||
return &MsgBlockLocator{
|
return &MsgBlockLocator{
|
||||||
BlockLocatorHashes: locatorHashes,
|
BlockLocatorHashes: locatorHashes,
|
||||||
}
|
}
|
||||||
@@ -3,20 +3,19 @@ package appmessage
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBlockLocator tests the MsgBlockLocator API.
|
// TestBlockLocator tests the MsgBlockLocator API.
|
||||||
func TestBlockLocator(t *testing.T) {
|
func TestBlockLocator(t *testing.T) {
|
||||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
locatorHash, err := externalapi.NewDomainHashFromString(hashStr)
|
locatorHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := NewMsgBlockLocator([]*externalapi.DomainHash{locatorHash})
|
msg := NewMsgBlockLocator([]*daghash.Hash{locatorHash})
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
// Ensure the command is expected value.
|
||||||
wantCmd := MessageCommand(10)
|
wantCmd := MessageCommand(10)
|
||||||
22
app/appmessage/msgdoneibdblocks.go
Normal file
22
app/appmessage/msgdoneibdblocks.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
// MsgDoneIBDBlocks implements the Message interface and represents a kaspa
|
||||||
|
// DoneIBDBlocks message. It is used to notify the IBD syncing peer that the
|
||||||
|
// syncer sent all the requested blocks.
|
||||||
|
//
|
||||||
|
// This message has no payload.
|
||||||
|
type MsgDoneIBDBlocks struct {
|
||||||
|
baseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgDoneIBDBlocks) Command() MessageCommand {
|
||||||
|
return CmdDoneIBDBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgDoneIBDBlocks returns a new kaspa DoneIBDBlocks message that conforms to the
|
||||||
|
// Message interface.
|
||||||
|
func NewMsgDoneIBDBlocks() *MsgDoneIBDBlocks {
|
||||||
|
return &MsgDoneIBDBlocks{}
|
||||||
|
}
|
||||||
118
app/appmessage/msgibdblock_test.go
Normal file
118
app/appmessage/msgibdblock_test.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// 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"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestIBDBlock tests the MsgIBDBlock API.
|
||||||
|
func TestIBDBlock(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
// Block 1 header.
|
||||||
|
parentHashes := blockOne.Header.ParentHashes
|
||||||
|
hashMerkleRoot := blockOne.Header.HashMerkleRoot
|
||||||
|
acceptedIDMerkleRoot := blockOne.Header.AcceptedIDMerkleRoot
|
||||||
|
utxoCommitment := blockOne.Header.UTXOCommitment
|
||||||
|
bits := blockOne.Header.Bits
|
||||||
|
nonce := blockOne.Header.Nonce
|
||||||
|
bh := NewBlockHeader(1, parentHashes, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce)
|
||||||
|
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := MessageCommand(17)
|
||||||
|
msg := NewMsgIBDBlock(NewMsgBlock(bh))
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgIBDBlock: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
|
wantPayload := uint32(1024 * 1024 * 32)
|
||||||
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
|
if maxPayload != wantPayload {
|
||||||
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
"protocol version %d - got %v, want %v", pver,
|
||||||
|
maxPayload, wantPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we get the same block header data back out.
|
||||||
|
if !reflect.DeepEqual(&msg.Header, bh) {
|
||||||
|
t.Errorf("NewMsgIBDBlock: wrong block header - got %v, want %v",
|
||||||
|
spew.Sdump(&msg.Header), spew.Sdump(bh))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transactions are added properly.
|
||||||
|
tx := blockOne.Transactions[0].Copy()
|
||||||
|
msg.AddTransaction(tx)
|
||||||
|
if !reflect.DeepEqual(msg.Transactions, blockOne.Transactions) {
|
||||||
|
t.Errorf("AddTransaction: wrong transactions - got %v, want %v",
|
||||||
|
spew.Sdump(msg.Transactions),
|
||||||
|
spew.Sdump(blockOne.Transactions))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transactions are properly cleared.
|
||||||
|
msg.ClearTransactions()
|
||||||
|
if len(msg.Transactions) != 0 {
|
||||||
|
t.Errorf("ClearTransactions: wrong transactions - got %v, want %v",
|
||||||
|
len(msg.Transactions), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIBDBlockEncoding tests the MsgIBDBlock appmessage encode and decode for various numbers
|
||||||
|
// of transaction inputs and outputs and protocol versions.
|
||||||
|
func TestIBDBlockEncoding(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgIBDBlock // Message to encode
|
||||||
|
out *MsgIBDBlock // Expected decoded message
|
||||||
|
buf []byte // Encoded value
|
||||||
|
txLocs []TxLoc // Expected transaction locations
|
||||||
|
pver uint32 // Protocol version for appmessage encoding
|
||||||
|
}{
|
||||||
|
// Latest protocol version.
|
||||||
|
{
|
||||||
|
&MsgIBDBlock{MsgBlock: &blockOne},
|
||||||
|
&MsgIBDBlock{MsgBlock: &blockOne},
|
||||||
|
blockOneBytes,
|
||||||
|
blockOneTxLocs,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode the message to appmessage format.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.KaspaEncode(&buf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaEncode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("KaspaEncode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the message from appmessage format.
|
||||||
|
var msg MsgIBDBlock
|
||||||
|
msg.MsgBlock = new(MsgBlock)
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = msg.KaspaDecode(rbuf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaDecode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&msg, test.out) {
|
||||||
|
t.Errorf("KaspaDecode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&msg), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgInvRelayBlock implements the Message interface and represents a kaspa
|
// MsgInvRelayBlock implements the Message interface and represents a kaspa
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
// by sending their hash, and let the receiving node decide if it needs it.
|
// by sending their hash, and let the receiving node decide if it needs it.
|
||||||
type MsgInvRelayBlock struct {
|
type MsgInvRelayBlock struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Hash *externalapi.DomainHash
|
Hash *daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -20,7 +20,7 @@ func (msg *MsgInvRelayBlock) Command() MessageCommand {
|
|||||||
|
|
||||||
// NewMsgInvBlock returns a new kaspa invrelblk message that conforms to
|
// NewMsgInvBlock returns a new kaspa invrelblk message that conforms to
|
||||||
// the Message interface. See MsgInvRelayBlock for details.
|
// the Message interface. See MsgInvRelayBlock for details.
|
||||||
func NewMsgInvBlock(hash *externalapi.DomainHash) *MsgInvRelayBlock {
|
func NewMsgInvBlock(hash *daghash.Hash) *MsgInvRelayBlock {
|
||||||
return &MsgInvRelayBlock{
|
return &MsgInvRelayBlock{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxInvPerTxInvMsg is the maximum number of hashes that can
|
// MaxInvPerTxInvMsg is the maximum number of hashes that can
|
||||||
@@ -13,7 +13,7 @@ const MaxInvPerTxInvMsg = MaxInvPerMsg
|
|||||||
// by sending their ID, and let the receiving node decide if it needs it.
|
// by sending their ID, and let the receiving node decide if it needs it.
|
||||||
type MsgInvTransaction struct {
|
type MsgInvTransaction struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
TxIDs []*externalapi.DomainTransactionID
|
TxIDs []*daghash.TxID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -24,7 +24,7 @@ func (msg *MsgInvTransaction) Command() MessageCommand {
|
|||||||
|
|
||||||
// NewMsgInvTransaction returns a new kaspa TxInv message that conforms to
|
// NewMsgInvTransaction returns a new kaspa TxInv message that conforms to
|
||||||
// the Message interface. See MsgInvTransaction for details.
|
// the Message interface. See MsgInvTransaction for details.
|
||||||
func NewMsgInvTransaction(ids []*externalapi.DomainTransactionID) *MsgInvTransaction {
|
func NewMsgInvTransaction(ids []*daghash.TxID) *MsgInvTransaction {
|
||||||
return &MsgInvTransaction{
|
return &MsgInvTransaction{
|
||||||
TxIDs: ids,
|
TxIDs: ids,
|
||||||
}
|
}
|
||||||
@@ -6,12 +6,17 @@ package appmessage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/random"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestPing tests the MsgPing API against the latest protocol version.
|
// TestPing tests the MsgPing API against the latest protocol version.
|
||||||
func TestPing(t *testing.T) {
|
func TestPing(t *testing.T) {
|
||||||
// Ensure we get the same nonce back out.
|
// Ensure we get the same nonce back out.
|
||||||
nonce := uint64(0x61c2c5535902862)
|
nonce, err := random.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("random.Uint64: Error generating nonce: %v", err)
|
||||||
|
}
|
||||||
msg := NewMsgPing(nonce)
|
msg := NewMsgPing(nonce)
|
||||||
if msg.Nonce != nonce {
|
if msg.Nonce != nonce {
|
||||||
t.Errorf("NewMsgPing: wrong nonce - got %v, want %v",
|
t.Errorf("NewMsgPing: wrong nonce - got %v, want %v",
|
||||||
@@ -6,11 +6,16 @@ package appmessage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/random"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestPongLatest tests the MsgPong API against the latest protocol version.
|
// TestPongLatest tests the MsgPong API against the latest protocol version.
|
||||||
func TestPongLatest(t *testing.T) {
|
func TestPongLatest(t *testing.T) {
|
||||||
nonce := uint64(0x1a05b581a5182c)
|
nonce, err := random.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("random.Uint64: error generating nonce: %v", err)
|
||||||
|
}
|
||||||
msg := NewMsgPong(nonce)
|
msg := NewMsgPong(nonce)
|
||||||
if msg.Nonce != nonce {
|
if msg.Nonce != nonce {
|
||||||
t.Errorf("NewMsgPong: wrong nonce - got %v, want %v",
|
t.Errorf("NewMsgPong: wrong nonce - got %v, want %v",
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgRequestAddresses implements the Message interface and represents a kaspa
|
// MsgRequestAddresses implements the Message interface and represents a kaspa
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
type MsgRequestAddresses struct {
|
type MsgRequestAddresses struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
IncludeAllSubnetworks bool
|
IncludeAllSubnetworks bool
|
||||||
SubnetworkID *externalapi.DomainSubnetworkID
|
SubnetworkID *subnetworkid.SubnetworkID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -28,7 +28,7 @@ func (msg *MsgRequestAddresses) Command() MessageCommand {
|
|||||||
|
|
||||||
// NewMsgRequestAddresses returns a new kaspa RequestAddresses message that conforms to the
|
// NewMsgRequestAddresses returns a new kaspa RequestAddresses message that conforms to the
|
||||||
// Message interface. See MsgRequestAddresses for details.
|
// Message interface. See MsgRequestAddresses for details.
|
||||||
func NewMsgRequestAddresses(includeAllSubnetworks bool, subnetworkID *externalapi.DomainSubnetworkID) *MsgRequestAddresses {
|
func NewMsgRequestAddresses(includeAllSubnetworks bool, subnetworkID *subnetworkid.SubnetworkID) *MsgRequestAddresses {
|
||||||
return &MsgRequestAddresses{
|
return &MsgRequestAddresses{
|
||||||
IncludeAllSubnetworks: includeAllSubnetworks,
|
IncludeAllSubnetworks: includeAllSubnetworks,
|
||||||
SubnetworkID: subnetworkID,
|
SubnetworkID: subnetworkID,
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgRequestBlockLocator implements the Message interface and represents a kaspa
|
// MsgRequestBlockLocator implements the Message interface and represents a kaspa
|
||||||
// RequestBlockLocator message. It is used to request a block locator between low
|
// RequestBlockLocator message. It is used to request a block locator between high
|
||||||
// and high hash.
|
// and low hash.
|
||||||
// The locator is returned via a locator message (MsgBlockLocator).
|
// The locator is returned via a locator message (MsgBlockLocator).
|
||||||
type MsgRequestBlockLocator struct {
|
type MsgRequestBlockLocator struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
HighHash *externalapi.DomainHash
|
HighHash *daghash.Hash
|
||||||
Limit uint32
|
LowHash *daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -23,9 +23,9 @@ func (msg *MsgRequestBlockLocator) Command() MessageCommand {
|
|||||||
// NewMsgRequestBlockLocator returns a new RequestBlockLocator message that conforms to the
|
// NewMsgRequestBlockLocator returns a new RequestBlockLocator message that conforms to the
|
||||||
// Message interface using the passed parameters and defaults for the remaining
|
// Message interface using the passed parameters and defaults for the remaining
|
||||||
// fields.
|
// fields.
|
||||||
func NewMsgRequestBlockLocator(highHash *externalapi.DomainHash, limit uint32) *MsgRequestBlockLocator {
|
func NewMsgRequestBlockLocator(highHash, lowHash *daghash.Hash) *MsgRequestBlockLocator {
|
||||||
return &MsgRequestBlockLocator{
|
return &MsgRequestBlockLocator{
|
||||||
HighHash: highHash,
|
HighHash: highHash,
|
||||||
Limit: limit,
|
LowHash: lowHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,20 +3,20 @@ package appmessage
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestRequestBlockLocator tests the MsgRequestBlockLocator API.
|
// TestRequestBlockLocator tests the MsgRequestBlockLocator API.
|
||||||
func TestRequestBlockLocator(t *testing.T) {
|
func TestRequestBlockLocator(t *testing.T) {
|
||||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
highHash, err := externalapi.NewDomainHashFromString(hashStr)
|
highHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
// Ensure the command is expected value.
|
||||||
wantCmd := MessageCommand(9)
|
wantCmd := MessageCommand(9)
|
||||||
msg := NewMsgRequestBlockLocator(highHash, 0)
|
msg := NewMsgRequestBlockLocator(highHash, &daghash.ZeroHash)
|
||||||
if cmd := msg.Command(); cmd != wantCmd {
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
t.Errorf("NewMsgRequestBlockLocator: wrong command - got %v want %v",
|
t.Errorf("NewMsgRequestBlockLocator: wrong command - got %v want %v",
|
||||||
cmd, wantCmd)
|
cmd, wantCmd)
|
||||||
34
app/appmessage/msgrequestibdblocks.go
Normal file
34
app/appmessage/msgrequestibdblocks.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgRequestIBDBlocks implements the Message interface and represents a kaspa
|
||||||
|
// RequestIBDBlocks message. It is used to request a list of blocks starting after the
|
||||||
|
// low hash and until the high hash.
|
||||||
|
type MsgRequestIBDBlocks struct {
|
||||||
|
baseMessage
|
||||||
|
LowHash *daghash.Hash
|
||||||
|
HighHash *daghash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgRequestIBDBlocks) Command() MessageCommand {
|
||||||
|
return CmdRequestIBDBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgRequstIBDBlocks returns a new kaspa RequestIBDBlocks message that conforms to the
|
||||||
|
// Message interface using the passed parameters and defaults for the remaining
|
||||||
|
// fields.
|
||||||
|
func NewMsgRequstIBDBlocks(lowHash, highHash *daghash.Hash) *MsgRequestIBDBlocks {
|
||||||
|
return &MsgRequestIBDBlocks{
|
||||||
|
LowHash: lowHash,
|
||||||
|
HighHash: highHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,26 +7,26 @@ package appmessage
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestRequstIBDBlocks tests the MsgRequestIBDBlocks API.
|
// TestRequstIBDBlocks tests the MsgRequestIBDBlocks API.
|
||||||
func TestRequstIBDBlocks(t *testing.T) {
|
func TestRequstIBDBlocks(t *testing.T) {
|
||||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
lowHash, err := externalapi.NewDomainHashFromString(hashStr)
|
lowHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hashStr = "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
highHash, err := externalapi.NewDomainHashFromString(hashStr)
|
highHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we get the same data back out.
|
// Ensure we get the same data back out.
|
||||||
msg := NewMsgRequstHeaders(lowHash, highHash)
|
msg := NewMsgRequstIBDBlocks(lowHash, highHash)
|
||||||
if !msg.HighHash.Equal(highHash) {
|
if !msg.HighHash.IsEqual(highHash) {
|
||||||
t.Errorf("NewMsgRequstIBDBlocks: wrong high hash - got %v, want %v",
|
t.Errorf("NewMsgRequstIBDBlocks: wrong high hash - got %v, want %v",
|
||||||
msg.HighHash, highHash)
|
msg.HighHash, highHash)
|
||||||
}
|
}
|
||||||
22
app/appmessage/msgrequestnextibdblocks.go
Normal file
22
app/appmessage/msgrequestnextibdblocks.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
// MsgRequestNextIBDBlocks implements the Message interface and represents a kaspa
|
||||||
|
// RequestNextIBDBlocks message. It is used to notify the IBD syncer peer to send
|
||||||
|
// more blocks.
|
||||||
|
//
|
||||||
|
// This message has no payload.
|
||||||
|
type MsgRequestNextIBDBlocks struct {
|
||||||
|
baseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgRequestNextIBDBlocks) Command() MessageCommand {
|
||||||
|
return CmdRequestNextIBDBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgRequestNextIBDBlocks returns a new kaspa RequestNextIBDBlocks message that conforms to the
|
||||||
|
// Message interface.
|
||||||
|
func NewMsgRequestNextIBDBlocks() *MsgRequestNextIBDBlocks {
|
||||||
|
return &MsgRequestNextIBDBlocks{}
|
||||||
|
}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxRequestRelayBlocksHashes is the maximum number of hashes that can
|
// MsgRequestRelayBlocksHashes is the maximum number of hashes that can
|
||||||
// be in a single RequestRelayBlocks message.
|
// be in a single RequestRelayBlocks message.
|
||||||
const MaxRequestRelayBlocksHashes = MaxInvPerMsg
|
const MsgRequestRelayBlocksHashes = MaxInvPerMsg
|
||||||
|
|
||||||
// MsgRequestRelayBlocks implements the Message interface and represents a kaspa
|
// MsgRequestRelayBlocks implements the Message interface and represents a kaspa
|
||||||
// RequestRelayBlocks message. It is used to request blocks as part of the block
|
// RequestRelayBlocks message. It is used to request blocks as part of the block
|
||||||
// relay protocol.
|
// relay protocol.
|
||||||
type MsgRequestRelayBlocks struct {
|
type MsgRequestRelayBlocks struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Hashes []*externalapi.DomainHash
|
Hashes []*daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -24,7 +24,7 @@ func (msg *MsgRequestRelayBlocks) Command() MessageCommand {
|
|||||||
|
|
||||||
// NewMsgRequestRelayBlocks returns a new kaspa RequestRelayBlocks message that conforms to
|
// NewMsgRequestRelayBlocks returns a new kaspa RequestRelayBlocks message that conforms to
|
||||||
// the Message interface. See MsgRequestRelayBlocks for details.
|
// the Message interface. See MsgRequestRelayBlocks for details.
|
||||||
func NewMsgRequestRelayBlocks(hashes []*externalapi.DomainHash) *MsgRequestRelayBlocks {
|
func NewMsgRequestRelayBlocks(hashes []*daghash.Hash) *MsgRequestRelayBlocks {
|
||||||
return &MsgRequestRelayBlocks{
|
return &MsgRequestRelayBlocks{
|
||||||
Hashes: hashes,
|
Hashes: hashes,
|
||||||
}
|
}
|
||||||
21
app/appmessage/msgrequestselectedtip.go
Normal file
21
app/appmessage/msgrequestselectedtip.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
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{}
|
||||||
|
}
|
||||||
20
app/appmessage/msgrequestselectedtip_test.go
Normal file
20
app/appmessage/msgrequestselectedtip_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// 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,7 +1,7 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxInvPerRequestTransactionsMsg is the maximum number of hashes that can
|
// MaxInvPerRequestTransactionsMsg is the maximum number of hashes that can
|
||||||
@@ -13,7 +13,7 @@ const MaxInvPerRequestTransactionsMsg = MaxInvPerMsg
|
|||||||
// transactions relay protocol.
|
// transactions relay protocol.
|
||||||
type MsgRequestTransactions struct {
|
type MsgRequestTransactions struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
IDs []*externalapi.DomainTransactionID
|
IDs []*daghash.TxID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -24,7 +24,7 @@ func (msg *MsgRequestTransactions) Command() MessageCommand {
|
|||||||
|
|
||||||
// NewMsgRequestTransactions returns a new kaspa RequestTransactions message that conforms to
|
// NewMsgRequestTransactions returns a new kaspa RequestTransactions message that conforms to
|
||||||
// the Message interface. See MsgRequestTransactions for details.
|
// the Message interface. See MsgRequestTransactions for details.
|
||||||
func NewMsgRequestTransactions(ids []*externalapi.DomainTransactionID) *MsgRequestTransactions {
|
func NewMsgRequestTransactions(ids []*daghash.TxID) *MsgRequestTransactions {
|
||||||
return &MsgRequestTransactions{
|
return &MsgRequestTransactions{
|
||||||
IDs: ids,
|
IDs: ids,
|
||||||
}
|
}
|
||||||
28
app/appmessage/msgselectedtip.go
Normal file
28
app/appmessage/msgselectedtip.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 *daghash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *daghash.Hash) *MsgSelectedTip {
|
||||||
|
return &MsgSelectedTip{
|
||||||
|
SelectedTipHash: selectedTipHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app/appmessage/msgselectedtip_test.go
Normal file
18
app/appmessage/msgselectedtip_test.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSelectedTip tests the MsgSelectedTip API.
|
||||||
|
func TestSelectedTip(t *testing.T) {
|
||||||
|
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := MessageCommand(11)
|
||||||
|
msg := NewMsgSelectedTip(&daghash.ZeroHash)
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgSelectedTip: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgTransactionNotFound defines a kaspa TransactionNotFound message which is sent in response to
|
// MsgTransactionNotFound defines a kaspa TransactionNotFound message which is sent in response to
|
||||||
// a RequestTransactions message if any of the requested data in not available on the peer.
|
// a RequestTransactions message if any of the requested data in not available on the peer.
|
||||||
type MsgTransactionNotFound struct {
|
type MsgTransactionNotFound struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
ID *externalapi.DomainTransactionID
|
ID *daghash.TxID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
@@ -23,7 +23,7 @@ func (msg *MsgTransactionNotFound) Command() MessageCommand {
|
|||||||
|
|
||||||
// NewMsgTransactionNotFound returns a new kaspa transactionsnotfound message that conforms to the
|
// NewMsgTransactionNotFound returns a new kaspa transactionsnotfound message that conforms to the
|
||||||
// Message interface. See MsgTransactionNotFound for details.
|
// Message interface. See MsgTransactionNotFound for details.
|
||||||
func NewMsgTransactionNotFound(id *externalapi.DomainTransactionID) *MsgTransactionNotFound {
|
func NewMsgTransactionNotFound(id *daghash.TxID) *MsgTransactionNotFound {
|
||||||
return &MsgTransactionNotFound{
|
return &MsgTransactionNotFound{
|
||||||
ID: id,
|
ID: id,
|
||||||
}
|
}
|
||||||
1004
app/appmessage/msgtx.go
Normal file
1004
app/appmessage/msgtx.go
Normal file
File diff suppressed because it is too large
Load Diff
942
app/appmessage/msgtx_test.go
Normal file
942
app/appmessage/msgtx_test.go
Normal file
@@ -0,0 +1,942 @@
|
|||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestTx tests the MsgTx API.
|
||||||
|
func TestTx(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
txIDStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||||
|
txID, err := daghash.NewTxIDFromStr(txIDStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewTxIDFromStr: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := MessageCommand(6)
|
||||||
|
msg := NewNativeMsgTx(1, nil, nil)
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgAddresses: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
|
wantPayload := uint32(1024 * 1024 * 32)
|
||||||
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
|
if maxPayload != wantPayload {
|
||||||
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
"protocol version %d - got %v, want %v", pver,
|
||||||
|
maxPayload, wantPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we get the same transaction outpoint data back out.
|
||||||
|
// NOTE: This is a block hash and made up index, but we're only
|
||||||
|
// testing package functionality.
|
||||||
|
prevOutIndex := uint32(1)
|
||||||
|
prevOut := NewOutpoint(txID, prevOutIndex)
|
||||||
|
if !prevOut.TxID.IsEqual(txID) {
|
||||||
|
t.Errorf("NewOutpoint: wrong ID - got %v, want %v",
|
||||||
|
spew.Sprint(&prevOut.TxID), spew.Sprint(txID))
|
||||||
|
}
|
||||||
|
if prevOut.Index != prevOutIndex {
|
||||||
|
t.Errorf("NewOutpoint: wrong index - got %v, want %v",
|
||||||
|
prevOut.Index, prevOutIndex)
|
||||||
|
}
|
||||||
|
prevOutStr := fmt.Sprintf("%s:%d", txID.String(), prevOutIndex)
|
||||||
|
if s := prevOut.String(); s != prevOutStr {
|
||||||
|
t.Errorf("Outpoint.String: unexpected result - got %v, "+
|
||||||
|
"want %v", s, prevOutStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we get the same transaction input back out.
|
||||||
|
sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}
|
||||||
|
txIn := NewTxIn(prevOut, sigScript)
|
||||||
|
if !reflect.DeepEqual(&txIn.PreviousOutpoint, prevOut) {
|
||||||
|
t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v",
|
||||||
|
spew.Sprint(&txIn.PreviousOutpoint),
|
||||||
|
spew.Sprint(prevOut))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(txIn.SignatureScript, sigScript) {
|
||||||
|
t.Errorf("NewTxIn: wrong signature script - got %v, want %v",
|
||||||
|
spew.Sdump(txIn.SignatureScript),
|
||||||
|
spew.Sdump(sigScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
t.Errorf("NewTxOut: wrong scriptPubKey - got %v, want %v",
|
||||||
|
spew.Sdump(txOut.ScriptPubKey),
|
||||||
|
spew.Sdump(scriptPubKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transaction inputs are added properly.
|
||||||
|
msg.AddTxIn(txIn)
|
||||||
|
if !reflect.DeepEqual(msg.TxIn[0], txIn) {
|
||||||
|
t.Errorf("AddTxIn: wrong transaction input added - got %v, want %v",
|
||||||
|
spew.Sprint(msg.TxIn[0]), spew.Sprint(txIn))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transaction outputs are added properly.
|
||||||
|
msg.AddTxOut(txOut)
|
||||||
|
if !reflect.DeepEqual(msg.TxOut[0], txOut) {
|
||||||
|
t.Errorf("AddTxIn: wrong transaction output added - got %v, want %v",
|
||||||
|
spew.Sprint(msg.TxOut[0]), spew.Sprint(txOut))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the copy produced an identical transaction message.
|
||||||
|
newMsg := msg.Copy()
|
||||||
|
if !reflect.DeepEqual(newMsg, msg) {
|
||||||
|
t.Errorf("Copy: mismatched tx messages - got %v, want %v",
|
||||||
|
spew.Sdump(newMsg), spew.Sdump(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
||||||
|
func TestTxHashAndID(t *testing.T) {
|
||||||
|
txID1Str := "edca872f27279674c7a52192b32fd68b8b8be714bfea52d98b2c3c86c30e85c6"
|
||||||
|
wantTxID1, err := daghash.NewTxIDFromStr(txID1Str)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewTxIDFromStr: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A coinbase transaction
|
||||||
|
txIn := &TxIn{
|
||||||
|
PreviousOutpoint: Outpoint{
|
||||||
|
TxID: daghash.TxID{},
|
||||||
|
Index: math.MaxUint32,
|
||||||
|
},
|
||||||
|
SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
|
||||||
|
Sequence: math.MaxUint64,
|
||||||
|
}
|
||||||
|
txOut := &TxOut{
|
||||||
|
Value: 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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tx1 := NewSubnetworkMsgTx(1, []*TxIn{txIn}, []*TxOut{txOut}, subnetworkid.SubnetworkIDCoinbase, 0, nil)
|
||||||
|
|
||||||
|
// Ensure the hash produced is expected.
|
||||||
|
tx1Hash := tx1.TxHash()
|
||||||
|
if !tx1Hash.IsEqual((*daghash.Hash)(wantTxID1)) {
|
||||||
|
t.Errorf("TxHash: wrong hash - got %v, want %v",
|
||||||
|
spew.Sprint(tx1Hash), spew.Sprint(wantTxID1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the TxID for coinbase transaction is the same as TxHash.
|
||||||
|
tx1ID := tx1.TxID()
|
||||||
|
if !tx1ID.IsEqual(wantTxID1) {
|
||||||
|
t.Errorf("TxID: wrong ID - got %v, want %v",
|
||||||
|
spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
|
||||||
|
}
|
||||||
|
|
||||||
|
hash2Str := "b11924b7eeffea821522222576c53dc5b8ddd97602f81e5e124d2626646d74ca"
|
||||||
|
wantHash2, err := daghash.NewHashFromStr(hash2Str)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewTxIDFromStr: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id2Str := "750499ae9e6d44961ef8bad8af27a44dd4bcbea166b71baf181e8d3997e1ff72"
|
||||||
|
wantID2, err := daghash.NewTxIDFromStr(id2Str)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewTxIDFromStr: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
payload := []byte{1, 2, 3}
|
||||||
|
txIns := []*TxIn{{
|
||||||
|
PreviousOutpoint: Outpoint{
|
||||||
|
Index: 0,
|
||||||
|
TxID: daghash.TxID{1, 2, 3},
|
||||||
|
},
|
||||||
|
SignatureScript: []byte{
|
||||||
|
0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xDA, 0x0D, 0xC6, 0xAE, 0xCE, 0xFE, 0x1E, 0x06, 0xEF, 0xDF,
|
||||||
|
0x05, 0x77, 0x37, 0x57, 0xDE, 0xB1, 0x68, 0x82, 0x09, 0x30, 0xE3, 0xB0, 0xD0, 0x3F, 0x46, 0xF5,
|
||||||
|
0xFC, 0xF1, 0x50, 0xBF, 0x99, 0x0C, 0x02, 0x21, 0x00, 0xD2, 0x5B, 0x5C, 0x87, 0x04, 0x00, 0x76,
|
||||||
|
0xE4, 0xF2, 0x53, 0xF8, 0x26, 0x2E, 0x76, 0x3E, 0x2D, 0xD5, 0x1E, 0x7F, 0xF0, 0xBE, 0x15, 0x77,
|
||||||
|
0x27, 0xC4, 0xBC, 0x42, 0x80, 0x7F, 0x17, 0xBD, 0x39, 0x01, 0x41, 0x04, 0xE6, 0xC2, 0x6E, 0xF6,
|
||||||
|
0x7D, 0xC6, 0x10, 0xD2, 0xCD, 0x19, 0x24, 0x84, 0x78, 0x9A, 0x6C, 0xF9, 0xAE, 0xA9, 0x93, 0x0B,
|
||||||
|
0x94, 0x4B, 0x7E, 0x2D, 0xB5, 0x34, 0x2B, 0x9D, 0x9E, 0x5B, 0x9F, 0xF7, 0x9A, 0xFF, 0x9A, 0x2E,
|
||||||
|
0xE1, 0x97, 0x8D, 0xD7, 0xFD, 0x01, 0xDF, 0xC5, 0x22, 0xEE, 0x02, 0x28, 0x3D, 0x3B, 0x06, 0xA9,
|
||||||
|
0xD0, 0x3A, 0xCF, 0x80, 0x96, 0x96, 0x8D, 0x7D, 0xBB, 0x0F, 0x91, 0x78,
|
||||||
|
},
|
||||||
|
Sequence: math.MaxUint64,
|
||||||
|
}}
|
||||||
|
txOuts := []*TxOut{
|
||||||
|
{
|
||||||
|
Value: 244623243,
|
||||||
|
ScriptPubKey: []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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 44602432,
|
||||||
|
ScriptPubKey: []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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tx2 := NewSubnetworkMsgTx(1, txIns, txOuts, &subnetworkid.SubnetworkID{1, 2, 3}, 0, payload)
|
||||||
|
|
||||||
|
// Ensure the hash produced is expected.
|
||||||
|
tx2Hash := tx2.TxHash()
|
||||||
|
if !tx2Hash.IsEqual(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.IsEqual(wantID2) {
|
||||||
|
t.Errorf("TxID: wrong ID - got %v, want %v",
|
||||||
|
spew.Sprint(tx2ID), spew.Sprint(wantID2))
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx2ID.IsEqual((*daghash.TxID)(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.IsEqual((*daghash.TxID)(newTx2Hash)) {
|
||||||
|
t.Errorf("tx2ID and newTx2Hash should be the same for transaction with an empty signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTxEncoding tests the MsgTx appmessage encode and decode for various numbers
|
||||||
|
// of transaction inputs and outputs and protocol versions.
|
||||||
|
func TestTxEncoding(t *testing.T) {
|
||||||
|
// Empty tx message.
|
||||||
|
noTx := NewNativeMsgTx(1, nil, nil)
|
||||||
|
noTxEncoded := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version
|
||||||
|
0x00, // Varint for number of input transactions
|
||||||
|
0x00, // Varint for number of output transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // Sub Network ID
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgTx // Message to encode
|
||||||
|
out *MsgTx // Expected decoded message
|
||||||
|
buf []byte // Encoded value
|
||||||
|
pver uint32 // Protocol version for appmessage encoding
|
||||||
|
}{
|
||||||
|
// Latest protocol version with no transactions.
|
||||||
|
{
|
||||||
|
noTx,
|
||||||
|
noTx,
|
||||||
|
noTxEncoded,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Latest protocol version with multiple transactions.
|
||||||
|
{
|
||||||
|
multiTx,
|
||||||
|
multiTx,
|
||||||
|
multiTxEncoded,
|
||||||
|
ProtocolVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode the message to appmessage format.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.KaspaEncode(&buf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaEncode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("KaspaEncode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the message from appmessage format.
|
||||||
|
var msg MsgTx
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = msg.KaspaDecode(rbuf, test.pver)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("KaspaDecode #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&msg, test.out) {
|
||||||
|
t.Errorf("KaspaDecode #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&msg), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTxEncodingErrors performs negative tests against appmessage encode and decode
|
||||||
|
// of MsgTx to confirm error paths work correctly.
|
||||||
|
func TestTxEncodingErrors(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgTx // 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
|
||||||
|
}{
|
||||||
|
// Force error in version.
|
||||||
|
{multiTx, multiTxEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in number of transaction inputs.
|
||||||
|
{multiTx, multiTxEncoded, pver, 4, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input previous block hash.
|
||||||
|
{multiTx, multiTxEncoded, pver, 5, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input previous block output index.
|
||||||
|
{multiTx, multiTxEncoded, pver, 37, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input signature script length.
|
||||||
|
{multiTx, multiTxEncoded, pver, 41, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input signature script.
|
||||||
|
{multiTx, multiTxEncoded, pver, 42, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input sequence.
|
||||||
|
{multiTx, multiTxEncoded, pver, 49, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in number of transaction outputs.
|
||||||
|
{multiTx, multiTxEncoded, pver, 57, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output value.
|
||||||
|
{multiTx, multiTxEncoded, pver, 58, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output scriptPubKey length.
|
||||||
|
{multiTx, multiTxEncoded, pver, 66, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output scriptPubKey.
|
||||||
|
{multiTx, multiTxEncoded, pver, 67, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output lock time.
|
||||||
|
{multiTx, multiTxEncoded, pver, 210, io.ErrShortWrite, io.EOF},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Encode to appmessage format.
|
||||||
|
w := newFixedWriter(test.max)
|
||||||
|
err := test.in.KaspaEncode(w, test.pver)
|
||||||
|
if !errors.Is(err, test.writeErr) {
|
||||||
|
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode from appmessage format.
|
||||||
|
var msg MsgTx
|
||||||
|
r := newFixedReader(test.max, test.buf)
|
||||||
|
err = msg.KaspaDecode(r, test.pver)
|
||||||
|
if !errors.Is(err, test.readErr) {
|
||||||
|
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTxSerialize tests MsgTx serialize and deserialize.
|
||||||
|
func TestTxSerialize(t *testing.T) {
|
||||||
|
noTx := NewNativeMsgTx(1, nil, nil)
|
||||||
|
noTxEncoded := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version
|
||||||
|
0x00, // Varint for number of input transactions
|
||||||
|
0x00, // Varint for number of output transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // Sub Network ID
|
||||||
|
}
|
||||||
|
|
||||||
|
registryTx := NewRegistryMsgTx(1, nil, nil, 16)
|
||||||
|
registryTxEncoded := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version
|
||||||
|
0x00, // Varint for number of input transactions
|
||||||
|
0x00, // Varint for number of output transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // Sub Network ID
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Gas
|
||||||
|
0x77, 0x56, 0x36, 0xb4, 0x89, 0x32, 0xe9, 0xa8,
|
||||||
|
0xbb, 0x67, 0xe6, 0x54, 0x84, 0x36, 0x93, 0x8d,
|
||||||
|
0x9f, 0xc5, 0x62, 0x49, 0x79, 0x5c, 0x0d, 0x0a,
|
||||||
|
0x86, 0xaf, 0x7c, 0x5d, 0x54, 0x45, 0x4c, 0x4b, // Payload hash
|
||||||
|
0x08, // Payload length varint
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Payload / Gas limit
|
||||||
|
}
|
||||||
|
|
||||||
|
subnetworkTx := NewSubnetworkMsgTx(1, nil, nil, &subnetworkid.SubnetworkID{0xff}, 5, []byte{0, 1, 2})
|
||||||
|
|
||||||
|
subnetworkTxEncoded := []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version
|
||||||
|
0x00, // Varint for number of input transactions
|
||||||
|
0x00, // Varint for number of output transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
||||||
|
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // Sub Network ID
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Gas
|
||||||
|
0x35, 0xf9, 0xf2, 0x93, 0x0e, 0xa3, 0x44, 0x61,
|
||||||
|
0x88, 0x22, 0x79, 0x5e, 0xee, 0xc5, 0x68, 0xae,
|
||||||
|
0x67, 0xab, 0x29, 0x87, 0xd8, 0xb1, 0x9e, 0x45,
|
||||||
|
0x91, 0xe1, 0x05, 0x27, 0xba, 0xa1, 0xdf, 0x3d, // Payload hash
|
||||||
|
0x03, // Payload length varint
|
||||||
|
0x00, 0x01, 0x02, // Payload
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in *MsgTx // Message to encode
|
||||||
|
out *MsgTx // Expected decoded message
|
||||||
|
buf []byte // Serialized data
|
||||||
|
scriptPubKeyLocs []int // Expected output script locations
|
||||||
|
}{
|
||||||
|
// No transactions.
|
||||||
|
{
|
||||||
|
"noTx",
|
||||||
|
noTx,
|
||||||
|
noTx,
|
||||||
|
noTxEncoded,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Registry Transaction.
|
||||||
|
{
|
||||||
|
"registryTx",
|
||||||
|
registryTx,
|
||||||
|
registryTx,
|
||||||
|
registryTxEncoded,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Sub Network Transaction.
|
||||||
|
{
|
||||||
|
"subnetworkTx",
|
||||||
|
subnetworkTx,
|
||||||
|
subnetworkTx,
|
||||||
|
subnetworkTxEncoded,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Multiple transactions.
|
||||||
|
{
|
||||||
|
"multiTx",
|
||||||
|
multiTx,
|
||||||
|
multiTx,
|
||||||
|
multiTxEncoded,
|
||||||
|
multiTxScriptPubKeyLocs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Serialize the transaction.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := test.in.Serialize(&buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Serialize %s: error %v", test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
||||||
|
t.Errorf("Serialize %s:\n got: %s want: %s", test.name,
|
||||||
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the transaction.
|
||||||
|
var tx MsgTx
|
||||||
|
rbuf := bytes.NewReader(test.buf)
|
||||||
|
err = tx.Deserialize(rbuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Deserialize #%d error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&tx, test.out) {
|
||||||
|
t.Errorf("Deserialize #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(&tx), spew.Sdump(test.out))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the public key script locations are accurate.
|
||||||
|
scriptPubKeyLocs := test.in.ScriptPubKeyLocs()
|
||||||
|
if !reflect.DeepEqual(scriptPubKeyLocs, test.scriptPubKeyLocs) {
|
||||||
|
t.Errorf("ScriptPubKeyLocs #%d\n got: %s want: %s", i,
|
||||||
|
spew.Sdump(scriptPubKeyLocs),
|
||||||
|
spew.Sdump(test.scriptPubKeyLocs))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j, loc := range scriptPubKeyLocs {
|
||||||
|
wantScriptPubKey := test.in.TxOut[j].ScriptPubKey
|
||||||
|
gotScriptPubKey := test.buf[loc : loc+len(wantScriptPubKey)]
|
||||||
|
if !bytes.Equal(gotScriptPubKey, wantScriptPubKey) {
|
||||||
|
t.Errorf("ScriptPubKeyLocs #%d:%d\n unexpected "+
|
||||||
|
"script got: %s want: %s", i, j,
|
||||||
|
spew.Sdump(gotScriptPubKey),
|
||||||
|
spew.Sdump(wantScriptPubKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTxSerializeErrors performs negative tests against appmessage encode and decode
|
||||||
|
// of MsgTx to confirm error paths work correctly.
|
||||||
|
func TestTxSerializeErrors(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgTx // Value to encode
|
||||||
|
buf []byte // Serialized data
|
||||||
|
max int // Max size of fixed buffer to induce errors
|
||||||
|
writeErr error // Expected write error
|
||||||
|
readErr error // Expected read error
|
||||||
|
}{
|
||||||
|
// Force error in version.
|
||||||
|
{multiTx, multiTxEncoded, 0, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in number of transaction inputs.
|
||||||
|
{multiTx, multiTxEncoded, 4, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input previous block hash.
|
||||||
|
{multiTx, multiTxEncoded, 5, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input previous block output index.
|
||||||
|
{multiTx, multiTxEncoded, 37, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input signature script length.
|
||||||
|
{multiTx, multiTxEncoded, 41, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input signature script.
|
||||||
|
{multiTx, multiTxEncoded, 42, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction input sequence.
|
||||||
|
{multiTx, multiTxEncoded, 49, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in number of transaction outputs.
|
||||||
|
{multiTx, multiTxEncoded, 57, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output value.
|
||||||
|
{multiTx, multiTxEncoded, 58, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output scriptPubKey length.
|
||||||
|
{multiTx, multiTxEncoded, 66, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output scriptPubKey.
|
||||||
|
{multiTx, multiTxEncoded, 67, io.ErrShortWrite, io.EOF},
|
||||||
|
// Force error in transaction output lock time.
|
||||||
|
{multiTx, multiTxEncoded, 210, io.ErrShortWrite, io.EOF},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Serialize the transaction.
|
||||||
|
w := newFixedWriter(test.max)
|
||||||
|
err := test.in.Serialize(w)
|
||||||
|
if !errors.Is(err, test.writeErr) {
|
||||||
|
t.Errorf("Serialize #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.writeErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the transaction.
|
||||||
|
var tx MsgTx
|
||||||
|
r := newFixedReader(test.max, test.buf)
|
||||||
|
err = tx.Deserialize(r)
|
||||||
|
if !errors.Is(err, test.readErr) {
|
||||||
|
t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, test.readErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registryTx := NewSubnetworkMsgTx(1, nil, nil, subnetworkid.SubnetworkIDRegistry, 1, nil)
|
||||||
|
|
||||||
|
w := bytes.NewBuffer(make([]byte, 0, registryTx.SerializeSize()))
|
||||||
|
err := registryTx.Serialize(w)
|
||||||
|
str := "Transactions from built-in should have 0 gas"
|
||||||
|
expectedErr := messageError("MsgTx.KaspaEncode", str)
|
||||||
|
if err == nil || err.Error() != expectedErr.Error() {
|
||||||
|
t.Errorf("TestTxSerializeErrors: expected error %v but got %v", expectedErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeTx := NewSubnetworkMsgTx(1, nil, nil, subnetworkid.SubnetworkIDNative, 1, nil)
|
||||||
|
w = bytes.NewBuffer(make([]byte, 0, registryTx.SerializeSize()))
|
||||||
|
err = nativeTx.Serialize(w)
|
||||||
|
|
||||||
|
str = "Transactions from native subnetwork should have 0 gas"
|
||||||
|
expectedErr = messageError("MsgTx.KaspaEncode", str)
|
||||||
|
if err == nil || err.Error() != expectedErr.Error() {
|
||||||
|
t.Errorf("TestTxSerializeErrors: expected error %v but got %v", expectedErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeTx.Gas = 0
|
||||||
|
nativeTx.Payload = []byte{1, 2, 3}
|
||||||
|
nativeTx.PayloadHash = daghash.DoubleHashP(nativeTx.Payload)
|
||||||
|
w = bytes.NewBuffer(make([]byte, 0, registryTx.SerializeSize()))
|
||||||
|
err = nativeTx.Serialize(w)
|
||||||
|
|
||||||
|
str = "Transactions from native subnetwork should have <nil> payload"
|
||||||
|
expectedErr = messageError("MsgTx.KaspaEncode", str)
|
||||||
|
if err == nil || err.Error() != expectedErr.Error() {
|
||||||
|
t.Errorf("TestTxSerializeErrors: expected error %v but got %v", expectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTxOverflowErrors performs tests to ensure deserializing transactions
|
||||||
|
// which are intentionally crafted to use large values for the variable number
|
||||||
|
// of inputs and outputs are handled properly. This could otherwise potentially
|
||||||
|
// be used as an attack vector.
|
||||||
|
func TestTxOverflowErrors(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
txVer := uint32(1)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
buf []byte // Encoded value
|
||||||
|
pver uint32 // Protocol version for appmessage encoding
|
||||||
|
version uint32 // Transaction version
|
||||||
|
err error // Expected error
|
||||||
|
}{
|
||||||
|
// Transaction that claims to have ~uint64(0) inputs.
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x00, 0x00, 0x00, 0x01, // Version
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, // Varint for number of input transactions
|
||||||
|
}, pver, txVer, &MessageError{},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Transaction that claims to have ~uint64(0) outputs.
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x00, 0x00, 0x00, 0x01, // Version
|
||||||
|
0x00, // Varint for number of input transactions
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, // Varint for number of output transactions
|
||||||
|
}, pver, txVer, &MessageError{},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Transaction that has an input with a signature script that
|
||||||
|
// claims to have ~uint64(0) length.
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x00, 0x00, 0x00, 0x01, // Version
|
||||||
|
0x01, // Varint for number of input transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
|
||||||
|
0xff, 0xff, 0xff, 0xff, // Prevous output index
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, // Varint for length of signature script
|
||||||
|
}, pver, txVer, &MessageError{},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Transaction that has an output with a public key script
|
||||||
|
// that claims to have ~uint64(0) length.
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x00, 0x00, 0x00, 0x01, // Version
|
||||||
|
0x01, // Varint for number of input transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
|
||||||
|
0xff, 0xff, 0xff, 0xff, // Prevous output index
|
||||||
|
0x00, // Varint for length of signature script
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Sequence
|
||||||
|
0x01, // Varint for number of output transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Transaction amount
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, // Varint for length of public key script
|
||||||
|
}, pver, txVer, &MessageError{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
// Decode from appmessage format.
|
||||||
|
var msg MsgTx
|
||||||
|
r := bytes.NewReader(test.buf)
|
||||||
|
err := msg.KaspaDecode(r, test.pver)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||||
|
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, reflect.TypeOf(test.err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode from appmessage format.
|
||||||
|
r = bytes.NewReader(test.buf)
|
||||||
|
err = msg.Deserialize(r)
|
||||||
|
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
||||||
|
t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
|
||||||
|
i, err, reflect.TypeOf(test.err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTxSerializeSize performs tests to ensure the serialize size for
|
||||||
|
// various transactions is accurate.
|
||||||
|
func TestTxSerializeSize(t *testing.T) {
|
||||||
|
// Empty tx message.
|
||||||
|
noTx := NewNativeMsgTx(1, nil, nil)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgTx // Tx to encode
|
||||||
|
size int // Expected serialized size
|
||||||
|
}{
|
||||||
|
// No inputs or outpus.
|
||||||
|
{noTx, 34},
|
||||||
|
|
||||||
|
// Transcaction with an input and an output.
|
||||||
|
{multiTx, 238},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
serializedSize := test.in.SerializeSize()
|
||||||
|
if serializedSize != test.size {
|
||||||
|
t.Errorf("MsgTx.SerializeSize: #%d got: %d, want: %d", i,
|
||||||
|
serializedSize, test.size)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsSubnetworkCompatible(t *testing.T) {
|
||||||
|
testTx := NewSubnetworkMsgTx(1, nil, nil, &subnetworkid.SubnetworkID{123}, 0, []byte{})
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
subnetworkID *subnetworkid.SubnetworkID
|
||||||
|
expectedResult bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Native subnetwork",
|
||||||
|
subnetworkID: subnetworkid.SubnetworkIDNative,
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same subnetwork as test tx",
|
||||||
|
subnetworkID: &subnetworkid.SubnetworkID{123},
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "other subnetwork",
|
||||||
|
subnetworkID: &subnetworkid.SubnetworkID{234},
|
||||||
|
expectedResult: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
result := testTx.IsSubnetworkCompatible(test.subnetworkID)
|
||||||
|
if result != test.expectedResult {
|
||||||
|
t.Errorf("IsSubnetworkCompatible got unexpected result in test '%s': "+
|
||||||
|
"expected: %t, want: %t", test.name, test.expectedResult, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScriptFreeList(t *testing.T) {
|
||||||
|
var list scriptFreeList = make(chan []byte, freeListMaxItems)
|
||||||
|
|
||||||
|
expectedCapacity := 512
|
||||||
|
expectedLengthFirst := 12
|
||||||
|
expectedLengthSecond := 13
|
||||||
|
|
||||||
|
first := list.Borrow(uint64(expectedLengthFirst))
|
||||||
|
if cap(first) != expectedCapacity {
|
||||||
|
t.Errorf("MsgTx.TestScriptFreeList: Expected capacity for first %d, but got %d",
|
||||||
|
expectedCapacity, cap(first))
|
||||||
|
}
|
||||||
|
if len(first) != expectedLengthFirst {
|
||||||
|
t.Errorf("MsgTx.TestScriptFreeList: Expected length for first %d, but got %d",
|
||||||
|
expectedLengthFirst, len(first))
|
||||||
|
}
|
||||||
|
list.Return(first)
|
||||||
|
|
||||||
|
// Borrow again, and check that the underlying array is re-used for second
|
||||||
|
second := list.Borrow(uint64(expectedLengthSecond))
|
||||||
|
if cap(second) != expectedCapacity {
|
||||||
|
t.Errorf("MsgTx.TestScriptFreeList: Expected capacity for second %d, but got %d",
|
||||||
|
expectedCapacity, cap(second))
|
||||||
|
}
|
||||||
|
if len(second) != expectedLengthSecond {
|
||||||
|
t.Errorf("MsgTx.TestScriptFreeList: Expected length for second %d, but got %d",
|
||||||
|
expectedLengthSecond, len(second))
|
||||||
|
}
|
||||||
|
|
||||||
|
firstArrayAddress := underlyingArrayAddress(first)
|
||||||
|
secondArrayAddress := underlyingArrayAddress(second)
|
||||||
|
|
||||||
|
if firstArrayAddress != secondArrayAddress {
|
||||||
|
t.Errorf("First underlying array is at address %d and second at address %d, "+
|
||||||
|
"which means memory was not re-used", firstArrayAddress, secondArrayAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Return(second)
|
||||||
|
|
||||||
|
// test for buffers bigger than freeListMaxScriptSize
|
||||||
|
expectedCapacityBig := freeListMaxScriptSize + 1
|
||||||
|
expectedLengthBig := expectedCapacityBig
|
||||||
|
big := list.Borrow(uint64(expectedCapacityBig))
|
||||||
|
|
||||||
|
if cap(big) != expectedCapacityBig {
|
||||||
|
t.Errorf("MsgTx.TestScriptFreeList: Expected capacity for second %d, but got %d",
|
||||||
|
expectedCapacityBig, cap(big))
|
||||||
|
}
|
||||||
|
if len(big) != expectedLengthBig {
|
||||||
|
t.Errorf("MsgTx.TestScriptFreeList: Expected length for second %d, but got %d",
|
||||||
|
expectedLengthBig, len(big))
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Return(big)
|
||||||
|
|
||||||
|
// test there's no crash when channel is full because borrowed too much
|
||||||
|
buffers := make([][]byte, freeListMaxItems+1)
|
||||||
|
for i := 0; i < freeListMaxItems+1; i++ {
|
||||||
|
buffers[i] = list.Borrow(1)
|
||||||
|
}
|
||||||
|
for i := 0; i < freeListMaxItems+1; i++ {
|
||||||
|
list.Return(buffers[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func underlyingArrayAddress(buf []byte) uint64 {
|
||||||
|
return uint64((*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiTx is a MsgTx with an input and output and used in various tests.
|
||||||
|
var multiTxIns = []*TxIn{
|
||||||
|
{
|
||||||
|
PreviousOutpoint: Outpoint{
|
||||||
|
TxID: daghash.TxID{},
|
||||||
|
Index: 0xffffffff,
|
||||||
|
},
|
||||||
|
SignatureScript: []byte{
|
||||||
|
0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62,
|
||||||
|
},
|
||||||
|
Sequence: math.MaxUint64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var multiTxOuts = []*TxOut{
|
||||||
|
{
|
||||||
|
Value: 0x12a05f200,
|
||||||
|
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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 0x5f5e100,
|
||||||
|
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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var multiTx = NewNativeMsgTx(1, multiTxIns, multiTxOuts)
|
||||||
|
|
||||||
|
// multiTxEncoded is the appmessage encoded bytes for multiTx using protocol version
|
||||||
|
// 60002 and is used in the various tests.
|
||||||
|
var multiTxEncoded = []byte{
|
||||||
|
0x01, 0x00, 0x00, 0x00, // Version
|
||||||
|
0x01, // Varint for number of input transactions
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
|
||||||
|
0xff, 0xff, 0xff, 0xff, // Prevous output index
|
||||||
|
0x07, // Varint for length of signature script
|
||||||
|
0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, // Signature script
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Sequence
|
||||||
|
0x02, // Varint for number of output transactions
|
||||||
|
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
|
||||||
|
0x43, // Varint for length of scriptPubKey
|
||||||
|
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
|
||||||
|
0x00, 0xe1, 0xf5, 0x05, 0x00, 0x00, 0x00, 0x00, // Transaction amount
|
||||||
|
0x43, // Varint for length of scriptPubKey
|
||||||
|
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
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // Sub Network ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiTxScriptPubKeyLocs is the location information for the public key scripts
|
||||||
|
// located in multiTx.
|
||||||
|
var multiTxScriptPubKeyLocs = []int{67, 143}
|
||||||
@@ -6,13 +6,14 @@ package appmessage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
"github.com/kaspanet/kaspad/version"
|
"github.com/kaspanet/kaspad/version"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||||
"github.com/kaspanet/kaspad/util/mstime"
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
|
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxUserAgentLen is the maximum allowed length for the user agent field in a
|
// MaxUserAgentLen is the maximum allowed length for the user agent field in a
|
||||||
@@ -53,11 +54,14 @@ type MsgVersion struct {
|
|||||||
// on the appmessage. This has a max length of MaxUserAgentLen.
|
// on the appmessage. This has a max length of MaxUserAgentLen.
|
||||||
UserAgent string
|
UserAgent string
|
||||||
|
|
||||||
|
// The selected tip hash of the generator of the version message.
|
||||||
|
SelectedTipHash *daghash.Hash
|
||||||
|
|
||||||
// Don't announce transactions to peer.
|
// Don't announce transactions to peer.
|
||||||
DisableRelayTx bool
|
DisableRelayTx bool
|
||||||
|
|
||||||
// The subnetwork of the generator of the version message. Should be nil in full nodes
|
// The subnetwork of the generator of the version message. Should be nil in full nodes
|
||||||
SubnetworkID *externalapi.DomainSubnetworkID
|
SubnetworkID *subnetworkid.SubnetworkID
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasService returns whether the specified service is supported by the peer
|
// HasService returns whether the specified service is supported by the peer
|
||||||
@@ -82,18 +86,19 @@ func (msg *MsgVersion) Command() MessageCommand {
|
|||||||
// Message interface using the passed parameters and defaults for the remaining
|
// Message interface using the passed parameters and defaults for the remaining
|
||||||
// fields.
|
// fields.
|
||||||
func NewMsgVersion(addr *NetAddress, id *id.ID, network string,
|
func NewMsgVersion(addr *NetAddress, id *id.ID, network string,
|
||||||
subnetworkID *externalapi.DomainSubnetworkID, protocolVersion uint32) *MsgVersion {
|
selectedTipHash *daghash.Hash, subnetworkID *subnetworkid.SubnetworkID) *MsgVersion {
|
||||||
|
|
||||||
// Limit the timestamp to one millisecond precision since the protocol
|
// Limit the timestamp to one millisecond precision since the protocol
|
||||||
// doesn't support better.
|
// doesn't support better.
|
||||||
return &MsgVersion{
|
return &MsgVersion{
|
||||||
ProtocolVersion: protocolVersion,
|
ProtocolVersion: ProtocolVersion,
|
||||||
Network: network,
|
Network: network,
|
||||||
Services: 0,
|
Services: 0,
|
||||||
Timestamp: mstime.Now(),
|
Timestamp: mstime.Now(),
|
||||||
Address: addr,
|
Address: addr,
|
||||||
ID: id,
|
ID: id,
|
||||||
UserAgent: DefaultUserAgent,
|
UserAgent: DefaultUserAgent,
|
||||||
|
SelectedTipHash: selectedTipHash,
|
||||||
DisableRelayTx: false,
|
DisableRelayTx: false,
|
||||||
SubnetworkID: subnetworkID,
|
SubnetworkID: subnetworkID,
|
||||||
}
|
}
|
||||||
@@ -5,28 +5,29 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestVersion tests the MsgVersion API.
|
// TestVersion tests the MsgVersion API.
|
||||||
func TestVersion(t *testing.T) {
|
func TestVersion(t *testing.T) {
|
||||||
pver := uint32(4)
|
pver := ProtocolVersion
|
||||||
|
|
||||||
// Create version message data.
|
// Create version message data.
|
||||||
|
selectedTipHash := &daghash.Hash{12, 34}
|
||||||
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
||||||
me := NewNetAddress(tcpAddrMe)
|
me := NewNetAddress(tcpAddrMe, SFNodeNetwork)
|
||||||
generatedID, err := id.GenerateID()
|
generatedID, err := id.GenerateID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("id.GenerateID: %s", err)
|
t.Fatalf("id.GenerateID: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we get the correct data back out.
|
// Ensure we get the correct data back out.
|
||||||
msg := NewMsgVersion(me, generatedID, "mainnet", nil, 4)
|
msg := NewMsgVersion(me, generatedID, "mainnet", selectedTipHash, nil)
|
||||||
if msg.ProtocolVersion != pver {
|
if msg.ProtocolVersion != pver {
|
||||||
t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v",
|
t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v",
|
||||||
msg.ProtocolVersion, pver)
|
msg.ProtocolVersion, pver)
|
||||||
@@ -43,6 +44,10 @@ func TestVersion(t *testing.T) {
|
|||||||
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
|
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
|
||||||
msg.UserAgent, DefaultUserAgent)
|
msg.UserAgent, DefaultUserAgent)
|
||||||
}
|
}
|
||||||
|
if !msg.SelectedTipHash.IsEqual(selectedTipHash) {
|
||||||
|
t.Errorf("NewMsgVersion: wrong selected tip hash - got %s, want %s",
|
||||||
|
msg.SelectedTipHash, selectedTipHash)
|
||||||
|
}
|
||||||
if msg.DisableRelayTx {
|
if msg.DisableRelayTx {
|
||||||
t.Errorf("NewMsgVersion: disable relay tx is not false by "+
|
t.Errorf("NewMsgVersion: disable relay tx is not false by "+
|
||||||
"default - got %v, want %v", msg.DisableRelayTx, false)
|
"default - got %v, want %v", msg.DisableRelayTx, false)
|
||||||
@@ -5,9 +5,8 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/util/mstime"
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NetAddress defines information about a peer on the network including the time
|
// NetAddress defines information about a peer on the network including the time
|
||||||
@@ -16,6 +15,9 @@ type NetAddress struct {
|
|||||||
// Last time the address was seen.
|
// Last time the address was seen.
|
||||||
Timestamp mstime.Time
|
Timestamp mstime.Time
|
||||||
|
|
||||||
|
// Bitfield which identifies the services supported by the address.
|
||||||
|
Services ServiceFlag
|
||||||
|
|
||||||
// IP address of the peer.
|
// IP address of the peer.
|
||||||
IP net.IP
|
IP net.IP
|
||||||
|
|
||||||
@@ -24,6 +26,17 @@ type NetAddress struct {
|
|||||||
Port uint16
|
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
|
// TCPAddress converts the NetAddress to *net.TCPAddr
|
||||||
func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
||||||
return &net.TCPAddr{
|
return &net.TCPAddr{
|
||||||
@@ -34,19 +47,20 @@ func (na *NetAddress) TCPAddress() *net.TCPAddr {
|
|||||||
|
|
||||||
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
|
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
|
||||||
// supported services with defaults for the remaining fields.
|
// supported services with defaults for the remaining fields.
|
||||||
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
|
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
|
||||||
return NewNetAddressTimestamp(mstime.Now(), ip, port)
|
return NewNetAddressTimestamp(mstime.Now(), services, ip, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNetAddressTimestamp returns a new NetAddress using the provided
|
// NewNetAddressTimestamp returns a new NetAddress using the provided
|
||||||
// timestamp, IP, port, and supported services. The timestamp is rounded to
|
// timestamp, IP, port, and supported services. The timestamp is rounded to
|
||||||
// single millisecond precision.
|
// single millisecond precision.
|
||||||
func NewNetAddressTimestamp(
|
func NewNetAddressTimestamp(
|
||||||
timestamp mstime.Time, ip net.IP, port uint16) *NetAddress {
|
timestamp mstime.Time, services ServiceFlag, ip net.IP, port uint16) *NetAddress {
|
||||||
// Limit the timestamp to one millisecond precision since the protocol
|
// Limit the timestamp to one millisecond precision since the protocol
|
||||||
// doesn't support better.
|
// doesn't support better.
|
||||||
na := NetAddress{
|
na := NetAddress{
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
|
Services: services,
|
||||||
IP: ip,
|
IP: ip,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
@@ -55,10 +69,6 @@ func NewNetAddressTimestamp(
|
|||||||
|
|
||||||
// NewNetAddress returns a new NetAddress using the provided TCP address and
|
// NewNetAddress returns a new NetAddress using the provided TCP address and
|
||||||
// supported services with defaults for the remaining fields.
|
// supported services with defaults for the remaining fields.
|
||||||
func NewNetAddress(addr *net.TCPAddr) *NetAddress {
|
func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
|
||||||
return NewNetAddressIPPort(addr.IP, uint16(addr.Port))
|
return NewNetAddressIPPort(addr.IP, uint16(addr.Port), services)
|
||||||
}
|
|
||||||
|
|
||||||
func (na NetAddress) String() string {
|
|
||||||
return na.TCPAddress().String()
|
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ func TestNetAddress(t *testing.T) {
|
|||||||
port := 16111
|
port := 16111
|
||||||
|
|
||||||
// Test NewNetAddress.
|
// Test NewNetAddress.
|
||||||
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port})
|
na := NewNetAddress(&net.TCPAddr{IP: ip, Port: port}, 0)
|
||||||
|
|
||||||
// Ensure we get the same ip, port, and services back out.
|
// Ensure we get the same ip, port, and services back out.
|
||||||
if !na.IP.Equal(ip) {
|
if !na.IP.Equal(ip) {
|
||||||
@@ -25,4 +25,21 @@ func TestNetAddress(t *testing.T) {
|
|||||||
t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port,
|
t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port,
|
||||||
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
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,30 +0,0 @@
|
|||||||
// Copyright (c) 2013-2015 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package appmessage
|
|
||||||
|
|
||||||
// MaxAddressesPerMsg is the maximum number of addresses that can be in a single
|
|
||||||
// kaspa Addresses message (MsgAddresses).
|
|
||||||
const MaxAddressesPerMsg = 1000
|
|
||||||
|
|
||||||
// MsgAddresses implements the Message interface and represents a kaspa
|
|
||||||
// Addresses message.
|
|
||||||
type MsgAddresses struct {
|
|
||||||
baseMessage
|
|
||||||
AddressList []*NetAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgAddresses) Command() MessageCommand {
|
|
||||||
return CmdAddresses
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgAddresses returns a new kaspa Addresses message that conforms to the
|
|
||||||
// Message interface. See MsgAddresses for details.
|
|
||||||
func NewMsgAddresses(addressList []*NetAddress) *MsgAddresses {
|
|
||||||
return &MsgAddresses{
|
|
||||||
AddressList: addressList,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +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 (
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultTransactionAlloc is the default size used for the backing array
|
|
||||||
// for transactions. The transaction array will dynamically grow as needed, but
|
|
||||||
// this figure is intended to provide enough space for the number of
|
|
||||||
// transactions in the vast majority of blocks without needing to grow the
|
|
||||||
// backing array multiple times.
|
|
||||||
const defaultTransactionAlloc = 2048
|
|
||||||
|
|
||||||
// TxLoc holds locator data for the offset and length of where a transaction is
|
|
||||||
// located within a MsgBlock data buffer.
|
|
||||||
type TxLoc struct {
|
|
||||||
TxStart int
|
|
||||||
TxLen int
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgBlock implements the Message interface and represents a kaspa
|
|
||||||
// block message. It is used to deliver block and transaction information in
|
|
||||||
// response to a getdata message (MsgGetData) for a given block hash.
|
|
||||||
type MsgBlock struct {
|
|
||||||
baseMessage
|
|
||||||
Header MsgBlockHeader
|
|
||||||
Transactions []*MsgTx
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTransaction adds a transaction to the message.
|
|
||||||
func (msg *MsgBlock) AddTransaction(tx *MsgTx) {
|
|
||||||
msg.Transactions = append(msg.Transactions, tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearTransactions removes all transactions from the message.
|
|
||||||
func (msg *MsgBlock) ClearTransactions() {
|
|
||||||
msg.Transactions = make([]*MsgTx, 0, defaultTransactionAlloc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgBlock) Command() MessageCommand {
|
|
||||||
return CmdBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
|
||||||
// receiver. This is part of the Message interface implementation.
|
|
||||||
func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
|
|
||||||
return MaxMessagePayload
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConvertToPartial clears out all the payloads of the subnetworks that are
|
|
||||||
// incompatible with the given subnetwork ID.
|
|
||||||
// Note: this operation modifies the block in place.
|
|
||||||
func (msg *MsgBlock) ConvertToPartial(subnetworkID *externalapi.DomainSubnetworkID) {
|
|
||||||
for _, tx := range msg.Transactions {
|
|
||||||
if !tx.SubnetworkID.Equal(subnetworkID) {
|
|
||||||
tx.Payload = []byte{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgBlock returns a new kaspa block message that conforms to the
|
|
||||||
// Message interface. See MsgBlock for details.
|
|
||||||
func NewMsgBlock(blockHeader *MsgBlockHeader) *MsgBlock {
|
|
||||||
return &MsgBlock{
|
|
||||||
Header: *blockHeader,
|
|
||||||
Transactions: make([]*MsgTx, 0, defaultTransactionAlloc),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,245 +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 (
|
|
||||||
"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/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestBlock tests the MsgBlock API.
|
|
||||||
func TestBlock(t *testing.T) {
|
|
||||||
pver := uint32(4)
|
|
||||||
|
|
||||||
// Block 1 header.
|
|
||||||
parents := blockOne.Header.Parents
|
|
||||||
hashMerkleRoot := blockOne.Header.HashMerkleRoot
|
|
||||||
acceptedIDMerkleRoot := blockOne.Header.AcceptedIDMerkleRoot
|
|
||||||
utxoCommitment := blockOne.Header.UTXOCommitment
|
|
||||||
bits := blockOne.Header.Bits
|
|
||||||
nonce := blockOne.Header.Nonce
|
|
||||||
daaScore := blockOne.Header.DAAScore
|
|
||||||
blueScore := blockOne.Header.BlueScore
|
|
||||||
blueWork := blockOne.Header.BlueWork
|
|
||||||
pruningPoint := blockOne.Header.PruningPoint
|
|
||||||
bh := NewBlockHeader(1, parents, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce,
|
|
||||||
daaScore, blueScore, blueWork, pruningPoint)
|
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
|
||||||
wantCmd := MessageCommand(5)
|
|
||||||
msg := NewMsgBlock(bh)
|
|
||||||
if cmd := msg.Command(); cmd != wantCmd {
|
|
||||||
t.Errorf("NewMsgBlock: wrong command - got %v want %v",
|
|
||||||
cmd, wantCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure max payload is expected value for latest protocol version.
|
|
||||||
wantPayload := uint32(1024 * 1024 * 32)
|
|
||||||
maxPayload := msg.MaxPayloadLength(pver)
|
|
||||||
if maxPayload != wantPayload {
|
|
||||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
|
||||||
"protocol version %d - got %v, want %v", pver,
|
|
||||||
maxPayload, wantPayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we get the same block header data back out.
|
|
||||||
if !reflect.DeepEqual(&msg.Header, bh) {
|
|
||||||
t.Errorf("NewMsgBlock: wrong block header - got %v, want %v",
|
|
||||||
spew.Sdump(&msg.Header), spew.Sdump(bh))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure transactions are added properly.
|
|
||||||
tx := blockOne.Transactions[0].Copy()
|
|
||||||
msg.AddTransaction(tx)
|
|
||||||
if !reflect.DeepEqual(msg.Transactions, blockOne.Transactions) {
|
|
||||||
t.Errorf("AddTransaction: wrong transactions - got %v, want %v",
|
|
||||||
spew.Sdump(msg.Transactions),
|
|
||||||
spew.Sdump(blockOne.Transactions))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure transactions are properly cleared.
|
|
||||||
msg.ClearTransactions()
|
|
||||||
if len(msg.Transactions) != 0 {
|
|
||||||
t.Errorf("ClearTransactions: wrong transactions - got %v, want %v",
|
|
||||||
len(msg.Transactions), 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertToPartial(t *testing.T) {
|
|
||||||
localSubnetworkID := &externalapi.DomainSubnetworkID{0x12}
|
|
||||||
|
|
||||||
transactions := []struct {
|
|
||||||
subnetworkID *externalapi.DomainSubnetworkID
|
|
||||||
payload []byte
|
|
||||||
expectedPayloadLength int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
subnetworkID: &subnetworks.SubnetworkIDNative,
|
|
||||||
payload: []byte{},
|
|
||||||
expectedPayloadLength: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
subnetworkID: &subnetworks.SubnetworkIDRegistry,
|
|
||||||
payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
|
|
||||||
expectedPayloadLength: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
subnetworkID: localSubnetworkID,
|
|
||||||
payload: []byte{0x01},
|
|
||||||
expectedPayloadLength: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
subnetworkID: &externalapi.DomainSubnetworkID{0x34},
|
|
||||||
payload: []byte{0x02},
|
|
||||||
expectedPayloadLength: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
block := MsgBlock{}
|
|
||||||
payload := []byte{1}
|
|
||||||
for _, transaction := range transactions {
|
|
||||||
block.Transactions = append(block.Transactions, NewSubnetworkMsgTx(1, nil, nil, transaction.subnetworkID, 0, payload))
|
|
||||||
}
|
|
||||||
|
|
||||||
block.ConvertToPartial(localSubnetworkID)
|
|
||||||
|
|
||||||
for _, testTransaction := range transactions {
|
|
||||||
var subnetworkTx *MsgTx
|
|
||||||
for _, blockTransaction := range block.Transactions {
|
|
||||||
if blockTransaction.SubnetworkID.Equal(testTransaction.subnetworkID) {
|
|
||||||
subnetworkTx = blockTransaction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if subnetworkTx == nil {
|
|
||||||
t.Errorf("ConvertToPartial: subnetworkID '%s' not found in block!", testTransaction.subnetworkID)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
payloadLength := len(subnetworkTx.Payload)
|
|
||||||
if payloadLength != testTransaction.expectedPayloadLength {
|
|
||||||
t.Errorf("ConvertToPartial: unexpected payload length for subnetwork '%s': expected: %d, got: %d",
|
|
||||||
testTransaction.subnetworkID, testTransaction.expectedPayloadLength, payloadLength)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//blockOne is the first block in the mainnet block DAG.
|
|
||||||
var blockOne = MsgBlock{
|
|
||||||
Header: MsgBlockHeader{
|
|
||||||
Version: 0,
|
|
||||||
Parents: []externalapi.BlockLevelParents{[]*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash}},
|
|
||||||
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
|
||||||
AcceptedIDMerkleRoot: exampleAcceptedIDMerkleRoot,
|
|
||||||
UTXOCommitment: exampleUTXOCommitment,
|
|
||||||
Timestamp: mstime.UnixMilliseconds(0x17315ed0f99),
|
|
||||||
Bits: 0x1d00ffff, // 486604799
|
|
||||||
Nonce: 0x9962e301, // 2573394689
|
|
||||||
},
|
|
||||||
Transactions: []*MsgTx{
|
|
||||||
NewNativeMsgTx(1,
|
|
||||||
[]*TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutpoint: Outpoint{
|
|
||||||
TxID: externalapi.DomainTransactionID{},
|
|
||||||
Index: 0xffffffff,
|
|
||||||
},
|
|
||||||
SignatureScript: []byte{
|
|
||||||
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04,
|
|
||||||
},
|
|
||||||
Sequence: math.MaxUint64,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]*TxOut{
|
|
||||||
{
|
|
||||||
Value: 0x12a05f200,
|
|
||||||
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},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block one serialized bytes.
|
|
||||||
var blockOneBytes = []byte{
|
|
||||||
0x00, 0x00, // Version 0
|
|
||||||
0x02, // NumParentBlocks
|
|
||||||
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, // mainnetGenesisHash
|
|
||||||
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
|
||||||
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
|
||||||
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
|
||||||
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
|
||||||
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
|
||||||
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
|
||||||
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
|
||||||
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a, // HashMerkleRoot
|
|
||||||
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
|
||||||
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
|
||||||
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
|
||||||
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
|
||||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
|
||||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
|
||||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
|
||||||
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
|
||||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
|
||||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
|
||||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
|
||||||
0x99, 0x0f, 0xed, 0x15, 0x73, 0x01, 0x00, 0x00, // Timestamp
|
|
||||||
0xff, 0xff, 0x00, 0x1d, // Bits
|
|
||||||
0x01, 0xe3, 0x62, 0x99, 0x00, 0x00, 0x00, 0x00, // Fake Nonce
|
|
||||||
0x01, // TxnCount
|
|
||||||
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,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Previous output hash
|
|
||||||
0xff, 0xff, 0xff, 0xff, // Prevous output index
|
|
||||||
0x07, // Varint for length of signature script
|
|
||||||
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, // Signature script (coinbase)
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Sequence
|
|
||||||
0x01, // Varint for number of transaction outputs
|
|
||||||
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
|
|
||||||
0x43, // Varint for length of scriptPubKey
|
|
||||||
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 uncompressed public key
|
|
||||||
0xac, // OP_CHECKSIG
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Lock time
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, // SubnetworkID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transaction location information for block one transactions.
|
|
||||||
var blockOneTxLocs = []TxLoc{
|
|
||||||
{TxStart: 186, TxLen: 162},
|
|
||||||
}
|
|
||||||
@@ -1,102 +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 (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
"github.com/kaspanet/kaspad/util/mstime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BaseBlockHeaderPayload is the base number of bytes a block header can be,
|
|
||||||
// not including the list of parent block headers.
|
|
||||||
// Version 4 bytes + Timestamp 8 bytes + Bits 4 bytes + Nonce 8 bytes +
|
|
||||||
// + NumParentBlocks 1 byte + HashMerkleRoot hash +
|
|
||||||
// + AcceptedIDMerkleRoot hash + UTXOCommitment hash.
|
|
||||||
// To get total size of block header len(ParentHashes) * externalapi.DomainHashSize should be
|
|
||||||
// added to this value
|
|
||||||
const BaseBlockHeaderPayload = 25 + 3*(externalapi.DomainHashSize)
|
|
||||||
|
|
||||||
// MaxNumParentBlocks is the maximum number of parent blocks a block can reference.
|
|
||||||
// Currently set to 255 as the maximum number NumParentBlocks can be due to it being a byte
|
|
||||||
const MaxNumParentBlocks = 255
|
|
||||||
|
|
||||||
// MaxBlockHeaderPayload is the maximum number of bytes a block header can be.
|
|
||||||
// BaseBlockHeaderPayload + up to MaxNumParentBlocks hashes of parent blocks
|
|
||||||
const MaxBlockHeaderPayload = BaseBlockHeaderPayload + (MaxNumParentBlocks * externalapi.DomainHashSize)
|
|
||||||
|
|
||||||
// MsgBlockHeader defines information about a block and is used in the kaspa
|
|
||||||
// block (MsgBlock) and headers (MsgHeader) messages.
|
|
||||||
type MsgBlockHeader struct {
|
|
||||||
baseMessage
|
|
||||||
|
|
||||||
// Version of the block. This is not the same as the protocol version.
|
|
||||||
Version uint16
|
|
||||||
|
|
||||||
// Parents are the parent block hashes of the block in the DAG per superblock level.
|
|
||||||
Parents []externalapi.BlockLevelParents
|
|
||||||
|
|
||||||
// HashMerkleRoot is the merkle tree reference to hash of all transactions for the block.
|
|
||||||
HashMerkleRoot *externalapi.DomainHash
|
|
||||||
|
|
||||||
// AcceptedIDMerkleRoot is merkle tree reference to hash all transactions
|
|
||||||
// accepted form the block.Blues
|
|
||||||
AcceptedIDMerkleRoot *externalapi.DomainHash
|
|
||||||
|
|
||||||
// UTXOCommitment is an ECMH UTXO commitment to the block UTXO.
|
|
||||||
UTXOCommitment *externalapi.DomainHash
|
|
||||||
|
|
||||||
// Time the block was created.
|
|
||||||
Timestamp mstime.Time
|
|
||||||
|
|
||||||
// Difficulty target for the block.
|
|
||||||
Bits uint32
|
|
||||||
|
|
||||||
// Nonce used to generate the block.
|
|
||||||
Nonce uint64
|
|
||||||
|
|
||||||
// DAASCore is the DAA score of the block.
|
|
||||||
DAAScore uint64
|
|
||||||
|
|
||||||
BlueScore uint64
|
|
||||||
|
|
||||||
// BlueWork is the blue work of the block.
|
|
||||||
BlueWork *big.Int
|
|
||||||
|
|
||||||
PruningPoint *externalapi.DomainHash
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockHash computes the block identifier hash for the given block header.
|
|
||||||
func (h *MsgBlockHeader) BlockHash() *externalapi.DomainHash {
|
|
||||||
return consensushashing.HeaderHash(BlockHeaderToDomainBlockHeader(h))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 uint16, parents []externalapi.BlockLevelParents, hashMerkleRoot *externalapi.DomainHash,
|
|
||||||
acceptedIDMerkleRoot *externalapi.DomainHash, utxoCommitment *externalapi.DomainHash, bits uint32, nonce,
|
|
||||||
daaScore, blueScore uint64, blueWork *big.Int, pruningPoint *externalapi.DomainHash) *MsgBlockHeader {
|
|
||||||
|
|
||||||
// Limit the timestamp to one millisecond precision since the protocol
|
|
||||||
// doesn't support better.
|
|
||||||
return &MsgBlockHeader{
|
|
||||||
Version: version,
|
|
||||||
Parents: parents,
|
|
||||||
HashMerkleRoot: hashMerkleRoot,
|
|
||||||
AcceptedIDMerkleRoot: acceptedIDMerkleRoot,
|
|
||||||
UTXOCommitment: utxoCommitment,
|
|
||||||
Timestamp: mstime.Now(),
|
|
||||||
Bits: bits,
|
|
||||||
Nonce: nonce,
|
|
||||||
DAAScore: daaScore,
|
|
||||||
BlueScore: blueScore,
|
|
||||||
BlueWork: blueWork,
|
|
||||||
PruningPoint: pruningPoint,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +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 (
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestBlockHeader tests the MsgBlockHeader API.
|
|
||||||
func TestBlockHeader(t *testing.T) {
|
|
||||||
nonce := uint64(0xba4d87a69924a93d)
|
|
||||||
|
|
||||||
parents := []externalapi.BlockLevelParents{[]*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash}}
|
|
||||||
|
|
||||||
merkleHash := mainnetGenesisMerkleRoot
|
|
||||||
acceptedIDMerkleRoot := exampleAcceptedIDMerkleRoot
|
|
||||||
bits := uint32(0x1d00ffff)
|
|
||||||
daaScore := uint64(123)
|
|
||||||
blueScore := uint64(456)
|
|
||||||
blueWork := big.NewInt(789)
|
|
||||||
pruningPoint := simnetGenesisHash
|
|
||||||
bh := NewBlockHeader(1, parents, merkleHash, acceptedIDMerkleRoot, exampleUTXOCommitment, bits, nonce,
|
|
||||||
daaScore, blueScore, blueWork, pruningPoint)
|
|
||||||
|
|
||||||
// Ensure we get the same data back out.
|
|
||||||
if !reflect.DeepEqual(bh.Parents, parents) {
|
|
||||||
t.Errorf("NewBlockHeader: wrong parents - got %v, want %v",
|
|
||||||
spew.Sprint(bh.Parents), spew.Sprint(parents))
|
|
||||||
}
|
|
||||||
if bh.HashMerkleRoot != merkleHash {
|
|
||||||
t.Errorf("NewBlockHeader: wrong merkle root - got %v, want %v",
|
|
||||||
spew.Sprint(bh.HashMerkleRoot), spew.Sprint(merkleHash))
|
|
||||||
}
|
|
||||||
if bh.Bits != bits {
|
|
||||||
t.Errorf("NewBlockHeader: wrong bits - got %v, want %v",
|
|
||||||
bh.Bits, bits)
|
|
||||||
}
|
|
||||||
if bh.Nonce != nonce {
|
|
||||||
t.Errorf("NewBlockHeader: wrong nonce - got %v, want %v",
|
|
||||||
bh.Nonce, nonce)
|
|
||||||
}
|
|
||||||
if bh.DAAScore != daaScore {
|
|
||||||
t.Errorf("NewBlockHeader: wrong daaScore - got %v, want %v",
|
|
||||||
bh.DAAScore, daaScore)
|
|
||||||
}
|
|
||||||
if bh.BlueScore != blueScore {
|
|
||||||
t.Errorf("NewBlockHeader: wrong blueScore - got %v, want %v",
|
|
||||||
bh.BlueScore, blueScore)
|
|
||||||
}
|
|
||||||
if bh.BlueWork != blueWork {
|
|
||||||
t.Errorf("NewBlockHeader: wrong blueWork - got %v, want %v",
|
|
||||||
bh.BlueWork, blueWork)
|
|
||||||
}
|
|
||||||
if !bh.PruningPoint.Equal(pruningPoint) {
|
|
||||||
t.Errorf("NewBlockHeader: wrong pruningPoint - got %v, want %v",
|
|
||||||
bh.PruningPoint, pruningPoint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MsgBlockWithTrustedData represents a kaspa BlockWithTrustedData message
|
|
||||||
type MsgBlockWithTrustedData struct {
|
|
||||||
baseMessage
|
|
||||||
|
|
||||||
Block *MsgBlock
|
|
||||||
DAAScore uint64
|
|
||||||
DAAWindow []*TrustedDataDataDAABlock
|
|
||||||
GHOSTDAGData []*BlockGHOSTDAGDataHashPair
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgBlockWithTrustedData) Command() MessageCommand {
|
|
||||||
return CmdBlockWithTrustedData
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgBlockWithTrustedData returns a new MsgBlockWithTrustedData.
|
|
||||||
func NewMsgBlockWithTrustedData() *MsgBlockWithTrustedData {
|
|
||||||
return &MsgBlockWithTrustedData{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrustedDataDataDAABlock is an appmessage representation of externalapi.TrustedDataDataDAABlock
|
|
||||||
type TrustedDataDataDAABlock struct {
|
|
||||||
Block *MsgBlock
|
|
||||||
GHOSTDAGData *BlockGHOSTDAGData
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockGHOSTDAGData is an appmessage representation of externalapi.BlockGHOSTDAGData
|
|
||||||
type BlockGHOSTDAGData struct {
|
|
||||||
BlueScore uint64
|
|
||||||
BlueWork *big.Int
|
|
||||||
SelectedParent *externalapi.DomainHash
|
|
||||||
MergeSetBlues []*externalapi.DomainHash
|
|
||||||
MergeSetReds []*externalapi.DomainHash
|
|
||||||
BluesAnticoneSizes []*BluesAnticoneSizes
|
|
||||||
}
|
|
||||||
|
|
||||||
// BluesAnticoneSizes is an appmessage representation of the BluesAnticoneSizes part of GHOSTDAG data.
|
|
||||||
type BluesAnticoneSizes struct {
|
|
||||||
BlueHash *externalapi.DomainHash
|
|
||||||
AnticoneSize externalapi.KType
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockGHOSTDAGDataHashPair is an appmessage representation of externalapi.BlockGHOSTDAGDataHashPair
|
|
||||||
type BlockGHOSTDAGDataHashPair struct {
|
|
||||||
Hash *externalapi.DomainHash
|
|
||||||
GHOSTDAGData *BlockGHOSTDAGData
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgBlockWithTrustedDataV4 represents a kaspa BlockWithTrustedDataV4 message
|
|
||||||
type MsgBlockWithTrustedDataV4 struct {
|
|
||||||
baseMessage
|
|
||||||
|
|
||||||
Block *MsgBlock
|
|
||||||
DAAWindowIndices []uint64
|
|
||||||
GHOSTDAGDataIndices []uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgBlockWithTrustedDataV4) Command() MessageCommand {
|
|
||||||
return CmdBlockWithTrustedDataV4
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgBlockWithTrustedDataV4 returns a new MsgBlockWithTrustedDataV4.
|
|
||||||
func NewMsgBlockWithTrustedDataV4() *MsgBlockWithTrustedDataV4 {
|
|
||||||
return &MsgBlockWithTrustedDataV4{}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgDoneBlocksWithTrustedData implements the Message interface and represents a kaspa
|
|
||||||
// DoneBlocksWithTrustedData message
|
|
||||||
//
|
|
||||||
// This message has no payload.
|
|
||||||
type MsgDoneBlocksWithTrustedData struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgDoneBlocksWithTrustedData) Command() MessageCommand {
|
|
||||||
return CmdDoneBlocksWithTrustedData
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgDoneBlocksWithTrustedData returns a new kaspa DoneBlocksWithTrustedData message that conforms to the
|
|
||||||
// Message interface.
|
|
||||||
func NewMsgDoneBlocksWithTrustedData() *MsgDoneBlocksWithTrustedData {
|
|
||||||
return &MsgDoneBlocksWithTrustedData{}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgDoneHeaders implements the Message interface and represents a kaspa
|
|
||||||
// DoneHeaders message. It is used to notify the IBD syncing peer that the
|
|
||||||
// syncer sent all the requested headers.
|
|
||||||
//
|
|
||||||
// This message has no payload.
|
|
||||||
type MsgDoneHeaders struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgDoneHeaders) Command() MessageCommand {
|
|
||||||
return CmdDoneHeaders
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgDoneHeaders returns a new kaspa DoneIBDBlocks message that conforms to the
|
|
||||||
// Message interface.
|
|
||||||
func NewMsgDoneHeaders() *MsgDoneHeaders {
|
|
||||||
return &MsgDoneHeaders{}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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{}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgRequestPruningPointAndItsAnticone represents a kaspa RequestPruningPointAndItsAnticone message
|
|
||||||
type MsgRequestPruningPointAndItsAnticone struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgRequestPruningPointAndItsAnticone) Command() MessageCommand {
|
|
||||||
return CmdRequestPruningPointAndItsAnticone
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgRequestPruningPointAndItsAnticone returns a new MsgRequestPruningPointAndItsAnticone.
|
|
||||||
func NewMsgRequestPruningPointAndItsAnticone() *MsgRequestPruningPointAndItsAnticone {
|
|
||||||
return &MsgRequestPruningPointAndItsAnticone{}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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,20 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgPruningPointProof represents a kaspa PruningPointProof message
|
|
||||||
type MsgPruningPointProof struct {
|
|
||||||
baseMessage
|
|
||||||
|
|
||||||
Headers [][]*MsgBlockHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgPruningPointProof) Command() MessageCommand {
|
|
||||||
return CmdPruningPointProof
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgPruningPointProof returns a new MsgPruningPointProof.
|
|
||||||
func NewMsgPruningPointProof(headers [][]*MsgBlockHeader) *MsgPruningPointProof {
|
|
||||||
return &MsgPruningPointProof{
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgPruningPoints represents a kaspa PruningPoints message
|
|
||||||
type MsgPruningPoints struct {
|
|
||||||
baseMessage
|
|
||||||
|
|
||||||
Headers []*MsgBlockHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgPruningPoints) Command() MessageCommand {
|
|
||||||
return CmdPruningPoints
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgPruningPoints returns a new MsgPruningPoints.
|
|
||||||
func NewMsgPruningPoints(headers []*MsgBlockHeader) *MsgPruningPoints {
|
|
||||||
return &MsgPruningPoints{
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,34 +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 (
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MsgRequestHeaders implements the Message interface and represents a kaspa
|
|
||||||
// RequestHeaders message. It is used to request a list of blocks starting after the
|
|
||||||
// low hash and until the high hash.
|
|
||||||
type MsgRequestHeaders struct {
|
|
||||||
baseMessage
|
|
||||||
LowHash *externalapi.DomainHash
|
|
||||||
HighHash *externalapi.DomainHash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgRequestHeaders) Command() MessageCommand {
|
|
||||||
return CmdRequestHeaders
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgRequstHeaders returns a new kaspa RequestHeaders message that conforms to the
|
|
||||||
// Message interface using the passed parameters and defaults for the remaining
|
|
||||||
// fields.
|
|
||||||
func NewMsgRequstHeaders(lowHash, highHash *externalapi.DomainHash) *MsgRequestHeaders {
|
|
||||||
return &MsgRequestHeaders{
|
|
||||||
LowHash: lowHash,
|
|
||||||
HighHash: highHash,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MsgRequestIBDBlocks implements the Message interface and represents a kaspa
|
|
||||||
// RequestIBDBlocks message. It is used to request blocks as part of the IBD
|
|
||||||
// protocol.
|
|
||||||
type MsgRequestIBDBlocks struct {
|
|
||||||
baseMessage
|
|
||||||
Hashes []*externalapi.DomainHash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgRequestIBDBlocks) Command() MessageCommand {
|
|
||||||
return CmdRequestIBDBlocks
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgRequestIBDBlocks returns a new MsgRequestIBDBlocks.
|
|
||||||
func NewMsgRequestIBDBlocks(hashes []*externalapi.DomainHash) *MsgRequestIBDBlocks {
|
|
||||||
return &MsgRequestIBDBlocks{
|
|
||||||
Hashes: hashes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgRequestNextHeaders implements the Message interface and represents a kaspa
|
|
||||||
// RequestNextHeaders message. It is used to notify the IBD syncer peer to send
|
|
||||||
// more headers.
|
|
||||||
//
|
|
||||||
// This message has no payload.
|
|
||||||
type MsgRequestNextHeaders struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgRequestNextHeaders) Command() MessageCommand {
|
|
||||||
return CmdRequestNextHeaders
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgRequestNextHeaders returns a new kaspa RequestNextHeaders message that conforms to the
|
|
||||||
// Message interface.
|
|
||||||
func NewMsgRequestNextHeaders() *MsgRequestNextHeaders {
|
|
||||||
return &MsgRequestNextHeaders{}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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{}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgRequestPruningPointProof represents a kaspa RequestPruningPointProof message
|
|
||||||
type MsgRequestPruningPointProof struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgRequestPruningPointProof) Command() MessageCommand {
|
|
||||||
return CmdRequestPruningPointProof
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgRequestPruningPointProof returns a new MsgRequestPruningPointProof.
|
|
||||||
func NewMsgRequestPruningPointProof() *MsgRequestPruningPointProof {
|
|
||||||
return &MsgRequestPruningPointProof{}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MsgRequestPruningPointUTXOSet represents a kaspa RequestPruningPointUTXOSet message
|
|
||||||
type MsgRequestPruningPointUTXOSet struct {
|
|
||||||
baseMessage
|
|
||||||
PruningPointHash *externalapi.DomainHash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgRequestPruningPointUTXOSet) Command() MessageCommand {
|
|
||||||
return CmdRequestPruningPointUTXOSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgRequestPruningPointUTXOSet returns a new MsgRequestPruningPointUTXOSet
|
|
||||||
func NewMsgRequestPruningPointUTXOSet(pruningPointHash *externalapi.DomainHash) *MsgRequestPruningPointUTXOSet {
|
|
||||||
return &MsgRequestPruningPointUTXOSet{
|
|
||||||
PruningPointHash: pruningPointHash,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgTrustedData represents a kaspa TrustedData message
|
|
||||||
type MsgTrustedData struct {
|
|
||||||
baseMessage
|
|
||||||
|
|
||||||
DAAWindow []*TrustedDataDAAHeader
|
|
||||||
GHOSTDAGData []*BlockGHOSTDAGDataHashPair
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *MsgTrustedData) Command() MessageCommand {
|
|
||||||
return CmdTrustedData
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgTrustedData returns a new MsgTrustedData.
|
|
||||||
func NewMsgTrustedData() *MsgTrustedData {
|
|
||||||
return &MsgTrustedData{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrustedDataDAAHeader is an appmessage representation of externalapi.TrustedDataDataDAAHeader
|
|
||||||
type TrustedDataDAAHeader struct {
|
|
||||||
Header *MsgBlockHeader
|
|
||||||
GHOSTDAGData *BlockGHOSTDAGData
|
|
||||||
}
|
|
||||||
@@ -1,319 +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 (
|
|
||||||
"encoding/binary"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxPrevOutIndex is the maximum index the index field of a previous
|
|
||||||
// outpoint can be.
|
|
||||||
MaxPrevOutIndex uint32 = 0xffffffff
|
|
||||||
|
|
||||||
// defaultTxInOutAlloc is the default size used for the backing array for
|
|
||||||
// transaction inputs and outputs. The array will dynamically grow as needed,
|
|
||||||
// but this figure is intended to provide enough space for the number of
|
|
||||||
// inputs and outputs in a typical transaction without needing to grow the
|
|
||||||
// backing array multiple times.
|
|
||||||
defaultTxInOutAlloc = 15
|
|
||||||
|
|
||||||
// minTxInPayload is the minimum payload size for a transaction input.
|
|
||||||
// PreviousOutpoint.TxID + PreviousOutpoint.Index 4 bytes + Varint for
|
|
||||||
// SignatureScript length 1 byte + Sequence 4 bytes.
|
|
||||||
minTxInPayload = 9 + externalapi.DomainHashSize
|
|
||||||
|
|
||||||
// maxTxInPerMessage is the maximum number of transactions inputs that
|
|
||||||
// a transaction which fits into a message could possibly have.
|
|
||||||
maxTxInPerMessage = (MaxMessagePayload / minTxInPayload) + 1
|
|
||||||
|
|
||||||
// MinTxOutPayload is the minimum payload size for a transaction output.
|
|
||||||
// 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.
|
|
||||||
maxTxOutPerMessage = (MaxMessagePayload / MinTxOutPayload) + 1
|
|
||||||
|
|
||||||
// minTxPayload is the minimum payload size for a transaction. Note
|
|
||||||
// that any realistically usable transaction must have at least one
|
|
||||||
// input or output, but that is a rule enforced at a higher layer, so
|
|
||||||
// it is intentionally not included here.
|
|
||||||
// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint
|
|
||||||
// number of transaction outputs 1 byte + LockTime 4 bytes + min input
|
|
||||||
// payload + min output payload.
|
|
||||||
minTxPayload = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
// Outpoint defines a kaspa data type that is used to track previous
|
|
||||||
// transaction outputs.
|
|
||||||
type Outpoint struct {
|
|
||||||
TxID externalapi.DomainTransactionID
|
|
||||||
Index uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOutpoint returns a new kaspa transaction outpoint point with the
|
|
||||||
// provided hash and index.
|
|
||||||
func NewOutpoint(txID *externalapi.DomainTransactionID, index uint32) *Outpoint {
|
|
||||||
return &Outpoint{
|
|
||||||
TxID: *txID,
|
|
||||||
Index: index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the Outpoint in the human-readable form "txID:index".
|
|
||||||
func (o Outpoint) String() string {
|
|
||||||
// Allocate enough for ID string, colon, and 10 digits. Although
|
|
||||||
// at the time of writing, the number of digits can be no greater than
|
|
||||||
// the length of the decimal representation of maxTxOutPerMessage, the
|
|
||||||
// maximum message payload may increase in the future and this
|
|
||||||
// optimization may go unnoticed, so allocate space for 10 decimal
|
|
||||||
// digits, which will fit any uint32.
|
|
||||||
buf := make([]byte, 2*externalapi.DomainHashSize+1, 2*externalapi.DomainHashSize+1+10)
|
|
||||||
copy(buf, o.TxID.String())
|
|
||||||
buf[2*externalapi.DomainHashSize] = ':'
|
|
||||||
buf = strconv.AppendUint(buf, uint64(o.Index), 10)
|
|
||||||
return string(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxIn defines a kaspa transaction input.
|
|
||||||
type TxIn struct {
|
|
||||||
PreviousOutpoint Outpoint
|
|
||||||
SignatureScript []byte
|
|
||||||
Sequence uint64
|
|
||||||
SigOpCount byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, sequence uint64, sigOpCount byte) *TxIn {
|
|
||||||
return &TxIn{
|
|
||||||
PreviousOutpoint: *prevOut,
|
|
||||||
SignatureScript: signatureScript,
|
|
||||||
Sequence: sequence,
|
|
||||||
SigOpCount: sigOpCount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxOut defines a kaspa transaction output.
|
|
||||||
type TxOut struct {
|
|
||||||
Value uint64
|
|
||||||
ScriptPubKey *externalapi.ScriptPublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTxOut returns a new kaspa transaction output with the provided
|
|
||||||
// transaction value and public key script.
|
|
||||||
func NewTxOut(value uint64, scriptPubKey *externalapi.ScriptPublicKey) *TxOut {
|
|
||||||
return &TxOut{
|
|
||||||
Value: value,
|
|
||||||
ScriptPubKey: scriptPubKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MsgTx implements the Message interface and represents a kaspa tx message.
|
|
||||||
// It is used to deliver transaction information in response to a getdata
|
|
||||||
// message (MsgGetData) for a given transaction.
|
|
||||||
//
|
|
||||||
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
|
|
||||||
// inputs and outputs.
|
|
||||||
type MsgTx struct {
|
|
||||||
baseMessage
|
|
||||||
Version uint16
|
|
||||||
TxIn []*TxIn
|
|
||||||
TxOut []*TxOut
|
|
||||||
LockTime uint64
|
|
||||||
SubnetworkID externalapi.DomainSubnetworkID
|
|
||||||
Gas uint64
|
|
||||||
Payload []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTxIn adds a transaction input to the message.
|
|
||||||
func (msg *MsgTx) AddTxIn(ti *TxIn) {
|
|
||||||
msg.TxIn = append(msg.TxIn, ti)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTxOut adds a transaction output to the message.
|
|
||||||
func (msg *MsgTx) AddTxOut(to *TxOut) {
|
|
||||||
msg.TxOut = append(msg.TxOut, to)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsCoinBase determines whether or not a transaction is a coinbase transaction. A coinbase
|
|
||||||
// transaction is a special transaction created by miners that distributes fees and block subsidy
|
|
||||||
// to the previous blocks' miners, and to specify the scriptPubKey that will be used to pay the current
|
|
||||||
// miner in future blocks. Each input of the coinbase transaction should set index to maximum
|
|
||||||
// value and reference the relevant block id, instead of previous transaction id.
|
|
||||||
func (msg *MsgTx) IsCoinBase() bool {
|
|
||||||
// A coinbase transaction must have subnetwork id SubnetworkIDCoinbase
|
|
||||||
return msg.SubnetworkID == subnetworks.SubnetworkIDCoinbase
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxHash generates the Hash for the transaction.
|
|
||||||
func (msg *MsgTx) TxHash() *externalapi.DomainHash {
|
|
||||||
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 consensushashing.TransactionID(MsgTxToDomainTransaction(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy creates a deep copy of a transaction so that the original does not get
|
|
||||||
// modified when the copy is manipulated.
|
|
||||||
func (msg *MsgTx) Copy() *MsgTx {
|
|
||||||
// Create new tx and start by copying primitive values and making space
|
|
||||||
// for the transaction inputs and outputs.
|
|
||||||
newTx := MsgTx{
|
|
||||||
Version: msg.Version,
|
|
||||||
TxIn: make([]*TxIn, 0, len(msg.TxIn)),
|
|
||||||
TxOut: make([]*TxOut, 0, len(msg.TxOut)),
|
|
||||||
LockTime: msg.LockTime,
|
|
||||||
SubnetworkID: msg.SubnetworkID,
|
|
||||||
Gas: msg.Gas,
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.Payload != nil {
|
|
||||||
newTx.Payload = make([]byte, len(msg.Payload))
|
|
||||||
copy(newTx.Payload, msg.Payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deep copy the old TxIn data.
|
|
||||||
for _, oldTxIn := range msg.TxIn {
|
|
||||||
// Deep copy the old previous outpoint.
|
|
||||||
oldOutpoint := oldTxIn.PreviousOutpoint
|
|
||||||
newOutpoint := Outpoint{}
|
|
||||||
newOutpoint.TxID = oldOutpoint.TxID
|
|
||||||
newOutpoint.Index = oldOutpoint.Index
|
|
||||||
|
|
||||||
// Deep copy the old signature script.
|
|
||||||
var newScript []byte
|
|
||||||
oldScript := oldTxIn.SignatureScript
|
|
||||||
oldScriptLen := len(oldScript)
|
|
||||||
if oldScriptLen > 0 {
|
|
||||||
newScript = make([]byte, oldScriptLen)
|
|
||||||
copy(newScript, oldScript[:oldScriptLen])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new txIn with the deep copied data.
|
|
||||||
newTxIn := TxIn{
|
|
||||||
PreviousOutpoint: newOutpoint,
|
|
||||||
SignatureScript: newScript,
|
|
||||||
Sequence: oldTxIn.Sequence,
|
|
||||||
SigOpCount: oldTxIn.SigOpCount,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, append this fully copied txin.
|
|
||||||
newTx.TxIn = append(newTx.TxIn, &newTxIn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deep copy the old TxOut data.
|
|
||||||
for _, oldTxOut := range msg.TxOut {
|
|
||||||
// Deep copy the old ScriptPublicKey
|
|
||||||
var newScript externalapi.ScriptPublicKey
|
|
||||||
oldScript := oldTxOut.ScriptPubKey
|
|
||||||
oldScriptLen := len(oldScript.Script)
|
|
||||||
if oldScriptLen > 0 {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
newTx.TxOut = append(newTx.TxOut, &newTxOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &newTx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgTx) Command() MessageCommand {
|
|
||||||
return CmdTx
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
|
||||||
// receiver. This is part of the Message interface implementation.
|
|
||||||
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
|
|
||||||
return MaxMessagePayload
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSubnetworkCompatible return true iff subnetworkID is one or more of the following:
|
|
||||||
// 1. The SupportsAll subnetwork (full node)
|
|
||||||
// 2. The native subnetwork
|
|
||||||
// 3. The transaction's subnetwork
|
|
||||||
func (msg *MsgTx) IsSubnetworkCompatible(subnetworkID *externalapi.DomainSubnetworkID) bool {
|
|
||||||
return subnetworkID == nil ||
|
|
||||||
subnetworkID.Equal(&subnetworks.SubnetworkIDNative) ||
|
|
||||||
subnetworkID.Equal(&msg.SubnetworkID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// newMsgTx returns a new tx message that conforms to the Message interface.
|
|
||||||
//
|
|
||||||
// All fields except version and gas has default values if nil is passed:
|
|
||||||
// txIn, txOut - empty arrays
|
|
||||||
// payload - an empty payload
|
|
||||||
//
|
|
||||||
// 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 uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
|
||||||
gas uint64, payload []byte, lockTime uint64) *MsgTx {
|
|
||||||
|
|
||||||
if txIn == nil {
|
|
||||||
txIn = make([]*TxIn, 0, defaultTxInOutAlloc)
|
|
||||||
}
|
|
||||||
|
|
||||||
if txOut == nil {
|
|
||||||
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MsgTx{
|
|
||||||
Version: version,
|
|
||||||
TxIn: txIn,
|
|
||||||
TxOut: txOut,
|
|
||||||
SubnetworkID: *subnetworkID,
|
|
||||||
Gas: gas,
|
|
||||||
Payload: payload,
|
|
||||||
LockTime: lockTime,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNativeMsgTx returns a new tx message in the native subnetwork
|
|
||||||
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 uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
|
||||||
gas uint64, payload []byte) *MsgTx {
|
|
||||||
|
|
||||||
return newMsgTx(version, txIn, txOut, subnetworkID, gas, payload, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNativeMsgTxWithLocktime returns a new tx message in the native subnetwork with a locktime.
|
|
||||||
//
|
|
||||||
// See newMsgTx for further documntation of the parameters
|
|
||||||
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 uint16, txIn []*TxIn, txOut []*TxOut, gasLimit uint64) *MsgTx {
|
|
||||||
payload := make([]byte, 8)
|
|
||||||
binary.LittleEndian.PutUint64(payload, gasLimit)
|
|
||||||
|
|
||||||
return NewSubnetworkMsgTx(version, txIn, txOut, &subnetworks.SubnetworkIDRegistry, 0, payload)
|
|
||||||
}
|
|
||||||
@@ -1,261 +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 (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestTx tests the MsgTx API.
|
|
||||||
func TestTx(t *testing.T) {
|
|
||||||
pver := uint32(4)
|
|
||||||
|
|
||||||
txIDStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
|
||||||
txID, err := transactionid.FromString(txIDStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewTxIDFromStr: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
|
||||||
wantCmd := MessageCommand(6)
|
|
||||||
msg := NewNativeMsgTx(1, nil, nil)
|
|
||||||
if cmd := msg.Command(); cmd != wantCmd {
|
|
||||||
t.Errorf("NewMsgAddresses: wrong command - got %v want %v",
|
|
||||||
cmd, wantCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure max payload is expected value for latest protocol version.
|
|
||||||
wantPayload := uint32(1024 * 1024 * 32)
|
|
||||||
maxPayload := msg.MaxPayloadLength(pver)
|
|
||||||
if maxPayload != wantPayload {
|
|
||||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
|
||||||
"protocol version %d - got %v, want %v", pver,
|
|
||||||
maxPayload, wantPayload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we get the same transaction outpoint data back out.
|
|
||||||
// NOTE: This is a block hash and made up index, but we're only
|
|
||||||
// testing package functionality.
|
|
||||||
prevOutIndex := uint32(1)
|
|
||||||
prevOut := NewOutpoint(txID, prevOutIndex)
|
|
||||||
if !prevOut.TxID.Equal(txID) {
|
|
||||||
t.Errorf("NewOutpoint: wrong ID - got %v, want %v",
|
|
||||||
spew.Sprint(&prevOut.TxID), spew.Sprint(txID))
|
|
||||||
}
|
|
||||||
if prevOut.Index != prevOutIndex {
|
|
||||||
t.Errorf("NewOutpoint: wrong index - got %v, want %v",
|
|
||||||
prevOut.Index, prevOutIndex)
|
|
||||||
}
|
|
||||||
prevOutStr := fmt.Sprintf("%s:%d", txID.String(), prevOutIndex)
|
|
||||||
if s := prevOut.String(); s != prevOutStr {
|
|
||||||
t.Errorf("Outpoint.String: unexpected result - got %v, "+
|
|
||||||
"want %v", s, prevOutStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we get the same transaction input back out.
|
|
||||||
sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}
|
|
||||||
txIn := NewTxIn(prevOut, sigScript, constants.MaxTxInSequenceNum, 1)
|
|
||||||
if !reflect.DeepEqual(&txIn.PreviousOutpoint, prevOut) {
|
|
||||||
t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v",
|
|
||||||
spew.Sprint(&txIn.PreviousOutpoint),
|
|
||||||
spew.Sprint(prevOut))
|
|
||||||
}
|
|
||||||
if !bytes.Equal(txIn.SignatureScript, sigScript) {
|
|
||||||
t.Errorf("NewTxIn: wrong signature script - got %v, want %v",
|
|
||||||
spew.Sdump(txIn.SignatureScript),
|
|
||||||
spew.Sdump(sigScript))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we get the same transaction output back out.
|
|
||||||
txValue := uint64(5000000000)
|
|
||||||
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.Script, scriptPubKey.Script) {
|
|
||||||
t.Errorf("NewTxOut: wrong scriptPubKey - got %v, want %v",
|
|
||||||
spew.Sdump(txOut.ScriptPubKey),
|
|
||||||
spew.Sdump(scriptPubKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure transaction inputs are added properly.
|
|
||||||
msg.AddTxIn(txIn)
|
|
||||||
if !reflect.DeepEqual(msg.TxIn[0], txIn) {
|
|
||||||
t.Errorf("AddTxIn: wrong transaction input added - got %v, want %v",
|
|
||||||
spew.Sprint(msg.TxIn[0]), spew.Sprint(txIn))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure transaction outputs are added properly.
|
|
||||||
msg.AddTxOut(txOut)
|
|
||||||
if !reflect.DeepEqual(msg.TxOut[0], txOut) {
|
|
||||||
t.Errorf("AddTxIn: wrong transaction output added - got %v, want %v",
|
|
||||||
spew.Sprint(msg.TxOut[0]), spew.Sprint(txOut))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the copy produced an identical transaction message.
|
|
||||||
newMsg := msg.Copy()
|
|
||||||
if !reflect.DeepEqual(newMsg, msg) {
|
|
||||||
t.Errorf("Copy: mismatched tx messages - got %v, want %v",
|
|
||||||
spew.Sdump(newMsg), spew.Sdump(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
|
||||||
func TestTxHashAndID(t *testing.T) {
|
|
||||||
txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
|
|
||||||
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7"
|
|
||||||
wantTxID1, err := transactionid.FromString(txID1Str)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
|
||||||
}
|
|
||||||
wantTxHash1, err := transactionid.FromString(txHash1Str)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A coinbase transaction
|
|
||||||
txIn := &TxIn{
|
|
||||||
PreviousOutpoint: Outpoint{
|
|
||||||
TxID: externalapi.DomainTransactionID{},
|
|
||||||
Index: math.MaxUint32,
|
|
||||||
},
|
|
||||||
SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62},
|
|
||||||
Sequence: math.MaxUint64,
|
|
||||||
}
|
|
||||||
txOut := &TxOut{
|
|
||||||
Value: 5000000000,
|
|
||||||
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},
|
|
||||||
}
|
|
||||||
tx1 := NewSubnetworkMsgTx(0, []*TxIn{txIn}, []*TxOut{txOut}, &subnetworks.SubnetworkIDCoinbase, 0, nil)
|
|
||||||
|
|
||||||
// Ensure the hash produced is expected.
|
|
||||||
tx1Hash := tx1.TxHash()
|
|
||||||
if *tx1Hash != (externalapi.DomainHash)(*wantTxHash1) {
|
|
||||||
t.Errorf("TxHash: wrong hash - got %v, want %v",
|
|
||||||
spew.Sprint(tx1Hash), spew.Sprint(wantTxHash1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the TxID for coinbase transaction is the same as TxHash.
|
|
||||||
tx1ID := tx1.TxID()
|
|
||||||
if !tx1ID.Equal(wantTxID1) {
|
|
||||||
t.Errorf("TxID: wrong ID - got %v, want %v",
|
|
||||||
spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
|
|
||||||
}
|
|
||||||
|
|
||||||
hash2Str := "8dafd1bec24527d8e3b443ceb0a3b92fffc0d60026317f890b2faf5e9afc177a"
|
|
||||||
wantHash2, err := externalapi.NewDomainHashFromString(hash2Str)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewTxIDFromStr: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
id2Str := "89ffb49474637502d9059af38b8a95fc2f0d3baef5c801d7a9b9c8830671b711"
|
|
||||||
wantID2, err := transactionid.FromString(id2Str)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewTxIDFromStr: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
payload := []byte{1, 2, 3}
|
|
||||||
txIns := []*TxIn{{
|
|
||||||
PreviousOutpoint: Outpoint{
|
|
||||||
Index: 0,
|
|
||||||
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,
|
|
||||||
0x05, 0x77, 0x37, 0x57, 0xDE, 0xB1, 0x68, 0x82, 0x09, 0x30, 0xE3, 0xB0, 0xD0, 0x3F, 0x46, 0xF5,
|
|
||||||
0xFC, 0xF1, 0x50, 0xBF, 0x99, 0x0C, 0x02, 0x21, 0x00, 0xD2, 0x5B, 0x5C, 0x87, 0x04, 0x00, 0x76,
|
|
||||||
0xE4, 0xF2, 0x53, 0xF8, 0x26, 0x2E, 0x76, 0x3E, 0x2D, 0xD5, 0x1E, 0x7F, 0xF0, 0xBE, 0x15, 0x77,
|
|
||||||
0x27, 0xC4, 0xBC, 0x42, 0x80, 0x7F, 0x17, 0xBD, 0x39, 0x01, 0x41, 0x04, 0xE6, 0xC2, 0x6E, 0xF6,
|
|
||||||
0x7D, 0xC6, 0x10, 0xD2, 0xCD, 0x19, 0x24, 0x84, 0x78, 0x9A, 0x6C, 0xF9, 0xAE, 0xA9, 0x93, 0x0B,
|
|
||||||
0x94, 0x4B, 0x7E, 0x2D, 0xB5, 0x34, 0x2B, 0x9D, 0x9E, 0x5B, 0x9F, 0xF7, 0x9A, 0xFF, 0x9A, 0x2E,
|
|
||||||
0xE1, 0x97, 0x8D, 0xD7, 0xFD, 0x01, 0xDF, 0xC5, 0x22, 0xEE, 0x02, 0x28, 0x3D, 0x3B, 0x06, 0xA9,
|
|
||||||
0xD0, 0x3A, 0xCF, 0x80, 0x96, 0x96, 0x8D, 0x7D, 0xBB, 0x0F, 0x91, 0x78,
|
|
||||||
},
|
|
||||||
Sequence: math.MaxUint64,
|
|
||||||
}}
|
|
||||||
txOuts := []*TxOut{
|
|
||||||
{
|
|
||||||
Value: 244623243,
|
|
||||||
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: &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.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.Equal(wantID2) {
|
|
||||||
t.Errorf("TxID: wrong ID - got %v, want %v",
|
|
||||||
spew.Sprint(tx2ID), spew.Sprint(wantID2))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 not be the same even for transaction with an empty signature")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// MsgReady implements the Message interface and represents a kaspa
|
|
||||||
// Ready message. It is used to notify that the peer is ready to receive
|
|
||||||
// messages.
|
|
||||||
//
|
|
||||||
// This message has no payload.
|
|
||||||
type MsgReady struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
|
||||||
// of the Message interface implementation.
|
|
||||||
func (msg *MsgReady) Command() MessageCommand {
|
|
||||||
return CmdReady
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMsgReady returns a new kaspa Ready message that conforms to the
|
|
||||||
// Message interface.
|
|
||||||
func NewMsgReady() *MsgReady {
|
|
||||||
return &MsgReady{}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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{}
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// ProtocolVersion is the latest protocol version this package supports.
|
||||||
|
ProtocolVersion uint32 = 1
|
||||||
|
|
||||||
// DefaultServices describes the default services that are supported by
|
// DefaultServices describes the default services that are supported by
|
||||||
// the server.
|
// the server.
|
||||||
DefaultServices = SFNodeNetwork | SFNodeBloom | SFNodeCF
|
DefaultServices = SFNodeNetwork | SFNodeBloom | SFNodeCF
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// AddPeerRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type AddPeerRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Address string
|
|
||||||
IsPermanent bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *AddPeerRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdAddPeerRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAddPeerRequestMessage returns a instance of the message
|
|
||||||
func NewAddPeerRequestMessage(address string, isPermanent bool) *AddPeerRequestMessage {
|
|
||||||
return &AddPeerRequestMessage{
|
|
||||||
Address: address,
|
|
||||||
IsPermanent: isPermanent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddPeerResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type AddPeerResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *AddPeerResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdAddPeerResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAddPeerResponseMessage returns a instance of the message
|
|
||||||
func NewAddPeerResponseMessage() *AddPeerResponseMessage {
|
|
||||||
return &AddPeerResponseMessage{}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
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{}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// EstimateNetworkHashesPerSecondRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type EstimateNetworkHashesPerSecondRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
StartHash string
|
|
||||||
WindowSize uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *EstimateNetworkHashesPerSecondRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdEstimateNetworkHashesPerSecondRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEstimateNetworkHashesPerSecondRequestMessage returns a instance of the message
|
|
||||||
func NewEstimateNetworkHashesPerSecondRequestMessage(startHash string, windowSize uint32) *EstimateNetworkHashesPerSecondRequestMessage {
|
|
||||||
return &EstimateNetworkHashesPerSecondRequestMessage{
|
|
||||||
StartHash: startHash,
|
|
||||||
WindowSize: windowSize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EstimateNetworkHashesPerSecondResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type EstimateNetworkHashesPerSecondResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
NetworkHashesPerSecond uint64
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *EstimateNetworkHashesPerSecondResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdEstimateNetworkHashesPerSecondResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEstimateNetworkHashesPerSecondResponseMessage returns a instance of the message
|
|
||||||
func NewEstimateNetworkHashesPerSecondResponseMessage(networkHashesPerSecond uint64) *EstimateNetworkHashesPerSecondResponseMessage {
|
|
||||||
return &EstimateNetworkHashesPerSecondResponseMessage{
|
|
||||||
NetworkHashesPerSecond: networkHashesPerSecond,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetBalanceByAddressRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBalanceByAddressRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Address string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBalanceByAddressRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBalanceByAddressRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBalanceByAddressRequest returns a instance of the message
|
|
||||||
func NewGetBalanceByAddressRequest(address string) *GetBalanceByAddressRequestMessage {
|
|
||||||
return &GetBalanceByAddressRequestMessage{
|
|
||||||
Address: address,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBalanceByAddressResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBalanceByAddressResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Balance uint64
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBalanceByAddressResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBalanceByAddressResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBalanceByAddressResponse returns an instance of the message
|
|
||||||
func NewGetBalanceByAddressResponse(Balance uint64) *GetBalanceByAddressResponseMessage {
|
|
||||||
return &GetBalanceByAddressResponseMessage{
|
|
||||||
Balance: Balance,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetBalancesByAddressesRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBalancesByAddressesRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Addresses []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBalancesByAddressesRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBalancesByAddressesRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBalancesByAddressesRequest returns a instance of the message
|
|
||||||
func NewGetBalancesByAddressesRequest(addresses []string) *GetBalancesByAddressesRequestMessage {
|
|
||||||
return &GetBalancesByAddressesRequestMessage{
|
|
||||||
Addresses: addresses,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BalancesByAddressesEntry represents the balance of some address
|
|
||||||
type BalancesByAddressesEntry struct {
|
|
||||||
Address string
|
|
||||||
Balance uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBalancesByAddressesResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBalancesByAddressesResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Entries []*BalancesByAddressesEntry
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBalancesByAddressesResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBalancesByAddressesResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBalancesByAddressesResponse returns an instance of the message
|
|
||||||
func NewGetBalancesByAddressesResponse(entries []*BalancesByAddressesEntry) *GetBalancesByAddressesResponseMessage {
|
|
||||||
return &GetBalancesByAddressesResponseMessage{
|
|
||||||
Entries: entries,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetBlockRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Hash string
|
|
||||||
IncludeTransactions bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockRequestMessage returns a instance of the message
|
|
||||||
func NewGetBlockRequestMessage(hash string, includeTransactions bool) *GetBlockRequestMessage {
|
|
||||||
return &GetBlockRequestMessage{
|
|
||||||
Hash: hash,
|
|
||||||
IncludeTransactions: includeTransactions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Block *RPCBlock
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockResponseMessage returns a instance of the message
|
|
||||||
func NewGetBlockResponseMessage() *GetBlockResponseMessage {
|
|
||||||
return &GetBlockResponseMessage{}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
|
|
||||||
// GetBlockCountRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockCountRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockCountRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockCountRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockCountRequestMessage returns a instance of the message
|
|
||||||
func NewGetBlockCountRequestMessage() *GetBlockCountRequestMessage {
|
|
||||||
return &GetBlockCountRequestMessage{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockCountResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockCountResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
BlockCount uint64
|
|
||||||
HeaderCount uint64
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockCountResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockCountResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockCountResponseMessage returns a instance of the message
|
|
||||||
func NewGetBlockCountResponseMessage(syncInfo *externalapi.SyncInfo) *GetBlockCountResponseMessage {
|
|
||||||
return &GetBlockCountResponseMessage{
|
|
||||||
BlockCount: syncInfo.BlockCount,
|
|
||||||
HeaderCount: syncInfo.HeaderCount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetBlockDAGInfoRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockDAGInfoRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockDAGInfoRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockDAGInfoRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockDAGInfoRequestMessage returns a instance of the message
|
|
||||||
func NewGetBlockDAGInfoRequestMessage() *GetBlockDAGInfoRequestMessage {
|
|
||||||
return &GetBlockDAGInfoRequestMessage{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockDAGInfoResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockDAGInfoResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
NetworkName string
|
|
||||||
BlockCount uint64
|
|
||||||
HeaderCount uint64
|
|
||||||
TipHashes []string
|
|
||||||
VirtualParentHashes []string
|
|
||||||
Difficulty float64
|
|
||||||
PastMedianTime int64
|
|
||||||
PruningPointHash string
|
|
||||||
VirtualDAAScore uint64
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockDAGInfoResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockDAGInfoResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockDAGInfoResponseMessage returns a instance of the message
|
|
||||||
func NewGetBlockDAGInfoResponseMessage() *GetBlockDAGInfoResponseMessage {
|
|
||||||
return &GetBlockDAGInfoResponseMessage{}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetBlockTemplateRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockTemplateRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
PayAddress string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockTemplateRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockTemplateRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockTemplateRequestMessage returns a instance of the message
|
|
||||||
func NewGetBlockTemplateRequestMessage(payAddress string) *GetBlockTemplateRequestMessage {
|
|
||||||
return &GetBlockTemplateRequestMessage{
|
|
||||||
PayAddress: payAddress,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlockTemplateResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlockTemplateResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Block *RPCBlock
|
|
||||||
IsSynced bool
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlockTemplateResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlockTemplateResponseMessage returns a instance of the message
|
|
||||||
func NewGetBlockTemplateResponseMessage(block *RPCBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
|
||||||
return &GetBlockTemplateResponseMessage{
|
|
||||||
Block: block,
|
|
||||||
IsSynced: isSynced,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetBlocksRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlocksRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
LowHash string
|
|
||||||
IncludeBlocks bool
|
|
||||||
IncludeTransactions bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlocksRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlocksRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlocksRequestMessage returns a instance of the message
|
|
||||||
func NewGetBlocksRequestMessage(lowHash string, includeBlocks bool,
|
|
||||||
includeTransactions bool) *GetBlocksRequestMessage {
|
|
||||||
return &GetBlocksRequestMessage{
|
|
||||||
LowHash: lowHash,
|
|
||||||
IncludeBlocks: includeBlocks,
|
|
||||||
IncludeTransactions: includeTransactions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlocksResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetBlocksResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
BlockHashes []string
|
|
||||||
Blocks []*RPCBlock
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetBlocksResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetBlocksResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetBlocksResponseMessage returns a instance of the message
|
|
||||||
func NewGetBlocksResponseMessage() *GetBlocksResponseMessage {
|
|
||||||
return &GetBlocksResponseMessage{}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetConnectedPeerInfoRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetConnectedPeerInfoRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetConnectedPeerInfoRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetConnectedPeerInfoRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetConnectedPeerInfoRequestMessage returns a instance of the message
|
|
||||||
func NewGetConnectedPeerInfoRequestMessage() *GetConnectedPeerInfoRequestMessage {
|
|
||||||
return &GetConnectedPeerInfoRequestMessage{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConnectedPeerInfoResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetConnectedPeerInfoResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Infos []*GetConnectedPeerInfoMessage
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetConnectedPeerInfoResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetConnectedPeerInfoResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetConnectedPeerInfoResponseMessage returns a instance of the message
|
|
||||||
func NewGetConnectedPeerInfoResponseMessage(infos []*GetConnectedPeerInfoMessage) *GetConnectedPeerInfoResponseMessage {
|
|
||||||
return &GetConnectedPeerInfoResponseMessage{
|
|
||||||
Infos: infos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConnectedPeerInfoMessage holds information about a connected peer
|
|
||||||
type GetConnectedPeerInfoMessage struct {
|
|
||||||
ID string
|
|
||||||
Address string
|
|
||||||
LastPingDuration int64
|
|
||||||
IsOutbound bool
|
|
||||||
TimeOffset int64
|
|
||||||
UserAgent string
|
|
||||||
AdvertisedProtocolVersion uint32
|
|
||||||
TimeConnected int64
|
|
||||||
IsIBDPeer bool
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetCurrentNetworkRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetCurrentNetworkRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetCurrentNetworkRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetCurrentNetworkRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetCurrentNetworkRequestMessage returns a instance of the message
|
|
||||||
func NewGetCurrentNetworkRequestMessage() *GetCurrentNetworkRequestMessage {
|
|
||||||
return &GetCurrentNetworkRequestMessage{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCurrentNetworkResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetCurrentNetworkResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
CurrentNetwork string
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetCurrentNetworkResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetCurrentNetworkResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetCurrentNetworkResponseMessage returns a instance of the message
|
|
||||||
func NewGetCurrentNetworkResponseMessage(currentNetwork string) *GetCurrentNetworkResponseMessage {
|
|
||||||
return &GetCurrentNetworkResponseMessage{
|
|
||||||
CurrentNetwork: currentNetwork,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package appmessage
|
|
||||||
|
|
||||||
// GetHeadersRequestMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetHeadersRequestMessage struct {
|
|
||||||
baseMessage
|
|
||||||
StartHash string
|
|
||||||
Limit uint64
|
|
||||||
IsAscending bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetHeadersRequestMessage) Command() MessageCommand {
|
|
||||||
return CmdGetHeadersRequestMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetHeadersRequestMessage returns a instance of the message
|
|
||||||
func NewGetHeadersRequestMessage(startHash string, limit uint64, isAscending bool) *GetHeadersRequestMessage {
|
|
||||||
return &GetHeadersRequestMessage{
|
|
||||||
StartHash: startHash,
|
|
||||||
Limit: limit,
|
|
||||||
IsAscending: isAscending,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHeadersResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetHeadersResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
Headers []string
|
|
||||||
|
|
||||||
Error *RPCError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
|
||||||
func (msg *GetHeadersResponseMessage) Command() MessageCommand {
|
|
||||||
return CmdGetHeadersResponseMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetHeadersResponseMessage returns a instance of the message
|
|
||||||
func NewGetHeadersResponseMessage(headers []string) *GetHeadersResponseMessage {
|
|
||||||
return &GetHeadersResponseMessage{
|
|
||||||
Headers: headers,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGetInfoRequestMessage returns a instance of the message
|
|
||||||
func NewGetInfoRequestMessage() *GetInfoRequestMessage {
|
|
||||||
return &GetInfoRequestMessage{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInfoResponseMessage is an appmessage corresponding to
|
|
||||||
// its respective RPC message
|
|
||||||
type GetInfoResponseMessage struct {
|
|
||||||
baseMessage
|
|
||||||
P2PID string
|
|
||||||
MempoolSize uint64
|
|
||||||
ServerVersion string
|
|
||||||
|
|
||||||
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, serverVersion string) *GetInfoResponseMessage {
|
|
||||||
return &GetInfoResponseMessage{
|
|
||||||
P2PID: p2pID,
|
|
||||||
MempoolSize: mempoolSize,
|
|
||||||
ServerVersion: serverVersion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user