Compare commits
88 commits
master
...
pruned_ram
Author | SHA1 | Date | |
---|---|---|---|
|
e056f8ac23 | ||
|
742881a29a | ||
|
55abc9d25c | ||
|
dc230888ef | ||
|
ba05456830 | ||
|
07d25ba7ce | ||
|
d884f96553 | ||
|
eb165fb359 | ||
|
52f41459d5 | ||
|
3910319e80 | ||
|
db25b9b11b | ||
|
6009f9048c | ||
|
fa9239e0c5 | ||
|
edfffd979d | ||
|
02f76b2e8e | ||
|
9ebe440325 | ||
|
a4cc516896 | ||
|
690954d843 | ||
|
035ff2a799 | ||
|
79d7e65a35 | ||
|
103ab86904 | ||
|
cee79f6635 | ||
|
96f3d421df | ||
|
3a046d153c | ||
|
a56581d967 | ||
|
3c4900275c | ||
|
9d3279f215 | ||
|
40fe74adb8 | ||
|
ac1fdda8a0 | ||
|
79e392b85d | ||
|
5588af7d5b | ||
|
83b6a8dc18 | ||
|
1ba1a10e59 | ||
|
a51be72029 | ||
|
79987722bb | ||
|
1834d95b43 | ||
|
9c9bd3e3a5 | ||
|
8a5e74ebe6 | ||
|
7154e57d49 | ||
|
bf86d03f84 | ||
|
d2f8ad7657 | ||
|
ca18f3e8a2 | ||
|
0be18205c8 | ||
|
61de064575 | ||
|
e4c637b02a | ||
|
d83eaa4fed | ||
|
c7285883a6 | ||
|
fe1ce376d8 | ||
|
d19bcd60db | ||
|
7414ad7b54 | ||
|
c8cd97fe4d | ||
|
a06875fe46 | ||
|
5f872b37cf | ||
|
9af0566433 | ||
|
aa518c59bb | ||
|
1402e61807 | ||
|
a061c31208 | ||
|
983e4c6000 | ||
|
a310f8b598 | ||
|
ed2c764c9d | ||
|
4b8bd4c238 | ||
|
9080abc2c6 | ||
|
0224bf295b | ||
|
8e059c14d7 | ||
|
9d0dfbe87e | ||
|
62479740ae | ||
|
1895c9069f | ||
|
9caab1e2e1 | ||
|
0a22e00498 | ||
|
866b0cb610 | ||
|
3d9d9b1912 | ||
|
27b01f77b3 | ||
|
7d412b4e2f | ||
|
a6189cb439 | ||
|
1264d4581d | ||
|
ba7266c20c | ||
|
dce6d65452 | ||
|
43bed18649 | ||
|
b704fef973 | ||
|
5b72ef6719 | ||
|
f535c88f4a | ||
|
eb3bde09f2 | ||
|
c65dd82284 | ||
|
b7d3e11250 | ||
|
31ce21deb6 | ||
|
0e5bf1c24f | ||
|
61c0f0df99 | ||
|
82141c408c |
352 changed files with 14240 additions and 28243 deletions
|
@ -3,26 +3,23 @@ on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Go CI
|
name: Go CI
|
||||||
runs-on: ubuntu-latest
|
runs-on: self-hosted
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go: [1.14, 1.15]
|
go: [1.16]
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
|
|
||||||
- name: Check out source
|
- name: Check out source
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install Linters
|
|
||||||
run: "curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.26.0"
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
|
||||||
GO111MODULE: "on"
|
|
||||||
run: go build ./...
|
run: go build ./...
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
env:
|
|
||||||
GO111MODULE: "on"
|
|
||||||
run: |
|
run: |
|
||||||
sh ./goclean.sh
|
sh ./goclean.sh
|
||||||
|
|
58
.github/workflows/create-release.yml
vendored
Normal file
58
.github/workflows/create-release.yml
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
name: Create release
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
note:
|
||||||
|
description: 'Note'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go: [1.16]
|
||||||
|
os: [linux, darwin, windows]
|
||||||
|
ar: [amd64, arm64]
|
||||||
|
exclude:
|
||||||
|
- go: 1.16
|
||||||
|
os: windows
|
||||||
|
ar: arm64
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- name: Checkout source
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Build executables
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.os }}
|
||||||
|
GOARCH: ${{ matrix.ar }}
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
run: |
|
||||||
|
go build -trimpath -ldflags="-s -w -buildid=" -v -o artifacts/ .
|
||||||
|
go build -trimpath -ldflags="-s -w -buildid=" -v -o artifacts/ ./cmd/lbcctl/
|
||||||
|
- name: SHA256 sum
|
||||||
|
run: sha256sum -b artifacts/* > artifacts/lbcd.sha256
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: lbcd-${{ matrix.os }}-${{ matrix.ar }}
|
||||||
|
path: artifacts/*
|
||||||
|
|
||||||
|
|
||||||
|
# for releases see https://trstringer.com/github-actions-create-release-upload-artifacts/
|
||||||
|
|
||||||
|
# AWS S3 support:
|
||||||
|
# - name: Upload to Amazon S3
|
||||||
|
# uses: ItsKarma/aws-cli@v1.70.0
|
||||||
|
# with:
|
||||||
|
# args: s3 sync .release s3://my-bucket-name
|
||||||
|
# env:
|
||||||
|
# # Make sure to add the secrets in the repo settings page
|
||||||
|
# # AWS_REGION is set to us-east-1 by default
|
||||||
|
# AWS_ACCESS_KEY_ID: $
|
||||||
|
# AWS_SECRET_ACCESS_KEY: $
|
||||||
|
# AWS_REGION: us-east-1
|
35
.github/workflows/full-sync-part-1.yml
vendored
Normal file
35
.github/workflows/full-sync-part-1.yml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
name: Full Sync From 0
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
note:
|
||||||
|
description: 'Note'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Go CI
|
||||||
|
runs-on: self-hosted
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go: [1.16]
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
echo "Note ${{ github.event.inputs.note }}!"
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- name: Checkout source
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Build lbcd
|
||||||
|
run: go build .
|
||||||
|
- name: Create datadir
|
||||||
|
run: echo "TEMP_DATA_DIR=$(mktemp -d)" >> $GITHUB_ENV
|
||||||
|
- name: Run lbcd
|
||||||
|
run: ./lbcd --datadir=${{env.TEMP_DATA_DIR}}/data --logdir=${{env.TEMP_DATA_DIR}}/logs --connect=127.0.0.1 --norpc
|
||||||
|
- name: Remove datadir
|
||||||
|
if: always()
|
||||||
|
run: rm -rf ${{env.TEMP_DATA_DIR}}
|
37
.github/workflows/full-sync-part-2.yml
vendored
Normal file
37
.github/workflows/full-sync-part-2.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
name: Full Sync From 814k
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
note:
|
||||||
|
description: 'Note'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Go CI
|
||||||
|
runs-on: self-hosted
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go: [1.16]
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
echo "Note ${{ github.event.inputs.note }}!"
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- name: Checkout source
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Build lbcd
|
||||||
|
run: go build .
|
||||||
|
- name: Create datadir
|
||||||
|
run: echo "TEMP_DATA_DIR=$(mktemp -d)" >> $GITHUB_ENV
|
||||||
|
- name: Copy initial data
|
||||||
|
run: cp -r /home/lbry/lbcd_814k/* ${{env.TEMP_DATA_DIR}}
|
||||||
|
- name: Run lbcd
|
||||||
|
run: ./lbcd --datadir=${{env.TEMP_DATA_DIR}}/data --logdir=${{env.TEMP_DATA_DIR}}/logs --connect=127.0.0.1 --norpc
|
||||||
|
- name: Remove datadir
|
||||||
|
if: always()
|
||||||
|
run: rm -rf ${{env.TEMP_DATA_DIR}}
|
57
.github/workflows/golangci-lint.yml
vendored
Normal file
57
.github/workflows/golangci-lint.yml
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
name: golangci-lint
|
||||||
|
|
||||||
|
env:
|
||||||
|
# go needs absolute directories, using the $HOME variable doesn't work here.
|
||||||
|
GOCACHE: /home/runner/work/go/pkg/build
|
||||||
|
GOPATH: /home/runner/work/go
|
||||||
|
GO_VERSION: '^1.17.0'
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
golangci:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: setup go ${{ env.GO_VERSION }}
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '${{ env.GO_VERSION }}'
|
||||||
|
|
||||||
|
- name: checkout source
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: compile code
|
||||||
|
run: go install -v ./...
|
||||||
|
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||||
|
version: latest
|
||||||
|
|
||||||
|
# Optional: working directory, useful for monorepos
|
||||||
|
# working-directory: somedir
|
||||||
|
|
||||||
|
# Optional: golangci-lint command line arguments.
|
||||||
|
# args: --issues-exit-code=0
|
||||||
|
|
||||||
|
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||||
|
# only-new-issues: true
|
||||||
|
|
||||||
|
# Optional: if set to true then the action will use pre-installed Go.
|
||||||
|
skip-go-installation: true
|
||||||
|
|
||||||
|
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
||||||
|
# skip-pkg-cache: true
|
||||||
|
|
||||||
|
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
||||||
|
# skip-build-cache: true
|
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
# Databases
|
# Databases
|
||||||
btcd.db
|
btcd.db
|
||||||
|
lbcd.db
|
||||||
*-shm
|
*-shm
|
||||||
*-wal
|
*-wal
|
||||||
|
|
||||||
|
@ -33,6 +34,18 @@ _testmain.go
|
||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Code coverage files
|
# Code coverage files
|
||||||
profile.tmp
|
profile.tmp
|
||||||
profile.cov
|
profile.cov
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
btcd
|
||||||
|
btcctl
|
||||||
|
lbcd
|
||||||
|
lbcctl
|
||||||
|
|
152
.golangci.yml
Normal file
152
.golangci.yml
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
linters-settings:
|
||||||
|
depguard:
|
||||||
|
list-type: blacklist
|
||||||
|
packages:
|
||||||
|
# logging is allowed only by logutils.Log, logrus
|
||||||
|
# is allowed to use only in logutils package
|
||||||
|
- github.com/sirupsen/logrus
|
||||||
|
packages-with-error-message:
|
||||||
|
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
|
||||||
|
dupl:
|
||||||
|
threshold: 100
|
||||||
|
funlen:
|
||||||
|
lines: 100
|
||||||
|
statements: 50
|
||||||
|
gci:
|
||||||
|
local-prefixes: github.com/golangci/golangci-lint
|
||||||
|
goconst:
|
||||||
|
min-len: 2
|
||||||
|
min-occurrences: 2
|
||||||
|
gocritic:
|
||||||
|
enabled-tags:
|
||||||
|
- diagnostic
|
||||||
|
- experimental
|
||||||
|
- opinionated
|
||||||
|
- performance
|
||||||
|
- style
|
||||||
|
disabled-checks:
|
||||||
|
- dupImport # https://github.com/go-critic/go-critic/issues/845
|
||||||
|
- ifElseChain
|
||||||
|
- octalLiteral
|
||||||
|
- whyNoLint
|
||||||
|
- wrapperFunc
|
||||||
|
gocyclo:
|
||||||
|
min-complexity: 15
|
||||||
|
goimports:
|
||||||
|
local-prefixes: github.com/golangci/golangci-lint
|
||||||
|
gomnd:
|
||||||
|
settings:
|
||||||
|
mnd:
|
||||||
|
# don't include the "operation" and "assign"
|
||||||
|
checks:
|
||||||
|
- argument
|
||||||
|
- case
|
||||||
|
- condition
|
||||||
|
- return
|
||||||
|
govet:
|
||||||
|
check-shadowing: true
|
||||||
|
settings:
|
||||||
|
printf:
|
||||||
|
funcs:
|
||||||
|
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
|
||||||
|
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
|
||||||
|
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
|
||||||
|
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
|
||||||
|
lll:
|
||||||
|
line-length: 140
|
||||||
|
maligned:
|
||||||
|
suggest-new: true
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
nolintlint:
|
||||||
|
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
|
||||||
|
allow-unused: false # report any unused nolint directives
|
||||||
|
require-explanation: false # don't require an explanation for nolint directives
|
||||||
|
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- asciicheck
|
||||||
|
- bodyclose
|
||||||
|
# - deadcode
|
||||||
|
- depguard
|
||||||
|
# - dogsled
|
||||||
|
# - dupl
|
||||||
|
# - errcheck
|
||||||
|
# - exhaustive
|
||||||
|
- exportloopref
|
||||||
|
# - funlen
|
||||||
|
# - gochecknoglobals
|
||||||
|
# - gochecknoinits
|
||||||
|
# - gocognit
|
||||||
|
# - goconst
|
||||||
|
# - gocritic
|
||||||
|
# - gocyclo
|
||||||
|
# - godot
|
||||||
|
# - godox
|
||||||
|
# - goerr113
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
# - gomnd
|
||||||
|
- goprintffuncname
|
||||||
|
# - gosec
|
||||||
|
# - gosimple
|
||||||
|
# - govet
|
||||||
|
# - ineffassign
|
||||||
|
# - interfacer
|
||||||
|
# - lll
|
||||||
|
# - maligned
|
||||||
|
# - misspell
|
||||||
|
- nakedret
|
||||||
|
# - nestif
|
||||||
|
# - noctx
|
||||||
|
# - nolintlint
|
||||||
|
# - prealloc
|
||||||
|
- rowserrcheck
|
||||||
|
# - revive
|
||||||
|
# - scopelint
|
||||||
|
# - staticcheck
|
||||||
|
# - structcheck
|
||||||
|
# - stylecheck
|
||||||
|
# - testpackage
|
||||||
|
# - typecheck
|
||||||
|
- unconvert
|
||||||
|
# - unparam
|
||||||
|
# - unused
|
||||||
|
# - varcheck
|
||||||
|
# - whitespace
|
||||||
|
# - wsl
|
||||||
|
|
||||||
|
issues:
|
||||||
|
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||||
|
exclude-rules:
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- gomnd
|
||||||
|
|
||||||
|
- path: pkg/golinters/errcheck.go
|
||||||
|
text: "SA1019: errCfg.Exclude is deprecated: use ExcludeFunctions instead"
|
||||||
|
- path: pkg/commands/run.go
|
||||||
|
text: "SA1019: lsc.Errcheck.Exclude is deprecated: use ExcludeFunctions instead"
|
||||||
|
|
||||||
|
# TODO must be removed after the release of the next version (v1.41.0)
|
||||||
|
- path: pkg/commands/run.go
|
||||||
|
linters:
|
||||||
|
- gomnd
|
||||||
|
# TODO must be removed after the release of the next version (v1.41.0)
|
||||||
|
- path: pkg/golinters/nolintlint/nolintlint.go
|
||||||
|
linters:
|
||||||
|
- gomnd
|
||||||
|
# TODO must be removed after the release of the next version (v1.41.0)
|
||||||
|
- path: pkg/printers/tab.go
|
||||||
|
linters:
|
||||||
|
- gomnd
|
||||||
|
|
||||||
|
|
||||||
|
run:
|
||||||
|
skip-dirs:
|
||||||
|
- test/testdata_etc
|
||||||
|
- internal/cache
|
||||||
|
- internal/renameio
|
||||||
|
- internal/robustio
|
13
Dockerfile
13
Dockerfile
|
@ -9,14 +9,14 @@
|
||||||
# docker build . -t yourregistry/btcd --build-arg ARCH=arm64v8
|
# docker build . -t yourregistry/btcd --build-arg ARCH=arm64v8
|
||||||
#
|
#
|
||||||
# For more information how to use this docker image visit:
|
# For more information how to use this docker image visit:
|
||||||
# https://github.com/btcsuite/btcd/tree/master/docs
|
# https://github.com/lbryio/lbcd/tree/master/docs
|
||||||
#
|
#
|
||||||
# 8333 Mainnet Bitcoin peer-to-peer port
|
# 8333 Mainnet Bitcoin peer-to-peer port
|
||||||
# 8334 Mainet RPC port
|
# 8334 Mainet RPC port
|
||||||
|
|
||||||
ARG ARCH=amd64
|
ARG ARCH=amd64
|
||||||
|
|
||||||
FROM golang:1.14-alpine3.12 AS build-container
|
FROM golang:1.16-alpine3.14 AS build-container
|
||||||
|
|
||||||
ARG ARCH
|
ARG ARCH
|
||||||
ENV GO111MODULE=on
|
ENV GO111MODULE=on
|
||||||
|
@ -25,17 +25,16 @@ ADD . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN set -ex \
|
RUN set -ex \
|
||||||
&& if [ "${ARCH}" = "amd64" ]; then export GOARCH=amd64; fi \
|
&& if [ "${ARCH}" = "amd64" ]; then export GOARCH=amd64; fi \
|
||||||
&& if [ "${ARCH}" = "arm32v7" ]; then export GOARCH=arm; fi \
|
|
||||||
&& if [ "${ARCH}" = "arm64v8" ]; then export GOARCH=arm64; fi \
|
&& if [ "${ARCH}" = "arm64v8" ]; then export GOARCH=arm64; fi \
|
||||||
&& echo "Compiling for $GOARCH" \
|
&& echo "Compiling for $GOARCH" \
|
||||||
&& go install -v . ./cmd/...
|
&& go install -v . ./cmd/...
|
||||||
|
|
||||||
FROM $ARCH/alpine:3.12
|
FROM $ARCH/alpine:3.14
|
||||||
|
|
||||||
COPY --from=build-container /go/bin /bin
|
COPY --from=build-container /go/bin /bin
|
||||||
|
|
||||||
VOLUME ["/root/.btcd"]
|
VOLUME ["/root/.lbcd"]
|
||||||
|
|
||||||
EXPOSE 8333 8334
|
EXPOSE 9245 9246
|
||||||
|
|
||||||
ENTRYPOINT ["btcd"]
|
ENTRYPOINT ["lbcd"]
|
||||||
|
|
1
LICENSE
1
LICENSE
|
@ -1,5 +1,6 @@
|
||||||
ISC License
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2021 The LBRY developers
|
||||||
Copyright (c) 2013-2017 The btcsuite developers
|
Copyright (c) 2013-2017 The btcsuite developers
|
||||||
Copyright (c) 2015-2016 The Decred developers
|
Copyright (c) 2015-2016 The Decred developers
|
||||||
|
|
||||||
|
|
143
README.md
143
README.md
|
@ -1,121 +1,80 @@
|
||||||
btcd
|
lbcd
|
||||||
====
|
====
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
[![Build Status](https://github.com/lbryio/lbcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/lbryio/lbcd/actions)
|
||||||
[![Coverage Status](https://coveralls.io/repos/github/btcsuite/btcd/badge.svg?branch=master)](https://coveralls.io/github/btcsuite/btcd?branch=master)
|
[![Coverage Status](https://coveralls.io/repos/github/lbryio/lbcd/badge.svg?branch=master)](https://coveralls.io/github/lbryio/lbcd?branch=master)
|
||||||
[![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd)
|
<!--[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/lbryio/lbcd)-->
|
||||||
|
|
||||||
btcd is an alternative full node bitcoin implementation written in Go (golang).
|
lbcd is a full node implementation of LBRY's blockchain written in Go (golang).
|
||||||
|
|
||||||
This project is currently under active development and is in a Beta state. It
|
This project is currently under active development and is in a Beta state while we
|
||||||
is extremely stable and has been in production use since October 2013.
|
ensure it matches LBRYcrd's functionality. The intention is that it properly downloads, validates, and serves the block chain using the exact
|
||||||
|
rules (including consensus bugs) for block acceptance as LBRYcrd. We have
|
||||||
|
taken great care to avoid lbcd causing a fork to the blockchain.
|
||||||
|
|
||||||
It properly downloads, validates, and serves the block chain using the exact
|
Note: lbcd does *NOT* include
|
||||||
rules (including consensus bugs) for block acceptance as Bitcoin Core. We have
|
wallet functionality. That functionality is provided by the
|
||||||
taken great care to avoid btcd causing a fork to the block chain. It includes a
|
[lbcwallet](https://github.com/lbryio/lbcwallet) and the [LBRY SDK](https://github.com/lbryio/lbry-sdk).
|
||||||
full block validation testing framework which contains all of the 'official'
|
|
||||||
block acceptance tests (and some additional ones) that is run on every pull
|
|
||||||
request to help ensure it properly follows consensus. Also, it passes all of
|
|
||||||
the JSON test data in the Bitcoin Core code.
|
|
||||||
|
|
||||||
It also properly relays newly mined blocks, maintains a transaction pool, and
|
## Security
|
||||||
relays individual transactions that have not yet made it into a block. It
|
|
||||||
ensures all individual transactions admitted to the pool follow the rules
|
|
||||||
required by the block chain and also includes more strict checks which filter
|
|
||||||
transactions based on miner requirements ("standard" transactions).
|
|
||||||
|
|
||||||
One key difference between btcd and Bitcoin Core is that btcd does *NOT* include
|
We take security seriously. Please contact [security](mailto:security@lbry.com) regarding any security issues.
|
||||||
wallet functionality and this was a very intentional design decision. See the
|
Our PGP key is [here](https://lbry.com/faq/pgp-key) if you need it.
|
||||||
blog entry [here](https://web.archive.org/web/20171125143919/https://blog.conformal.com/btcd-not-your-moms-bitcoin-daemon)
|
|
||||||
for more details. This means you can't actually make or receive payments
|
We maintain a mailing list for notifications of upgrades, security issues,
|
||||||
directly with btcd. That functionality is provided by the
|
and soft/hard forks. To join, visit https://lbry.com/forklist
|
||||||
[btcwallet](https://github.com/btcsuite/btcwallet) and
|
|
||||||
[Paymetheus](https://github.com/btcsuite/Paymetheus) (Windows-only) projects
|
|
||||||
which are both under active development.
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
[Go](http://golang.org) 1.14 or newer.
|
All common operating systems are supported. lbcd requires at least 8GB of RAM
|
||||||
|
and at least 100GB of disk storage. Both RAM and disk requirements increase slowly over time.
|
||||||
|
Using a fast NVMe disk is recommended.
|
||||||
|
|
||||||
|
lbcd is not immune to data loss. It expects a clean shutdown
|
||||||
|
via SIGINT or SIGTERM. SIGKILL, immediate VM kills, and sudden power loss
|
||||||
|
can cause data corruption, thus requiring chain resynchronization for recovery.
|
||||||
|
|
||||||
|
For compilation, [Go](http://golang.org) 1.16 or newer is required.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
https://github.com/btcsuite/btcd/releases
|
Acquire binary files from https://github.com/lbryio/lbcd/releases
|
||||||
|
|
||||||
#### Linux/BSD/MacOSX/POSIX - Build from Source
|
#### To build from Source on Linux/BSD/MacOSX/POSIX:
|
||||||
|
|
||||||
- Install Go according to the installation instructions here:
|
- Install Go according to its [installation instructions](http://golang.org/doc/install).
|
||||||
http://golang.org/doc/install
|
- Use your favorite git tool to acquire the lbcd source.
|
||||||
|
- lbcd has no non-Go dependencies; it can be built by simply running `go build .`
|
||||||
|
- lbcctl can be built similarly:
|
||||||
|
|
||||||
- Ensure Go was installed properly and is a supported version:
|
Both [GoLand](https://www.jetbrains.com/go/)
|
||||||
|
and [VS Code](https://code.visualstudio.com/docs/languages/go) IDEs are supported.
|
||||||
|
|
||||||
```bash
|
## Usage
|
||||||
$ go version
|
|
||||||
$ go env GOROOT GOPATH
|
By default, data and logs are stored in `~/.lbcd/`
|
||||||
|
|
||||||
|
To enable RPC access a username and password is required. Example:
|
||||||
```
|
```
|
||||||
|
./lbcd --notls --rpcuser=x --rpcpass=y --txindex &
|
||||||
NOTE: The `GOROOT` and `GOPATH` above must not be the same path. It is
|
./lbcctl --notls --rpcuser=x --rpcpass=y getblocktemplate
|
||||||
recommended that `GOPATH` is set to a directory in your home directory such as
|
|
||||||
`~/goprojects` 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 btcd, all dependencies, and install it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cd $GOPATH/src/github.com/btcsuite/btcd
|
|
||||||
$ GO111MODULE=on go install -v . ./cmd/...
|
|
||||||
```
|
```
|
||||||
|
<!-- TODO: explain how to use TLS certificates. -->
|
||||||
|
|
||||||
- btcd (and utilities) will now be installed in ```$GOPATH/bin```. If you did
|
## Contributing
|
||||||
not already add the bin directory to your system path during Go installation,
|
|
||||||
we recommend you do so now.
|
|
||||||
|
|
||||||
## Updating
|
Contributions to this project are welcome, encouraged, and compensated.
|
||||||
|
The [integrated github issue tracker](https://github.com/lbryio/lbcd/issues)
|
||||||
#### Linux/BSD/MacOSX/POSIX - Build from Source
|
is used for this project. All pull requests will be considered.
|
||||||
|
|
||||||
- Run the following commands to update btcd, all dependencies, and install it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cd $GOPATH/src/github.com/btcsuite/btcd
|
|
||||||
$ git pull
|
|
||||||
$ GO111MODULE=on go install -v . ./cmd/...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
btcd has several configuration options available to tweak how it runs, but all
|
|
||||||
of the basic operations described in the intro section work with zero
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
#### Linux/BSD/POSIX/Source
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ./btcd
|
|
||||||
```
|
|
||||||
|
|
||||||
## IRC
|
|
||||||
|
|
||||||
- irc.freenode.net
|
|
||||||
- channel #btcd
|
|
||||||
- [webchat](https://webchat.freenode.net/?channels=btcd)
|
|
||||||
|
|
||||||
## Issue Tracker
|
|
||||||
|
|
||||||
The [integrated github issue tracker](https://github.com/btcsuite/btcd/issues)
|
|
||||||
is used for this project.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
The documentation is a work-in-progress. It is located in the [docs](https://github.com/btcsuite/btcd/tree/master/docs) folder.
|
|
||||||
|
|
||||||
## Release Verification
|
|
||||||
|
|
||||||
|
<!-- ## Release Verification
|
||||||
Please see our [documentation on the current build/verification
|
Please see our [documentation on the current build/verification
|
||||||
process](https://github.com/btcsuite/btcd/tree/master/release) for all our
|
process](https://github.com/lbryio/lbcd/tree/master/release) for all our
|
||||||
releases for information on how to verify the integrity of published releases
|
releases for information on how to verify the integrity of published releases
|
||||||
using our reproducible build system.
|
using our reproducible build system.
|
||||||
|
-->
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
btcd is licensed under the [copyfree](http://copyfree.org) ISC License.
|
lbcd is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||||
|
|
|
@ -23,8 +23,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddrManager provides a concurrency safe address manager for caching potential
|
// AddrManager provides a concurrency safe address manager for caching potential
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// randAddr generates a *wire.NetAddress backed by a random IPv4/IPv6 address.
|
// randAddr generates a *wire.NetAddress backed by a random IPv4/IPv6 address.
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/addrmgr"
|
"github.com/lbryio/lbcd/addrmgr"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// naTest is used to describe a test to be performed against the NetAddressKey
|
// naTest is used to describe a test to be performed against the NetAddressKey
|
||||||
|
|
|
@ -7,7 +7,7 @@ package addrmgr
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TstKnownAddressIsBad(ka *KnownAddress) bool {
|
func TstKnownAddressIsBad(ka *KnownAddress) bool {
|
||||||
|
|
|
@ -7,7 +7,7 @@ package addrmgr
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KnownAddress tracks information about a known network address that is used
|
// KnownAddress tracks information about a known network address that is used
|
||||||
|
|
|
@ -1,114 +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 addrmgr_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/addrmgr"
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestChance(t *testing.T) {
|
|
||||||
now := time.Unix(time.Now().Unix(), 0)
|
|
||||||
var tests = []struct {
|
|
||||||
addr *addrmgr.KnownAddress
|
|
||||||
expected float64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
//Test normal case
|
|
||||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
|
||||||
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
|
|
||||||
1.0,
|
|
||||||
}, {
|
|
||||||
//Test case in which lastseen < 0
|
|
||||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(20 * time.Second)},
|
|
||||||
0, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
|
|
||||||
1.0,
|
|
||||||
}, {
|
|
||||||
//Test case in which lastattempt < 0
|
|
||||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
|
||||||
0, time.Now().Add(30*time.Minute), time.Now(), false, 0),
|
|
||||||
1.0 * .01,
|
|
||||||
}, {
|
|
||||||
//Test case in which lastattempt < ten minutes
|
|
||||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
|
||||||
0, time.Now().Add(-5*time.Minute), time.Now(), false, 0),
|
|
||||||
1.0 * .01,
|
|
||||||
}, {
|
|
||||||
//Test case with several failed attempts.
|
|
||||||
addrmgr.TstNewKnownAddress(&wire.NetAddress{Timestamp: now.Add(-35 * time.Second)},
|
|
||||||
2, time.Now().Add(-30*time.Minute), time.Now(), false, 0),
|
|
||||||
1 / 1.5 / 1.5,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := .0001
|
|
||||||
for i, test := range tests {
|
|
||||||
chance := addrmgr.TstKnownAddressChance(test.addr)
|
|
||||||
if math.Abs(test.expected-chance) >= err {
|
|
||||||
t.Errorf("case %d: got %f, expected %f", i, chance, test.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsBad(t *testing.T) {
|
|
||||||
now := time.Unix(time.Now().Unix(), 0)
|
|
||||||
future := now.Add(35 * time.Minute)
|
|
||||||
monthOld := now.Add(-43 * time.Hour * 24)
|
|
||||||
secondsOld := now.Add(-2 * time.Second)
|
|
||||||
minutesOld := now.Add(-27 * time.Minute)
|
|
||||||
hoursOld := now.Add(-5 * time.Hour)
|
|
||||||
zeroTime := time.Time{}
|
|
||||||
|
|
||||||
futureNa := &wire.NetAddress{Timestamp: future}
|
|
||||||
minutesOldNa := &wire.NetAddress{Timestamp: minutesOld}
|
|
||||||
monthOldNa := &wire.NetAddress{Timestamp: monthOld}
|
|
||||||
currentNa := &wire.NetAddress{Timestamp: secondsOld}
|
|
||||||
|
|
||||||
//Test addresses that have been tried in the last minute.
|
|
||||||
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(futureNa, 3, secondsOld, zeroTime, false, 0)) {
|
|
||||||
t.Errorf("test case 1: addresses that have been tried in the last minute are not bad.")
|
|
||||||
}
|
|
||||||
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(monthOldNa, 3, secondsOld, zeroTime, false, 0)) {
|
|
||||||
t.Errorf("test case 2: addresses that have been tried in the last minute are not bad.")
|
|
||||||
}
|
|
||||||
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(currentNa, 3, secondsOld, zeroTime, false, 0)) {
|
|
||||||
t.Errorf("test case 3: addresses that have been tried in the last minute are not bad.")
|
|
||||||
}
|
|
||||||
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(currentNa, 3, secondsOld, monthOld, true, 0)) {
|
|
||||||
t.Errorf("test case 4: addresses that have been tried in the last minute are not bad.")
|
|
||||||
}
|
|
||||||
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(currentNa, 2, secondsOld, secondsOld, true, 0)) {
|
|
||||||
t.Errorf("test case 5: addresses that have been tried in the last minute are not bad.")
|
|
||||||
}
|
|
||||||
|
|
||||||
//Test address that claims to be from the future.
|
|
||||||
if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(futureNa, 0, minutesOld, hoursOld, true, 0)) {
|
|
||||||
t.Errorf("test case 6: addresses that claim to be from the future are bad.")
|
|
||||||
}
|
|
||||||
|
|
||||||
//Test address that has not been seen in over a month.
|
|
||||||
if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(monthOldNa, 0, minutesOld, hoursOld, true, 0)) {
|
|
||||||
t.Errorf("test case 7: addresses more than a month old are bad.")
|
|
||||||
}
|
|
||||||
|
|
||||||
//It has failed at least three times and never succeeded.
|
|
||||||
if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(minutesOldNa, 3, minutesOld, zeroTime, true, 0)) {
|
|
||||||
t.Errorf("test case 8: addresses that have never succeeded are bad.")
|
|
||||||
}
|
|
||||||
|
|
||||||
//It has failed ten times in the last week
|
|
||||||
if !addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(minutesOldNa, 10, minutesOld, monthOld, true, 0)) {
|
|
||||||
t.Errorf("test case 9: addresses that have not succeeded in too long are bad.")
|
|
||||||
}
|
|
||||||
|
|
||||||
//Test an address that should work.
|
|
||||||
if addrmgr.TstKnownAddressIsBad(addrmgr.TstNewKnownAddress(minutesOldNa, 2, minutesOld, hoursOld, true, 0)) {
|
|
||||||
t.Errorf("test case 10: This should be a valid address.")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/addrmgr"
|
"github.com/lbryio/lbcd/addrmgr"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestIPTypes ensures the various functions which determine the type of an IP
|
// TestIPTypes ensures the various functions which determine the type of an IP
|
||||||
|
|
|
@ -1,30 +1,9 @@
|
||||||
blockchain
|
blockchain
|
||||||
==========
|
==========
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
|
||||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain)
|
|
||||||
|
|
||||||
Package blockchain implements bitcoin block handling and chain selection rules.
|
### Bitcoin Chain Processing Overview
|
||||||
The test coverage is currently only around 60%, but will be increasing over
|
|
||||||
time. See `test_coverage.txt` for the gocov coverage report. Alternatively, if
|
|
||||||
you are running a POSIX OS, you can run the `cov_report.sh` script for a
|
|
||||||
real-time report. Package blockchain is licensed under the liberal ISC license.
|
|
||||||
|
|
||||||
There is an associated blog post about the release of this package
|
|
||||||
[here](https://blog.conformal.com/btcchain-the-bitcoin-chain-package-from-bctd/).
|
|
||||||
|
|
||||||
This package has intentionally been designed so it can be used as a standalone
|
|
||||||
package for any projects needing to handle processing of blocks into the bitcoin
|
|
||||||
block chain.
|
|
||||||
|
|
||||||
## Installation and Updating
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get -u github.com/btcsuite/btcd/blockchain
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bitcoin Chain Processing Overview
|
|
||||||
|
|
||||||
Before a block is allowed into the block chain, it must go through an intensive
|
Before a block is allowed into the block chain, it must go through an intensive
|
||||||
series of validation rules. The following list serves as a general outline of
|
series of validation rules. The following list serves as a general outline of
|
||||||
|
@ -57,47 +36,4 @@ is by no means exhaustive:
|
||||||
transaction values
|
transaction values
|
||||||
- Run the transaction scripts to verify the spender is allowed to spend the
|
- Run the transaction scripts to verify the spender is allowed to spend the
|
||||||
coins
|
coins
|
||||||
- Insert the block into the block database
|
- Insert the block into the block database
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
* [ProcessBlock Example](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain#example-BlockChain-ProcessBlock)
|
|
||||||
Demonstrates how to create a new chain instance and use ProcessBlock to
|
|
||||||
attempt to add a block to the chain. This example intentionally
|
|
||||||
attempts to insert a duplicate genesis block to illustrate how an invalid
|
|
||||||
block is handled.
|
|
||||||
|
|
||||||
* [CompactToBig Example](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain#example-CompactToBig)
|
|
||||||
Demonstrates how to convert the compact "bits" in a block header which
|
|
||||||
represent the target difficulty to a big integer and display it using the
|
|
||||||
typical hex notation.
|
|
||||||
|
|
||||||
* [BigToCompact Example](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain#example-BigToCompact)
|
|
||||||
Demonstrates how to convert a target difficulty into the
|
|
||||||
compact "bits" in a block header which represent that target difficulty.
|
|
||||||
|
|
||||||
## GPG Verification Key
|
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
|
||||||
has not been tampered with and is coming from the btcsuite developers. To
|
|
||||||
verify the signature perform the following:
|
|
||||||
|
|
||||||
- Download the public key from the Conformal website at
|
|
||||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
|
||||||
|
|
||||||
- Import the public key into your GPG keyring:
|
|
||||||
```bash
|
|
||||||
gpg --import GIT-GPG-KEY-conformal.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
|
||||||
placeholder for the specific tag:
|
|
||||||
```bash
|
|
||||||
git tag -v TAG_NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
|
|
||||||
Package blockchain is licensed under the [copyfree](http://copyfree.org) ISC
|
|
||||||
License.
|
|
|
@ -7,8 +7,8 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// maybeAcceptBlock potentially accepts a block into the block chain and, if
|
// maybeAcceptBlock potentially accepts a block into the block chain and, if
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
// Copyright (c) 2015 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BenchmarkIsCoinBase performs a simple benchmark against the IsCoinBase
|
|
||||||
// function.
|
|
||||||
func BenchmarkIsCoinBase(b *testing.B) {
|
|
||||||
tx, _ := btcutil.NewBlock(&Block100000).Tx(1)
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
IsCoinBase(tx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkIsCoinBaseTx performs a simple benchmark against the IsCoinBaseTx
|
|
||||||
// function.
|
|
||||||
func BenchmarkIsCoinBaseTx(b *testing.B) {
|
|
||||||
tx := Block100000.Transactions[1]
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
IsCoinBaseTx(tx)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,10 +10,10 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// blockStatus is a bit field representing the validation state of the block.
|
// blockStatus is a bit field representing the validation state of the block.
|
||||||
|
@ -93,6 +93,7 @@ type blockNode struct {
|
||||||
nonce uint32
|
nonce uint32
|
||||||
timestamp int64
|
timestamp int64
|
||||||
merkleRoot chainhash.Hash
|
merkleRoot chainhash.Hash
|
||||||
|
claimTrie chainhash.Hash
|
||||||
|
|
||||||
// status is a bitfield representing the validation state of the block. The
|
// status is a bitfield representing the validation state of the block. The
|
||||||
// status field, unlike the other fields, may be written to and so should
|
// status field, unlike the other fields, may be written to and so should
|
||||||
|
@ -114,6 +115,7 @@ func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *block
|
||||||
nonce: blockHeader.Nonce,
|
nonce: blockHeader.Nonce,
|
||||||
timestamp: blockHeader.Timestamp.Unix(),
|
timestamp: blockHeader.Timestamp.Unix(),
|
||||||
merkleRoot: blockHeader.MerkleRoot,
|
merkleRoot: blockHeader.MerkleRoot,
|
||||||
|
claimTrie: blockHeader.ClaimTrie,
|
||||||
}
|
}
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
node.parent = parent
|
node.parent = parent
|
||||||
|
@ -144,6 +146,7 @@ func (node *blockNode) Header() wire.BlockHeader {
|
||||||
Version: node.version,
|
Version: node.version,
|
||||||
PrevBlock: *prevHash,
|
PrevBlock: *prevHash,
|
||||||
MerkleRoot: node.merkleRoot,
|
MerkleRoot: node.merkleRoot,
|
||||||
|
ClaimTrie: node.claimTrie,
|
||||||
Timestamp: time.Unix(node.timestamp, 0),
|
Timestamp: time.Unix(node.timestamp, 0),
|
||||||
Bits: node.bits,
|
Bits: node.bits,
|
||||||
Nonce: node.nonce,
|
Nonce: node.nonce,
|
||||||
|
|
|
@ -11,12 +11,14 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
|
|
||||||
|
"github.com/lbryio/lbcd/claimtrie"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -180,6 +182,8 @@ type BlockChain struct {
|
||||||
// certain blockchain events.
|
// certain blockchain events.
|
||||||
notificationsLock sync.RWMutex
|
notificationsLock sync.RWMutex
|
||||||
notifications []NotificationCallback
|
notifications []NotificationCallback
|
||||||
|
|
||||||
|
claimTrie *claimtrie.ClaimTrie
|
||||||
}
|
}
|
||||||
|
|
||||||
// HaveBlock returns whether or not the chain instance has the block represented
|
// HaveBlock returns whether or not the chain instance has the block represented
|
||||||
|
@ -571,7 +575,8 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
// No warnings about unknown rules until the chain is current.
|
// No warnings about unknown rules until the chain is current.
|
||||||
if b.isCurrent() {
|
current := b.isCurrent()
|
||||||
|
if current {
|
||||||
// Warn if any unknown new rules are either about to activate or
|
// Warn if any unknown new rules are either about to activate or
|
||||||
// have already been activated.
|
// have already been activated.
|
||||||
if err := b.warnUnknownRuleActivations(node); err != nil {
|
if err := b.warnUnknownRuleActivations(node); err != nil {
|
||||||
|
@ -579,6 +584,13 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle LBRY Claim Scripts
|
||||||
|
if b.claimTrie != nil {
|
||||||
|
if err := b.ParseClaimScripts(block, node, view, current); err != nil {
|
||||||
|
return ruleError(ErrBadClaimTrie, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write any block status changes to DB before updating best state.
|
// Write any block status changes to DB before updating best state.
|
||||||
err := b.index.flushToDB()
|
err := b.index.flushToDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -761,6 +773,12 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.claimTrie != nil {
|
||||||
|
if err = b.claimTrie.ResetHeight(node.parent.height); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prune fully spent entries and mark all entries in the view unmodified
|
// Prune fully spent entries and mark all entries in the view unmodified
|
||||||
// now that the modifications have been committed to the database.
|
// now that the modifications have been committed to the database.
|
||||||
view.commit()
|
view.commit()
|
||||||
|
@ -1208,7 +1226,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
// factors are used to guess, but the key factors that allow the chain to
|
// factors are used to guess, but the key factors that allow the chain to
|
||||||
// believe it is current are:
|
// believe it is current are:
|
||||||
// - Latest block height is after the latest checkpoint (if enabled)
|
// - Latest block height is after the latest checkpoint (if enabled)
|
||||||
// - Latest block has a timestamp newer than 24 hours ago
|
// - Latest block has a timestamp newer than ~6 hours ago (as LBRY block time is one fourth of bitcoin)
|
||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for reads).
|
// This function MUST be called with the chain state lock held (for reads).
|
||||||
func (b *BlockChain) isCurrent() bool {
|
func (b *BlockChain) isCurrent() bool {
|
||||||
|
@ -1219,13 +1237,13 @@ func (b *BlockChain) isCurrent() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not current if the latest best block has a timestamp before 24 hours
|
// Not current if the latest best block has a timestamp before 7 hours
|
||||||
// ago.
|
// ago.
|
||||||
//
|
//
|
||||||
// The chain appears to be current if none of the checks reported
|
// The chain appears to be current if none of the checks reported
|
||||||
// otherwise.
|
// otherwise.
|
||||||
minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix()
|
hours := b.timeSource.AdjustedTime().Add(-7 * time.Hour).Unix()
|
||||||
return b.bestChain.Tip().timestamp >= minus24Hours
|
return b.bestChain.Tip().timestamp >= hours
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCurrent returns whether or not the chain believes it is current. Several
|
// IsCurrent returns whether or not the chain believes it is current. Several
|
||||||
|
@ -1614,6 +1632,11 @@ func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Has
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClaimTrie returns the claimTrie associated wit hthe chain.
|
||||||
|
func (b *BlockChain) ClaimTrie() *claimtrie.ClaimTrie {
|
||||||
|
return b.claimTrie
|
||||||
|
}
|
||||||
|
|
||||||
// IndexManager provides a generic interface that the is called when blocks are
|
// IndexManager provides a generic interface that the is called when blocks are
|
||||||
// connected and disconnected to and from the tip of the main chain for the
|
// connected and disconnected to and from the tip of the main chain for the
|
||||||
// purpose of supporting optional indexes.
|
// purpose of supporting optional indexes.
|
||||||
|
@ -1700,6 +1723,8 @@ type Config struct {
|
||||||
// This field can be nil if the caller is not interested in using a
|
// This field can be nil if the caller is not interested in using a
|
||||||
// signature cache.
|
// signature cache.
|
||||||
HashCache *txscript.HashCache
|
HashCache *txscript.HashCache
|
||||||
|
|
||||||
|
ClaimTrie *claimtrie.ClaimTrie
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a BlockChain instance using the provided configuration details.
|
// New returns a BlockChain instance using the provided configuration details.
|
||||||
|
@ -1736,7 +1761,6 @@ func New(config *Config) (*BlockChain, error) {
|
||||||
params := config.ChainParams
|
params := config.ChainParams
|
||||||
targetTimespan := int64(params.TargetTimespan / time.Second)
|
targetTimespan := int64(params.TargetTimespan / time.Second)
|
||||||
targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
|
targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
|
||||||
adjustmentFactor := params.RetargetAdjustmentFactor
|
|
||||||
b := BlockChain{
|
b := BlockChain{
|
||||||
checkpoints: config.Checkpoints,
|
checkpoints: config.Checkpoints,
|
||||||
checkpointsByHeight: checkpointsByHeight,
|
checkpointsByHeight: checkpointsByHeight,
|
||||||
|
@ -1745,8 +1769,8 @@ func New(config *Config) (*BlockChain, error) {
|
||||||
timeSource: config.TimeSource,
|
timeSource: config.TimeSource,
|
||||||
sigCache: config.SigCache,
|
sigCache: config.SigCache,
|
||||||
indexManager: config.IndexManager,
|
indexManager: config.IndexManager,
|
||||||
minRetargetTimespan: targetTimespan / adjustmentFactor,
|
minRetargetTimespan: targetTimespan - (targetTimespan / 8),
|
||||||
maxRetargetTimespan: targetTimespan * adjustmentFactor,
|
maxRetargetTimespan: targetTimespan + (targetTimespan / 2),
|
||||||
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
|
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
|
||||||
index: newBlockIndex(config.DB, params),
|
index: newBlockIndex(config.DB, params),
|
||||||
hashCache: config.HashCache,
|
hashCache: config.HashCache,
|
||||||
|
@ -1755,6 +1779,7 @@ func New(config *Config) (*BlockChain, error) {
|
||||||
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
|
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
|
||||||
warningCaches: newThresholdCaches(vbNumBits),
|
warningCaches: newThresholdCaches(vbNumBits),
|
||||||
deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments),
|
deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments),
|
||||||
|
claimTrie: config.ClaimTrie,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the chain state from the passed database. When the db
|
// Initialize the chain state from the passed database. When the db
|
||||||
|
@ -1764,6 +1789,20 @@ func New(config *Config) (*BlockChain, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to insert the output in genesis block in to the
|
||||||
|
// transaction database.
|
||||||
|
fn := func(dbTx database.Tx) error {
|
||||||
|
genesisBlock := btcutil.NewBlock(b.chainParams.GenesisBlock)
|
||||||
|
view := NewUtxoViewpoint()
|
||||||
|
if err := view.connectTransactions(genesisBlock, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return dbPutUtxoView(dbTx, view)
|
||||||
|
}
|
||||||
|
if err := b.db.Update(fn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Perform any upgrades to the various chain-specific buckets as needed.
|
// Perform any upgrades to the various chain-specific buckets as needed.
|
||||||
if err := b.maybeUpgradeDbBuckets(config.Interrupt); err != nil {
|
if err := b.maybeUpgradeDbBuckets(config.Interrupt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1783,6 +1822,14 @@ func New(config *Config) (*BlockChain, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.claimTrie != nil {
|
||||||
|
err := rebuildMissingClaimTrieData(&b, config.Interrupt)
|
||||||
|
if err != nil {
|
||||||
|
b.claimTrie.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bestNode := b.bestChain.Tip()
|
bestNode := b.bestChain.Tip()
|
||||||
log.Infof("Chain state (height %d, hash %v, totaltx %d, work %v)",
|
log.Infof("Chain state (height %d, hash %v, totaltx %d, work %v)",
|
||||||
bestNode.height, bestNode.hash, b.stateSnapshot.TotalTxns,
|
bestNode.height, bestNode.hash, b.stateSnapshot.TotalTxns,
|
||||||
|
@ -1790,3 +1837,63 @@ func New(config *Config) (*BlockChain, error) {
|
||||||
|
|
||||||
return &b, nil
|
return &b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rebuildMissingClaimTrieData(b *BlockChain, done <-chan struct{}) error {
|
||||||
|
target := b.bestChain.Height()
|
||||||
|
if b.claimTrie.Height() == target {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if b.claimTrie.Height() > target {
|
||||||
|
return b.claimTrie.ResetHeight(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
lastReport := time.Now()
|
||||||
|
// TODO: move this view inside the loop (or recreate it every 5 sec.)
|
||||||
|
// as accumulating all inputs has potential to use a huge amount of RAM
|
||||||
|
// but we need to get the spent inputs working for that to be possible
|
||||||
|
view := NewUtxoViewpoint()
|
||||||
|
for h := int32(0); h < target; h++ {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return fmt.Errorf("rebuild unfinished at height %d", b.claimTrie.Height())
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
n := b.bestChain.NodeByHeight(h + 1)
|
||||||
|
|
||||||
|
var block *btcutil.Block
|
||||||
|
err := b.db.View(func(dbTx database.Tx) error {
|
||||||
|
var err error
|
||||||
|
block, err = dbFetchBlockByNode(dbTx, n)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = view.fetchInputUtxos(b.db, block)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = view.connectTransactions(block, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if h >= b.claimTrie.Height() {
|
||||||
|
err = b.ParseClaimScripts(block, n, view, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if time.Since(lastReport) > time.Second*5 {
|
||||||
|
lastReport = time.Now()
|
||||||
|
log.Infof("Rebuilding claim trie data to %d. At: %d", target, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof("Completed rebuilding claim trie data to %d. Took %s ",
|
||||||
|
b.claimTrie.Height(), time.Since(start))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,966 +0,0 @@
|
||||||
// Copyright (c) 2013-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestHaveBlock tests the HaveBlock API to ensure proper functionality.
|
|
||||||
func TestHaveBlock(t *testing.T) {
|
|
||||||
// Load up blocks such that there is a side chain.
|
|
||||||
// (genesis block) -> 1 -> 2 -> 3 -> 4
|
|
||||||
// \-> 3a
|
|
||||||
testFiles := []string{
|
|
||||||
"blk_0_to_4.dat.bz2",
|
|
||||||
"blk_3A.dat.bz2",
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks []*btcutil.Block
|
|
||||||
for _, file := range testFiles {
|
|
||||||
blockTmp, err := loadBlocks(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error loading file: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
blocks = append(blocks, blockTmp...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new database and chain instance to run tests against.
|
|
||||||
chain, teardownFunc, err := chainSetup("haveblock",
|
|
||||||
&chaincfg.MainNetParams)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to setup chain instance: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer teardownFunc()
|
|
||||||
|
|
||||||
// Since we're not dealing with the real block chain, set the coinbase
|
|
||||||
// maturity to 1.
|
|
||||||
chain.TstSetCoinbaseMaturity(1)
|
|
||||||
|
|
||||||
for i := 1; i < len(blocks); i++ {
|
|
||||||
_, isOrphan, err := chain.ProcessBlock(blocks[i], BFNone)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("ProcessBlock fail on block %v: %v\n", i, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if isOrphan {
|
|
||||||
t.Errorf("ProcessBlock incorrectly returned block %v "+
|
|
||||||
"is an orphan\n", i)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert an orphan block.
|
|
||||||
_, isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000),
|
|
||||||
BFNone)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unable to process block: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !isOrphan {
|
|
||||||
t.Errorf("ProcessBlock indicated block is an not orphan when " +
|
|
||||||
"it should be\n")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
hash string
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// Genesis block should be present (in the main chain).
|
|
||||||
{hash: chaincfg.MainNetParams.GenesisHash.String(), want: true},
|
|
||||||
|
|
||||||
// Block 3a should be present (on a side chain).
|
|
||||||
{hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true},
|
|
||||||
|
|
||||||
// Block 100000 should be present (as an orphan).
|
|
||||||
{hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true},
|
|
||||||
|
|
||||||
// Random hashes should not be available.
|
|
||||||
{hash: "123", want: false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
hash, err := chainhash.NewHashFromStr(test.hash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := chain.HaveBlock(hash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("HaveBlock #%d unexpected error: %v", i, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if result != test.want {
|
|
||||||
t.Errorf("HaveBlock #%d got %v want %v", i, result,
|
|
||||||
test.want)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestCalcSequenceLock tests the LockTimeToSequence function, and the
|
|
||||||
// CalcSequenceLock method of a Chain instance. The tests exercise several
|
|
||||||
// combinations of inputs to the CalcSequenceLock function in order to ensure
|
|
||||||
// the returned SequenceLocks are correct for each test instance.
|
|
||||||
func TestCalcSequenceLock(t *testing.T) {
|
|
||||||
netParams := &chaincfg.SimNetParams
|
|
||||||
|
|
||||||
// We need to activate CSV in order to test the processing logic, so
|
|
||||||
// manually craft the block version that's used to signal the soft-fork
|
|
||||||
// activation.
|
|
||||||
csvBit := netParams.Deployments[chaincfg.DeploymentCSV].BitNumber
|
|
||||||
blockVersion := int32(0x20000000 | (uint32(1) << csvBit))
|
|
||||||
|
|
||||||
// Generate enough synthetic blocks to activate CSV.
|
|
||||||
chain := newFakeChain(netParams)
|
|
||||||
node := chain.bestChain.Tip()
|
|
||||||
blockTime := node.Header().Timestamp
|
|
||||||
numBlocksToActivate := (netParams.MinerConfirmationWindow * 3)
|
|
||||||
for i := uint32(0); i < numBlocksToActivate; i++ {
|
|
||||||
blockTime = blockTime.Add(time.Second)
|
|
||||||
node = newFakeNode(node, blockVersion, 0, blockTime)
|
|
||||||
chain.index.AddNode(node)
|
|
||||||
chain.bestChain.SetTip(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a utxo view with a fake utxo for the inputs used in the
|
|
||||||
// transactions created below. This utxo is added such that it has an
|
|
||||||
// age of 4 blocks.
|
|
||||||
targetTx := btcutil.NewTx(&wire.MsgTx{
|
|
||||||
TxOut: []*wire.TxOut{{
|
|
||||||
PkScript: nil,
|
|
||||||
Value: 10,
|
|
||||||
}},
|
|
||||||
})
|
|
||||||
utxoView := NewUtxoViewpoint()
|
|
||||||
utxoView.AddTxOuts(targetTx, int32(numBlocksToActivate)-4)
|
|
||||||
utxoView.SetBestHash(&node.hash)
|
|
||||||
|
|
||||||
// Create a utxo that spends the fake utxo created above for use in the
|
|
||||||
// transactions created in the tests. It has an age of 4 blocks. Note
|
|
||||||
// that the sequence lock heights are always calculated from the same
|
|
||||||
// point of view that they were originally calculated from for a given
|
|
||||||
// utxo. That is to say, the height prior to it.
|
|
||||||
utxo := wire.OutPoint{
|
|
||||||
Hash: *targetTx.Hash(),
|
|
||||||
Index: 0,
|
|
||||||
}
|
|
||||||
prevUtxoHeight := int32(numBlocksToActivate) - 4
|
|
||||||
|
|
||||||
// Obtain the median time past from the PoV of the input created above.
|
|
||||||
// The MTP for the input is the MTP from the PoV of the block *prior*
|
|
||||||
// to the one that included it.
|
|
||||||
medianTime := node.RelativeAncestor(5).CalcPastMedianTime().Unix()
|
|
||||||
|
|
||||||
// The median time calculated from the PoV of the best block in the
|
|
||||||
// test chain. For unconfirmed inputs, this value will be used since
|
|
||||||
// the MTP will be calculated from the PoV of the yet-to-be-mined
|
|
||||||
// block.
|
|
||||||
nextMedianTime := node.CalcPastMedianTime().Unix()
|
|
||||||
nextBlockHeight := int32(numBlocksToActivate) + 1
|
|
||||||
|
|
||||||
// Add an additional transaction which will serve as our unconfirmed
|
|
||||||
// output.
|
|
||||||
unConfTx := &wire.MsgTx{
|
|
||||||
TxOut: []*wire.TxOut{{
|
|
||||||
PkScript: nil,
|
|
||||||
Value: 5,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
unConfUtxo := wire.OutPoint{
|
|
||||||
Hash: unConfTx.TxHash(),
|
|
||||||
Index: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding a utxo with a height of 0x7fffffff indicates that the output
|
|
||||||
// is currently unmined.
|
|
||||||
utxoView.AddTxOuts(btcutil.NewTx(unConfTx), 0x7fffffff)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
tx *wire.MsgTx
|
|
||||||
view *UtxoViewpoint
|
|
||||||
mempool bool
|
|
||||||
want *SequenceLock
|
|
||||||
}{
|
|
||||||
// A transaction of version one should disable sequence locks
|
|
||||||
// as the new sequence number semantics only apply to
|
|
||||||
// transactions version 2 or higher.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 3),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: -1,
|
|
||||||
BlockHeight: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with a single input with max sequence number.
|
|
||||||
// This sequence number has the high bit set, so sequence locks
|
|
||||||
// should be disabled.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: wire.MaxTxInSequenceNum,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: -1,
|
|
||||||
BlockHeight: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with a single input whose lock time is
|
|
||||||
// expressed in seconds. However, the specified lock time is
|
|
||||||
// below the required floor for time based lock times since
|
|
||||||
// they have time granularity of 512 seconds. As a result, the
|
|
||||||
// seconds lock-time should be just before the median time of
|
|
||||||
// the targeted block.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 2),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: medianTime - 1,
|
|
||||||
BlockHeight: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with a single input whose lock time is
|
|
||||||
// expressed in seconds. The number of seconds should be 1023
|
|
||||||
// seconds after the median past time of the last block in the
|
|
||||||
// chain.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 1024),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: medianTime + 1023,
|
|
||||||
BlockHeight: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with multiple inputs. The first input has a
|
|
||||||
// lock time expressed in seconds. The second input has a
|
|
||||||
// sequence lock in blocks with a value of 4. The last input
|
|
||||||
// has a sequence number with a value of 5, but has the disable
|
|
||||||
// bit set. So the first lock should be selected as it's the
|
|
||||||
// latest lock that isn't disabled.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 2560),
|
|
||||||
}, {
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 4),
|
|
||||||
}, {
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 5) |
|
|
||||||
wire.SequenceLockTimeDisabled,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1,
|
|
||||||
BlockHeight: prevUtxoHeight + 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Transaction with a single input. The input's sequence number
|
|
||||||
// encodes a relative lock-time in blocks (3 blocks). The
|
|
||||||
// sequence lock should have a value of -1 for seconds, but a
|
|
||||||
// height of 2 meaning it can be included at height 3.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 3),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: -1,
|
|
||||||
BlockHeight: prevUtxoHeight + 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with two inputs with lock times expressed in
|
|
||||||
// seconds. The selected sequence lock value for seconds should
|
|
||||||
// be the time further in the future.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 5120),
|
|
||||||
}, {
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 2560),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: medianTime + (10 << wire.SequenceLockTimeGranularity) - 1,
|
|
||||||
BlockHeight: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with two inputs with lock times expressed in
|
|
||||||
// blocks. The selected sequence lock value for blocks should
|
|
||||||
// be the height further in the future, so a height of 10
|
|
||||||
// indicating it can be included at height 11.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 1),
|
|
||||||
}, {
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 11),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: -1,
|
|
||||||
BlockHeight: prevUtxoHeight + 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with multiple inputs. Two inputs are time
|
|
||||||
// based, and the other two are block based. The lock lying
|
|
||||||
// further into the future for both inputs should be chosen.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 2560),
|
|
||||||
}, {
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 6656),
|
|
||||||
}, {
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 3),
|
|
||||||
}, {
|
|
||||||
PreviousOutPoint: utxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 9),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1,
|
|
||||||
BlockHeight: prevUtxoHeight + 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with a single unconfirmed input. As the input
|
|
||||||
// is confirmed, the height of the input should be interpreted
|
|
||||||
// as the height of the *next* block. So, a 2 block relative
|
|
||||||
// lock means the sequence lock should be for 1 block after the
|
|
||||||
// *next* block height, indicating it can be included 2 blocks
|
|
||||||
// after that.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: unConfUtxo,
|
|
||||||
Sequence: LockTimeToSequence(false, 2),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
mempool: true,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: -1,
|
|
||||||
BlockHeight: nextBlockHeight + 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// A transaction with a single unconfirmed input. The input has
|
|
||||||
// a time based lock, so the lock time should be based off the
|
|
||||||
// MTP of the *next* block.
|
|
||||||
{
|
|
||||||
tx: &wire.MsgTx{
|
|
||||||
Version: 2,
|
|
||||||
TxIn: []*wire.TxIn{{
|
|
||||||
PreviousOutPoint: unConfUtxo,
|
|
||||||
Sequence: LockTimeToSequence(true, 1024),
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
view: utxoView,
|
|
||||||
mempool: true,
|
|
||||||
want: &SequenceLock{
|
|
||||||
Seconds: nextMedianTime + 1023,
|
|
||||||
BlockHeight: -1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %v SequenceLock tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
utilTx := btcutil.NewTx(test.tx)
|
|
||||||
seqLock, err := chain.CalcSequenceLock(utilTx, test.view, test.mempool)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("test #%d, unable to calc sequence lock: %v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if seqLock.Seconds != test.want.Seconds {
|
|
||||||
t.Fatalf("test #%d got %v seconds want %v seconds",
|
|
||||||
i, seqLock.Seconds, test.want.Seconds)
|
|
||||||
}
|
|
||||||
if seqLock.BlockHeight != test.want.BlockHeight {
|
|
||||||
t.Fatalf("test #%d got height of %v want height of %v ",
|
|
||||||
i, seqLock.BlockHeight, test.want.BlockHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeHashes is a convenience function that returns the hashes for all of the
|
|
||||||
// passed indexes of the provided nodes. It is used to construct expected hash
|
|
||||||
// slices in the tests.
|
|
||||||
func nodeHashes(nodes []*blockNode, indexes ...int) []chainhash.Hash {
|
|
||||||
hashes := make([]chainhash.Hash, 0, len(indexes))
|
|
||||||
for _, idx := range indexes {
|
|
||||||
hashes = append(hashes, nodes[idx].hash)
|
|
||||||
}
|
|
||||||
return hashes
|
|
||||||
}
|
|
||||||
|
|
||||||
// nodeHeaders is a convenience function that returns the headers for all of
|
|
||||||
// the passed indexes of the provided nodes. It is used to construct expected
|
|
||||||
// located headers in the tests.
|
|
||||||
func nodeHeaders(nodes []*blockNode, indexes ...int) []wire.BlockHeader {
|
|
||||||
headers := make([]wire.BlockHeader, 0, len(indexes))
|
|
||||||
for _, idx := range indexes {
|
|
||||||
headers = append(headers, nodes[idx].Header())
|
|
||||||
}
|
|
||||||
return headers
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestLocateInventory ensures that locating inventory via the LocateHeaders and
|
|
||||||
// LocateBlocks functions behaves as expected.
|
|
||||||
func TestLocateInventory(t *testing.T) {
|
|
||||||
// Construct a synthetic block chain with a block index consisting of
|
|
||||||
// the following structure.
|
|
||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
|
||||||
// \-> 16a -> 17a
|
|
||||||
tip := tstTip
|
|
||||||
chain := newFakeChain(&chaincfg.MainNetParams)
|
|
||||||
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
|
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[14], 2)
|
|
||||||
for _, node := range branch0Nodes {
|
|
||||||
chain.index.AddNode(node)
|
|
||||||
}
|
|
||||||
for _, node := range branch1Nodes {
|
|
||||||
chain.index.AddNode(node)
|
|
||||||
}
|
|
||||||
chain.bestChain.SetTip(tip(branch0Nodes))
|
|
||||||
|
|
||||||
// Create chain views for different branches of the overall chain to
|
|
||||||
// simulate a local and remote node on different parts of the chain.
|
|
||||||
localView := newChainView(tip(branch0Nodes))
|
|
||||||
remoteView := newChainView(tip(branch1Nodes))
|
|
||||||
|
|
||||||
// Create a chain view for a completely unrelated block chain to
|
|
||||||
// simulate a remote node on a totally different chain.
|
|
||||||
unrelatedBranchNodes := chainedNodes(nil, 5)
|
|
||||||
unrelatedView := newChainView(tip(unrelatedBranchNodes))
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
locator BlockLocator // locator for requested inventory
|
|
||||||
hashStop chainhash.Hash // stop hash for locator
|
|
||||||
maxAllowed uint32 // max to locate, 0 = wire const
|
|
||||||
headers []wire.BlockHeader // expected located headers
|
|
||||||
hashes []chainhash.Hash // expected located hashes
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// Empty block locators and unknown stop hash. No
|
|
||||||
// inventory should be located.
|
|
||||||
name: "no locators, no stop",
|
|
||||||
locator: nil,
|
|
||||||
hashStop: chainhash.Hash{},
|
|
||||||
headers: nil,
|
|
||||||
hashes: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Empty block locators and stop hash in side chain.
|
|
||||||
// The expected result is the requested block.
|
|
||||||
name: "no locators, stop in side",
|
|
||||||
locator: nil,
|
|
||||||
hashStop: tip(branch1Nodes).hash,
|
|
||||||
headers: nodeHeaders(branch1Nodes, 1),
|
|
||||||
hashes: nodeHashes(branch1Nodes, 1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Empty block locators and stop hash in main chain.
|
|
||||||
// The expected result is the requested block.
|
|
||||||
name: "no locators, stop in main",
|
|
||||||
locator: nil,
|
|
||||||
hashStop: branch0Nodes[12].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 12),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 12),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on side chain and a
|
|
||||||
// stop hash local node doesn't know about. The
|
|
||||||
// expected result is the blocks after the fork point in
|
|
||||||
// the main chain and the stop hash has no effect.
|
|
||||||
name: "remote side chain, unknown stop",
|
|
||||||
locator: remoteView.BlockLocator(nil),
|
|
||||||
hashStop: chainhash.Hash{0x01},
|
|
||||||
headers: nodeHeaders(branch0Nodes, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on side chain and a
|
|
||||||
// stop hash in side chain. The expected result is the
|
|
||||||
// blocks after the fork point in the main chain and the
|
|
||||||
// stop hash has no effect.
|
|
||||||
name: "remote side chain, stop in side",
|
|
||||||
locator: remoteView.BlockLocator(nil),
|
|
||||||
hashStop: tip(branch1Nodes).hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on side chain and a
|
|
||||||
// stop hash in main chain, but before fork point. The
|
|
||||||
// expected result is the blocks after the fork point in
|
|
||||||
// the main chain and the stop hash has no effect.
|
|
||||||
name: "remote side chain, stop in main before",
|
|
||||||
locator: remoteView.BlockLocator(nil),
|
|
||||||
hashStop: branch0Nodes[13].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on side chain and a
|
|
||||||
// stop hash in main chain, but exactly at the fork
|
|
||||||
// point. The expected result is the blocks after the
|
|
||||||
// fork point in the main chain and the stop hash has no
|
|
||||||
// effect.
|
|
||||||
name: "remote side chain, stop in main exact",
|
|
||||||
locator: remoteView.BlockLocator(nil),
|
|
||||||
hashStop: branch0Nodes[14].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on side chain and a
|
|
||||||
// stop hash in main chain just after the fork point.
|
|
||||||
// The expected result is the blocks after the fork
|
|
||||||
// point in the main chain up to and including the stop
|
|
||||||
// hash.
|
|
||||||
name: "remote side chain, stop in main after",
|
|
||||||
locator: remoteView.BlockLocator(nil),
|
|
||||||
hashStop: branch0Nodes[15].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 15),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 15),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on side chain and a
|
|
||||||
// stop hash in main chain some time after the fork
|
|
||||||
// point. The expected result is the blocks after the
|
|
||||||
// fork point in the main chain up to and including the
|
|
||||||
// stop hash.
|
|
||||||
name: "remote side chain, stop in main after more",
|
|
||||||
locator: remoteView.BlockLocator(nil),
|
|
||||||
hashStop: branch0Nodes[16].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 15, 16),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 15, 16),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on main chain in the
|
|
||||||
// past and a stop hash local node doesn't know about.
|
|
||||||
// The expected result is the blocks after the known
|
|
||||||
// point in the main chain and the stop hash has no
|
|
||||||
// effect.
|
|
||||||
name: "remote main chain past, unknown stop",
|
|
||||||
locator: localView.BlockLocator(branch0Nodes[12]),
|
|
||||||
hashStop: chainhash.Hash{0x01},
|
|
||||||
headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on main chain in the
|
|
||||||
// past and a stop hash in a side chain. The expected
|
|
||||||
// result is the blocks after the known point in the
|
|
||||||
// main chain and the stop hash has no effect.
|
|
||||||
name: "remote main chain past, stop in side",
|
|
||||||
locator: localView.BlockLocator(branch0Nodes[12]),
|
|
||||||
hashStop: tip(branch1Nodes).hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on main chain in the
|
|
||||||
// past and a stop hash in the main chain before that
|
|
||||||
// point. The expected result is the blocks after the
|
|
||||||
// known point in the main chain and the stop hash has
|
|
||||||
// no effect.
|
|
||||||
name: "remote main chain past, stop in main before",
|
|
||||||
locator: localView.BlockLocator(branch0Nodes[12]),
|
|
||||||
hashStop: branch0Nodes[11].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on main chain in the
|
|
||||||
// past and a stop hash in the main chain exactly at that
|
|
||||||
// point. The expected result is the blocks after the
|
|
||||||
// known point in the main chain and the stop hash has
|
|
||||||
// no effect.
|
|
||||||
name: "remote main chain past, stop in main exact",
|
|
||||||
locator: localView.BlockLocator(branch0Nodes[12]),
|
|
||||||
hashStop: branch0Nodes[12].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 13, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on main chain in the
|
|
||||||
// past and a stop hash in the main chain just after
|
|
||||||
// that point. The expected result is the blocks after
|
|
||||||
// the known point in the main chain and the stop hash
|
|
||||||
// has no effect.
|
|
||||||
name: "remote main chain past, stop in main after",
|
|
||||||
locator: localView.BlockLocator(branch0Nodes[12]),
|
|
||||||
hashStop: branch0Nodes[13].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 13),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 13),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being on main chain in the
|
|
||||||
// past and a stop hash in the main chain some time
|
|
||||||
// after that point. The expected result is the blocks
|
|
||||||
// after the known point in the main chain and the stop
|
|
||||||
// hash has no effect.
|
|
||||||
name: "remote main chain past, stop in main after more",
|
|
||||||
locator: localView.BlockLocator(branch0Nodes[12]),
|
|
||||||
hashStop: branch0Nodes[15].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 13, 14, 15),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 13, 14, 15),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being at exactly the same
|
|
||||||
// point in the main chain and a stop hash local node
|
|
||||||
// doesn't know about. The expected result is no
|
|
||||||
// located inventory.
|
|
||||||
name: "remote main chain same, unknown stop",
|
|
||||||
locator: localView.BlockLocator(nil),
|
|
||||||
hashStop: chainhash.Hash{0x01},
|
|
||||||
headers: nil,
|
|
||||||
hashes: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators based on remote being at exactly the same
|
|
||||||
// point in the main chain and a stop hash at exactly
|
|
||||||
// the same point. The expected result is no located
|
|
||||||
// inventory.
|
|
||||||
name: "remote main chain same, stop same point",
|
|
||||||
locator: localView.BlockLocator(nil),
|
|
||||||
hashStop: tip(branch0Nodes).hash,
|
|
||||||
headers: nil,
|
|
||||||
hashes: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators from remote that don't include any blocks
|
|
||||||
// the local node knows. This would happen if the
|
|
||||||
// remote node is on a completely separate chain that
|
|
||||||
// isn't rooted with the same genesis block. The
|
|
||||||
// expected result is the blocks after the genesis
|
|
||||||
// block.
|
|
||||||
name: "remote unrelated chain",
|
|
||||||
locator: unrelatedView.BlockLocator(nil),
|
|
||||||
hashStop: chainhash.Hash{},
|
|
||||||
headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
|
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
|
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Locators from remote for second block in main chain
|
|
||||||
// and no stop hash, but with an overridden max limit.
|
|
||||||
// The expected result is the blocks after the second
|
|
||||||
// block limited by the max.
|
|
||||||
name: "remote genesis",
|
|
||||||
locator: locatorHashes(branch0Nodes, 0),
|
|
||||||
hashStop: chainhash.Hash{},
|
|
||||||
maxAllowed: 3,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 1, 2, 3),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 1, 2, 3),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Poorly formed locator.
|
|
||||||
//
|
|
||||||
// Locator from remote that only includes a single
|
|
||||||
// block on a side chain the local node knows. The
|
|
||||||
// expected result is the blocks after the genesis
|
|
||||||
// block since even though the block is known, it is on
|
|
||||||
// a side chain and there are no more locators to find
|
|
||||||
// the fork point.
|
|
||||||
name: "weak locator, single known side block",
|
|
||||||
locator: locatorHashes(branch1Nodes, 1),
|
|
||||||
hashStop: chainhash.Hash{},
|
|
||||||
headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
|
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
|
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Poorly formed locator.
|
|
||||||
//
|
|
||||||
// Locator from remote that only includes multiple
|
|
||||||
// blocks on a side chain the local node knows however
|
|
||||||
// none in the main chain. The expected result is the
|
|
||||||
// blocks after the genesis block since even though the
|
|
||||||
// blocks are known, they are all on a side chain and
|
|
||||||
// there are no more locators to find the fork point.
|
|
||||||
name: "weak locator, multiple known side blocks",
|
|
||||||
locator: locatorHashes(branch1Nodes, 1),
|
|
||||||
hashStop: chainhash.Hash{},
|
|
||||||
headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
|
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5, 6,
|
|
||||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Poorly formed locator.
|
|
||||||
//
|
|
||||||
// Locator from remote that only includes multiple
|
|
||||||
// blocks on a side chain the local node knows however
|
|
||||||
// none in the main chain but includes a stop hash in
|
|
||||||
// the main chain. The expected result is the blocks
|
|
||||||
// after the genesis block up to the stop hash since
|
|
||||||
// even though the blocks are known, they are all on a
|
|
||||||
// side chain and there are no more locators to find the
|
|
||||||
// fork point.
|
|
||||||
name: "weak locator, multiple known side blocks, stop in main",
|
|
||||||
locator: locatorHashes(branch1Nodes, 1),
|
|
||||||
hashStop: branch0Nodes[5].hash,
|
|
||||||
headers: nodeHeaders(branch0Nodes, 0, 1, 2, 3, 4, 5),
|
|
||||||
hashes: nodeHashes(branch0Nodes, 0, 1, 2, 3, 4, 5),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
// Ensure the expected headers are located.
|
|
||||||
var headers []wire.BlockHeader
|
|
||||||
if test.maxAllowed != 0 {
|
|
||||||
// Need to use the unexported function to override the
|
|
||||||
// max allowed for headers.
|
|
||||||
chain.chainLock.RLock()
|
|
||||||
headers = chain.locateHeaders(test.locator,
|
|
||||||
&test.hashStop, test.maxAllowed)
|
|
||||||
chain.chainLock.RUnlock()
|
|
||||||
} else {
|
|
||||||
headers = chain.LocateHeaders(test.locator,
|
|
||||||
&test.hashStop)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(headers, test.headers) {
|
|
||||||
t.Errorf("%s: unxpected headers -- got %v, want %v",
|
|
||||||
test.name, headers, test.headers)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the expected block hashes are located.
|
|
||||||
maxAllowed := uint32(wire.MaxBlocksPerMsg)
|
|
||||||
if test.maxAllowed != 0 {
|
|
||||||
maxAllowed = test.maxAllowed
|
|
||||||
}
|
|
||||||
hashes := chain.LocateBlocks(test.locator, &test.hashStop,
|
|
||||||
maxAllowed)
|
|
||||||
if !reflect.DeepEqual(hashes, test.hashes) {
|
|
||||||
t.Errorf("%s: unxpected hashes -- got %v, want %v",
|
|
||||||
test.name, hashes, test.hashes)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHeightToHashRange ensures that fetching a range of block hashes by start
|
|
||||||
// height and end hash works as expected.
|
|
||||||
func TestHeightToHashRange(t *testing.T) {
|
|
||||||
// Construct a synthetic block chain with a block index consisting of
|
|
||||||
// the following structure.
|
|
||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
|
||||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
|
||||||
tip := tstTip
|
|
||||||
chain := newFakeChain(&chaincfg.MainNetParams)
|
|
||||||
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
|
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[14], 3)
|
|
||||||
for _, node := range branch0Nodes {
|
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
|
||||||
chain.index.AddNode(node)
|
|
||||||
}
|
|
||||||
for _, node := range branch1Nodes {
|
|
||||||
if node.height < 18 {
|
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
|
||||||
}
|
|
||||||
chain.index.AddNode(node)
|
|
||||||
}
|
|
||||||
chain.bestChain.SetTip(tip(branch0Nodes))
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
startHeight int32 // locator for requested inventory
|
|
||||||
endHash chainhash.Hash // stop hash for locator
|
|
||||||
maxResults int // max to locate, 0 = wire const
|
|
||||||
hashes []chainhash.Hash // expected located hashes
|
|
||||||
expectError bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "blocks below tip",
|
|
||||||
startHeight: 11,
|
|
||||||
endHash: branch0Nodes[14].hash,
|
|
||||||
maxResults: 10,
|
|
||||||
hashes: nodeHashes(branch0Nodes, 10, 11, 12, 13, 14),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "blocks on main chain",
|
|
||||||
startHeight: 15,
|
|
||||||
endHash: branch0Nodes[17].hash,
|
|
||||||
maxResults: 10,
|
|
||||||
hashes: nodeHashes(branch0Nodes, 14, 15, 16, 17),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "blocks on stale chain",
|
|
||||||
startHeight: 15,
|
|
||||||
endHash: branch1Nodes[1].hash,
|
|
||||||
maxResults: 10,
|
|
||||||
hashes: append(nodeHashes(branch0Nodes, 14),
|
|
||||||
nodeHashes(branch1Nodes, 0, 1)...),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid start height",
|
|
||||||
startHeight: 19,
|
|
||||||
endHash: branch0Nodes[17].hash,
|
|
||||||
maxResults: 10,
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "too many results",
|
|
||||||
startHeight: 1,
|
|
||||||
endHash: branch0Nodes[17].hash,
|
|
||||||
maxResults: 10,
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unvalidated block",
|
|
||||||
startHeight: 15,
|
|
||||||
endHash: branch1Nodes[2].hash,
|
|
||||||
maxResults: 10,
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
hashes, err := chain.HeightToHashRange(test.startHeight, &test.endHash,
|
|
||||||
test.maxResults)
|
|
||||||
if err != nil {
|
|
||||||
if !test.expectError {
|
|
||||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(hashes, test.hashes) {
|
|
||||||
t.Errorf("%s: unxpected hashes -- got %v, want %v",
|
|
||||||
test.name, hashes, test.hashes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestIntervalBlockHashes ensures that fetching block hashes at specified
|
|
||||||
// intervals by end hash works as expected.
|
|
||||||
func TestIntervalBlockHashes(t *testing.T) {
|
|
||||||
// Construct a synthetic block chain with a block index consisting of
|
|
||||||
// the following structure.
|
|
||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
|
||||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
|
||||||
tip := tstTip
|
|
||||||
chain := newFakeChain(&chaincfg.MainNetParams)
|
|
||||||
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
|
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[14], 3)
|
|
||||||
for _, node := range branch0Nodes {
|
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
|
||||||
chain.index.AddNode(node)
|
|
||||||
}
|
|
||||||
for _, node := range branch1Nodes {
|
|
||||||
if node.height < 18 {
|
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
|
||||||
}
|
|
||||||
chain.index.AddNode(node)
|
|
||||||
}
|
|
||||||
chain.bestChain.SetTip(tip(branch0Nodes))
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
endHash chainhash.Hash
|
|
||||||
interval int
|
|
||||||
hashes []chainhash.Hash
|
|
||||||
expectError bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "blocks on main chain",
|
|
||||||
endHash: branch0Nodes[17].hash,
|
|
||||||
interval: 8,
|
|
||||||
hashes: nodeHashes(branch0Nodes, 7, 15),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "blocks on stale chain",
|
|
||||||
endHash: branch1Nodes[1].hash,
|
|
||||||
interval: 8,
|
|
||||||
hashes: append(nodeHashes(branch0Nodes, 7),
|
|
||||||
nodeHashes(branch1Nodes, 0)...),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no results",
|
|
||||||
endHash: branch0Nodes[17].hash,
|
|
||||||
interval: 20,
|
|
||||||
hashes: []chainhash.Hash{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unvalidated block",
|
|
||||||
endHash: branch1Nodes[2].hash,
|
|
||||||
interval: 8,
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
hashes, err := chain.IntervalBlockHashes(&test.endHash, test.interval)
|
|
||||||
if err != nil {
|
|
||||||
if !test.expectError {
|
|
||||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(hashes, test.hashes) {
|
|
||||||
t.Errorf("%s: unxpected hashes -- got %v, want %v",
|
|
||||||
test.name, hashes, test.hashes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,10 +12,10 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestErrNotInMainChain ensures the functions related to errNotInMainChain work
|
// TestErrNotInMainChain ensures the functions related to errNotInMainChain work
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// testNoncePrng provides a deterministic prng for the nonce in generated fake
|
// testNoncePrng provides a deterministic prng for the nonce in generated fake
|
||||||
|
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckpointConfirmations is the number of blocks before the end of the current
|
// CheckpointConfirmations is the number of blocks before the end of the current
|
||||||
|
|
183
blockchain/claimtrie.go
Normal file
183
blockchain/claimtrie.go
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/lbryio/lbcd/txscript"
|
||||||
|
"github.com/lbryio/lbcd/wire"
|
||||||
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
|
|
||||||
|
"github.com/lbryio/lbcd/claimtrie"
|
||||||
|
"github.com/lbryio/lbcd/claimtrie/change"
|
||||||
|
"github.com/lbryio/lbcd/claimtrie/node"
|
||||||
|
"github.com/lbryio/lbcd/claimtrie/normalization"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *BlockChain) SetClaimtrieHeader(block *btcutil.Block, view *UtxoViewpoint) error {
|
||||||
|
b.chainLock.Lock()
|
||||||
|
defer b.chainLock.Unlock()
|
||||||
|
|
||||||
|
err := b.ParseClaimScripts(block, nil, view, false)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "in parse claim scripts")
|
||||||
|
}
|
||||||
|
|
||||||
|
block.MsgBlock().Header.ClaimTrie = *b.claimTrie.MerkleHash()
|
||||||
|
err = b.claimTrie.ResetHeight(b.claimTrie.Height() - 1)
|
||||||
|
|
||||||
|
return errors.Wrapf(err, "in reset height")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BlockChain) ParseClaimScripts(block *btcutil.Block, bn *blockNode, view *UtxoViewpoint, shouldFlush bool) error {
|
||||||
|
ht := block.Height()
|
||||||
|
|
||||||
|
for _, tx := range block.Transactions() {
|
||||||
|
h := handler{ht, tx, view, map[string][]byte{}}
|
||||||
|
if err := h.handleTxIns(b.claimTrie); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := h.handleTxOuts(b.claimTrie); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := b.claimTrie.AppendBlock()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "in append block")
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldFlush {
|
||||||
|
b.claimTrie.FlushToDisk()
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := b.claimTrie.MerkleHash()
|
||||||
|
if bn != nil && bn.claimTrie != *hash {
|
||||||
|
// undo our AppendBlock call as we've decided that our interpretation of the block data is incorrect,
|
||||||
|
// or that the person who made the block assembled the pieces incorrectly.
|
||||||
|
_ = b.claimTrie.ResetHeight(b.claimTrie.Height() - 1)
|
||||||
|
return errors.Errorf("height: %d, computed hash: %s != header's ClaimTrie: %s", ht, *hash, bn.claimTrie)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
ht int32
|
||||||
|
tx *btcutil.Tx
|
||||||
|
view *UtxoViewpoint
|
||||||
|
spent map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) handleTxIns(ct *claimtrie.ClaimTrie) error {
|
||||||
|
if IsCoinBase(h.tx) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, txIn := range h.tx.MsgTx().TxIn {
|
||||||
|
op := txIn.PreviousOutPoint
|
||||||
|
e := h.view.LookupEntry(op)
|
||||||
|
if e == nil {
|
||||||
|
return errors.Errorf("missing input in view for %s", op.String())
|
||||||
|
}
|
||||||
|
cs, err := txscript.DecodeClaimScript(e.pkScript)
|
||||||
|
if err == txscript.ErrNotClaimScript {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var id change.ClaimID
|
||||||
|
name := cs.Name() // name of the previous one (that we're now spending)
|
||||||
|
|
||||||
|
switch cs.Opcode() {
|
||||||
|
case txscript.OP_CLAIMNAME: // OP code from previous transaction
|
||||||
|
id = change.NewClaimID(op) // claimID of the previous item now being spent
|
||||||
|
h.spent[id.Key()] = normalization.NormalizeIfNecessary(name, ct.Height())
|
||||||
|
err = ct.SpendClaim(name, op, id)
|
||||||
|
case txscript.OP_UPDATECLAIM:
|
||||||
|
copy(id[:], cs.ClaimID())
|
||||||
|
h.spent[id.Key()] = normalization.NormalizeIfNecessary(name, ct.Height())
|
||||||
|
err = ct.SpendClaim(name, op, id)
|
||||||
|
case txscript.OP_SUPPORTCLAIM:
|
||||||
|
copy(id[:], cs.ClaimID())
|
||||||
|
err = ct.SpendSupport(name, op, id)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "handleTxIns")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) handleTxOuts(ct *claimtrie.ClaimTrie) error {
|
||||||
|
for i, txOut := range h.tx.MsgTx().TxOut {
|
||||||
|
op := *wire.NewOutPoint(h.tx.Hash(), uint32(i))
|
||||||
|
cs, err := txscript.DecodeClaimScript(txOut.PkScript)
|
||||||
|
if err == txscript.ErrNotClaimScript {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var id change.ClaimID
|
||||||
|
name := cs.Name()
|
||||||
|
amt := txOut.Value
|
||||||
|
|
||||||
|
switch cs.Opcode() {
|
||||||
|
case txscript.OP_CLAIMNAME:
|
||||||
|
id = change.NewClaimID(op)
|
||||||
|
err = ct.AddClaim(name, op, id, amt)
|
||||||
|
case txscript.OP_SUPPORTCLAIM:
|
||||||
|
copy(id[:], cs.ClaimID())
|
||||||
|
err = ct.AddSupport(name, op, amt, id)
|
||||||
|
case txscript.OP_UPDATECLAIM:
|
||||||
|
// old code wouldn't run the update if name or claimID didn't match existing data
|
||||||
|
// that was a safety feature, but it should have rejected the transaction instead
|
||||||
|
// TODO: reject transactions with invalid update commands
|
||||||
|
copy(id[:], cs.ClaimID())
|
||||||
|
normName := normalization.NormalizeIfNecessary(name, ct.Height())
|
||||||
|
if !bytes.Equal(h.spent[id.Key()], normName) {
|
||||||
|
node.LogOnce(fmt.Sprintf("Invalid update operation: name or ID mismatch at %d for: %s, %s",
|
||||||
|
ct.Height(), normName, id.String()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(h.spent, id.Key())
|
||||||
|
err = ct.UpdateClaim(name, op, amt, id)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "handleTxOuts")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BlockChain) GetNamesChangedInBlock(height int32) ([]string, error) {
|
||||||
|
b.chainLock.RLock()
|
||||||
|
defer b.chainLock.RUnlock()
|
||||||
|
|
||||||
|
return b.claimTrie.NamesChangedInBlock(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BlockChain) GetClaimsForName(height int32, name string) (string, *node.Node, error) {
|
||||||
|
|
||||||
|
normalizedName := normalization.NormalizeIfNecessary([]byte(name), height)
|
||||||
|
|
||||||
|
b.chainLock.RLock()
|
||||||
|
defer b.chainLock.RUnlock()
|
||||||
|
|
||||||
|
n, err := b.claimTrie.NodeAt(height, normalizedName)
|
||||||
|
if err != nil {
|
||||||
|
return string(normalizedName), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == nil {
|
||||||
|
return string(normalizedName), nil, fmt.Errorf("name does not exist at height %d: %s", height, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.SortClaimsByBid()
|
||||||
|
return string(normalizedName), n, nil
|
||||||
|
}
|
|
@ -14,13 +14,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
_ "github.com/btcsuite/btcd/database/ffldb"
|
_ "github.com/lbryio/lbcd/database/ffldb"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -69,7 +69,7 @@ func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
|
||||||
|
|
||||||
fi, err = os.Open(filename)
|
fi, err = os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return blocks, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(filename, ".bz2") {
|
if strings.HasSuffix(filename, ".bz2") {
|
||||||
|
@ -107,12 +107,12 @@ func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
|
||||||
|
|
||||||
block, err = btcutil.NewBlockFromBytes(rbytes)
|
block, err = btcutil.NewBlockFromBytes(rbytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return blocks, err
|
||||||
}
|
}
|
||||||
blocks = append(blocks, block)
|
blocks = append(blocks, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return blocks, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// chainSetup is used to create a new db and chain instance with the genesis
|
// chainSetup is used to create a new db and chain instance with the genesis
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/lbryio/lbcd/btcec"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -159,7 +159,6 @@ func CalcWork(bits uint32) *big.Int {
|
||||||
func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 {
|
func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 {
|
||||||
// Convert types used in the calculations below.
|
// Convert types used in the calculations below.
|
||||||
durationVal := int64(duration / time.Second)
|
durationVal := int64(duration / time.Second)
|
||||||
adjustmentFactor := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
|
|
||||||
|
|
||||||
// The test network rules allow minimum difficulty blocks after more
|
// The test network rules allow minimum difficulty blocks after more
|
||||||
// than twice the desired amount of time needed to generate a block has
|
// than twice the desired amount of time needed to generate a block has
|
||||||
|
@ -178,7 +177,8 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
|
||||||
// multiplied by the max adjustment factor.
|
// multiplied by the max adjustment factor.
|
||||||
newTarget := CompactToBig(bits)
|
newTarget := CompactToBig(bits)
|
||||||
for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 {
|
for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 {
|
||||||
newTarget.Mul(newTarget, adjustmentFactor)
|
adj := new(big.Int).Div(newTarget, big.NewInt(2))
|
||||||
|
newTarget.Add(newTarget, adj)
|
||||||
durationVal -= b.maxRetargetTimespan
|
durationVal -= b.maxRetargetTimespan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,47 +224,45 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
|
||||||
return b.chainParams.PowLimitBits, nil
|
return b.chainParams.PowLimitBits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the previous block's difficulty requirements if this block
|
// For networks that support it, allow special reduction of the
|
||||||
// is not at a difficulty retarget interval.
|
// required difficulty once too much time has elapsed without
|
||||||
if (lastNode.height+1)%b.blocksPerRetarget != 0 {
|
// mining a block.
|
||||||
// For networks that support it, allow special reduction of the
|
if b.chainParams.ReduceMinDifficulty {
|
||||||
// required difficulty once too much time has elapsed without
|
// Return minimum difficulty when more than the desired
|
||||||
// mining a block.
|
// amount of time has elapsed without mining a block.
|
||||||
if b.chainParams.ReduceMinDifficulty {
|
reductionTime := int64(b.chainParams.MinDiffReductionTime /
|
||||||
// Return minimum difficulty when more than the desired
|
time.Second)
|
||||||
// amount of time has elapsed without mining a block.
|
allowMinTime := lastNode.timestamp + reductionTime
|
||||||
reductionTime := int64(b.chainParams.MinDiffReductionTime /
|
if newBlockTime.Unix() > allowMinTime {
|
||||||
time.Second)
|
return b.chainParams.PowLimitBits, nil
|
||||||
allowMinTime := lastNode.timestamp + reductionTime
|
|
||||||
if newBlockTime.Unix() > allowMinTime {
|
|
||||||
return b.chainParams.PowLimitBits, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The block was mined within the desired timeframe, so
|
|
||||||
// return the difficulty for the last block which did
|
|
||||||
// not have the special minimum difficulty rule applied.
|
|
||||||
return b.findPrevTestNetDifficulty(lastNode), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the main network (or any unrecognized networks), simply
|
// The block was mined within the desired timeframe, so
|
||||||
// return the previous block's difficulty requirements.
|
// return the difficulty for the last block which did
|
||||||
return lastNode.bits, nil
|
// not have the special minimum difficulty rule applied.
|
||||||
|
return b.findPrevTestNetDifficulty(lastNode), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the block node at the previous retarget (targetTimespan days
|
// Get the block node at the previous retarget (targetTimespan days
|
||||||
// worth of blocks).
|
// worth of blocks).
|
||||||
firstNode := lastNode.RelativeAncestor(b.blocksPerRetarget - 1)
|
blocksBack := b.blocksPerRetarget
|
||||||
|
if blocksBack > lastNode.height {
|
||||||
|
blocksBack = lastNode.height
|
||||||
|
}
|
||||||
|
firstNode := lastNode.RelativeAncestor(blocksBack)
|
||||||
if firstNode == nil {
|
if firstNode == nil {
|
||||||
return 0, AssertError("unable to obtain previous retarget block")
|
return 0, AssertError("unable to obtain previous retarget block")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetTimeSpan := int64(b.chainParams.TargetTimespan / time.Second)
|
||||||
|
|
||||||
// Limit the amount of adjustment that can occur to the previous
|
// Limit the amount of adjustment that can occur to the previous
|
||||||
// difficulty.
|
// difficulty.
|
||||||
actualTimespan := lastNode.timestamp - firstNode.timestamp
|
actualTimespan := lastNode.timestamp - firstNode.timestamp
|
||||||
adjustedTimespan := actualTimespan
|
adjustedTimespan := targetTimeSpan + (actualTimespan-targetTimeSpan)/8
|
||||||
if actualTimespan < b.minRetargetTimespan {
|
if adjustedTimespan < b.minRetargetTimespan {
|
||||||
adjustedTimespan = b.minRetargetTimespan
|
adjustedTimespan = b.minRetargetTimespan
|
||||||
} else if actualTimespan > b.maxRetargetTimespan {
|
} else if adjustedTimespan > b.maxRetargetTimespan {
|
||||||
adjustedTimespan = b.maxRetargetTimespan
|
adjustedTimespan = b.maxRetargetTimespan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +273,6 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
|
||||||
// result.
|
// result.
|
||||||
oldTarget := CompactToBig(lastNode.bits)
|
oldTarget := CompactToBig(lastNode.bits)
|
||||||
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
|
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
|
||||||
targetTimeSpan := int64(b.chainParams.TargetTimespan / time.Second)
|
|
||||||
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
|
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
|
||||||
|
|
||||||
// Limit new value to the proof of work limit.
|
// Limit new value to the proof of work limit.
|
||||||
|
|
|
@ -220,6 +220,10 @@ const (
|
||||||
// current chain tip. This is not a block validation rule, but is required
|
// current chain tip. This is not a block validation rule, but is required
|
||||||
// for block proposals submitted via getblocktemplate RPC.
|
// for block proposals submitted via getblocktemplate RPC.
|
||||||
ErrPrevBlockNotBest
|
ErrPrevBlockNotBest
|
||||||
|
|
||||||
|
// ErrBadClaimTrie indicates the calculated ClaimTrie root does not match
|
||||||
|
// the expected value.
|
||||||
|
ErrBadClaimTrie
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||||
|
@ -267,6 +271,7 @@ var errorCodeStrings = map[ErrorCode]string{
|
||||||
ErrPreviousBlockUnknown: "ErrPreviousBlockUnknown",
|
ErrPreviousBlockUnknown: "ErrPreviousBlockUnknown",
|
||||||
ErrInvalidAncestorBlock: "ErrInvalidAncestorBlock",
|
ErrInvalidAncestorBlock: "ErrInvalidAncestorBlock",
|
||||||
ErrPrevBlockNotBest: "ErrPrevBlockNotBest",
|
ErrPrevBlockNotBest: "ErrPrevBlockNotBest",
|
||||||
|
ErrBadClaimTrie: "ErrBadClaimTrie",
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the ErrorCode as a human-readable name.
|
// String returns the ErrorCode as a human-readable name.
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
// Copyright (c) 2014-2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
|
||||||
"github.com/btcsuite/btcd/database"
|
|
||||||
_ "github.com/btcsuite/btcd/database/ffldb"
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This example demonstrates how to create a new chain instance and use
|
|
||||||
// ProcessBlock to attempt to add a block to the chain. As the package
|
|
||||||
// overview documentation describes, this includes all of the Bitcoin consensus
|
|
||||||
// rules. This example intentionally attempts to insert a duplicate genesis
|
|
||||||
// block to illustrate how an invalid block is handled.
|
|
||||||
func ExampleBlockChain_ProcessBlock() {
|
|
||||||
// Create a new database to store the accepted blocks into. Typically
|
|
||||||
// this would be opening an existing database and would not be deleting
|
|
||||||
// and creating a new database like this, but it is done here so this is
|
|
||||||
// a complete working example and does not leave temporary files laying
|
|
||||||
// around.
|
|
||||||
dbPath := filepath.Join(os.TempDir(), "exampleprocessblock")
|
|
||||||
_ = os.RemoveAll(dbPath)
|
|
||||||
db, err := database.Create("ffldb", dbPath, chaincfg.MainNetParams.Net)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to create database: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dbPath)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
// Create a new BlockChain instance using the underlying database for
|
|
||||||
// the main bitcoin network. This example does not demonstrate some
|
|
||||||
// of the other available configuration options such as specifying a
|
|
||||||
// notification callback and signature cache. Also, the caller would
|
|
||||||
// ordinarily keep a reference to the median time source and add time
|
|
||||||
// values obtained from other peers on the network so the local time is
|
|
||||||
// adjusted to be in agreement with other peers.
|
|
||||||
chain, err := blockchain.New(&blockchain.Config{
|
|
||||||
DB: db,
|
|
||||||
ChainParams: &chaincfg.MainNetParams,
|
|
||||||
TimeSource: blockchain.NewMedianTime(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to create chain instance: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process a block. For this example, we are going to intentionally
|
|
||||||
// cause an error by trying to process the genesis block which already
|
|
||||||
// exists.
|
|
||||||
genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock)
|
|
||||||
isMainChain, isOrphan, err := chain.ProcessBlock(genesisBlock,
|
|
||||||
blockchain.BFNone)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to process block: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Printf("Block accepted. Is it on the main chain?: %v", isMainChain)
|
|
||||||
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Failed to process block: already have block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to convert the compact "bits" in a block header
|
|
||||||
// which represent the target difficulty to a big integer and display it using
|
|
||||||
// the typical hex notation.
|
|
||||||
func ExampleCompactToBig() {
|
|
||||||
// Convert the bits from block 300000 in the main block chain.
|
|
||||||
bits := uint32(419465580)
|
|
||||||
targetDifficulty := blockchain.CompactToBig(bits)
|
|
||||||
|
|
||||||
// Display it in hex.
|
|
||||||
fmt.Printf("%064x\n", targetDifficulty.Bytes())
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// 0000000000000000896c00000000000000000000000000000000000000000000
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example demonstrates how to convert a target difficulty into the compact
|
|
||||||
// "bits" in a block header which represent that target difficulty .
|
|
||||||
func ExampleBigToCompact() {
|
|
||||||
// Convert the target difficulty from block 300000 in the main block
|
|
||||||
// chain to compact form.
|
|
||||||
t := "0000000000000000896c00000000000000000000000000000000000000000000"
|
|
||||||
targetDifficulty, success := new(big.Int).SetString(t, 16)
|
|
||||||
if !success {
|
|
||||||
fmt.Println("invalid target difficulty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
bits := blockchain.BigToCompact(targetDifficulty)
|
|
||||||
|
|
||||||
fmt.Println(bits)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// 419465580
|
|
||||||
}
|
|
|
@ -1,310 +0,0 @@
|
||||||
// Copyright (c) 2016 The Decred developers
|
|
||||||
// Copyright (c) 2016-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
|
||||||
"github.com/btcsuite/btcd/blockchain/fullblocktests"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/btcsuite/btcd/database"
|
|
||||||
_ "github.com/btcsuite/btcd/database/ffldb"
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// testDbType is the database backend type to use for the tests.
|
|
||||||
testDbType = "ffldb"
|
|
||||||
|
|
||||||
// testDbRoot is the root directory used to create all test databases.
|
|
||||||
testDbRoot = "testdbs"
|
|
||||||
|
|
||||||
// blockDataNet is the expected network in the test block data.
|
|
||||||
blockDataNet = wire.MainNet
|
|
||||||
)
|
|
||||||
|
|
||||||
// filesExists returns whether or not the named file or directory exists.
|
|
||||||
func fileExists(name string) bool {
|
|
||||||
if _, err := os.Stat(name); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// isSupportedDbType returns whether or not the passed database type is
|
|
||||||
// currently supported.
|
|
||||||
func isSupportedDbType(dbType string) bool {
|
|
||||||
supportedDrivers := database.SupportedDrivers()
|
|
||||||
for _, driver := range supportedDrivers {
|
|
||||||
if dbType == driver {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// chainSetup is used to create a new db and chain instance with the genesis
|
|
||||||
// block already inserted. In addition to the new chain instance, it returns
|
|
||||||
// a teardown function the caller should invoke when done testing to clean up.
|
|
||||||
func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, func(), error) {
|
|
||||||
if !isSupportedDbType(testDbType) {
|
|
||||||
return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle memory database specially since it doesn't need the disk
|
|
||||||
// specific handling.
|
|
||||||
var db database.DB
|
|
||||||
var teardown func()
|
|
||||||
if testDbType == "memdb" {
|
|
||||||
ndb, err := database.Create(testDbType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("error creating db: %v", err)
|
|
||||||
}
|
|
||||||
db = ndb
|
|
||||||
|
|
||||||
// Setup a teardown function for cleaning up. This function is
|
|
||||||
// returned to the caller to be invoked when it is done testing.
|
|
||||||
teardown = func() {
|
|
||||||
db.Close()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Create the root directory for test databases.
|
|
||||||
if !fileExists(testDbRoot) {
|
|
||||||
if err := os.MkdirAll(testDbRoot, 0700); err != nil {
|
|
||||||
err := fmt.Errorf("unable to create test db "+
|
|
||||||
"root: %v", err)
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new database to store the accepted blocks into.
|
|
||||||
dbPath := filepath.Join(testDbRoot, dbName)
|
|
||||||
_ = os.RemoveAll(dbPath)
|
|
||||||
ndb, err := database.Create(testDbType, dbPath, blockDataNet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("error creating db: %v", err)
|
|
||||||
}
|
|
||||||
db = ndb
|
|
||||||
|
|
||||||
// Setup a teardown function for cleaning up. This function is
|
|
||||||
// returned to the caller to be invoked when it is done testing.
|
|
||||||
teardown = func() {
|
|
||||||
db.Close()
|
|
||||||
os.RemoveAll(dbPath)
|
|
||||||
os.RemoveAll(testDbRoot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the chain params to ensure any modifications the tests do to
|
|
||||||
// the chain parameters do not affect the global instance.
|
|
||||||
paramsCopy := *params
|
|
||||||
|
|
||||||
// Create the main chain instance.
|
|
||||||
chain, err := blockchain.New(&blockchain.Config{
|
|
||||||
DB: db,
|
|
||||||
ChainParams: ¶msCopy,
|
|
||||||
Checkpoints: nil,
|
|
||||||
TimeSource: blockchain.NewMedianTime(),
|
|
||||||
SigCache: txscript.NewSigCache(1000),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
teardown()
|
|
||||||
err := fmt.Errorf("failed to create chain instance: %v", err)
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return chain, teardown, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestFullBlocks ensures all tests generated by the fullblocktests package
|
|
||||||
// have the expected result when processed via ProcessBlock.
|
|
||||||
func TestFullBlocks(t *testing.T) {
|
|
||||||
tests, err := fullblocktests.Generate(false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to generate tests: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new database and chain instance to run tests against.
|
|
||||||
chain, teardownFunc, err := chainSetup("fullblocktest",
|
|
||||||
&chaincfg.RegressionNetParams)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to setup chain instance: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer teardownFunc()
|
|
||||||
|
|
||||||
// testAcceptedBlock attempts to process the block in the provided test
|
|
||||||
// instance and ensures that it was accepted according to the flags
|
|
||||||
// specified in the test.
|
|
||||||
testAcceptedBlock := func(item fullblocktests.AcceptedBlock) {
|
|
||||||
blockHeight := item.Height
|
|
||||||
block := btcutil.NewBlock(item.Block)
|
|
||||||
block.SetHeight(blockHeight)
|
|
||||||
t.Logf("Testing block %s (hash %s, height %d)",
|
|
||||||
item.Name, block.Hash(), blockHeight)
|
|
||||||
|
|
||||||
isMainChain, isOrphan, err := chain.ProcessBlock(block,
|
|
||||||
blockchain.BFNone)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) should "+
|
|
||||||
"have been accepted: %v", item.Name,
|
|
||||||
block.Hash(), blockHeight, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the main chain and orphan flags match the values
|
|
||||||
// specified in the test.
|
|
||||||
if isMainChain != item.IsMainChain {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) unexpected main "+
|
|
||||||
"chain flag -- got %v, want %v", item.Name,
|
|
||||||
block.Hash(), blockHeight, isMainChain,
|
|
||||||
item.IsMainChain)
|
|
||||||
}
|
|
||||||
if isOrphan != item.IsOrphan {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) unexpected "+
|
|
||||||
"orphan flag -- got %v, want %v", item.Name,
|
|
||||||
block.Hash(), blockHeight, isOrphan,
|
|
||||||
item.IsOrphan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// testRejectedBlock attempts to process the block in the provided test
|
|
||||||
// instance and ensures that it was rejected with the reject code
|
|
||||||
// specified in the test.
|
|
||||||
testRejectedBlock := func(item fullblocktests.RejectedBlock) {
|
|
||||||
blockHeight := item.Height
|
|
||||||
block := btcutil.NewBlock(item.Block)
|
|
||||||
block.SetHeight(blockHeight)
|
|
||||||
t.Logf("Testing block %s (hash %s, height %d)",
|
|
||||||
item.Name, block.Hash(), blockHeight)
|
|
||||||
|
|
||||||
_, _, err := chain.ProcessBlock(block, blockchain.BFNone)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) should not "+
|
|
||||||
"have been accepted", item.Name, block.Hash(),
|
|
||||||
blockHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the error code is of the expected type and the reject
|
|
||||||
// code matches the value specified in the test instance.
|
|
||||||
rerr, ok := err.(blockchain.RuleError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) returned "+
|
|
||||||
"unexpected error type -- got %T, want "+
|
|
||||||
"blockchain.RuleError", item.Name, block.Hash(),
|
|
||||||
blockHeight, err)
|
|
||||||
}
|
|
||||||
if rerr.ErrorCode != item.RejectCode {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) does not have "+
|
|
||||||
"expected reject code -- got %v, want %v",
|
|
||||||
item.Name, block.Hash(), blockHeight,
|
|
||||||
rerr.ErrorCode, item.RejectCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// testRejectedNonCanonicalBlock attempts to decode the block in the
|
|
||||||
// provided test instance and ensures that it failed to decode with a
|
|
||||||
// message error.
|
|
||||||
testRejectedNonCanonicalBlock := func(item fullblocktests.RejectedNonCanonicalBlock) {
|
|
||||||
headerLen := len(item.RawBlock)
|
|
||||||
if headerLen > 80 {
|
|
||||||
headerLen = 80
|
|
||||||
}
|
|
||||||
blockHash := chainhash.DoubleHashH(item.RawBlock[0:headerLen])
|
|
||||||
blockHeight := item.Height
|
|
||||||
t.Logf("Testing block %s (hash %s, height %d)", item.Name,
|
|
||||||
blockHash, blockHeight)
|
|
||||||
|
|
||||||
// Ensure there is an error due to deserializing the block.
|
|
||||||
var msgBlock wire.MsgBlock
|
|
||||||
err := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0, wire.BaseEncoding)
|
|
||||||
if _, ok := err.(*wire.MessageError); !ok {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) should have "+
|
|
||||||
"failed to decode", item.Name, blockHash,
|
|
||||||
blockHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// testOrphanOrRejectedBlock attempts to process the block in the
|
|
||||||
// provided test instance and ensures that it was either accepted as an
|
|
||||||
// orphan or rejected with a rule violation.
|
|
||||||
testOrphanOrRejectedBlock := func(item fullblocktests.OrphanOrRejectedBlock) {
|
|
||||||
blockHeight := item.Height
|
|
||||||
block := btcutil.NewBlock(item.Block)
|
|
||||||
block.SetHeight(blockHeight)
|
|
||||||
t.Logf("Testing block %s (hash %s, height %d)",
|
|
||||||
item.Name, block.Hash(), blockHeight)
|
|
||||||
|
|
||||||
_, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone)
|
|
||||||
if err != nil {
|
|
||||||
// Ensure the error code is of the expected type.
|
|
||||||
if _, ok := err.(blockchain.RuleError); !ok {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) "+
|
|
||||||
"returned unexpected error type -- "+
|
|
||||||
"got %T, want blockchain.RuleError",
|
|
||||||
item.Name, block.Hash(), blockHeight,
|
|
||||||
err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isOrphan {
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) was accepted, "+
|
|
||||||
"but is not considered an orphan", item.Name,
|
|
||||||
block.Hash(), blockHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// testExpectedTip ensures the current tip of the blockchain is the
|
|
||||||
// block specified in the provided test instance.
|
|
||||||
testExpectedTip := func(item fullblocktests.ExpectedTip) {
|
|
||||||
blockHeight := item.Height
|
|
||||||
block := btcutil.NewBlock(item.Block)
|
|
||||||
block.SetHeight(blockHeight)
|
|
||||||
t.Logf("Testing tip for block %s (hash %s, height %d)",
|
|
||||||
item.Name, block.Hash(), blockHeight)
|
|
||||||
|
|
||||||
// Ensure hash and height match.
|
|
||||||
best := chain.BestSnapshot()
|
|
||||||
if best.Hash != item.Block.BlockHash() ||
|
|
||||||
best.Height != blockHeight {
|
|
||||||
|
|
||||||
t.Fatalf("block %q (hash %s, height %d) should be "+
|
|
||||||
"the current tip -- got (hash %s, height %d)",
|
|
||||||
item.Name, block.Hash(), blockHeight, best.Hash,
|
|
||||||
best.Height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for testNum, test := range tests {
|
|
||||||
for itemNum, item := range test {
|
|
||||||
switch item := item.(type) {
|
|
||||||
case fullblocktests.AcceptedBlock:
|
|
||||||
testAcceptedBlock(item)
|
|
||||||
case fullblocktests.RejectedBlock:
|
|
||||||
testRejectedBlock(item)
|
|
||||||
case fullblocktests.RejectedNonCanonicalBlock:
|
|
||||||
testRejectedNonCanonicalBlock(item)
|
|
||||||
case fullblocktests.OrphanOrRejectedBlock:
|
|
||||||
testOrphanOrRejectedBlock(item)
|
|
||||||
case fullblocktests.ExpectedTip:
|
|
||||||
testExpectedTip(item)
|
|
||||||
default:
|
|
||||||
t.Fatalf("test #%d, item #%d is not one of "+
|
|
||||||
"the supported test instance types -- "+
|
|
||||||
"got type: %T", testNum, itemNum, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
fullblocktests
|
fullblocktests
|
||||||
==============
|
==============
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
[![Build Status](https://github.com/lbryio/lbcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/lbryio/lbcd/actions)
|
||||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain/fullblocktests)
|
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/lbryio/lbcd/blockchain/fullblocktests)
|
||||||
|
|
||||||
Package fullblocktests provides a set of full block tests to be used for testing
|
Package fullblocktests provides a set of full block tests to be used for testing
|
||||||
the consensus validation rules. The tests are intended to be flexible enough to
|
the consensus validation rules. The tests are intended to be flexible enough to
|
||||||
|
@ -20,7 +20,7 @@ of blocks that exercise the consensus validation rules.
|
||||||
## Installation and Updating
|
## Installation and Updating
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go get -u github.com/btcsuite/btcd/blockchain/fullblocktests
|
$ go get -u github.com/lbryio/lbcd/blockchain/fullblocktests
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
@ -18,24 +18,24 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/lbryio/lbcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/lbryio/lbcd/btcec"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Intentionally defined here rather than using constants from codebase
|
// Intentionally defined here rather than using constants from codebase
|
||||||
// to ensure consensus changes are detected.
|
// to ensure consensus changes are detected.
|
||||||
maxBlockSigOps = 20000
|
maxBlockSigOps = 20000
|
||||||
maxBlockSize = 1000000
|
maxBlockSize = 2000000
|
||||||
minCoinbaseScriptLen = 2
|
minCoinbaseScriptLen = 2
|
||||||
maxCoinbaseScriptLen = 100
|
maxCoinbaseScriptLen = 100
|
||||||
medianTimeBlocks = 11
|
medianTimeBlocks = 11
|
||||||
maxScriptElementSize = 520
|
maxScriptElementSize = 20000
|
||||||
|
|
||||||
// numLargeReorgBlocks is the number of blocks to use in the large block
|
// numLargeReorgBlocks is the number of blocks to use in the large block
|
||||||
// reorg test (when enabled). This is the equivalent of 1 week's worth
|
// reorg test (when enabled). This is the equivalent of 1 week's worth
|
||||||
|
@ -1875,7 +1875,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
||||||
//
|
//
|
||||||
// Comment assumptions:
|
// Comment assumptions:
|
||||||
// maxBlockSigOps = 20000
|
// maxBlockSigOps = 20000
|
||||||
// maxScriptElementSize = 520
|
// maxScriptElementSize = 20000
|
||||||
//
|
//
|
||||||
// [0-19999] : OP_CHECKSIG
|
// [0-19999] : OP_CHECKSIG
|
||||||
// [20000] : OP_PUSHDATA4
|
// [20000] : OP_PUSHDATA4
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// newHashFromStr converts the passed big-endian hex string into a
|
// newHashFromStr converts the passed big-endian hex string into a
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
indexers
|
indexers
|
||||||
========
|
========
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
[![Build Status](https://github.com/lbryio/lbcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/lbryio/lbcd/actions)
|
||||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain/indexers?status.png)](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain/indexers)
|
[![GoDoc](https://pkg.go.dev/github.com/lbryio/lbcd/blockchain/indexers?status.png)](https://pkg.go.dev/github.com/lbryio/lbcd/blockchain/indexers)
|
||||||
|
|
||||||
Package indexers implements optional block chain indexes.
|
Package indexers implements optional block chain indexes.
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ via an RPC interface.
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go get -u github.com/btcsuite/btcd/blockchain/indexers
|
$ go get -u github.com/lbryio/lbcd/blockchain/indexers
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
@ -9,13 +9,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/lbryio/lbcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// addrIndexBucket provides a mock address index database bucket by implementing
|
// addrIndexBucket provides a mock address index database bucket by implementing
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btclog"
|
"github.com/btcsuite/btclog"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// blockProgressLogger provides periodic logging for other services in order
|
// blockProgressLogger provides periodic logging for other services in order
|
||||||
|
|
|
@ -7,14 +7,14 @@ package indexers
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/lbryio/lbcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
"github.com/btcsuite/btcutil/gcs"
|
"github.com/lbryio/lbcutil/gcs"
|
||||||
"github.com/btcsuite/btcutil/gcs/builder"
|
"github.com/lbryio/lbcutil/gcs/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -11,9 +11,9 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/lbryio/lbcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -8,11 +8,11 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/lbryio/lbcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -8,11 +8,11 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/lbryio/lbcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -183,7 +183,7 @@ func (m *medianTime) AddTimeSample(sourceID string, timeVal time.Time) {
|
||||||
// Warn if none of the time samples are close.
|
// Warn if none of the time samples are close.
|
||||||
if !remoteHasCloseTime {
|
if !remoteHasCloseTime {
|
||||||
log.Warnf("Please check your date and time " +
|
log.Warnf("Please check your date and time " +
|
||||||
"are correct! btcd will not work " +
|
"are correct! lbcd will not work " +
|
||||||
"properly with an invalid time")
|
"properly with an invalid time")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -230,8 +230,8 @@ func ValidateWitnessCommitment(blk *btcutil.Block) error {
|
||||||
coinbaseWitness := coinbaseTx.MsgTx().TxIn[0].Witness
|
coinbaseWitness := coinbaseTx.MsgTx().TxIn[0].Witness
|
||||||
if len(coinbaseWitness) != 1 {
|
if len(coinbaseWitness) != 1 {
|
||||||
str := fmt.Sprintf("the coinbase transaction has %d items in "+
|
str := fmt.Sprintf("the coinbase transaction has %d items in "+
|
||||||
"its witness stack when only one is allowed",
|
"its witness stack when only one is allowed. Height: %d",
|
||||||
len(coinbaseWitness))
|
len(coinbaseWitness), blk.Height())
|
||||||
return ruleError(ErrInvalidWitnessCommitment, str)
|
return ruleError(ErrInvalidWitnessCommitment, str)
|
||||||
}
|
}
|
||||||
witnessNonce := coinbaseWitness[0]
|
witnessNonce := coinbaseWitness[0]
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Copyright (c) 2013-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestMerkle tests the BuildMerkleTreeStore API.
|
|
||||||
func TestMerkle(t *testing.T) {
|
|
||||||
block := btcutil.NewBlock(&Block100000)
|
|
||||||
merkles := BuildMerkleTreeStore(block.Transactions(), false)
|
|
||||||
calculatedMerkleRoot := merkles[len(merkles)-1]
|
|
||||||
wantMerkle := &Block100000.Header.MerkleRoot
|
|
||||||
if !wantMerkle.IsEqual(calculatedMerkleRoot) {
|
|
||||||
t.Errorf("BuildMerkleTreeStore: merkle root mismatch - "+
|
|
||||||
"got %v, want %v", calculatedMerkleRoot, wantMerkle)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
// Copyright (c) 2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestNotifications ensures that notification callbacks are fired on events.
|
|
||||||
func TestNotifications(t *testing.T) {
|
|
||||||
blocks, err := loadBlocks("blk_0_to_4.dat.bz2")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error loading file: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new database and chain instance to run tests against.
|
|
||||||
chain, teardownFunc, err := chainSetup("notifications",
|
|
||||||
&chaincfg.MainNetParams)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to setup chain instance: %v", err)
|
|
||||||
}
|
|
||||||
defer teardownFunc()
|
|
||||||
|
|
||||||
notificationCount := 0
|
|
||||||
callback := func(notification *Notification) {
|
|
||||||
if notification.Type == NTBlockAccepted {
|
|
||||||
notificationCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register callback multiple times then assert it is called that many
|
|
||||||
// times.
|
|
||||||
const numSubscribers = 3
|
|
||||||
for i := 0; i < numSubscribers; i++ {
|
|
||||||
chain.Subscribe(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = chain.ProcessBlock(blocks[1], BFNone)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ProcessBlock fail on block 1: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if notificationCount != numSubscribers {
|
|
||||||
t.Fatalf("Expected notification callback to be executed %d "+
|
|
||||||
"times, found %d", numSubscribers, notificationCount)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,9 +8,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BehaviorFlags is a bitmask defining tweaks to the normal behavior when
|
// BehaviorFlags is a bitmask defining tweaks to the normal behavior when
|
||||||
|
|
|
@ -10,9 +10,9 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// txValidateItem holds a transaction along with which input to validate.
|
// txValidateItem holds a transaction along with which input to validate.
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
// Copyright (c) 2013-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestCheckBlockScripts ensures that validating the all of the scripts in a
|
|
||||||
// known-good block doesn't return an error.
|
|
||||||
func TestCheckBlockScripts(t *testing.T) {
|
|
||||||
testBlockNum := 277647
|
|
||||||
blockDataFile := fmt.Sprintf("%d.dat.bz2", testBlockNum)
|
|
||||||
blocks, err := loadBlocks(blockDataFile)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error loading file: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(blocks) > 1 {
|
|
||||||
t.Errorf("The test block file must only have one block in it")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(blocks) == 0 {
|
|
||||||
t.Errorf("The test block file may not be empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
storeDataFile := fmt.Sprintf("%d.utxostore.bz2", testBlockNum)
|
|
||||||
view, err := loadUtxoView(storeDataFile)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error loading txstore: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptFlags := txscript.ScriptBip16
|
|
||||||
err = checkBlockScripts(blocks[0], view, scriptFlags, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Transaction script validation failed: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ThresholdState define the various threshold states used when voting on
|
// ThresholdState define the various threshold states used when voting on
|
||||||
|
@ -302,6 +302,12 @@ func (b *BlockChain) deploymentState(prevNode *blockNode, deploymentID uint32) (
|
||||||
}
|
}
|
||||||
|
|
||||||
deployment := &b.chainParams.Deployments[deploymentID]
|
deployment := &b.chainParams.Deployments[deploymentID]
|
||||||
|
|
||||||
|
// added to mimic LBRYcrd:
|
||||||
|
if deployment.ForceActiveAt > 0 && prevNode != nil && prevNode.height+1 >= deployment.ForceActiveAt {
|
||||||
|
return ThresholdActive, nil
|
||||||
|
}
|
||||||
|
|
||||||
checker := deploymentChecker{deployment: deployment, chain: b}
|
checker := deploymentChecker{deployment: deployment, chain: b}
|
||||||
cache := &b.deploymentCaches[deploymentID]
|
cache := &b.deploymentCaches[deploymentID]
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestThresholdStateStringer tests the stringized output for the
|
// TestThresholdStateStringer tests the stringized output for the
|
||||||
|
|
|
@ -11,9 +11,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -7,11 +7,11 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/database"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// txoFlags is a bitmask defining additional information and state for a
|
// txoFlags is a bitmask defining additional information and state for a
|
||||||
|
|
|
@ -11,11 +11,11 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -40,7 +40,7 @@ const (
|
||||||
|
|
||||||
// baseSubsidy is the starting subsidy amount for mined blocks. This
|
// baseSubsidy is the starting subsidy amount for mined blocks. This
|
||||||
// value is halved every SubsidyHalvingInterval blocks.
|
// value is halved every SubsidyHalvingInterval blocks.
|
||||||
baseSubsidy = 50 * btcutil.SatoshiPerBitcoin
|
baseSubsidy = 500 * btcutil.SatoshiPerBitcoin
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -192,17 +192,44 @@ func isBIP0030Node(node *blockNode) bool {
|
||||||
// At the target block generation rate for the main network, this is
|
// At the target block generation rate for the main network, this is
|
||||||
// approximately every 4 years.
|
// approximately every 4 years.
|
||||||
func CalcBlockSubsidy(height int32, chainParams *chaincfg.Params) int64 {
|
func CalcBlockSubsidy(height int32, chainParams *chaincfg.Params) int64 {
|
||||||
if chainParams.SubsidyReductionInterval == 0 {
|
h := int64(height)
|
||||||
return baseSubsidy
|
if h == 0 {
|
||||||
|
return btcutil.SatoshiPerBitcoin * 4e8
|
||||||
|
}
|
||||||
|
if h <= 5100 {
|
||||||
|
return btcutil.SatoshiPerBitcoin
|
||||||
|
}
|
||||||
|
if h <= 55000 {
|
||||||
|
return btcutil.SatoshiPerBitcoin * (1 + (h-5001)/100)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equivalent to: baseSubsidy / 2^(height/subsidyHalvingInterval)
|
lv := (h - 55001) / int64(chainParams.SubsidyReductionInterval)
|
||||||
return baseSubsidy >> uint(height/chainParams.SubsidyReductionInterval)
|
reduction := (int64(math.Sqrt((float64(8*lv))+1)) - 1) / 2
|
||||||
|
for !withinLevelBounds(reduction, lv) {
|
||||||
|
if ((reduction*reduction + reduction) >> 1) > lv {
|
||||||
|
reduction--
|
||||||
|
} else {
|
||||||
|
reduction++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subsidyReduction := btcutil.SatoshiPerBitcoin * reduction
|
||||||
|
if subsidyReduction >= baseSubsidy {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return baseSubsidy - subsidyReduction
|
||||||
|
}
|
||||||
|
|
||||||
|
func withinLevelBounds(reduction int64, lv int64) bool {
|
||||||
|
if ((reduction*reduction + reduction) >> 1) > lv {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
reduction++
|
||||||
|
return ((reduction*reduction + reduction) >> 1) > lv
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTransactionSanity performs some preliminary checks on a transaction to
|
// CheckTransactionSanity performs some preliminary checks on a transaction to
|
||||||
// ensure it is sane. These checks are context free.
|
// ensure it is sane.
|
||||||
func CheckTransactionSanity(tx *btcutil.Tx) error {
|
func CheckTransactionSanity(tx *btcutil.Tx, enforceSoftFork bool) error {
|
||||||
// A transaction must have at least one input.
|
// A transaction must have at least one input.
|
||||||
msgTx := tx.MsgTx()
|
msgTx := tx.MsgTx()
|
||||||
if len(msgTx.TxIn) == 0 {
|
if len(msgTx.TxIn) == 0 {
|
||||||
|
@ -261,6 +288,11 @@ func CheckTransactionSanity(tx *btcutil.Tx) error {
|
||||||
btcutil.MaxSatoshi)
|
btcutil.MaxSatoshi)
|
||||||
return ruleError(ErrBadTxOutValue, str)
|
return ruleError(ErrBadTxOutValue, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := txscript.AllClaimsAreSane(txOut.PkScript, enforceSoftFork)
|
||||||
|
if err != nil {
|
||||||
|
return ruleError(ErrBadTxOutValue, err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate transaction inputs.
|
// Check for duplicate transaction inputs.
|
||||||
|
@ -324,7 +356,7 @@ func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags Behavio
|
||||||
// to avoid proof of work checks is set.
|
// to avoid proof of work checks is set.
|
||||||
if flags&BFNoPoWCheck != BFNoPoWCheck {
|
if flags&BFNoPoWCheck != BFNoPoWCheck {
|
||||||
// The block hash must be less than the claimed target.
|
// The block hash must be less than the claimed target.
|
||||||
hash := header.BlockHash()
|
hash := header.BlockPoWHash()
|
||||||
hashNum := HashToBig(&hash)
|
hashNum := HashToBig(&hash)
|
||||||
if hashNum.Cmp(target) > 0 {
|
if hashNum.Cmp(target) > 0 {
|
||||||
str := fmt.Sprintf("block hash of %064x is higher than "+
|
str := fmt.Sprintf("block hash of %064x is higher than "+
|
||||||
|
@ -515,7 +547,7 @@ func checkBlockSanity(block *btcutil.Block, powLimit *big.Int, timeSource Median
|
||||||
// Do some preliminary checks on each transaction to ensure they are
|
// Do some preliminary checks on each transaction to ensure they are
|
||||||
// sane before continuing.
|
// sane before continuing.
|
||||||
for _, tx := range transactions {
|
for _, tx := range transactions {
|
||||||
err := CheckTransactionSanity(tx)
|
err := CheckTransactionSanity(tx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,487 +0,0 @@
|
||||||
// Copyright (c) 2013-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package blockchain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestSequenceLocksActive tests the SequenceLockActive function to ensure it
|
|
||||||
// works as expected in all possible combinations/scenarios.
|
|
||||||
func TestSequenceLocksActive(t *testing.T) {
|
|
||||||
seqLock := func(h int32, s int64) *SequenceLock {
|
|
||||||
return &SequenceLock{
|
|
||||||
Seconds: s,
|
|
||||||
BlockHeight: h,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
seqLock *SequenceLock
|
|
||||||
blockHeight int32
|
|
||||||
mtp time.Time
|
|
||||||
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// Block based sequence lock with equal block height.
|
|
||||||
{seqLock: seqLock(1000, -1), blockHeight: 1001, mtp: time.Unix(9, 0), want: true},
|
|
||||||
|
|
||||||
// Time based sequence lock with mtp past the absolute time.
|
|
||||||
{seqLock: seqLock(-1, 30), blockHeight: 2, mtp: time.Unix(31, 0), want: true},
|
|
||||||
|
|
||||||
// Block based sequence lock with current height below seq lock block height.
|
|
||||||
{seqLock: seqLock(1000, -1), blockHeight: 90, mtp: time.Unix(9, 0), want: false},
|
|
||||||
|
|
||||||
// Time based sequence lock with current time before lock time.
|
|
||||||
{seqLock: seqLock(-1, 30), blockHeight: 2, mtp: time.Unix(29, 0), want: false},
|
|
||||||
|
|
||||||
// Block based sequence lock at the same height, so shouldn't yet be active.
|
|
||||||
{seqLock: seqLock(1000, -1), blockHeight: 1000, mtp: time.Unix(9, 0), want: false},
|
|
||||||
|
|
||||||
// Time based sequence lock with current time equal to lock time, so shouldn't yet be active.
|
|
||||||
{seqLock: seqLock(-1, 30), blockHeight: 2, mtp: time.Unix(30, 0), want: false},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d sequence locks tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
got := SequenceLockActive(test.seqLock,
|
|
||||||
test.blockHeight, test.mtp)
|
|
||||||
if got != test.want {
|
|
||||||
t.Fatalf("SequenceLockActive #%d got %v want %v", i,
|
|
||||||
got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestCheckConnectBlockTemplate tests the CheckConnectBlockTemplate function to
|
|
||||||
// ensure it fails.
|
|
||||||
func TestCheckConnectBlockTemplate(t *testing.T) {
|
|
||||||
// Create a new database and chain instance to run tests against.
|
|
||||||
chain, teardownFunc, err := chainSetup("checkconnectblocktemplate",
|
|
||||||
&chaincfg.MainNetParams)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to setup chain instance: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer teardownFunc()
|
|
||||||
|
|
||||||
// Since we're not dealing with the real block chain, set the coinbase
|
|
||||||
// maturity to 1.
|
|
||||||
chain.TstSetCoinbaseMaturity(1)
|
|
||||||
|
|
||||||
// Load up blocks such that there is a side chain.
|
|
||||||
// (genesis block) -> 1 -> 2 -> 3 -> 4
|
|
||||||
// \-> 3a
|
|
||||||
testFiles := []string{
|
|
||||||
"blk_0_to_4.dat.bz2",
|
|
||||||
"blk_3A.dat.bz2",
|
|
||||||
}
|
|
||||||
|
|
||||||
var blocks []*btcutil.Block
|
|
||||||
for _, file := range testFiles {
|
|
||||||
blockTmp, err := loadBlocks(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Error loading file: %v\n", err)
|
|
||||||
}
|
|
||||||
blocks = append(blocks, blockTmp...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i <= 3; i++ {
|
|
||||||
isMainChain, _, err := chain.ProcessBlock(blocks[i], BFNone)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("CheckConnectBlockTemplate: Received unexpected error "+
|
|
||||||
"processing block %d: %v", i, err)
|
|
||||||
}
|
|
||||||
if !isMainChain {
|
|
||||||
t.Fatalf("CheckConnectBlockTemplate: Expected block %d to connect "+
|
|
||||||
"to main chain", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block 3 should fail to connect since it's already inserted.
|
|
||||||
err = chain.CheckConnectBlockTemplate(blocks[3])
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("CheckConnectBlockTemplate: Did not received expected error " +
|
|
||||||
"on block 3")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block 4 should connect successfully to tip of chain.
|
|
||||||
err = chain.CheckConnectBlockTemplate(blocks[4])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("CheckConnectBlockTemplate: Received unexpected error on "+
|
|
||||||
"block 4: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block 3a should fail to connect since does not build on chain tip.
|
|
||||||
err = chain.CheckConnectBlockTemplate(blocks[5])
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("CheckConnectBlockTemplate: Did not received expected error " +
|
|
||||||
"on block 3a")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block 4 should connect even if proof of work is invalid.
|
|
||||||
invalidPowBlock := *blocks[4].MsgBlock()
|
|
||||||
invalidPowBlock.Header.Nonce++
|
|
||||||
err = chain.CheckConnectBlockTemplate(btcutil.NewBlock(&invalidPowBlock))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("CheckConnectBlockTemplate: Received unexpected error on "+
|
|
||||||
"block 4 with bad nonce: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid block building on chain tip should fail to connect.
|
|
||||||
invalidBlock := *blocks[4].MsgBlock()
|
|
||||||
invalidBlock.Header.Bits--
|
|
||||||
err = chain.CheckConnectBlockTemplate(btcutil.NewBlock(&invalidBlock))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("CheckConnectBlockTemplate: Did not received expected error " +
|
|
||||||
"on block 4 with invalid difficulty bits")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works
|
|
||||||
// as expected.
|
|
||||||
func TestCheckBlockSanity(t *testing.T) {
|
|
||||||
powLimit := chaincfg.MainNetParams.PowLimit
|
|
||||||
block := btcutil.NewBlock(&Block100000)
|
|
||||||
timeSource := NewMedianTime()
|
|
||||||
err := CheckBlockSanity(block, powLimit, timeSource)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("CheckBlockSanity: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a block that has a timestamp with a precision higher than one
|
|
||||||
// second fails.
|
|
||||||
timestamp := block.MsgBlock().Header.Timestamp
|
|
||||||
block.MsgBlock().Header.Timestamp = timestamp.Add(time.Nanosecond)
|
|
||||||
err = CheckBlockSanity(block, powLimit, timeSource)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestCheckSerializedHeight tests the checkSerializedHeight function with
|
|
||||||
// various serialized heights and also does negative tests to ensure errors
|
|
||||||
// and handled properly.
|
|
||||||
func TestCheckSerializedHeight(t *testing.T) {
|
|
||||||
// Create an empty coinbase template to be used in the tests below.
|
|
||||||
coinbaseOutpoint := wire.NewOutPoint(&chainhash.Hash{}, math.MaxUint32)
|
|
||||||
coinbaseTx := wire.NewMsgTx(1)
|
|
||||||
coinbaseTx.AddTxIn(wire.NewTxIn(coinbaseOutpoint, nil, nil))
|
|
||||||
|
|
||||||
// Expected rule errors.
|
|
||||||
missingHeightError := RuleError{
|
|
||||||
ErrorCode: ErrMissingCoinbaseHeight,
|
|
||||||
}
|
|
||||||
badHeightError := RuleError{
|
|
||||||
ErrorCode: ErrBadCoinbaseHeight,
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
sigScript []byte // Serialized data
|
|
||||||
wantHeight int32 // Expected height
|
|
||||||
err error // Expected error type
|
|
||||||
}{
|
|
||||||
// No serialized height length.
|
|
||||||
{[]byte{}, 0, missingHeightError},
|
|
||||||
// Serialized height length with no height bytes.
|
|
||||||
{[]byte{0x02}, 0, missingHeightError},
|
|
||||||
// Serialized height length with too few height bytes.
|
|
||||||
{[]byte{0x02, 0x4a}, 0, missingHeightError},
|
|
||||||
// Serialized height that needs 2 bytes to encode.
|
|
||||||
{[]byte{0x02, 0x4a, 0x52}, 21066, nil},
|
|
||||||
// Serialized height that needs 2 bytes to encode, but backwards
|
|
||||||
// endianness.
|
|
||||||
{[]byte{0x02, 0x4a, 0x52}, 19026, badHeightError},
|
|
||||||
// Serialized height that needs 3 bytes to encode.
|
|
||||||
{[]byte{0x03, 0x40, 0x0d, 0x03}, 200000, nil},
|
|
||||||
// Serialized height that needs 3 bytes to encode, but backwards
|
|
||||||
// endianness.
|
|
||||||
{[]byte{0x03, 0x40, 0x0d, 0x03}, 1074594560, badHeightError},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
msgTx := coinbaseTx.Copy()
|
|
||||||
msgTx.TxIn[0].SignatureScript = test.sigScript
|
|
||||||
tx := btcutil.NewTx(msgTx)
|
|
||||||
|
|
||||||
err := checkSerializedHeight(tx, test.wantHeight)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("checkSerializedHeight #%d wrong error type "+
|
|
||||||
"got: %v <%T>, want: %T", i, err, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if rerr, ok := err.(RuleError); ok {
|
|
||||||
trerr := test.err.(RuleError)
|
|
||||||
if rerr.ErrorCode != trerr.ErrorCode {
|
|
||||||
t.Errorf("checkSerializedHeight #%d wrong "+
|
|
||||||
"error code got: %v, want: %v", i,
|
|
||||||
rerr.ErrorCode, trerr.ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block100000 defines block 100,000 of the block chain. It is used to
|
|
||||||
// test Block operations.
|
|
||||||
var Block100000 = wire.MsgBlock{
|
|
||||||
Header: wire.BlockHeader{
|
|
||||||
Version: 1,
|
|
||||||
PrevBlock: chainhash.Hash([32]byte{ // Make go vet happy.
|
|
||||||
0x50, 0x12, 0x01, 0x19, 0x17, 0x2a, 0x61, 0x04,
|
|
||||||
0x21, 0xa6, 0xc3, 0x01, 0x1d, 0xd3, 0x30, 0xd9,
|
|
||||||
0xdf, 0x07, 0xb6, 0x36, 0x16, 0xc2, 0xcc, 0x1f,
|
|
||||||
0x1c, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
}), // 000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250
|
|
||||||
MerkleRoot: chainhash.Hash([32]byte{ // Make go vet happy.
|
|
||||||
0x66, 0x57, 0xa9, 0x25, 0x2a, 0xac, 0xd5, 0xc0,
|
|
||||||
0xb2, 0x94, 0x09, 0x96, 0xec, 0xff, 0x95, 0x22,
|
|
||||||
0x28, 0xc3, 0x06, 0x7c, 0xc3, 0x8d, 0x48, 0x85,
|
|
||||||
0xef, 0xb5, 0xa4, 0xac, 0x42, 0x47, 0xe9, 0xf3,
|
|
||||||
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
|
|
||||||
Timestamp: time.Unix(1293623863, 0), // 2010-12-29 11:57:43 +0000 UTC
|
|
||||||
Bits: 0x1b04864c, // 453281356
|
|
||||||
Nonce: 0x10572b0f, // 274148111
|
|
||||||
},
|
|
||||||
Transactions: []*wire.MsgTx{
|
|
||||||
{
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
Hash: chainhash.Hash{},
|
|
||||||
Index: 0xffffffff,
|
|
||||||
},
|
|
||||||
SignatureScript: []byte{
|
|
||||||
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
|
|
||||||
},
|
|
||||||
Sequence: 0xffffffff,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TxOut: []*wire.TxOut{
|
|
||||||
{
|
|
||||||
Value: 0x12a05f200, // 5000000000
|
|
||||||
PkScript: []byte{
|
|
||||||
0x41, // OP_DATA_65
|
|
||||||
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
|
|
||||||
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
|
|
||||||
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
|
|
||||||
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
|
|
||||||
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
|
|
||||||
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
|
|
||||||
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
|
|
||||||
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
|
|
||||||
0x84, // 65-byte signature
|
|
||||||
0xac, // OP_CHECKSIG
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LockTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
Hash: chainhash.Hash([32]byte{ // Make go vet happy.
|
|
||||||
0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60,
|
|
||||||
0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac,
|
|
||||||
0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07,
|
|
||||||
0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87,
|
|
||||||
}), // 87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
SignatureScript: []byte{
|
|
||||||
0x49, // OP_DATA_73
|
|
||||||
0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3,
|
|
||||||
0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6,
|
|
||||||
0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94,
|
|
||||||
0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58,
|
|
||||||
0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00,
|
|
||||||
0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62,
|
|
||||||
0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c,
|
|
||||||
0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60,
|
|
||||||
0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48,
|
|
||||||
0x01, // 73-byte signature
|
|
||||||
0x41, // OP_DATA_65
|
|
||||||
0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d,
|
|
||||||
0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38,
|
|
||||||
0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25,
|
|
||||||
0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e,
|
|
||||||
0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8,
|
|
||||||
0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd,
|
|
||||||
0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b,
|
|
||||||
0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3,
|
|
||||||
0xd3, // 65-byte pubkey
|
|
||||||
},
|
|
||||||
Sequence: 0xffffffff,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TxOut: []*wire.TxOut{
|
|
||||||
{
|
|
||||||
Value: 0x2123e300, // 556000000
|
|
||||||
PkScript: []byte{
|
|
||||||
0x76, // OP_DUP
|
|
||||||
0xa9, // OP_HASH160
|
|
||||||
0x14, // OP_DATA_20
|
|
||||||
0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60,
|
|
||||||
0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
|
|
||||||
0xf7, 0xf5, 0x8b, 0x32,
|
|
||||||
0x88, // OP_EQUALVERIFY
|
|
||||||
0xac, // OP_CHECKSIG
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 0x108e20f00, // 4444000000
|
|
||||||
PkScript: []byte{
|
|
||||||
0x76, // OP_DUP
|
|
||||||
0xa9, // OP_HASH160
|
|
||||||
0x14, // OP_DATA_20
|
|
||||||
0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f,
|
|
||||||
0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
|
|
||||||
0x52, 0xde, 0x3d, 0x7c,
|
|
||||||
0x88, // OP_EQUALVERIFY
|
|
||||||
0xac, // OP_CHECKSIG
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LockTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
Hash: chainhash.Hash([32]byte{ // Make go vet happy.
|
|
||||||
0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d,
|
|
||||||
0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27,
|
|
||||||
0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65,
|
|
||||||
0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf,
|
|
||||||
}), // cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3
|
|
||||||
Index: 1,
|
|
||||||
},
|
|
||||||
SignatureScript: []byte{
|
|
||||||
0x47, // OP_DATA_71
|
|
||||||
0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf,
|
|
||||||
0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5,
|
|
||||||
0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34,
|
|
||||||
0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31,
|
|
||||||
0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee,
|
|
||||||
0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f,
|
|
||||||
0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c,
|
|
||||||
0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e,
|
|
||||||
0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01,
|
|
||||||
0x41, // OP_DATA_65
|
|
||||||
0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78,
|
|
||||||
0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5,
|
|
||||||
0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39,
|
|
||||||
0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21,
|
|
||||||
0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee,
|
|
||||||
0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3,
|
|
||||||
0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95,
|
|
||||||
0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85,
|
|
||||||
0x0f, // 65-byte pubkey
|
|
||||||
},
|
|
||||||
Sequence: 0xffffffff,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TxOut: []*wire.TxOut{
|
|
||||||
{
|
|
||||||
Value: 0xf4240, // 1000000
|
|
||||||
PkScript: []byte{
|
|
||||||
0x76, // OP_DUP
|
|
||||||
0xa9, // OP_HASH160
|
|
||||||
0x14, // OP_DATA_20
|
|
||||||
0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04,
|
|
||||||
0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
|
|
||||||
0xad, 0xbe, 0x7e, 0x10,
|
|
||||||
0x88, // OP_EQUALVERIFY
|
|
||||||
0xac, // OP_CHECKSIG
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Value: 0x11d260c0, // 299000000
|
|
||||||
PkScript: []byte{
|
|
||||||
0x76, // OP_DUP
|
|
||||||
0xa9, // OP_HASH160
|
|
||||||
0x14, // OP_DATA_20
|
|
||||||
0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1,
|
|
||||||
0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
|
|
||||||
0xb3, 0x40, 0x9c, 0xd9,
|
|
||||||
0x88, // OP_EQUALVERIFY
|
|
||||||
0xac, // OP_CHECKSIG
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LockTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Version: 1,
|
|
||||||
TxIn: []*wire.TxIn{
|
|
||||||
{
|
|
||||||
PreviousOutPoint: wire.OutPoint{
|
|
||||||
Hash: chainhash.Hash([32]byte{ // Make go vet happy.
|
|
||||||
0x0b, 0x60, 0x72, 0xb3, 0x86, 0xd4, 0xa7, 0x73,
|
|
||||||
0x23, 0x52, 0x37, 0xf6, 0x4c, 0x11, 0x26, 0xac,
|
|
||||||
0x3b, 0x24, 0x0c, 0x84, 0xb9, 0x17, 0xa3, 0x90,
|
|
||||||
0x9b, 0xa1, 0xc4, 0x3d, 0xed, 0x5f, 0x51, 0xf4,
|
|
||||||
}), // f4515fed3dc4a19b90a317b9840c243bac26114cf637522373a7d486b372600b
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
SignatureScript: []byte{
|
|
||||||
0x49, // OP_DATA_73
|
|
||||||
0x30, 0x46, 0x02, 0x21, 0x00, 0xbb, 0x1a, 0xd2,
|
|
||||||
0x6d, 0xf9, 0x30, 0xa5, 0x1c, 0xce, 0x11, 0x0c,
|
|
||||||
0xf4, 0x4f, 0x7a, 0x48, 0xc3, 0xc5, 0x61, 0xfd,
|
|
||||||
0x97, 0x75, 0x00, 0xb1, 0xae, 0x5d, 0x6b, 0x6f,
|
|
||||||
0xd1, 0x3d, 0x0b, 0x3f, 0x4a, 0x02, 0x21, 0x00,
|
|
||||||
0xc5, 0xb4, 0x29, 0x51, 0xac, 0xed, 0xff, 0x14,
|
|
||||||
0xab, 0xba, 0x27, 0x36, 0xfd, 0x57, 0x4b, 0xdb,
|
|
||||||
0x46, 0x5f, 0x3e, 0x6f, 0x8d, 0xa1, 0x2e, 0x2c,
|
|
||||||
0x53, 0x03, 0x95, 0x4a, 0xca, 0x7f, 0x78, 0xf3,
|
|
||||||
0x01, // 73-byte signature
|
|
||||||
0x41, // OP_DATA_65
|
|
||||||
0x04, 0xa7, 0x13, 0x5b, 0xfe, 0x82, 0x4c, 0x97,
|
|
||||||
0xec, 0xc0, 0x1e, 0xc7, 0xd7, 0xe3, 0x36, 0x18,
|
|
||||||
0x5c, 0x81, 0xe2, 0xaa, 0x2c, 0x41, 0xab, 0x17,
|
|
||||||
0x54, 0x07, 0xc0, 0x94, 0x84, 0xce, 0x96, 0x94,
|
|
||||||
0xb4, 0x49, 0x53, 0xfc, 0xb7, 0x51, 0x20, 0x65,
|
|
||||||
0x64, 0xa9, 0xc2, 0x4d, 0xd0, 0x94, 0xd4, 0x2f,
|
|
||||||
0xdb, 0xfd, 0xd5, 0xaa, 0xd3, 0xe0, 0x63, 0xce,
|
|
||||||
0x6a, 0xf4, 0xcf, 0xaa, 0xea, 0x4e, 0xa1, 0x4f,
|
|
||||||
0xbb, // 65-byte pubkey
|
|
||||||
},
|
|
||||||
Sequence: 0xffffffff,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TxOut: []*wire.TxOut{
|
|
||||||
{
|
|
||||||
Value: 0xf4240, // 1000000
|
|
||||||
PkScript: []byte{
|
|
||||||
0x76, // OP_DUP
|
|
||||||
0xa9, // OP_HASH160
|
|
||||||
0x14, // OP_DATA_20
|
|
||||||
0x39, 0xaa, 0x3d, 0x56, 0x9e, 0x06, 0xa1, 0xd7,
|
|
||||||
0x92, 0x6d, 0xc4, 0xbe, 0x11, 0x93, 0xc9, 0x9b,
|
|
||||||
0xf2, 0xeb, 0x9e, 0xe0,
|
|
||||||
0x88, // OP_EQUALVERIFY
|
|
||||||
0xac, // OP_CHECKSIG
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LockTime: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/lbryio/lbcd/chaincfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -195,6 +195,12 @@ func (b *BlockChain) calcNextBlockVersion(prevNode *blockNode) (int32, error) {
|
||||||
expectedVersion := uint32(vbTopBits)
|
expectedVersion := uint32(vbTopBits)
|
||||||
for id := 0; id < len(b.chainParams.Deployments); id++ {
|
for id := 0; id < len(b.chainParams.Deployments); id++ {
|
||||||
deployment := &b.chainParams.Deployments[id]
|
deployment := &b.chainParams.Deployments[id]
|
||||||
|
|
||||||
|
// added to mimic LBRYcrd:
|
||||||
|
if deployment.ForceActiveAt > 0 && prevNode != nil && prevNode.height+1 >= deployment.ForceActiveAt {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
cache := &b.deploymentCaches[id]
|
cache := &b.deploymentCaches[id]
|
||||||
checker := deploymentChecker{deployment: deployment, chain: b}
|
checker := deploymentChecker{deployment: deployment, chain: b}
|
||||||
state, err := b.thresholdState(prevNode, checker, cache)
|
state, err := b.thresholdState(prevNode, checker, cache)
|
||||||
|
|
|
@ -7,9 +7,9 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -20,11 +20,11 @@ const (
|
||||||
// weight of a "base" byte is 4, while the weight of a witness byte is
|
// weight of a "base" byte is 4, while the weight of a witness byte is
|
||||||
// 1. As a result, for a block to be valid, the BlockWeight MUST be
|
// 1. As a result, for a block to be valid, the BlockWeight MUST be
|
||||||
// less than, or equal to MaxBlockWeight.
|
// less than, or equal to MaxBlockWeight.
|
||||||
MaxBlockWeight = 4000000
|
MaxBlockWeight = 8000000
|
||||||
|
|
||||||
// MaxBlockBaseSize is the maximum number of bytes within a block
|
// MaxBlockBaseSize is the maximum number of bytes within a block
|
||||||
// which can be allocated to non-witness data.
|
// which can be allocated to non-witness data.
|
||||||
MaxBlockBaseSize = 1000000
|
MaxBlockBaseSize = 8000000
|
||||||
|
|
||||||
// MaxBlockSigOpsCost is the maximum number of signature operations
|
// MaxBlockSigOpsCost is the maximum number of signature operations
|
||||||
// allowed for a block. It is calculated via a weighted algorithm which
|
// allowed for a block. It is calculated via a weighted algorithm which
|
||||||
|
|
25
btcd.go
25
btcd.go
|
@ -15,9 +15,12 @@ import (
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain/indexers"
|
"github.com/lbryio/lbcd/blockchain/indexers"
|
||||||
"github.com/btcsuite/btcd/database"
|
"github.com/lbryio/lbcd/claimtrie/param"
|
||||||
"github.com/btcsuite/btcd/limits"
|
"github.com/lbryio/lbcd/database"
|
||||||
|
"github.com/lbryio/lbcd/limits"
|
||||||
|
|
||||||
|
"github.com/felixge/fgprof"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -65,6 +68,7 @@ func btcdMain(serverChan chan<- *server) error {
|
||||||
|
|
||||||
// Enable http profiling server if requested.
|
// Enable http profiling server if requested.
|
||||||
if cfg.Profile != "" {
|
if cfg.Profile != "" {
|
||||||
|
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
|
||||||
go func() {
|
go func() {
|
||||||
listenAddr := net.JoinHostPort("", cfg.Profile)
|
listenAddr := net.JoinHostPort("", cfg.Profile)
|
||||||
btcdLog.Infof("Profile server listening on %s", listenAddr)
|
btcdLog.Infof("Profile server listening on %s", listenAddr)
|
||||||
|
@ -144,6 +148,10 @@ func btcdMain(serverChan chan<- *server) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
param.SetNetwork(activeNetParams.Params.Net) // prep the claimtrie params
|
||||||
|
|
||||||
|
go logMemoryUsage()
|
||||||
|
|
||||||
// Create server and start it.
|
// Create server and start it.
|
||||||
server, err := newServer(cfg.Listeners, cfg.AgentBlacklist,
|
server, err := newServer(cfg.Listeners, cfg.AgentBlacklist,
|
||||||
cfg.AgentWhitelist, db, activeNetParams.Params, interrupt)
|
cfg.AgentWhitelist, db, activeNetParams.Params, interrupt)
|
||||||
|
@ -158,6 +166,10 @@ func btcdMain(serverChan chan<- *server) error {
|
||||||
server.Stop()
|
server.Stop()
|
||||||
server.WaitForShutdown()
|
server.WaitForShutdown()
|
||||||
srvrLog.Infof("Server shutdown complete")
|
srvrLog.Infof("Server shutdown complete")
|
||||||
|
// TODO: tie into the sync manager for shutdown instead
|
||||||
|
if ct := server.chain.ClaimTrie(); ct != nil {
|
||||||
|
ct.Close()
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
server.Start()
|
server.Start()
|
||||||
if serverChan != nil {
|
if serverChan != nil {
|
||||||
|
@ -196,6 +208,13 @@ func removeRegressionDB(dbPath string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbPath = filepath.Join(cfg.DataDir, activeNetParams.Name, "claim_dbs")
|
||||||
|
btcdLog.Infof("Removing regression test claim databases from '%s'", dbPath)
|
||||||
|
err = os.RemoveAll(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,68 +1,11 @@
|
||||||
btcec
|
btcec
|
||||||
=====
|
=====
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
|
||||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://pkg.go.dev/github.com/btcsuite/btcd/btcec?status.png)](https://pkg.go.dev/github.com/btcsuite/btcd/btcec)
|
|
||||||
|
|
||||||
Package btcec implements elliptic curve cryptography needed for working with
|
btcec implements elliptic curve cryptography needed for working with
|
||||||
Bitcoin (secp256k1 only for now). It is designed so that it may be used with the
|
Bitcoin (secp256k1 only for now). It is designed so that it may be used with the
|
||||||
standard crypto/ecdsa packages provided with go. A comprehensive suite of test
|
standard crypto/ecdsa packages provided with go. A comprehensive suite of test
|
||||||
is provided to ensure proper functionality. Package btcec was originally based
|
is provided to ensure proper functionality. Package btcec was originally based
|
||||||
on work from ThePiachu which is licensed under the same terms as Go, but it has
|
on work from ThePiachu which is licensed under the same terms as Go, but it has
|
||||||
signficantly diverged since then. The btcsuite developers original is licensed
|
signficantly diverged since then.
|
||||||
under the liberal ISC license.
|
|
||||||
|
|
||||||
Although this package was primarily written for btcd, it has intentionally been
|
|
||||||
designed so it can be used as a standalone package for any projects needing to
|
|
||||||
use secp256k1 elliptic curve cryptography.
|
|
||||||
|
|
||||||
## Installation and Updating
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get -u github.com/btcsuite/btcd/btcec
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
* [Sign Message](https://pkg.go.dev/github.com/btcsuite/btcd/btcec#example-package--SignMessage)
|
|
||||||
Demonstrates signing a message with a secp256k1 private key that is first
|
|
||||||
parsed form raw bytes and serializing the generated signature.
|
|
||||||
|
|
||||||
* [Verify Signature](https://pkg.go.dev/github.com/btcsuite/btcd/btcec#example-package--VerifySignature)
|
|
||||||
Demonstrates verifying a secp256k1 signature against a public key that is
|
|
||||||
first parsed from raw bytes. The signature is also parsed from raw bytes.
|
|
||||||
|
|
||||||
* [Encryption](https://pkg.go.dev/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage)
|
|
||||||
Demonstrates encrypting a message for a public key that is first parsed from
|
|
||||||
raw bytes, then decrypting it using the corresponding private key.
|
|
||||||
|
|
||||||
* [Decryption](https://pkg.go.dev/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage)
|
|
||||||
Demonstrates decrypting a message using a private key that is first parsed
|
|
||||||
from raw bytes.
|
|
||||||
|
|
||||||
## GPG Verification Key
|
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
|
||||||
has not been tampered with and is coming from the btcsuite developers. To
|
|
||||||
verify the signature perform the following:
|
|
||||||
|
|
||||||
- Download the public key from the Conformal website at
|
|
||||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
|
||||||
|
|
||||||
- Import the public key into your GPG keyring:
|
|
||||||
```bash
|
|
||||||
gpg --import GIT-GPG-KEY-conformal.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
|
||||||
placeholder for the specific tag:
|
|
||||||
```bash
|
|
||||||
git tag -v TAG_NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Package btcec is licensed under the [copyfree](http://copyfree.org) ISC License
|
|
||||||
except for btcec.go and btcec_test.go which is under the same license as Go.
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/lbryio/lbcd/btcec"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This example demonstrates signing a message with a secp256k1 private key that
|
// This example demonstrates signing a message with a secp256k1 private key that
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// This file is ignored during the regular build due to the following build tag.
|
// This file is ignored during the regular build due to the following build tag.
|
||||||
// It is called by go generate and used to automatically generate pre-computed
|
// It is called by go generate and used to automatically generate pre-computed
|
||||||
// tables used to accelerate operations.
|
// tables used to accelerate operations.
|
||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
@ -17,7 +18,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/lbryio/lbcd/btcec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
// This file is ignored during the regular build due to the following build tag.
|
// This file is ignored during the regular build due to the following build tag.
|
||||||
// This build tag is set during go generate.
|
// This build tag is set during go generate.
|
||||||
|
//go:build gensecp256k1
|
||||||
// +build gensecp256k1
|
// +build gensecp256k1
|
||||||
|
|
||||||
package btcec
|
package btcec
|
||||||
|
|
|
@ -1,70 +1,8 @@
|
||||||
btcjson
|
btcjson
|
||||||
=======
|
=======
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
|
||||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd/btcjson)
|
|
||||||
|
|
||||||
Package btcjson implements concrete types for marshalling to and from the
|
Package btcjson implements concrete types for marshalling to and from the
|
||||||
bitcoin JSON-RPC API. A comprehensive suite of tests is provided to ensure
|
bitcoin JSON-RPC API. A comprehensive suite of tests is provided to ensure
|
||||||
proper functionality.
|
proper functionality.
|
||||||
|
|
||||||
Although this package was primarily written for the btcsuite, it has
|
|
||||||
intentionally been designed so it can be used as a standalone package for any
|
|
||||||
projects needing to marshal to and from bitcoin JSON-RPC requests and responses.
|
|
||||||
|
|
||||||
Note that although it's possible to use this package directly to implement an
|
|
||||||
RPC client, it is not recommended since it is only intended as an infrastructure
|
|
||||||
package. Instead, RPC clients should use the
|
|
||||||
[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package which provides
|
|
||||||
a full blown RPC client with many features such as automatic connection
|
|
||||||
management, websocket support, automatic notification re-registration on
|
|
||||||
reconnect, and conversion from the raw underlying RPC types (strings, floats,
|
|
||||||
ints, etc) to higher-level types with many nice and useful properties.
|
|
||||||
|
|
||||||
## Installation and Updating
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get -u github.com/btcsuite/btcd/btcjson
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
* [Marshal Command](https://pkg.go.dev/github.com/btcsuite/btcd/btcjson#example-MarshalCmd)
|
|
||||||
Demonstrates how to create and marshal a command into a JSON-RPC request.
|
|
||||||
|
|
||||||
* [Unmarshal Command](https://pkg.go.dev/github.com/btcsuite/btcd/btcjson#example-UnmarshalCmd)
|
|
||||||
Demonstrates how to unmarshal a JSON-RPC request and then unmarshal the
|
|
||||||
concrete request into a concrete command.
|
|
||||||
|
|
||||||
* [Marshal Response](https://pkg.go.dev/github.com/btcsuite/btcd/btcjson#example-MarshalResponse)
|
|
||||||
Demonstrates how to marshal a JSON-RPC response.
|
|
||||||
|
|
||||||
* [Unmarshal Response](https://pkg.go.dev/github.com/btcsuite/btcd/btcjson#example-package--UnmarshalResponse)
|
|
||||||
Demonstrates how to unmarshal a JSON-RPC response and then unmarshal the
|
|
||||||
result field in the response to a concrete type.
|
|
||||||
|
|
||||||
## GPG Verification Key
|
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
|
||||||
has not been tampered with and is coming from the btcsuite developers. To
|
|
||||||
verify the signature perform the following:
|
|
||||||
|
|
||||||
- Download the public key from the Conformal website at
|
|
||||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
|
||||||
|
|
||||||
- Import the public key into your GPG keyring:
|
|
||||||
```bash
|
|
||||||
gpg --import GIT-GPG-KEY-conformal.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
|
||||||
placeholder for the specific tag:
|
|
||||||
```bash
|
|
||||||
git tag -v TAG_NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Package btcjson is licensed under the [copyfree](http://copyfree.org) ISC
|
|
||||||
License.
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBtcdExtCmds tests all of the btcd extended commands marshal and unmarshal
|
// TestBtcdExtCmds tests all of the btcd extended commands marshal and unmarshal
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBtcdExtCustomResults ensures any results that have custom marshalling
|
// TestBtcdExtCustomResults ensures any results that have custom marshalling
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBtcWalletExtCmds tests all of the btcwallet extended commands marshal and
|
// TestBtcWalletExtCmds tests all of the btcwallet extended commands marshal and
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddNodeSubCmd defines the type used in the addnode JSON-RPC command for the
|
// AddNodeSubCmd defines the type used in the addnode JSON-RPC command for the
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrCmds tests all of the chain server commands marshal and unmarshal
|
// TestChainSvrCmds tests all of the chain server commands marshal and unmarshal
|
||||||
|
@ -388,7 +388,7 @@ func TestChainSvrCmds(t *testing.T) {
|
||||||
return btcjson.NewGetBlockFilterCmd("0000afaf", nil)
|
return btcjson.NewGetBlockFilterCmd("0000afaf", nil)
|
||||||
},
|
},
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"getblockfilter","params":["0000afaf"],"id":1}`,
|
marshalled: `{"jsonrpc":"1.0","method":"getblockfilter","params":["0000afaf"],"id":1}`,
|
||||||
unmarshalled: &btcjson.GetBlockFilterCmd{"0000afaf", nil},
|
unmarshalled: &btcjson.GetBlockFilterCmd{BlockHash: "0000afaf", FilterType: nil},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "getblockfilter optional filtertype",
|
name: "getblockfilter optional filtertype",
|
||||||
|
@ -399,7 +399,7 @@ func TestChainSvrCmds(t *testing.T) {
|
||||||
return btcjson.NewGetBlockFilterCmd("0000afaf", btcjson.NewFilterTypeName(btcjson.FilterTypeBasic))
|
return btcjson.NewGetBlockFilterCmd("0000afaf", btcjson.NewFilterTypeName(btcjson.FilterTypeBasic))
|
||||||
},
|
},
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"getblockfilter","params":["0000afaf","basic"],"id":1}`,
|
marshalled: `{"jsonrpc":"1.0","method":"getblockfilter","params":["0000afaf","basic"],"id":1}`,
|
||||||
unmarshalled: &btcjson.GetBlockFilterCmd{"0000afaf", btcjson.NewFilterTypeName(btcjson.FilterTypeBasic)},
|
unmarshalled: &btcjson.GetBlockFilterCmd{BlockHash: "0000afaf", FilterType: btcjson.NewFilterTypeName(btcjson.FilterTypeBasic)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "getblockhash",
|
name: "getblockhash",
|
||||||
|
|
|
@ -9,10 +9,10 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
|
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
|
||||||
|
@ -25,6 +25,7 @@ type GetBlockHeaderVerboseResult struct {
|
||||||
Version int32 `json:"version"`
|
Version int32 `json:"version"`
|
||||||
VersionHex string `json:"versionHex"`
|
VersionHex string `json:"versionHex"`
|
||||||
MerkleRoot string `json:"merkleroot"`
|
MerkleRoot string `json:"merkleroot"`
|
||||||
|
ClaimTrie string `json:"claimtrie,omitempty"`
|
||||||
Time int64 `json:"time"`
|
Time int64 `json:"time"`
|
||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
Bits string `json:"bits"`
|
Bits string `json:"bits"`
|
||||||
|
@ -65,6 +66,27 @@ type GetBlockStatsResult struct {
|
||||||
UTXOSizeIncrease int64 `json:"utxo_size_inc"`
|
UTXOSizeIncrease int64 `json:"utxo_size_inc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetBlockVerboseResultBase struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Confirmations int64 `json:"confirmations"`
|
||||||
|
StrippedSize int32 `json:"strippedsize"`
|
||||||
|
Size int32 `json:"size"`
|
||||||
|
Weight int32 `json:"weight"`
|
||||||
|
Height int64 `json:"height"`
|
||||||
|
Version int32 `json:"version"`
|
||||||
|
VersionHex string `json:"versionHex"`
|
||||||
|
MerkleRoot string `json:"merkleroot"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
Nonce uint32 `json:"nonce"`
|
||||||
|
Bits string `json:"bits"`
|
||||||
|
Difficulty float64 `json:"difficulty"`
|
||||||
|
PreviousHash string `json:"previousblockhash,omitempty"`
|
||||||
|
NextHash string `json:"nextblockhash,omitempty"`
|
||||||
|
|
||||||
|
ClaimTrie string `json:"claimTrie,omitempty"`
|
||||||
|
TxCount int `json:"nTx"` // For backwards compatibility only
|
||||||
|
}
|
||||||
|
|
||||||
// GetBlockVerboseResult models the data from the getblock command when the
|
// GetBlockVerboseResult models the data from the getblock command when the
|
||||||
// verbose flag is set to 1. When the verbose flag is set to 0, getblock returns a
|
// verbose flag is set to 1. When the verbose flag is set to 0, getblock returns a
|
||||||
// hex-encoded string. When the verbose flag is set to 1, getblock returns an object
|
// hex-encoded string. When the verbose flag is set to 1, getblock returns an object
|
||||||
|
@ -72,23 +94,8 @@ type GetBlockStatsResult struct {
|
||||||
// getblock returns an object whose tx field is an array of raw transactions.
|
// getblock returns an object whose tx field is an array of raw transactions.
|
||||||
// Use GetBlockVerboseTxResult to unmarshal data received from passing verbose=2 to getblock.
|
// Use GetBlockVerboseTxResult to unmarshal data received from passing verbose=2 to getblock.
|
||||||
type GetBlockVerboseResult struct {
|
type GetBlockVerboseResult struct {
|
||||||
Hash string `json:"hash"`
|
GetBlockVerboseResultBase
|
||||||
Confirmations int64 `json:"confirmations"`
|
Tx []string `json:"tx"`
|
||||||
StrippedSize int32 `json:"strippedsize"`
|
|
||||||
Size int32 `json:"size"`
|
|
||||||
Weight int32 `json:"weight"`
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
VersionHex string `json:"versionHex"`
|
|
||||||
MerkleRoot string `json:"merkleroot"`
|
|
||||||
Tx []string `json:"tx,omitempty"`
|
|
||||||
RawTx []TxRawResult `json:"rawtx,omitempty"` // Note: this field is always empty when verbose != 2.
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
Nonce uint32 `json:"nonce"`
|
|
||||||
Bits string `json:"bits"`
|
|
||||||
Difficulty float64 `json:"difficulty"`
|
|
||||||
PreviousHash string `json:"previousblockhash"`
|
|
||||||
NextHash string `json:"nextblockhash,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockVerboseTxResult models the data from the getblock command when the
|
// GetBlockVerboseTxResult models the data from the getblock command when the
|
||||||
|
@ -98,23 +105,8 @@ type GetBlockVerboseResult struct {
|
||||||
// getblock returns an object whose tx field is an array of raw transactions.
|
// getblock returns an object whose tx field is an array of raw transactions.
|
||||||
// Use GetBlockVerboseResult to unmarshal data received from passing verbose=1 to getblock.
|
// Use GetBlockVerboseResult to unmarshal data received from passing verbose=1 to getblock.
|
||||||
type GetBlockVerboseTxResult struct {
|
type GetBlockVerboseTxResult struct {
|
||||||
Hash string `json:"hash"`
|
GetBlockVerboseResultBase
|
||||||
Confirmations int64 `json:"confirmations"`
|
Tx []TxRawResult `json:"tx"`
|
||||||
StrippedSize int32 `json:"strippedsize"`
|
|
||||||
Size int32 `json:"size"`
|
|
||||||
Weight int32 `json:"weight"`
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
Version int32 `json:"version"`
|
|
||||||
VersionHex string `json:"versionHex"`
|
|
||||||
MerkleRoot string `json:"merkleroot"`
|
|
||||||
Tx []TxRawResult `json:"tx,omitempty"`
|
|
||||||
RawTx []TxRawResult `json:"rawtx,omitempty"` // Deprecated: removed in Bitcoin Core
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
Nonce uint32 `json:"nonce"`
|
|
||||||
Bits string `json:"bits"`
|
|
||||||
Difficulty float64 `json:"difficulty"`
|
|
||||||
PreviousHash string `json:"previousblockhash"`
|
|
||||||
NextHash string `json:"nextblockhash,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChainTxStatsResult models the data from the getchaintxstats command.
|
// GetChainTxStatsResult models the data from the getchaintxstats command.
|
||||||
|
@ -296,6 +288,10 @@ type GetBlockTemplateResult struct {
|
||||||
// Block proposal from BIP 0023.
|
// Block proposal from BIP 0023.
|
||||||
Capabilities []string `json:"capabilities,omitempty"`
|
Capabilities []string `json:"capabilities,omitempty"`
|
||||||
RejectReasion string `json:"reject-reason,omitempty"`
|
RejectReasion string `json:"reject-reason,omitempty"`
|
||||||
|
|
||||||
|
ClaimTrieHash string `json:"claimtrie"`
|
||||||
|
|
||||||
|
Rules []string `json:"rules,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMempoolEntryResult models the data returned from the getmempoolentry's
|
// GetMempoolEntryResult models the data returned from the getmempoolentry's
|
||||||
|
@ -428,6 +424,9 @@ type ScriptPubKeyResult struct {
|
||||||
Hex string `json:"hex,omitempty"`
|
Hex string `json:"hex,omitempty"`
|
||||||
ReqSigs int32 `json:"reqSigs,omitempty"`
|
ReqSigs int32 `json:"reqSigs,omitempty"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
SubType string `json:"subtype"`
|
||||||
|
IsClaim bool `json:"isclaim"`
|
||||||
|
IsSupport bool `json:"issupport"`
|
||||||
Addresses []string `json:"addresses,omitempty"`
|
Addresses []string `json:"addresses,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,6 +585,8 @@ func (v *Vin) MarshalJSON() ([]byte, error) {
|
||||||
type PrevOut struct {
|
type PrevOut struct {
|
||||||
Addresses []string `json:"addresses,omitempty"`
|
Addresses []string `json:"addresses,omitempty"`
|
||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
|
IsClaim bool `json:"isclaim"`
|
||||||
|
IsSupport bool `json:"issupport"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// VinPrevOut is like Vin except it includes PrevOut. It is used by searchrawtransaction
|
// VinPrevOut is like Vin except it includes PrevOut. It is used by searchrawtransaction
|
||||||
|
|
|
@ -9,10 +9,10 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrCustomResults ensures any results that have custom marshalling
|
// TestChainSvrCustomResults ensures any results that have custom marshalling
|
||||||
|
@ -70,7 +70,7 @@ func TestChainSvrCustomResults(t *testing.T) {
|
||||||
},
|
},
|
||||||
Sequence: 4294967295,
|
Sequence: 4294967295,
|
||||||
},
|
},
|
||||||
expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"prevOut":{"addresses":["addr1"],"value":0},"sequence":4294967295}`,
|
expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"prevOut":{"addresses":["addr1"],"value":0,"isclaim":false,"issupport":false},"sequence":4294967295}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrWsCmds tests all of the chain server websocket-specific commands
|
// TestChainSvrWsCmds tests all of the chain server websocket-specific commands
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrWsNtfns tests all of the chain server websocket-specific
|
// TestChainSvrWsNtfns tests all of the chain server websocket-specific
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChainSvrWsResults ensures any results that have custom marshalling
|
// TestChainSvrWsResults ensures any results that have custom marshalling
|
||||||
|
|
95
btcjson/claimcmds.go
Normal file
95
btcjson/claimcmds.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package btcjson
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// No special flags for commands in this file.
|
||||||
|
flags := UsageFlag(0)
|
||||||
|
|
||||||
|
MustRegisterCmd("getchangesinblock", (*GetChangesInBlockCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsforname", (*GetClaimsForNameCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsfornamebyid", (*GetClaimsForNameByIDCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsfornamebybid", (*GetClaimsForNameByBidCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsfornamebyseq", (*GetClaimsForNameBySeqCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("normalize", (*GetNormalizedCmd)(nil), flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional inputs are required to be pointers, but they support things like `jsonrpcdefault:"false"`
|
||||||
|
// optional inputs have to be at the bottom of the struct
|
||||||
|
// optional outputs require ",omitempty"
|
||||||
|
// traditional bitcoin fields are all lowercase
|
||||||
|
|
||||||
|
type GetChangesInBlockCmd struct {
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetChangesInBlockResult struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
Names []string `json:"names"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameByIDCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
PartialClaimIDs []string `json:"partialclaimids"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameByBidCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Bids []int32 `json:"bids"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameBySeqCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Sequences []int32 `json:"sequences" jsonrpcusage:"[sequence,...]"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameResult struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
NormalizedName string `json:"normalizedname"`
|
||||||
|
Claims []ClaimResult `json:"claims"`
|
||||||
|
// UnclaimedSupports []SupportResult `json:"unclaimedSupports"` how would this work with other constraints?
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupportResult struct {
|
||||||
|
TXID string `json:"txid"`
|
||||||
|
N uint32 `json:"n"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
ValidAtHeight int32 `json:"validatheight"`
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClaimResult struct {
|
||||||
|
ClaimID string `json:"claimid"`
|
||||||
|
TXID string `json:"txid"`
|
||||||
|
N uint32 `json:"n"`
|
||||||
|
Bid int32 `json:"bid"`
|
||||||
|
Sequence int32 `json:"sequence"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
ValidAtHeight int32 `json:"validatheight"`
|
||||||
|
EffectiveAmount int64 `json:"effectiveamount"`
|
||||||
|
Supports []SupportResult `json:"supports,omitempty"`
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetNormalizedCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetNormalizedResult struct {
|
||||||
|
NormalizedName string `json:"normalizedname"`
|
||||||
|
}
|
|
@ -1,430 +0,0 @@
|
||||||
// Copyright (c) 2015 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestCmdMethod tests the CmdMethod function to ensure it retunrs the expected
|
|
||||||
// methods and errors.
|
|
||||||
func TestCmdMethod(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
cmd interface{}
|
|
||||||
method string
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "unregistered type",
|
|
||||||
cmd: (*int)(nil),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nil pointer of registered type",
|
|
||||||
cmd: (*btcjson.GetBlockCmd)(nil),
|
|
||||||
method: "getblock",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nil instance of registered type",
|
|
||||||
cmd: &btcjson.GetBlockCountCmd{},
|
|
||||||
method: "getblockcount",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
method, err := btcjson.CmdMethod(test.cmd)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+
|
|
||||||
"want %T", i, test.name, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.(btcjson.Error).ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code "+
|
|
||||||
"- got %v (%v), want %v", i, test.name,
|
|
||||||
gotErrorCode, err,
|
|
||||||
test.err.(btcjson.Error).ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure method matches the expected value.
|
|
||||||
if method != test.method {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched method - got %v, "+
|
|
||||||
"want %v", i, test.name, method, test.method)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestMethodUsageFlags tests the MethodUsage function ensure it returns the
|
|
||||||
// expected flags and errors.
|
|
||||||
func TestMethodUsageFlags(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
method string
|
|
||||||
err error
|
|
||||||
flags btcjson.UsageFlag
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "unregistered type",
|
|
||||||
method: "bogusmethod",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getblock",
|
|
||||||
method: "getblock",
|
|
||||||
flags: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "walletpassphrase",
|
|
||||||
method: "walletpassphrase",
|
|
||||||
flags: btcjson.UFWalletOnly,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
flags, err := btcjson.MethodUsageFlags(test.method)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+
|
|
||||||
"want %T", i, test.name, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.(btcjson.Error).ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code "+
|
|
||||||
"- got %v (%v), want %v", i, test.name,
|
|
||||||
gotErrorCode, err,
|
|
||||||
test.err.(btcjson.Error).ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure flags match the expected value.
|
|
||||||
if flags != test.flags {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched flags - got %v, "+
|
|
||||||
"want %v", i, test.name, flags, test.flags)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestMethodUsageText tests the MethodUsageText function ensure it returns the
|
|
||||||
// expected text.
|
|
||||||
func TestMethodUsageText(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
method string
|
|
||||||
err error
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "unregistered type",
|
|
||||||
method: "bogusmethod",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getblockcount",
|
|
||||||
method: "getblockcount",
|
|
||||||
expected: "getblockcount",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getblock",
|
|
||||||
method: "getblock",
|
|
||||||
expected: `getblock "hash" (verbosity=1)`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
usage, err := btcjson.MethodUsageText(test.method)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+
|
|
||||||
"want %T", i, test.name, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.(btcjson.Error).ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code "+
|
|
||||||
"- got %v (%v), want %v", i, test.name,
|
|
||||||
gotErrorCode, err,
|
|
||||||
test.err.(btcjson.Error).ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure usage matches the expected value.
|
|
||||||
if usage != test.expected {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched usage - got %v, "+
|
|
||||||
"want %v", i, test.name, usage, test.expected)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the usage again to exercise caching.
|
|
||||||
usage, err = btcjson.MethodUsageText(test.method)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected error: %v", i,
|
|
||||||
test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure usage still matches the expected value.
|
|
||||||
if usage != test.expected {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched usage - got %v, "+
|
|
||||||
"want %v", i, test.name, usage, test.expected)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestFieldUsage tests the internal fieldUsage function ensure it returns the
|
|
||||||
// expected text.
|
|
||||||
func TestFieldUsage(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
field reflect.StructField
|
|
||||||
defValue *reflect.Value
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "jsonrpcusage tag override",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Test int `jsonrpcusage:"testvalue"`
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: "testvalue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "generic interface",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Test interface{}
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `test`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "string without default value",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Test string
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `"test"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "string with default value",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Test string
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: func() *reflect.Value {
|
|
||||||
value := "default"
|
|
||||||
rv := reflect.ValueOf(&value)
|
|
||||||
return &rv
|
|
||||||
}(),
|
|
||||||
expected: `test="default"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of strings",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Test []string
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `["test",...]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of strings with plural field name 1",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Keys []string
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `["key",...]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of strings with plural field name 2",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Addresses []string
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `["address",...]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of strings with plural field name 3",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Capabilities []string
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `["capability",...]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of structs",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s2 struct {
|
|
||||||
Txid string
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Capabilities []s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `[{"txid":"value"},...]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of ints",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s struct {
|
|
||||||
Test []int
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `[test,...]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "sub struct with jsonrpcusage tag override",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s2 struct {
|
|
||||||
Test string `jsonrpcusage:"testusage"`
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Test s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `{testusage}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "sub struct with string",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s2 struct {
|
|
||||||
Txid string
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Test s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `{"txid":"value"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "sub struct with int",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s2 struct {
|
|
||||||
Vout int
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Test s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `{"vout":n}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "sub struct with float",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s2 struct {
|
|
||||||
Amount float64
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Test s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `{"amount":n.nnn}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "sub struct with sub struct",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s3 struct {
|
|
||||||
Amount float64
|
|
||||||
}
|
|
||||||
type s2 struct {
|
|
||||||
Template s3
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Test s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `{"template":{"amount":n.nnn}}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "sub struct with slice",
|
|
||||||
field: func() reflect.StructField {
|
|
||||||
type s2 struct {
|
|
||||||
Capabilities []string
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Test s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil)).Elem().Field(0)
|
|
||||||
}(),
|
|
||||||
defValue: nil,
|
|
||||||
expected: `{"capabilities":["capability",...]}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
// Ensure usage matches the expected value.
|
|
||||||
usage := btcjson.TstFieldUsage(test.field, test.defValue)
|
|
||||||
if usage != test.expected {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched usage - got %v, "+
|
|
||||||
"want %v", i, test.name, usage, test.expected)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,593 +0,0 @@
|
||||||
// Copyright (c) 2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestAssignField tests the assignField function handles supported combinations
|
|
||||||
// properly.
|
|
||||||
func TestAssignField(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
dest interface{}
|
|
||||||
src interface{}
|
|
||||||
expected interface{}
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "same types",
|
|
||||||
dest: int8(0),
|
|
||||||
src: int8(100),
|
|
||||||
expected: int8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "same types - more source pointers",
|
|
||||||
dest: int8(0),
|
|
||||||
src: func() interface{} {
|
|
||||||
i := int8(100)
|
|
||||||
return &i
|
|
||||||
}(),
|
|
||||||
expected: int8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "same types - more dest pointers",
|
|
||||||
dest: func() interface{} {
|
|
||||||
i := int8(0)
|
|
||||||
return &i
|
|
||||||
}(),
|
|
||||||
src: int8(100),
|
|
||||||
expected: int8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - more source pointers",
|
|
||||||
dest: int16(0),
|
|
||||||
src: func() interface{} {
|
|
||||||
i := int8(100)
|
|
||||||
return &i
|
|
||||||
}(),
|
|
||||||
expected: int16(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - both pointers",
|
|
||||||
dest: func() interface{} {
|
|
||||||
i := int8(0)
|
|
||||||
return &i
|
|
||||||
}(),
|
|
||||||
src: func() interface{} {
|
|
||||||
i := int16(100)
|
|
||||||
return &i
|
|
||||||
}(),
|
|
||||||
expected: int8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - int16 -> int8",
|
|
||||||
dest: int8(0),
|
|
||||||
src: int16(100),
|
|
||||||
expected: int8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - int16 -> uint8",
|
|
||||||
dest: uint8(0),
|
|
||||||
src: int16(100),
|
|
||||||
expected: uint8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - uint16 -> int8",
|
|
||||||
dest: int8(0),
|
|
||||||
src: uint16(100),
|
|
||||||
expected: int8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - uint16 -> uint8",
|
|
||||||
dest: uint8(0),
|
|
||||||
src: uint16(100),
|
|
||||||
expected: uint8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - float32 -> float64",
|
|
||||||
dest: float64(0),
|
|
||||||
src: float32(1.5),
|
|
||||||
expected: float64(1.5),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - float64 -> float32",
|
|
||||||
dest: float32(0),
|
|
||||||
src: float64(1.5),
|
|
||||||
expected: float32(1.5),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> bool",
|
|
||||||
dest: false,
|
|
||||||
src: "true",
|
|
||||||
expected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> int8",
|
|
||||||
dest: int8(0),
|
|
||||||
src: "100",
|
|
||||||
expected: int8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> uint8",
|
|
||||||
dest: uint8(0),
|
|
||||||
src: "100",
|
|
||||||
expected: uint8(100),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> float32",
|
|
||||||
dest: float32(0),
|
|
||||||
src: "1.5",
|
|
||||||
expected: float32(1.5),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - typecase string -> string",
|
|
||||||
dest: "",
|
|
||||||
src: func() interface{} {
|
|
||||||
type foo string
|
|
||||||
return foo("foo")
|
|
||||||
}(),
|
|
||||||
expected: "foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> array",
|
|
||||||
dest: [2]string{},
|
|
||||||
src: `["test","test2"]`,
|
|
||||||
expected: [2]string{"test", "test2"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> slice",
|
|
||||||
dest: []string{},
|
|
||||||
src: `["test","test2"]`,
|
|
||||||
expected: []string{"test", "test2"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> struct",
|
|
||||||
dest: struct{ A int }{},
|
|
||||||
src: `{"A":100}`,
|
|
||||||
expected: struct{ A int }{100},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "convertible types - string -> map",
|
|
||||||
dest: map[string]float64{},
|
|
||||||
src: `{"1Address":1.5}`,
|
|
||||||
expected: map[string]float64{"1Address": 1.5},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: `null optional field - "null" -> *int32`,
|
|
||||||
dest: btcjson.Int32(0),
|
|
||||||
src: "null",
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: `null optional field - "null" -> *string`,
|
|
||||||
dest: btcjson.String(""),
|
|
||||||
src: "null",
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
dst := reflect.New(reflect.TypeOf(test.dest)).Elem()
|
|
||||||
src := reflect.ValueOf(test.src)
|
|
||||||
err := btcjson.TstAssignField(1, "testField", dst, src)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected error: %v", i,
|
|
||||||
test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check case where null string is used on optional field
|
|
||||||
if dst.Kind() == reflect.Ptr && test.src == "null" {
|
|
||||||
if !dst.IsNil() {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected value - got %v, "+
|
|
||||||
"want nil", i, test.name, dst.Interface())
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inidirect through to the base types to ensure their values
|
|
||||||
// are the same.
|
|
||||||
for dst.Kind() == reflect.Ptr {
|
|
||||||
dst = dst.Elem()
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(dst.Interface(), test.expected) {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected value - got %v, "+
|
|
||||||
"want %v", i, test.name, dst.Interface(),
|
|
||||||
test.expected)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestAssignFieldErrors tests the assignField function error paths.
|
|
||||||
func TestAssignFieldErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
dest interface{}
|
|
||||||
src interface{}
|
|
||||||
err btcjson.Error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "general incompatible int -> string",
|
|
||||||
dest: "\x00",
|
|
||||||
src: int(0),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow source int -> dest int",
|
|
||||||
dest: int8(0),
|
|
||||||
src: int(128),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow source int -> dest uint",
|
|
||||||
dest: uint8(0),
|
|
||||||
src: int(256),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int -> float",
|
|
||||||
dest: float32(0),
|
|
||||||
src: int(256),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow source uint64 -> dest int64",
|
|
||||||
dest: int64(0),
|
|
||||||
src: uint64(1 << 63),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow source uint -> dest int",
|
|
||||||
dest: int8(0),
|
|
||||||
src: uint(128),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow source uint -> dest uint",
|
|
||||||
dest: uint8(0),
|
|
||||||
src: uint(256),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint -> float",
|
|
||||||
dest: float32(0),
|
|
||||||
src: uint(256),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "float -> int",
|
|
||||||
dest: int(0),
|
|
||||||
src: float32(1.0),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow float64 -> float32",
|
|
||||||
dest: float32(0),
|
|
||||||
src: float64(math.MaxFloat64),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> bool",
|
|
||||||
dest: true,
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> int",
|
|
||||||
dest: int8(0),
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow string -> int",
|
|
||||||
dest: int8(0),
|
|
||||||
src: "128",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> uint",
|
|
||||||
dest: uint8(0),
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow string -> uint",
|
|
||||||
dest: uint8(0),
|
|
||||||
src: "256",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> float",
|
|
||||||
dest: float32(0),
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow string -> float",
|
|
||||||
dest: float32(0),
|
|
||||||
src: "1.7976931348623157e+308",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> array",
|
|
||||||
dest: [3]int{},
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> slice",
|
|
||||||
dest: []int{},
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> struct",
|
|
||||||
dest: struct{ A int }{},
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid string -> map",
|
|
||||||
dest: map[string]int{},
|
|
||||||
src: "foo",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
dst := reflect.New(reflect.TypeOf(test.dest)).Elem()
|
|
||||||
src := reflect.ValueOf(test.src)
|
|
||||||
err := btcjson.TstAssignField(1, "testField", dst, src)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+
|
|
||||||
"want %T", i, test.name, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code - got "+
|
|
||||||
"%v (%v), want %v", i, test.name, gotErrorCode,
|
|
||||||
err, test.err.ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestNewCmdErrors ensures the error paths of NewCmd behave as expected.
|
|
||||||
func TestNewCmdErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
method string
|
|
||||||
args []interface{}
|
|
||||||
err btcjson.Error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "unregistered command",
|
|
||||||
method: "boguscommand",
|
|
||||||
args: []interface{}{},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "too few parameters to command with required + optional",
|
|
||||||
method: "getblock",
|
|
||||||
args: []interface{}{},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrNumParams},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "too many parameters to command with no optional",
|
|
||||||
method: "getblockcount",
|
|
||||||
args: []interface{}{"123"},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrNumParams},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "incorrect parameter type",
|
|
||||||
method: "getblock",
|
|
||||||
args: []interface{}{1},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
_, err := btcjson.NewCmd(test.method, test.args...)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
|
|
||||||
"want %T", i, test.name, err, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code - got "+
|
|
||||||
"%v (%v), want %v", i, test.name, gotErrorCode,
|
|
||||||
err, test.err.ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestMarshalCmd tests the MarshalCmd function.
|
|
||||||
func TestMarshalCmd(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
id interface{}
|
|
||||||
cmd interface{}
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "include all parameters",
|
|
||||||
id: 1,
|
|
||||||
cmd: btcjson.NewGetNetworkHashPSCmd(btcjson.Int(100), btcjson.Int(2000)),
|
|
||||||
expected: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[100,2000],"id":1}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "include padding null parameter",
|
|
||||||
id: 1,
|
|
||||||
cmd: btcjson.NewGetNetworkHashPSCmd(nil, btcjson.Int(2000)),
|
|
||||||
expected: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[null,2000],"id":1}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "omit single unnecessary null parameter",
|
|
||||||
id: 1,
|
|
||||||
cmd: btcjson.NewGetNetworkHashPSCmd(btcjson.Int(100), nil),
|
|
||||||
expected: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[100],"id":1}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "omit unnecessary null parameters",
|
|
||||||
id: 1,
|
|
||||||
cmd: btcjson.NewGetNetworkHashPSCmd(nil, nil),
|
|
||||||
expected: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[],"id":1}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
bytes, err := btcjson.MarshalCmd(btcjson.RpcVersion1, test.id, test.cmd)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%v)",
|
|
||||||
i, test.name, err, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
marshalled := string(bytes)
|
|
||||||
if marshalled != test.expected {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched marshall result - got "+
|
|
||||||
"%v, want %v", i, test.name, marshalled, test.expected)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestMarshalCmdErrors tests the error paths of the MarshalCmd function.
|
|
||||||
func TestMarshalCmdErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
id interface{}
|
|
||||||
cmd interface{}
|
|
||||||
err btcjson.Error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "unregistered type",
|
|
||||||
id: 1,
|
|
||||||
cmd: (*int)(nil),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nil instance of registered type",
|
|
||||||
id: 1,
|
|
||||||
cmd: (*btcjson.GetBlockCmd)(nil),
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nil instance of registered type",
|
|
||||||
id: []int{0, 1},
|
|
||||||
cmd: &btcjson.GetBlockCountCmd{},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
_, err := btcjson.MarshalCmd(btcjson.RpcVersion1, test.id, test.cmd)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
|
|
||||||
"want %T", i, test.name, err, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code - got "+
|
|
||||||
"%v (%v), want %v", i, test.name, gotErrorCode,
|
|
||||||
err, test.err.ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestUnmarshalCmdErrors tests the error paths of the UnmarshalCmd function.
|
|
||||||
func TestUnmarshalCmdErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
request btcjson.Request
|
|
||||||
err btcjson.Error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "unregistered type",
|
|
||||||
request: btcjson.Request{
|
|
||||||
Jsonrpc: btcjson.RpcVersion1,
|
|
||||||
Method: "bogusmethod",
|
|
||||||
Params: nil,
|
|
||||||
ID: nil,
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "incorrect number of params",
|
|
||||||
request: btcjson.Request{
|
|
||||||
Jsonrpc: btcjson.RpcVersion1,
|
|
||||||
Method: "getblockcount",
|
|
||||||
Params: []json.RawMessage{[]byte(`"bogusparam"`)},
|
|
||||||
ID: nil,
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrNumParams},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid type for a parameter",
|
|
||||||
request: btcjson.Request{
|
|
||||||
Jsonrpc: btcjson.RpcVersion1,
|
|
||||||
Method: "getblock",
|
|
||||||
Params: []json.RawMessage{[]byte("1")},
|
|
||||||
ID: nil,
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid JSON for a parameter",
|
|
||||||
request: btcjson.Request{
|
|
||||||
Jsonrpc: btcjson.RpcVersion1,
|
|
||||||
Method: "getblock",
|
|
||||||
Params: []json.RawMessage{[]byte(`"1`)},
|
|
||||||
ID: nil,
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
_, err := btcjson.UnmarshalCmd(&test.request)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
|
|
||||||
"want %T", i, test.name, err, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code - got "+
|
|
||||||
"%v (%v), want %v", i, test.name, gotErrorCode,
|
|
||||||
err, test.err.ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
// Copyright (c) 2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestErrorCodeStringer tests the stringized output for the ErrorCode type.
|
|
||||||
func TestErrorCodeStringer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
in btcjson.ErrorCode
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{btcjson.ErrDuplicateMethod, "ErrDuplicateMethod"},
|
|
||||||
{btcjson.ErrInvalidUsageFlags, "ErrInvalidUsageFlags"},
|
|
||||||
{btcjson.ErrInvalidType, "ErrInvalidType"},
|
|
||||||
{btcjson.ErrEmbeddedType, "ErrEmbeddedType"},
|
|
||||||
{btcjson.ErrUnexportedField, "ErrUnexportedField"},
|
|
||||||
{btcjson.ErrUnsupportedFieldType, "ErrUnsupportedFieldType"},
|
|
||||||
{btcjson.ErrNonOptionalField, "ErrNonOptionalField"},
|
|
||||||
{btcjson.ErrNonOptionalDefault, "ErrNonOptionalDefault"},
|
|
||||||
{btcjson.ErrMismatchedDefault, "ErrMismatchedDefault"},
|
|
||||||
{btcjson.ErrUnregisteredMethod, "ErrUnregisteredMethod"},
|
|
||||||
{btcjson.ErrNumParams, "ErrNumParams"},
|
|
||||||
{btcjson.ErrMissingDescription, "ErrMissingDescription"},
|
|
||||||
{0xffff, "Unknown ErrorCode (65535)"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect additional error codes that don't have the stringer added.
|
|
||||||
if len(tests)-1 != int(btcjson.TstNumErrorCodes) {
|
|
||||||
t.Errorf("It appears an error code was added without adding an " +
|
|
||||||
"associated stringer test")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
result := test.in.String()
|
|
||||||
if result != test.want {
|
|
||||||
t.Errorf("String #%d\n got: %s want: %s", i, result,
|
|
||||||
test.want)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestError tests the error output for the Error type.
|
|
||||||
func TestError(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
in btcjson.Error
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
btcjson.Error{Description: "some error"},
|
|
||||||
"some error",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
btcjson.Error{Description: "human-readable error"},
|
|
||||||
"human-readable error",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
result := test.in.Error()
|
|
||||||
if result != test.want {
|
|
||||||
t.Errorf("Error #%d\n got: %s want: %s", i, result,
|
|
||||||
test.want)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This example demonstrates how to create and marshal a command into a JSON-RPC
|
// This example demonstrates how to create and marshal a command into a JSON-RPC
|
||||||
|
|
|
@ -1,737 +0,0 @@
|
||||||
// Copyright (c) 2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestHelpReflectInternals ensures the various help functions which deal with
|
|
||||||
// reflect types work as expected for various Go types.
|
|
||||||
func TestHelpReflectInternals(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
reflectType reflect.Type
|
|
||||||
indentLevel int
|
|
||||||
key string
|
|
||||||
examples []string
|
|
||||||
isComplex bool
|
|
||||||
help string
|
|
||||||
isInvalid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "int",
|
|
||||||
reflectType: reflect.TypeOf(int(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "*int",
|
|
||||||
reflectType: reflect.TypeOf((*int)(nil)),
|
|
||||||
key: "json-type-value",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-value) fdk",
|
|
||||||
isInvalid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int8",
|
|
||||||
reflectType: reflect.TypeOf(int8(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int16",
|
|
||||||
reflectType: reflect.TypeOf(int16(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int32",
|
|
||||||
reflectType: reflect.TypeOf(int32(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int64",
|
|
||||||
reflectType: reflect.TypeOf(int64(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint",
|
|
||||||
reflectType: reflect.TypeOf(uint(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint8",
|
|
||||||
reflectType: reflect.TypeOf(uint8(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint16",
|
|
||||||
reflectType: reflect.TypeOf(uint16(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint32",
|
|
||||||
reflectType: reflect.TypeOf(uint32(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint64",
|
|
||||||
reflectType: reflect.TypeOf(uint64(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n"},
|
|
||||||
help: "n (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "float32",
|
|
||||||
reflectType: reflect.TypeOf(float32(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n.nnn"},
|
|
||||||
help: "n.nnn (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "float64",
|
|
||||||
reflectType: reflect.TypeOf(float64(0)),
|
|
||||||
key: "json-type-numeric",
|
|
||||||
examples: []string{"n.nnn"},
|
|
||||||
help: "n.nnn (json-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "string",
|
|
||||||
reflectType: reflect.TypeOf(""),
|
|
||||||
key: "json-type-string",
|
|
||||||
examples: []string{`"json-example-string"`},
|
|
||||||
help: "\"json-example-string\" (json-type-string) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "bool",
|
|
||||||
reflectType: reflect.TypeOf(true),
|
|
||||||
key: "json-type-bool",
|
|
||||||
examples: []string{"json-example-bool"},
|
|
||||||
help: "json-example-bool (json-type-bool) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of int",
|
|
||||||
reflectType: reflect.TypeOf([1]int{0}),
|
|
||||||
key: "json-type-arrayjson-type-numeric",
|
|
||||||
examples: []string{"[n,...]"},
|
|
||||||
help: "[n,...] (json-type-arrayjson-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "slice of int",
|
|
||||||
reflectType: reflect.TypeOf([]int{0}),
|
|
||||||
key: "json-type-arrayjson-type-numeric",
|
|
||||||
examples: []string{"[n,...]"},
|
|
||||||
help: "[n,...] (json-type-arrayjson-type-numeric) fdk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct",
|
|
||||||
reflectType: reflect.TypeOf(struct{}{}),
|
|
||||||
key: "json-type-object",
|
|
||||||
examples: []string{"{", "}\t\t"},
|
|
||||||
isComplex: true,
|
|
||||||
help: "{\n} ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct indent level 1",
|
|
||||||
reflectType: reflect.TypeOf(struct{ field int }{}),
|
|
||||||
indentLevel: 1,
|
|
||||||
key: "json-type-object",
|
|
||||||
examples: []string{
|
|
||||||
" \"field\": n,\t(json-type-numeric)\t-field",
|
|
||||||
" },\t\t",
|
|
||||||
},
|
|
||||||
help: "{\n" +
|
|
||||||
" \"field\": n, (json-type-numeric) -field\n" +
|
|
||||||
"} ",
|
|
||||||
isComplex: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of struct indent level 0",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
field int
|
|
||||||
}
|
|
||||||
return reflect.TypeOf([]s{})
|
|
||||||
}(),
|
|
||||||
key: "json-type-arrayjson-type-object",
|
|
||||||
examples: []string{
|
|
||||||
"[{",
|
|
||||||
" \"field\": n,\t(json-type-numeric)\ts-field",
|
|
||||||
"},...]",
|
|
||||||
},
|
|
||||||
help: "[{\n" +
|
|
||||||
" \"field\": n, (json-type-numeric) s-field\n" +
|
|
||||||
"},...]",
|
|
||||||
isComplex: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "array of struct indent level 1",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
field int
|
|
||||||
}
|
|
||||||
return reflect.TypeOf([]s{})
|
|
||||||
}(),
|
|
||||||
indentLevel: 1,
|
|
||||||
key: "json-type-arrayjson-type-object",
|
|
||||||
examples: []string{
|
|
||||||
" \"field\": n,\t(json-type-numeric)\ts-field",
|
|
||||||
" },...],\t\t",
|
|
||||||
},
|
|
||||||
help: "[{\n" +
|
|
||||||
" \"field\": n, (json-type-numeric) s-field\n" +
|
|
||||||
"},...]",
|
|
||||||
isComplex: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "map",
|
|
||||||
reflectType: reflect.TypeOf(map[string]string{}),
|
|
||||||
key: "json-type-object",
|
|
||||||
examples: []string{"{",
|
|
||||||
" \"fdk--key\": fdk--value, (json-type-object) fdk--desc",
|
|
||||||
" ...", "}",
|
|
||||||
},
|
|
||||||
help: "{\n" +
|
|
||||||
" \"fdk--key\": fdk--value, (json-type-object) fdk--desc\n" +
|
|
||||||
" ...\n" +
|
|
||||||
"}",
|
|
||||||
isComplex: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "complex",
|
|
||||||
reflectType: reflect.TypeOf(complex64(0)),
|
|
||||||
key: "json-type-value",
|
|
||||||
examples: []string{"json-example-unknown"},
|
|
||||||
help: "json-example-unknown (json-type-value) fdk",
|
|
||||||
isInvalid: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
xT := func(key string) string {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
// Ensure the description key is the expected value.
|
|
||||||
key := btcjson.TstReflectTypeToJSONType(xT, test.reflectType)
|
|
||||||
if key != test.key {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected key - got: %v, "+
|
|
||||||
"want: %v", i, test.name, key, test.key)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the generated example is as expected.
|
|
||||||
examples, isComplex := btcjson.TstReflectTypeToJSONExample(xT,
|
|
||||||
test.reflectType, test.indentLevel, "fdk")
|
|
||||||
if isComplex != test.isComplex {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected isComplex - got: %v, "+
|
|
||||||
"want: %v", i, test.name, isComplex,
|
|
||||||
test.isComplex)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(examples) != len(test.examples) {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected result length - "+
|
|
||||||
"got: %v, want: %v", i, test.name, len(examples),
|
|
||||||
len(test.examples))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for j, example := range examples {
|
|
||||||
if example != test.examples[j] {
|
|
||||||
t.Errorf("Test #%d (%s) example #%d unexpected "+
|
|
||||||
"example - got: %v, want: %v", i,
|
|
||||||
test.name, j, example, test.examples[j])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the generated result type help is as expected.
|
|
||||||
helpText := btcjson.TstResultTypeHelp(xT, test.reflectType, "fdk")
|
|
||||||
if helpText != test.help {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected result help - "+
|
|
||||||
"got: %v, want: %v", i, test.name, helpText,
|
|
||||||
test.help)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
isValid := btcjson.TstIsValidResultType(test.reflectType.Kind())
|
|
||||||
if isValid != !test.isInvalid {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected result type validity "+
|
|
||||||
"- got: %v", i, test.name, isValid)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestResultStructHelp ensures the expected help text format is returned for
|
|
||||||
// various Go struct types.
|
|
||||||
func TestResultStructHelp(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
reflectType reflect.Type
|
|
||||||
expected []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty struct",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct{}
|
|
||||||
return reflect.TypeOf(s{})
|
|
||||||
}(),
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct with primitive field",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
field int
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(s{})
|
|
||||||
}(),
|
|
||||||
expected: []string{
|
|
||||||
"\"field\": n,\t(json-type-numeric)\ts-field",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct with primitive field and json tag",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Field int `json:"f"`
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(s{})
|
|
||||||
}(),
|
|
||||||
expected: []string{
|
|
||||||
"\"f\": n,\t(json-type-numeric)\ts-f",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct with array of primitive field",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
field []int
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(s{})
|
|
||||||
}(),
|
|
||||||
expected: []string{
|
|
||||||
"\"field\": [n,...],\t(json-type-arrayjson-type-numeric)\ts-field",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct with sub-struct field",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s2 struct {
|
|
||||||
subField int
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
field s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(s{})
|
|
||||||
}(),
|
|
||||||
expected: []string{
|
|
||||||
"\"field\": {\t(json-type-object)\ts-field",
|
|
||||||
"{",
|
|
||||||
" \"subfield\": n,\t(json-type-numeric)\ts2-subfield",
|
|
||||||
"}\t\t",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct with sub-struct field pointer",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s2 struct {
|
|
||||||
subField int
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
field *s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(s{})
|
|
||||||
}(),
|
|
||||||
expected: []string{
|
|
||||||
"\"field\": {\t(json-type-object)\ts-field",
|
|
||||||
"{",
|
|
||||||
" \"subfield\": n,\t(json-type-numeric)\ts2-subfield",
|
|
||||||
"}\t\t",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "struct with array of structs field",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s2 struct {
|
|
||||||
subField int
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
field []s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(s{})
|
|
||||||
}(),
|
|
||||||
expected: []string{
|
|
||||||
"\"field\": [{\t(json-type-arrayjson-type-object)\ts-field",
|
|
||||||
"[{",
|
|
||||||
" \"subfield\": n,\t(json-type-numeric)\ts2-subfield",
|
|
||||||
"},...]",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
xT := func(key string) string {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
results := btcjson.TstResultStructHelp(xT, test.reflectType, 0)
|
|
||||||
if len(results) != len(test.expected) {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected result length - "+
|
|
||||||
"got: %v, want: %v", i, test.name, len(results),
|
|
||||||
len(test.expected))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for j, result := range results {
|
|
||||||
if result != test.expected[j] {
|
|
||||||
t.Errorf("Test #%d (%s) result #%d unexpected "+
|
|
||||||
"result - got: %v, want: %v", i,
|
|
||||||
test.name, j, result, test.expected[j])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHelpArgInternals ensures the various help functions which deal with
|
|
||||||
// arguments work as expected for various argument types.
|
|
||||||
func TestHelpArgInternals(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
method string
|
|
||||||
reflectType reflect.Type
|
|
||||||
defaults map[int]reflect.Value
|
|
||||||
help string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "command with no args",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct{}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: nil,
|
|
||||||
help: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with one required arg",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Field int
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: nil,
|
|
||||||
help: "1. field (json-type-numeric, help-required) test-field\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with one optional arg, no default",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Optional *int
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: nil,
|
|
||||||
help: "1. optional (json-type-numeric, help-optional) test-optional\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with one optional arg with default",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Optional *string
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: func() map[int]reflect.Value {
|
|
||||||
defVal := "test"
|
|
||||||
return map[int]reflect.Value{
|
|
||||||
0: reflect.ValueOf(&defVal),
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
help: "1. optional (json-type-string, help-optional, help-default=\"test\") test-optional\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with struct field",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s2 struct {
|
|
||||||
F int8
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Field s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: nil,
|
|
||||||
help: "1. field (json-type-object, help-required) test-field\n" +
|
|
||||||
"{\n" +
|
|
||||||
" \"f\": n, (json-type-numeric) s2-f\n" +
|
|
||||||
"} \n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with map field",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Field map[string]float64
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: nil,
|
|
||||||
help: "1. field (json-type-object, help-required) test-field\n" +
|
|
||||||
"{\n" +
|
|
||||||
" \"test-field--key\": test-field--value, (json-type-object) test-field--desc\n" +
|
|
||||||
" ...\n" +
|
|
||||||
"}\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with slice of primitives field",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Field []int64
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: nil,
|
|
||||||
help: "1. field (json-type-arrayjson-type-numeric, help-required) test-field\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with slice of structs field",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s2 struct {
|
|
||||||
F int64
|
|
||||||
}
|
|
||||||
type s struct {
|
|
||||||
Field []s2
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
defaults: nil,
|
|
||||||
help: "1. field (json-type-arrayjson-type-object, help-required) test-field\n" +
|
|
||||||
"[{\n" +
|
|
||||||
" \"f\": n, (json-type-numeric) s2-f\n" +
|
|
||||||
"},...]\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
xT := func(key string) string {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
help := btcjson.TstArgHelp(xT, test.reflectType, test.defaults,
|
|
||||||
test.method)
|
|
||||||
if help != test.help {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+
|
|
||||||
"want:\n%v", i, test.name, help, test.help)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestMethodHelp ensures the method help function works as expected for various
|
|
||||||
// command structs.
|
|
||||||
func TestMethodHelp(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
method string
|
|
||||||
reflectType reflect.Type
|
|
||||||
defaults map[int]reflect.Value
|
|
||||||
resultTypes []interface{}
|
|
||||||
help string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "command with no args or results",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct{}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
help: "test\n\ntest--synopsis\n\n" +
|
|
||||||
"help-arguments:\nhelp-arguments-none\n\n" +
|
|
||||||
"help-result:\nhelp-result-nothing\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with no args and one primitive result",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct{}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
resultTypes: []interface{}{(*int64)(nil)},
|
|
||||||
help: "test\n\ntest--synopsis\n\n" +
|
|
||||||
"help-arguments:\nhelp-arguments-none\n\n" +
|
|
||||||
"help-result:\nn (json-type-numeric) test--result0\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with no args and two results",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct{}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
resultTypes: []interface{}{(*int64)(nil), nil},
|
|
||||||
help: "test\n\ntest--synopsis\n\n" +
|
|
||||||
"help-arguments:\nhelp-arguments-none\n\n" +
|
|
||||||
"help-result (test--condition0):\nn (json-type-numeric) test--result0\n\n" +
|
|
||||||
"help-result (test--condition1):\nhelp-result-nothing\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with primitive arg and no results",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Field bool
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
help: "test field\n\ntest--synopsis\n\n" +
|
|
||||||
"help-arguments:\n1. field (json-type-bool, help-required) test-field\n\n" +
|
|
||||||
"help-result:\nhelp-result-nothing\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "command with primitive optional and no results",
|
|
||||||
method: "test",
|
|
||||||
reflectType: func() reflect.Type {
|
|
||||||
type s struct {
|
|
||||||
Field *bool
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*s)(nil))
|
|
||||||
}(),
|
|
||||||
help: "test (field)\n\ntest--synopsis\n\n" +
|
|
||||||
"help-arguments:\n1. field (json-type-bool, help-optional) test-field\n\n" +
|
|
||||||
"help-result:\nhelp-result-nothing\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
xT := func(key string) string {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
help := btcjson.TestMethodHelp(xT, test.reflectType,
|
|
||||||
test.defaults, test.method, test.resultTypes)
|
|
||||||
if help != test.help {
|
|
||||||
t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+
|
|
||||||
"want:\n%v", i, test.name, help, test.help)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGenerateHelpErrors ensures the GenerateHelp function returns the expected
|
|
||||||
// errors.
|
|
||||||
func TestGenerateHelpErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
method string
|
|
||||||
resultTypes []interface{}
|
|
||||||
err btcjson.Error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "unregistered command",
|
|
||||||
method: "boguscommand",
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnregisteredMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "non-pointer result type",
|
|
||||||
method: "help",
|
|
||||||
resultTypes: []interface{}{0},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid result type",
|
|
||||||
method: "help",
|
|
||||||
resultTypes: []interface{}{(*complex64)(nil)},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing description",
|
|
||||||
method: "help",
|
|
||||||
resultTypes: []interface{}{(*string)(nil), nil},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrMissingDescription},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
_, err := btcjson.GenerateHelp(test.method, nil,
|
|
||||||
test.resultTypes...)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
|
|
||||||
"want %T", i, test.name, err, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code - got "+
|
|
||||||
"%v (%v), want %v", i, test.name, gotErrorCode,
|
|
||||||
err, test.err.ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGenerateHelp performs a very basic test to ensure GenerateHelp is working
|
|
||||||
// as expected. The internal are testd much more thoroughly in other tests, so
|
|
||||||
// there is no need to add more tests here.
|
|
||||||
func TestGenerateHelp(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
descs := map[string]string{
|
|
||||||
"help--synopsis": "test",
|
|
||||||
"help-command": "test",
|
|
||||||
}
|
|
||||||
help, err := btcjson.GenerateHelp("help", descs)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GenerateHelp: unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
wantHelp := "help (\"command\")\n\n" +
|
|
||||||
"test\n\nArguments:\n1. command (string, optional) test\n\n" +
|
|
||||||
"Result:\nNothing\n"
|
|
||||||
if help != wantHelp {
|
|
||||||
t.Fatalf("GenerateHelp: unexpected help - got\n%v\nwant\n%v",
|
|
||||||
help, wantHelp)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestHelpers tests the various helper functions which create pointers to
|
// TestHelpers tests the various helper functions which create pointers to
|
||||||
|
|
|
@ -226,8 +226,12 @@ func NewResponse(rpcVersion RPCVersion, id interface{}, marshalledResult []byte,
|
||||||
// JSON-RPC client.
|
// JSON-RPC client.
|
||||||
func MarshalResponse(rpcVersion RPCVersion, id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) {
|
func MarshalResponse(rpcVersion RPCVersion, id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) {
|
||||||
if !rpcVersion.IsValid() {
|
if !rpcVersion.IsValid() {
|
||||||
str := fmt.Sprintf("rpcversion '%s' is invalid", rpcVersion)
|
if rpcVersion == "" {
|
||||||
return nil, makeError(ErrInvalidType, str)
|
rpcVersion = RpcVersion1
|
||||||
|
} else {
|
||||||
|
str := fmt.Sprintf("rpcversion '%s' is unsupported", rpcVersion)
|
||||||
|
return nil, makeError(ErrInvalidType, str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
marshalledResult, err := json.Marshal(result)
|
marshalledResult, err := json.Marshal(result)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestIsValidIDType ensures the IsValidIDType function behaves as expected.
|
// TestIsValidIDType ensures the IsValidIDType function behaves as expected.
|
||||||
|
|
|
@ -1,263 +0,0 @@
|
||||||
// Copyright (c) 2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcjson_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestUsageFlagStringer tests the stringized output for the UsageFlag type.
|
|
||||||
func TestUsageFlagStringer(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
in btcjson.UsageFlag
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{0, "0x0"},
|
|
||||||
{btcjson.UFWalletOnly, "UFWalletOnly"},
|
|
||||||
{btcjson.UFWebsocketOnly, "UFWebsocketOnly"},
|
|
||||||
{btcjson.UFNotification, "UFNotification"},
|
|
||||||
{btcjson.UFWalletOnly | btcjson.UFWebsocketOnly,
|
|
||||||
"UFWalletOnly|UFWebsocketOnly"},
|
|
||||||
{btcjson.UFWalletOnly | btcjson.UFWebsocketOnly | (1 << 31),
|
|
||||||
"UFWalletOnly|UFWebsocketOnly|0x80000000"},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect additional usage flags that don't have the stringer added.
|
|
||||||
numUsageFlags := 0
|
|
||||||
highestUsageFlagBit := btcjson.TstHighestUsageFlagBit
|
|
||||||
for highestUsageFlagBit > 1 {
|
|
||||||
numUsageFlags++
|
|
||||||
highestUsageFlagBit >>= 1
|
|
||||||
}
|
|
||||||
if len(tests)-3 != numUsageFlags {
|
|
||||||
t.Errorf("It appears a usage flag was added without adding " +
|
|
||||||
"an associated stringer test")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
result := test.in.String()
|
|
||||||
if result != test.want {
|
|
||||||
t.Errorf("String #%d\n got: %s want: %s", i, result,
|
|
||||||
test.want)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRegisterCmdErrors ensures the RegisterCmd function returns the expected
|
|
||||||
// error when provided with invalid types.
|
|
||||||
func TestRegisterCmdErrors(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
method string
|
|
||||||
cmdFunc func() interface{}
|
|
||||||
flags btcjson.UsageFlag
|
|
||||||
err btcjson.Error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "duplicate method",
|
|
||||||
method: "getblock",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
return struct{}{}
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrDuplicateMethod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid usage flags",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
flags: btcjson.TstHighestUsageFlagBit,
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidUsageFlags},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid type",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid type 2",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
return &[]string{}
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "embedded field",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ int }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrEmbeddedType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unexported field",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ a int }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnexportedField},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unsupported field type 1",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ A **int }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unsupported field type 2",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ A chan int }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unsupported field type 3",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ A complex64 }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unsupported field type 4",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ A complex128 }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unsupported field type 5",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ A func() }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unsupported field type 6",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct{ A interface{} }
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrUnsupportedFieldType},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "required after optional",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct {
|
|
||||||
A *int
|
|
||||||
B int
|
|
||||||
}
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrNonOptionalField},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "non-optional with default",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct {
|
|
||||||
A int `jsonrpcdefault:"1"`
|
|
||||||
}
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrNonOptionalDefault},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "mismatched default",
|
|
||||||
method: "registertestcmd",
|
|
||||||
cmdFunc: func() interface{} {
|
|
||||||
type test struct {
|
|
||||||
A *int `jsonrpcdefault:"1.7"`
|
|
||||||
}
|
|
||||||
return (*test)(nil)
|
|
||||||
},
|
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrMismatchedDefault},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
err := btcjson.RegisterCmd(test.method, test.cmdFunc(),
|
|
||||||
test.flags)
|
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
|
|
||||||
t.Errorf("Test #%d (%s) wrong error - got %T, "+
|
|
||||||
"want %T", i, test.name, err, test.err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gotErrorCode := err.(btcjson.Error).ErrorCode
|
|
||||||
if gotErrorCode != test.err.ErrorCode {
|
|
||||||
t.Errorf("Test #%d (%s) mismatched error code - got "+
|
|
||||||
"%v, want %v", i, test.name, gotErrorCode,
|
|
||||||
test.err.ErrorCode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestMustRegisterCmdPanic ensures the MustRegisterCmd function panics when
|
|
||||||
// used to register an invalid type.
|
|
||||||
func TestMustRegisterCmdPanic(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Setup a defer to catch the expected panic to ensure it actually
|
|
||||||
// paniced.
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err == nil {
|
|
||||||
t.Error("MustRegisterCmd did not panic as expected")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Intentionally try to register an invalid type to force a panic.
|
|
||||||
btcjson.MustRegisterCmd("panicme", 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRegisteredCmdMethods tests the RegisteredCmdMethods function ensure it
|
|
||||||
// works as expected.
|
|
||||||
func TestRegisteredCmdMethods(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Ensure the registered methods are returned.
|
|
||||||
methods := btcjson.RegisteredCmdMethods()
|
|
||||||
if len(methods) == 0 {
|
|
||||||
t.Fatal("RegisteredCmdMethods: no methods")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the returned methods are sorted.
|
|
||||||
sortedMethods := make([]string, len(methods))
|
|
||||||
copy(sortedMethods, methods)
|
|
||||||
sort.Strings(sortedMethods)
|
|
||||||
if !reflect.DeepEqual(sortedMethods, methods) {
|
|
||||||
t.Fatal("RegisteredCmdMethods: methods are not sorted")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddMultisigAddressCmd defines the addmutisigaddress JSON-RPC command.
|
// AddMultisigAddressCmd defines the addmutisigaddress JSON-RPC command.
|
||||||
|
@ -1029,7 +1029,7 @@ type WalletCreateFundedPsbtOpts struct {
|
||||||
ChangeType *ChangeType `json:"change_type,omitempty"`
|
ChangeType *ChangeType `json:"change_type,omitempty"`
|
||||||
IncludeWatching *bool `json:"includeWatching,omitempty"`
|
IncludeWatching *bool `json:"includeWatching,omitempty"`
|
||||||
LockUnspents *bool `json:"lockUnspents,omitempty"`
|
LockUnspents *bool `json:"lockUnspents,omitempty"`
|
||||||
FeeRate *int64 `json:"feeRate,omitempty"`
|
FeeRate *float64 `json:"feeRate,omitempty"`
|
||||||
SubtractFeeFromOutputs *[]int64 `json:"subtractFeeFromOutputs,omitempty"`
|
SubtractFeeFromOutputs *[]int64 `json:"subtractFeeFromOutputs,omitempty"`
|
||||||
Replaceable *bool `json:"replaceable,omitempty"`
|
Replaceable *bool `json:"replaceable,omitempty"`
|
||||||
ConfTarget *int64 `json:"conf_target,omitempty"`
|
ConfTarget *int64 `json:"conf_target,omitempty"`
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
"github.com/btcsuite/btcutil"
|
btcutil "github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWalletSvrCmds tests all of the wallet server commands marshal and
|
// TestWalletSvrCmds tests all of the wallet server commands marshal and
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/lbryio/lbcd/txscript"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateWalletResult models the result of the createwallet command.
|
// CreateWalletResult models the result of the createwallet command.
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/lbryio/lbcd/txscript"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestGetAddressInfoResult ensures that custom unmarshalling of
|
// TestGetAddressInfoResult ensures that custom unmarshalling of
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWalletSvrWsCmds tests all of the wallet server websocket-specific
|
// TestWalletSvrWsCmds tests all of the wallet server websocket-specific
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/lbryio/lbcd/btcjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWalletSvrWsNtfns tests all of the chain server websocket-specific
|
// TestWalletSvrWsNtfns tests all of the chain server websocket-specific
|
||||||
|
|
|
@ -1,85 +1,8 @@
|
||||||
chaincfg
|
chaincfg
|
||||||
========
|
========
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
|
||||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd/chaincfg)
|
|
||||||
|
|
||||||
Package chaincfg defines chain configuration parameters for the three standard
|
Package chaincfg defines chain configuration parameters for the three standard
|
||||||
Bitcoin networks and provides the ability for callers to define their own custom
|
LBRY networks and provides the ability for callers to define their own custom
|
||||||
Bitcoin networks.
|
LBRY networks.
|
||||||
|
|
||||||
Although this package was primarily written for btcd, it has intentionally been
|
|
||||||
designed so it can be used as a standalone package for any projects needing to
|
|
||||||
use parameters for the standard Bitcoin networks or for projects needing to
|
|
||||||
define their own network.
|
|
||||||
|
|
||||||
## Sample Use
|
|
||||||
|
|
||||||
```Go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
|
||||||
)
|
|
||||||
|
|
||||||
var testnet = flag.Bool("testnet", false, "operate on the testnet Bitcoin network")
|
|
||||||
|
|
||||||
// By default (without -testnet), use mainnet.
|
|
||||||
var chainParams = &chaincfg.MainNetParams
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// Modify active network parameters if operating on testnet.
|
|
||||||
if *testnet {
|
|
||||||
chainParams = &chaincfg.TestNet3Params
|
|
||||||
}
|
|
||||||
|
|
||||||
// later...
|
|
||||||
|
|
||||||
// Create and print new payment address, specific to the active network.
|
|
||||||
pubKeyHash := make([]byte, 20)
|
|
||||||
addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, chainParams)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(addr)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation and Updating
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go get -u github.com/btcsuite/btcd/chaincfg
|
|
||||||
```
|
|
||||||
|
|
||||||
## GPG Verification Key
|
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
|
||||||
has not been tampered with and is coming from the btcsuite developers. To
|
|
||||||
verify the signature perform the following:
|
|
||||||
|
|
||||||
- Download the public key from the Conformal website at
|
|
||||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
|
||||||
|
|
||||||
- Import the public key into your GPG keyring:
|
|
||||||
```bash
|
|
||||||
gpg --import GIT-GPG-KEY-conformal.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
|
||||||
placeholder for the specific tag:
|
|
||||||
```bash
|
|
||||||
git tag -v TAG_NAME
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Package chaincfg is licensed under the [copyfree](http://copyfree.org) ISC
|
|
||||||
License.
|
|
|
@ -1,9 +1,9 @@
|
||||||
chainhash
|
chainhash
|
||||||
=========
|
=========
|
||||||
|
|
||||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
[![Build Status](https://github.com/lbryio/lbcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/lbryio/lbcd/actions)
|
||||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd/chaincfg/chainhash)
|
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/lbryio/lbcd/chaincfg/chainhash)
|
||||||
=======
|
=======
|
||||||
|
|
||||||
chainhash provides a generic hash type and associated functions that allows the
|
chainhash provides a generic hash type and associated functions that allows the
|
||||||
|
@ -12,7 +12,7 @@ specific hash algorithm to be abstracted.
|
||||||
## Installation and Updating
|
## Installation and Updating
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go get -u github.com/btcsuite/btcd/chaincfg/chainhash
|
$ go get -u github.com/lbryio/lbcd/chaincfg/chainhash
|
||||||
```
|
```
|
||||||
|
|
||||||
## GPG Verification Key
|
## GPG Verification Key
|
||||||
|
|
|
@ -5,7 +5,12 @@
|
||||||
|
|
||||||
package chainhash
|
package chainhash
|
||||||
|
|
||||||
import "crypto/sha256"
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ripemd160"
|
||||||
|
)
|
||||||
|
|
||||||
// HashB calculates hash(b) and returns the resulting bytes.
|
// HashB calculates hash(b) and returns the resulting bytes.
|
||||||
func HashB(b []byte) []byte {
|
func HashB(b []byte) []byte {
|
||||||
|
@ -31,3 +36,26 @@ func DoubleHashH(b []byte) Hash {
|
||||||
first := sha256.Sum256(b)
|
first := sha256.Sum256(b)
|
||||||
return Hash(sha256.Sum256(first[:]))
|
return Hash(sha256.Sum256(first[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LbryPoWHashH calculates returns the PoW Hash.
|
||||||
|
//
|
||||||
|
// doubled := SHA256(SHA256(b))
|
||||||
|
// expanded := SHA512(doubled)
|
||||||
|
// left := RIPEMD160(expanded[0:32])
|
||||||
|
// right := RIPEMD160(expanded[32:64])
|
||||||
|
// result := SHA256(SHA256(left||right))
|
||||||
|
func LbryPoWHashH(b []byte) Hash {
|
||||||
|
doubled := DoubleHashB(b)
|
||||||
|
expanded := sha512.Sum512(doubled)
|
||||||
|
|
||||||
|
r := ripemd160.New()
|
||||||
|
r.Reset()
|
||||||
|
r.Write(expanded[:sha256.Size])
|
||||||
|
left := r.Sum(nil)
|
||||||
|
|
||||||
|
r.Reset()
|
||||||
|
r.Write(expanded[sha256.Size:])
|
||||||
|
|
||||||
|
combined := r.Sum(left)
|
||||||
|
return DoubleHashH(combined)
|
||||||
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
// "fmt"
|
// "fmt"
|
||||||
// "log"
|
// "log"
|
||||||
//
|
//
|
||||||
// "github.com/btcsuite/btcutil"
|
// btcutil "github.com/lbryio/lbcutil"
|
||||||
// "github.com/btcsuite/btcd/chaincfg"
|
// "github.com/lbryio/lbcd/chaincfg"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// var testnet = flag.Bool("testnet", false, "operate on the testnet Bitcoin network")
|
// var testnet = flag.Bool("testnet", false, "operate on the testnet Bitcoin network")
|
||||||
|
|
|
@ -7,8 +7,8 @@ package chaincfg
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/lbryio/lbcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
|
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
|
||||||
|
@ -22,33 +22,22 @@ var genesisCoinbaseTx = wire.MsgTx{
|
||||||
Index: 0xffffffff,
|
Index: 0xffffffff,
|
||||||
},
|
},
|
||||||
SignatureScript: []byte{
|
SignatureScript: []byte{
|
||||||
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, /* |.......E| */
|
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x17,
|
||||||
0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, /* |The Time| */
|
0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x74,
|
||||||
0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, /* |s 03/Jan| */
|
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||||
0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, /* |/2009 Ch| */
|
0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
|
||||||
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: 0xffffffff,
|
Sequence: 0xffffffff,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TxOut: []*wire.TxOut{
|
TxOut: []*wire.TxOut{
|
||||||
{
|
{
|
||||||
Value: 0x12a05f200,
|
Value: 0x8e1bc9bf040000, // 400000000 * COIN
|
||||||
PkScript: []byte{
|
PkScript: []byte{
|
||||||
0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */
|
0x76, 0xa9, 0x14, 0x34, 0x59, 0x91, 0xdb, 0xf5,
|
||||||
0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */
|
0x7b, 0xfb, 0x01, 0x4b, 0x87, 0x00, 0x6a, 0xcd,
|
||||||
0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */
|
0xfa, 0xfb, 0xfc, 0x5f, 0xe8, 0x29, 0x2f, 0x88,
|
||||||
0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */
|
0xac,
|
||||||
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, /* |._.| */
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -58,19 +47,28 @@ var genesisCoinbaseTx = wire.MsgTx{
|
||||||
// genesisHash is the hash of the first block in the block chain for the main
|
// genesisHash is the hash of the first block in the block chain for the main
|
||||||
// network (genesis block).
|
// network (genesis block).
|
||||||
var genesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
var genesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
|
0x63, 0xf4, 0x34, 0x6a, 0x4d, 0xb3, 0x4f, 0xdf,
|
||||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
0xce, 0x29, 0xa7, 0x0f, 0x5e, 0x8d, 0x11, 0xf0,
|
||||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
0x65, 0xf6, 0xb9, 0x16, 0x02, 0xb7, 0x03, 0x6c,
|
||||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x7f, 0x22, 0xf3, 0xa0, 0x3b, 0x28, 0x89, 0x9c,
|
||||||
})
|
})
|
||||||
|
|
||||||
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
||||||
// for the main network.
|
// for the main network.
|
||||||
var genesisMerkleRoot = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
var genesisMerkleRoot = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||||
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
|
0xcc, 0x59, 0xe5, 0x9f, 0xf9, 0x7a, 0xc0, 0x92,
|
||||||
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
|
0xb5, 0x5e, 0x42, 0x3a, 0xa5, 0x49, 0x51, 0x51,
|
||||||
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
|
0xed, 0x6f, 0xb8, 0x05, 0x70, 0xa5, 0xbb, 0x78,
|
||||||
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a,
|
0xcd, 0x5b, 0xd1, 0xc3, 0x82, 0x1c, 0x21, 0xb8,
|
||||||
|
})
|
||||||
|
|
||||||
|
// genesisClaimTrie is the hash of the first transaction in the genesis block
|
||||||
|
// for the main network.
|
||||||
|
var genesisClaimTrie = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||||
|
0x01, 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,
|
||||||
})
|
})
|
||||||
|
|
||||||
// genesisBlock defines the genesis block of the block chain which serves as the
|
// genesisBlock defines the genesis block of the block chain which serves as the
|
||||||
|
@ -79,10 +77,11 @@ var genesisBlock = wire.MsgBlock{
|
||||||
Header: wire.BlockHeader{
|
Header: wire.BlockHeader{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||||
MerkleRoot: genesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
MerkleRoot: genesisMerkleRoot, // b8211c82c3d15bcd78bba57005b86fed515149a53a425eb592c07af99fe559cc
|
||||||
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 18:15:05 +0000 UTC
|
ClaimTrie: genesisClaimTrie, // 0000000000000000000000000000000000000000000000000000000000000001
|
||||||
Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
Timestamp: time.Unix(1446058291, 0), // 28 Oct 2015 18:51:31 +0000 UTC
|
||||||
Nonce: 0x7c2bac1d, // 2083236893
|
Bits: 0x1f00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
||||||
|
Nonce: 0x00000507, // 1287
|
||||||
},
|
},
|
||||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||||
}
|
}
|
||||||
|
@ -90,10 +89,10 @@ var genesisBlock = wire.MsgBlock{
|
||||||
// regTestGenesisHash is the hash of the first block in the block chain for the
|
// regTestGenesisHash is the hash of the first block in the block chain for the
|
||||||
// regression test network (genesis block).
|
// regression test network (genesis block).
|
||||||
var regTestGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
var regTestGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||||
0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59,
|
0x56, 0x75, 0x68, 0x69, 0x76, 0x67, 0x4f, 0x50,
|
||||||
0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf,
|
0xa0, 0xa1, 0x95, 0x3d, 0x17, 0x2e, 0x9e, 0xcf,
|
||||||
0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f,
|
0x4a, 0x4a, 0x62, 0x1d, 0xc9, 0xa4, 0xc3, 0x79,
|
||||||
0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f,
|
0x5d, 0xec, 0xd4, 0x99, 0x12, 0xcf, 0x3f, 0x6e,
|
||||||
})
|
})
|
||||||
|
|
||||||
// regTestGenesisMerkleRoot is the hash of the first transaction in the genesis
|
// regTestGenesisMerkleRoot is the hash of the first transaction in the genesis
|
||||||
|
@ -107,10 +106,11 @@ var regTestGenesisBlock = wire.MsgBlock{
|
||||||
Header: wire.BlockHeader{
|
Header: wire.BlockHeader{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||||
MerkleRoot: regTestGenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
MerkleRoot: regTestGenesisMerkleRoot, // b8211c82c3d15bcd78bba57005b86fed515149a53a425eb592c07af99fe559cc
|
||||||
Timestamp: time.Unix(1296688602, 0), // 2011-02-02 23:16:42 +0000 UTC
|
ClaimTrie: genesisClaimTrie, // 0000000000000000000000000000000000000000000000000000000000000001
|
||||||
|
Timestamp: time.Unix(1446058291, 0), // 28 Oct 2015 18:51:31 +0000 UTC
|
||||||
Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000]
|
Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000]
|
||||||
Nonce: 2,
|
Nonce: 1,
|
||||||
},
|
},
|
||||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||||
}
|
}
|
||||||
|
@ -135,10 +135,11 @@ var testNet3GenesisBlock = wire.MsgBlock{
|
||||||
Header: wire.BlockHeader{
|
Header: wire.BlockHeader{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||||
MerkleRoot: testNet3GenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
MerkleRoot: testNet3GenesisMerkleRoot, // b8211c82c3d15bcd78bba57005b86fed515149a53a425eb592c07af99fe559cc
|
||||||
Timestamp: time.Unix(1296688602, 0), // 2011-02-02 23:16:42 +0000 UTC
|
ClaimTrie: genesisClaimTrie, // 0000000000000000000000000000000000000000000000000000000000000001
|
||||||
Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
Timestamp: time.Unix(1446058291, 0), // 28 Oct 2015 18:51:31 +0000 UTC
|
||||||
Nonce: 0x18aea41a, // 414098458
|
Bits: 0x1f00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
||||||
|
Nonce: 0x00000507, // 1287
|
||||||
},
|
},
|
||||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,351 +0,0 @@
|
||||||
// Copyright (c) 2014-2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package chaincfg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestGenesisBlock tests the genesis block of the main network for validity by
|
|
||||||
// checking the encoded bytes and hashes.
|
|
||||||
func TestGenesisBlock(t *testing.T) {
|
|
||||||
// Encode the genesis block to raw bytes.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := MainNetParams.GenesisBlock.Serialize(&buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TestGenesisBlock: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the encoded block matches the expected bytes.
|
|
||||||
if !bytes.Equal(buf.Bytes(), genesisBlockBytes) {
|
|
||||||
t.Fatalf("TestGenesisBlock: Genesis block does not appear valid - "+
|
|
||||||
"got %v, want %v", spew.Sdump(buf.Bytes()),
|
|
||||||
spew.Sdump(genesisBlockBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hash of the block against expected hash.
|
|
||||||
hash := MainNetParams.GenesisBlock.BlockHash()
|
|
||||||
if !MainNetParams.GenesisHash.IsEqual(&hash) {
|
|
||||||
t.Fatalf("TestGenesisBlock: Genesis block hash does not "+
|
|
||||||
"appear valid - got %v, want %v", spew.Sdump(hash),
|
|
||||||
spew.Sdump(MainNetParams.GenesisHash))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRegTestGenesisBlock tests the genesis block of the regression test
|
|
||||||
// network for validity by checking the encoded bytes and hashes.
|
|
||||||
func TestRegTestGenesisBlock(t *testing.T) {
|
|
||||||
// Encode the genesis block to raw bytes.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := RegressionNetParams.GenesisBlock.Serialize(&buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TestRegTestGenesisBlock: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the encoded block matches the expected bytes.
|
|
||||||
if !bytes.Equal(buf.Bytes(), regTestGenesisBlockBytes) {
|
|
||||||
t.Fatalf("TestRegTestGenesisBlock: Genesis block does not "+
|
|
||||||
"appear valid - got %v, want %v",
|
|
||||||
spew.Sdump(buf.Bytes()),
|
|
||||||
spew.Sdump(regTestGenesisBlockBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hash of the block against expected hash.
|
|
||||||
hash := RegressionNetParams.GenesisBlock.BlockHash()
|
|
||||||
if !RegressionNetParams.GenesisHash.IsEqual(&hash) {
|
|
||||||
t.Fatalf("TestRegTestGenesisBlock: Genesis block hash does "+
|
|
||||||
"not appear valid - got %v, want %v", spew.Sdump(hash),
|
|
||||||
spew.Sdump(RegressionNetParams.GenesisHash))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestTestNet3GenesisBlock tests the genesis block of the test network (version
|
|
||||||
// 3) for validity by checking the encoded bytes and hashes.
|
|
||||||
func TestTestNet3GenesisBlock(t *testing.T) {
|
|
||||||
// Encode the genesis block to raw bytes.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := TestNet3Params.GenesisBlock.Serialize(&buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TestTestNet3GenesisBlock: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the encoded block matches the expected bytes.
|
|
||||||
if !bytes.Equal(buf.Bytes(), testNet3GenesisBlockBytes) {
|
|
||||||
t.Fatalf("TestTestNet3GenesisBlock: Genesis block does not "+
|
|
||||||
"appear valid - got %v, want %v",
|
|
||||||
spew.Sdump(buf.Bytes()),
|
|
||||||
spew.Sdump(testNet3GenesisBlockBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hash of the block against expected hash.
|
|
||||||
hash := TestNet3Params.GenesisBlock.BlockHash()
|
|
||||||
if !TestNet3Params.GenesisHash.IsEqual(&hash) {
|
|
||||||
t.Fatalf("TestTestNet3GenesisBlock: Genesis block hash does "+
|
|
||||||
"not appear valid - got %v, want %v", spew.Sdump(hash),
|
|
||||||
spew.Sdump(TestNet3Params.GenesisHash))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSimNetGenesisBlock tests the genesis block of the simulation test network
|
|
||||||
// for validity by checking the encoded bytes and hashes.
|
|
||||||
func TestSimNetGenesisBlock(t *testing.T) {
|
|
||||||
// Encode the genesis block to raw bytes.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := SimNetParams.GenesisBlock.Serialize(&buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TestSimNetGenesisBlock: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the encoded block matches the expected bytes.
|
|
||||||
if !bytes.Equal(buf.Bytes(), simNetGenesisBlockBytes) {
|
|
||||||
t.Fatalf("TestSimNetGenesisBlock: Genesis block does not "+
|
|
||||||
"appear valid - got %v, want %v",
|
|
||||||
spew.Sdump(buf.Bytes()),
|
|
||||||
spew.Sdump(simNetGenesisBlockBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hash of the block against expected hash.
|
|
||||||
hash := SimNetParams.GenesisBlock.BlockHash()
|
|
||||||
if !SimNetParams.GenesisHash.IsEqual(&hash) {
|
|
||||||
t.Fatalf("TestSimNetGenesisBlock: Genesis block hash does "+
|
|
||||||
"not appear valid - got %v, want %v", spew.Sdump(hash),
|
|
||||||
spew.Sdump(SimNetParams.GenesisHash))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSigNetGenesisBlock tests the genesis block of the signet test network for
|
|
||||||
// validity by checking the encoded bytes and hashes.
|
|
||||||
func TestSigNetGenesisBlock(t *testing.T) {
|
|
||||||
// Encode the genesis block to raw bytes.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := SigNetParams.GenesisBlock.Serialize(&buf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TestSigNetGenesisBlock: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the encoded block matches the expected bytes.
|
|
||||||
if !bytes.Equal(buf.Bytes(), sigNetGenesisBlockBytes) {
|
|
||||||
t.Fatalf("TestSigNetGenesisBlock: Genesis block does not "+
|
|
||||||
"appear valid - got %v, want %v",
|
|
||||||
spew.Sdump(buf.Bytes()),
|
|
||||||
spew.Sdump(sigNetGenesisBlockBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hash of the block against expected hash.
|
|
||||||
hash := SigNetParams.GenesisBlock.BlockHash()
|
|
||||||
if !SigNetParams.GenesisHash.IsEqual(&hash) {
|
|
||||||
t.Fatalf("TestSigNetGenesisBlock: Genesis block hash does "+
|
|
||||||
"not appear valid - got %v, want %v", spew.Sdump(hash),
|
|
||||||
spew.Sdump(SigNetParams.GenesisHash))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// genesisBlockBytes are the wire encoded bytes for the genesis block of the
|
|
||||||
// main network as of protocol version 60002.
|
|
||||||
var genesisBlockBytes = []byte{
|
|
||||||
0x01, 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, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */
|
|
||||||
0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */
|
|
||||||
0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */
|
|
||||||
0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */
|
|
||||||
0x4b, 0x1e, 0x5e, 0x4a, 0x29, 0xab, 0x5f, 0x49, /* |K.^J)._I| */
|
|
||||||
0xff, 0xff, 0x00, 0x1d, 0x1d, 0xac, 0x2b, 0x7c, /* |......+|| */
|
|
||||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 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, 0xff, 0xff, /* |........| */
|
|
||||||
0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */
|
|
||||||
0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */
|
|
||||||
0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */
|
|
||||||
0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */
|
|
||||||
0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */
|
|
||||||
0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */
|
|
||||||
0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */
|
|
||||||
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */
|
|
||||||
0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */
|
|
||||||
0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */
|
|
||||||
0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */
|
|
||||||
0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */
|
|
||||||
0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */
|
|
||||||
0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */
|
|
||||||
0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */
|
|
||||||
0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */
|
|
||||||
0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */
|
|
||||||
0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */
|
|
||||||
0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/
|
|
||||||
0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */
|
|
||||||
}
|
|
||||||
|
|
||||||
// regTestGenesisBlockBytes are the wire encoded bytes for the genesis block of
|
|
||||||
// the regression test network as of protocol version 60002.
|
|
||||||
var regTestGenesisBlockBytes = []byte{
|
|
||||||
0x01, 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, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */
|
|
||||||
0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */
|
|
||||||
0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */
|
|
||||||
0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */
|
|
||||||
0x4b, 0x1e, 0x5e, 0x4a, 0xda, 0xe5, 0x49, 0x4d, /* |K.^J)._I| */
|
|
||||||
0xff, 0xff, 0x7f, 0x20, 0x02, 0x00, 0x00, 0x00, /* |......+|| */
|
|
||||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 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, 0xff, 0xff, /* |........| */
|
|
||||||
0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */
|
|
||||||
0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */
|
|
||||||
0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */
|
|
||||||
0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */
|
|
||||||
0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */
|
|
||||||
0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */
|
|
||||||
0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */
|
|
||||||
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */
|
|
||||||
0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */
|
|
||||||
0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */
|
|
||||||
0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */
|
|
||||||
0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */
|
|
||||||
0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */
|
|
||||||
0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */
|
|
||||||
0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */
|
|
||||||
0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */
|
|
||||||
0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */
|
|
||||||
0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */
|
|
||||||
0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/
|
|
||||||
0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */
|
|
||||||
}
|
|
||||||
|
|
||||||
// testNet3GenesisBlockBytes are the wire encoded bytes for the genesis block of
|
|
||||||
// the test network (version 3) as of protocol version 60002.
|
|
||||||
var testNet3GenesisBlockBytes = []byte{
|
|
||||||
0x01, 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, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */
|
|
||||||
0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */
|
|
||||||
0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */
|
|
||||||
0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */
|
|
||||||
0x4b, 0x1e, 0x5e, 0x4a, 0xda, 0xe5, 0x49, 0x4d, /* |K.^J)._I| */
|
|
||||||
0xff, 0xff, 0x00, 0x1d, 0x1a, 0xa4, 0xae, 0x18, /* |......+|| */
|
|
||||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 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, 0xff, 0xff, /* |........| */
|
|
||||||
0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */
|
|
||||||
0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */
|
|
||||||
0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */
|
|
||||||
0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */
|
|
||||||
0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */
|
|
||||||
0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */
|
|
||||||
0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */
|
|
||||||
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */
|
|
||||||
0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */
|
|
||||||
0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */
|
|
||||||
0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */
|
|
||||||
0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */
|
|
||||||
0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */
|
|
||||||
0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */
|
|
||||||
0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */
|
|
||||||
0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */
|
|
||||||
0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */
|
|
||||||
0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */
|
|
||||||
0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/
|
|
||||||
0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */
|
|
||||||
}
|
|
||||||
|
|
||||||
// simNetGenesisBlockBytes are the wire encoded bytes for the genesis block of
|
|
||||||
// the simulation test network as of protocol version 70002.
|
|
||||||
var simNetGenesisBlockBytes = []byte{
|
|
||||||
0x01, 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, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |....;...| */
|
|
||||||
0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |z{..z.,>| */
|
|
||||||
0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |gv.a....| */
|
|
||||||
0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |..Q2:...| */
|
|
||||||
0x4b, 0x1e, 0x5e, 0x4a, 0x45, 0x06, 0x86, 0x53, /* |K.^J)._I| */
|
|
||||||
0xff, 0xff, 0x7f, 0x20, 0x02, 0x00, 0x00, 0x00, /* |......+|| */
|
|
||||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 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, 0xff, 0xff, /* |........| */
|
|
||||||
0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |..M.....| */
|
|
||||||
0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..EThe T| */
|
|
||||||
0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |imes 03/| */
|
|
||||||
0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |Jan/2009| */
|
|
||||||
0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* | Chancel| */
|
|
||||||
0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* |lor on b| */
|
|
||||||
0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |rink of | */
|
|
||||||
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |second b| */
|
|
||||||
0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |ailout f| */
|
|
||||||
0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |or banks| */
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |........| */
|
|
||||||
0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |*....CA.| */
|
|
||||||
0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |g....UH'| */
|
|
||||||
0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |.g..q0..| */
|
|
||||||
0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |\..(.9..| */
|
|
||||||
0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |yb...a..| */
|
|
||||||
0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |I..?L.8.| */
|
|
||||||
0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |.U......| */
|
|
||||||
0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |\8M....W| */
|
|
||||||
0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |.Lp+k.._|*/
|
|
||||||
0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */
|
|
||||||
}
|
|
||||||
|
|
||||||
// sigNetGenesisBlockBytes are the wire encoded bytes for the genesis block of
|
|
||||||
// the signet test network as of protocol version 70002.
|
|
||||||
var sigNetGenesisBlockBytes = []byte{
|
|
||||||
0x01, 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, 0x00, 0x00, 0x00, 0x3b, 0xa3, 0xed, 0xfd, /* |........| */
|
|
||||||
0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, /* |....;...| */
|
|
||||||
0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, /* |z{..z.,>| */
|
|
||||||
0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, /* |gv.a....| */
|
|
||||||
0x4b, 0x1e, 0x5e, 0x4a, 0x00, 0x8f, 0x4d, 0x5f, /* |..Q2:...| */
|
|
||||||
0xae, 0x77, 0x03, 0x1e, 0x8a, 0xd2, 0x22, 0x03, /* |K.^J..M_| */
|
|
||||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, /* |.w....".| */
|
|
||||||
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, 0xff, 0xff, /* |........| */
|
|
||||||
0xff, 0xff, 0x4d, 0x04, 0xff, 0xff, 0x00, 0x1d, /* |........| */
|
|
||||||
0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, /* |..M.....| */
|
|
||||||
0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, /* |..EThe T| */
|
|
||||||
0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, /* |imes 03/| */
|
|
||||||
0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, /* |Jan/2009| */
|
|
||||||
0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, /* | Chancel| */
|
|
||||||
0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, /* |lor on b| */
|
|
||||||
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, /* |rink of| */
|
|
||||||
0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, /* |second b| */
|
|
||||||
0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, /* |ailout f| */
|
|
||||||
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05, /* |or banks| */
|
|
||||||
0x2a, 0x01, 0x00, 0x00, 0x00, 0x43, 0x41, 0x04, /* |........| */
|
|
||||||
0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, /* |*....CA.| */
|
|
||||||
0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, /* |g....UH'| */
|
|
||||||
0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, /* |.g..q0..| */
|
|
||||||
0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, /* |\..(.9..| */
|
|
||||||
0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, /* |yb...a..| */
|
|
||||||
0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, /* |I..?L.8.| */
|
|
||||||
0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, /* |.U......| */
|
|
||||||
0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, /* |\8M....W| */
|
|
||||||
0xac, 0x00, 0x00, 0x00, 0x00, /* |.....| */
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue