Compare commits
80 commits
master
...
mem_pressu
Author | SHA1 | Date | |
---|---|---|---|
|
cb7aad11c6 | ||
|
531428d306 | ||
|
ccfa7af546 | ||
|
9aa4259382 | ||
|
ade593da33 | ||
|
132b93713d | ||
|
3c0e288e0f | ||
|
1a3f34c345 | ||
|
85a7f74f83 | ||
|
e8b2910b36 | ||
|
88dbf2267c | ||
|
0377a3e7ac | ||
|
26a4ffe2e3 | ||
|
2ead2539c0 | ||
|
424235655c | ||
|
c5b4662aa8 | ||
|
353d08bb91 | ||
|
15ded4ed0f | ||
|
a553f2e9c8 | ||
|
d4073bd18d | ||
|
cb7175bd70 | ||
|
ade6adb7dc | ||
|
b23710bc33 | ||
|
82d4b6657b | ||
|
0a01170422 | ||
|
1a65a6a19d | ||
|
ab852a6e9f | ||
|
d691ab7a9e | ||
|
9a177f3a9a | ||
|
d013bb7e72 | ||
|
9615516b51 | ||
|
ceb72948ec | ||
|
7dff7f9dd8 | ||
|
ac6a7ad121 | ||
|
26e4083f38 | ||
|
9937f66b6a | ||
|
b6cf5f2665 | ||
|
d74924992a | ||
|
a1631880be | ||
|
d46bedf5ef | ||
|
0518180508 | ||
|
a0469820a2 | ||
|
f829fb6206 | ||
|
d7f97ab750 | ||
|
2e75ce6583 | ||
|
6b55968ccd | ||
|
ceba136a70 | ||
|
9e5a717c39 | ||
|
f218c04488 | ||
|
328705f579 | ||
|
3c85e6e56a | ||
|
27c81de4e5 | ||
|
ccaa6dd816 | ||
|
2dcdb458e8 | ||
|
56c21c6bd6 | ||
|
87c3243bf1 | ||
|
4e68d1fb81 | ||
|
b0f1458ff7 | ||
|
e0b451a76a | ||
|
57b3f96f3f | ||
|
c4a5ae339c | ||
|
ced137f9e2 | ||
|
fc77c6db6a | ||
|
9b1c4fbc04 | ||
|
818ad52cdf | ||
|
35eaa76e42 | ||
|
42793ad871 | ||
|
f6450deacb | ||
|
3b9d3ab05f | ||
|
c84ced2f10 | ||
|
34bdf58303 | ||
|
3cf16aad88 | ||
|
be9fc27e6f | ||
|
e62432dc95 | ||
|
0636c889f5 | ||
|
4f422e29cf | ||
|
363cc18b31 | ||
|
860529321f | ||
|
43566e6f2b | ||
|
7ae6608f48 |
419 changed files with 9311 additions and 55656 deletions
16
.github/workflows/basic-check.yml
vendored
16
.github/workflows/basic-check.yml
vendored
|
@ -2,27 +2,27 @@ name: Build and Test
|
|||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
# https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/
|
||||
permissions:
|
||||
contents: read
|
||||
name: Go CI
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: self-hosted
|
||||
strategy:
|
||||
matrix:
|
||||
go: [1.19]
|
||||
go: [1.16]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Check out source
|
||||
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
|
||||
env:
|
||||
GO111MODULE: "on"
|
||||
run: go build ./...
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
GO111MODULE: "on"
|
||||
run: |
|
||||
sh ./goclean.sh
|
||||
|
||||
|
|
52
.github/workflows/create-release.yml
vendored
Normal file
52
.github/workflows/create-release.yml
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
name: Create release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
note:
|
||||
description: 'Note'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go: [1.16]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v2
|
||||
- name: Install coreutils, ICU for macOS
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: brew install coreutils icu4c
|
||||
- name: Build executables
|
||||
env:
|
||||
GO111MODULE: "on"
|
||||
run: go build -trimpath -o artifacts/ --tags use_icu_normalization .
|
||||
- name: SHA256 sum
|
||||
run: sha256sum -b artifacts/* > artifacts/chain.sha256
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: chain-${{ matrix.os }}
|
||||
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
|
11
.github/workflows/full-sync-part-1.yml
vendored
11
.github/workflows/full-sync-part-1.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
runs-on: self-hosted
|
||||
strategy:
|
||||
matrix:
|
||||
go: [1.19]
|
||||
go: [1.16]
|
||||
steps:
|
||||
- run: |
|
||||
echo "Note ${{ github.event.inputs.note }}!"
|
||||
|
@ -24,12 +24,11 @@ jobs:
|
|||
go-version: ${{ matrix.go }}
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v2
|
||||
- name: Build lbcd
|
||||
run: go build .
|
||||
- name: Build chain
|
||||
run: go build --tags use_icu_normalization .
|
||||
- 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 --nolisten --norpc
|
||||
- name: Run chain
|
||||
run: ./chain --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}}
|
||||
|
|
15
.github/workflows/full-sync-part-2.yml
vendored
15
.github/workflows/full-sync-part-2.yml
vendored
|
@ -14,24 +14,23 @@ jobs:
|
|||
runs-on: self-hosted
|
||||
strategy:
|
||||
matrix:
|
||||
go: [1.19]
|
||||
go: [1.16]
|
||||
steps:
|
||||
- run: |
|
||||
echo "Note ${{ github.event.inputs.note }}!"
|
||||
- name: Setup Go
|
||||
- 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: Build chain
|
||||
run: go build --tags use_icu_normalization .
|
||||
- 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 --nolisten --norpc
|
||||
run: cp -r /home/lbry/chain_814k/* ${{env.TEMP_DATA_DIR}}
|
||||
- name: Run chain
|
||||
run: ./chain --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
57
.github/workflows/golangci-lint.yml
vendored
|
@ -1,57 +0,0 @@
|
|||
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.19'
|
||||
|
||||
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
|
57
.github/workflows/release.yml
vendored
57
.github/workflows/release.yml
vendored
|
@ -1,57 +0,0 @@
|
|||
name: goreleaser
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
note:
|
||||
description: 'Note'
|
||||
required: false
|
||||
default: ''
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry docker.io
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
-
|
||||
name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: lbcd-${{ github.sha }}
|
||||
path: |
|
||||
dist/checksums.txt
|
||||
dist/*.tar.gz
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -3,7 +3,6 @@
|
|||
|
||||
# Databases
|
||||
btcd.db
|
||||
lbcd.db
|
||||
*-shm
|
||||
*-wal
|
||||
|
||||
|
@ -47,9 +46,5 @@ profile.cov
|
|||
# Binaries
|
||||
btcd
|
||||
btcctl
|
||||
lbcd
|
||||
lbcctl
|
||||
|
||||
# CI artifacts
|
||||
dist
|
||||
debug
|
||||
chain
|
||||
chainctl
|
||||
|
|
152
.golangci.yml
152
.golangci.yml
|
@ -1,152 +0,0 @@
|
|||
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
|
|
@ -1,68 +0,0 @@
|
|||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod tidy
|
||||
# you may remove this if you don't need go generate
|
||||
- go generate ./...
|
||||
builds:
|
||||
-
|
||||
main: .
|
||||
id: "lbcd"
|
||||
binary: "lbcd"
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -buildid=
|
||||
- -X github.com/lbryio/lbcd/version.appTag={{ .Tag }}
|
||||
targets:
|
||||
- linux_amd64
|
||||
- linux_arm64
|
||||
- darwin_amd64
|
||||
- darwin_arm64
|
||||
- windows_amd64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
-
|
||||
main: ./cmd/lbcctl
|
||||
id: "lbcctl"
|
||||
binary: "lbcctl"
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -buildid=
|
||||
- -X github.com/lbryio/lbcd/version.appTag={{ .Tag }}
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
targets:
|
||||
- linux_amd64
|
||||
- linux_arm64
|
||||
- darwin_amd64
|
||||
- darwin_arm64
|
||||
- windows_amd64
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ .Version }}+{{ .Commit }}"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
|
||||
dockers:
|
||||
- use: buildx
|
||||
dockerfile: Dockerfile.goreleaser
|
||||
image_templates:
|
||||
- "docker.io/lbry/lbcd:{{ .Tag }}"
|
||||
- "docker.io/lbry/lbcd:latest"
|
||||
|
||||
release:
|
||||
draft: true
|
||||
prerelease: auto
|
25
Dockerfile
25
Dockerfile
|
@ -1,24 +1,25 @@
|
|||
# This Dockerfile builds lbcd from source and creates a small (55 MB) docker container based on alpine linux.
|
||||
# This Dockerfile builds btcd from source and creates a small (55 MB) docker container based on alpine linux.
|
||||
#
|
||||
# Clone this repository and run the following command to build and tag a fresh lbcd amd64 container:
|
||||
# Clone this repository and run the following command to build and tag a fresh btcd amd64 container:
|
||||
#
|
||||
# docker build . -t yourregistry/lbcd
|
||||
# docker build . -t yourregistry/btcd
|
||||
#
|
||||
# You can use the following command to buid an arm64v8 container:
|
||||
#
|
||||
# docker build . -t yourregistry/lbcd --build-arg ARCH=arm64v8
|
||||
# docker build . -t yourregistry/btcd --build-arg ARCH=arm64v8
|
||||
#
|
||||
# For more information how to use this docker image visit:
|
||||
# https://github.com/lbryio/lbcd/tree/master/docs
|
||||
# https://github.com/btcsuite/btcd/tree/master/docs
|
||||
#
|
||||
# 9246 Mainnet LBRY peer-to-peer port
|
||||
# 9245 Mainet RPC port
|
||||
# 8333 Mainnet Bitcoin peer-to-peer port
|
||||
# 8334 Mainet RPC port
|
||||
|
||||
ARG ARCH=amd64
|
||||
|
||||
FROM golang:1.19 AS build-container
|
||||
FROM golang:1.14-alpine3.12 AS build-container
|
||||
|
||||
ARG ARCH
|
||||
ENV GO111MODULE=on
|
||||
|
||||
ADD . /app
|
||||
WORKDIR /app
|
||||
|
@ -29,12 +30,12 @@ RUN set -ex \
|
|||
&& echo "Compiling for $GOARCH" \
|
||||
&& go install -v . ./cmd/...
|
||||
|
||||
FROM $ARCH/debian:bullseye-20220418-slim
|
||||
FROM $ARCH/alpine:3.12
|
||||
|
||||
COPY --from=build-container /go/bin /bin
|
||||
|
||||
VOLUME ["/root/.lbcd"]
|
||||
VOLUME ["/root/.btcd"]
|
||||
|
||||
EXPOSE 9245 9246
|
||||
EXPOSE 8333 8334
|
||||
|
||||
ENTRYPOINT ["lbcd"]
|
||||
ENTRYPOINT ["chain"]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
FROM debian:bullseye-20220418-slim
|
||||
|
||||
COPY lbcd lbcctl /bin/
|
||||
|
||||
VOLUME ["/root/.lbcd"]
|
||||
|
||||
EXPOSE 9245 9246
|
||||
|
||||
ENTRYPOINT ["lbcd"]
|
1
LICENSE
1
LICENSE
|
@ -1,6 +1,5 @@
|
|||
ISC License
|
||||
|
||||
Copyright (c) 2021 The LBRY developers
|
||||
Copyright (c) 2013-2017 The btcsuite developers
|
||||
Copyright (c) 2015-2016 The Decred developers
|
||||
|
||||
|
|
364
README.md
364
README.md
|
@ -1,335 +1,121 @@
|
|||
# lbcd
|
||||
btcd
|
||||
====
|
||||
|
||||
[![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/lbryio/lbcd/badge.svg?branch=master)](https://coveralls.io/github/lbryio/lbcd?branch=master)
|
||||
[![Build Status](https://github.com/btcsuite/btcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/btcsuite/btcd/actions)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/btcsuite/btcd/badge.svg?branch=master)](https://coveralls.io/github/btcsuite/btcd?branch=master)
|
||||
[![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/lbryio/lbcd)-->
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd)
|
||||
|
||||
**lbcd** is a full node implementation of LBRY's blockchain written in Go (golang).
|
||||
btcd is an alternative full node bitcoin implementation written in Go (golang).
|
||||
|
||||
Software stack developed by LBRY teams has been all migrated to **lbcd**.
|
||||
This project is currently under active development and is in a Beta state. It
|
||||
is extremely stable and has been in production use since October 2013.
|
||||
|
||||
We're working with exchanges and pool oerators to migrate from **lbrycrd** to **lbcd**.
|
||||
It properly downloads, validates, and serves the block chain using the exact
|
||||
rules (including consensus bugs) for block acceptance as Bitcoin Core. We have
|
||||
taken great care to avoid btcd causing a fork to the block chain. It includes a
|
||||
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.
|
||||
|
||||
If you're integrating with **lbcd+lbcwallet**, please check the Wiki for current [supported RPCs](wiki/RPC-availability).
|
||||
It also properly relays newly mined blocks, maintains a transaction pool, and
|
||||
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).
|
||||
|
||||
Note: **lbcd** does *NOT* include wallet functionality. That functionality is provided by the
|
||||
[lbcwallet](https://github.com/lbryio/lbcwallet) and the [LBRY SDK](https://github.com/lbryio/lbry-sdk).
|
||||
One key difference between btcd and Bitcoin Core is that btcd does *NOT* include
|
||||
wallet functionality and this was a very intentional design decision. See the
|
||||
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
|
||||
directly with btcd. That functionality is provided by the
|
||||
[btcwallet](https://github.com/btcsuite/btcwallet) and
|
||||
[Paymetheus](https://github.com/btcsuite/Paymetheus) (Windows-only) projects
|
||||
which are both under active development.
|
||||
|
||||
## Requirements
|
||||
|
||||
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.
|
||||
[Go](http://golang.org) 1.14 or newer.
|
||||
|
||||
## Installation
|
||||
|
||||
Acquire binary files from [releases](https://github.com/lbryio/lbcd/releases)
|
||||
https://github.com/btcsuite/btcd/releases
|
||||
|
||||
For compilation, [Go](http://golang.org) 1.19 or newer is required.
|
||||
Install Go according to its [installation instructions](http://golang.org/doc/install).
|
||||
#### Linux/BSD/MacOSX/POSIX - Build from Source
|
||||
|
||||
``` sh
|
||||
# lbcd (full node)
|
||||
$ go install github.com/lbryio/lbcd@latest
|
||||
- Install Go according to the installation instructions here:
|
||||
http://golang.org/doc/install
|
||||
|
||||
# lbcctl (rpc client utility)
|
||||
$ go install github.com/lbryio/lbcd/cmd/lbcctl@latest
|
||||
- Ensure Go was installed properly and is a supported version:
|
||||
|
||||
```bash
|
||||
$ go version
|
||||
$ go env GOROOT GOPATH
|
||||
```
|
||||
|
||||
## Usage
|
||||
NOTE: The `GOROOT` and `GOPATH` above must not be the same path. It is
|
||||
recommended that `GOPATH` is set to a directory in your home directory such as
|
||||
`~/goprojects` to avoid write permission issues. It is also recommended to add
|
||||
`$GOPATH/bin` to your `PATH` at this point.
|
||||
|
||||
Default application folder `${LBCDDIR}`:
|
||||
- Run the following commands to obtain btcd, all dependencies, and install it:
|
||||
|
||||
- Linux: `~/.lbcd/`
|
||||
- MacOS: `/Users/<username>/Library/Application Support/Lbcd/`
|
||||
|
||||
### Start the **lbcd**
|
||||
|
||||
``` sh
|
||||
./lbcd
|
||||
```bash
|
||||
$ cd $GOPATH/src/github.com/btcsuite/btcd
|
||||
$ GO111MODULE=on go install -v . ./cmd/...
|
||||
```
|
||||
|
||||
**lbcd** loads config file at `"${LBCDDIR}/lbcd.conf"`.
|
||||
- btcd (and utilities) will now be installed in ```$GOPATH/bin```. If you did
|
||||
not already add the bin directory to your system path during Go installation,
|
||||
we recommend you do so now.
|
||||
|
||||
If no config is found, it creates a [default one](sample-lbcd.conf), which includes all available options with default settings except randomly generated *RPC credentials* (see below).
|
||||
## Updating
|
||||
|
||||
### RPC server
|
||||
#### Linux/BSD/MacOSX/POSIX - Build from Source
|
||||
|
||||
RPC credentials (`rpcuser` and `rpcpass`) is required to enable RPC server. It can be specify in the `"${LBCDDIR}/lbcd.conf"`, using command line options:
|
||||
- Run the following commands to update btcd, all dependencies, and install it:
|
||||
|
||||
``` sh
|
||||
./lbcd --rpcuser=rpcuser --rpcpass=rpcpass
|
||||
|
||||
2022-07-28 12:28:19.627 [INF] RPCS: RPC server listening on 0.0.0.0:9245
|
||||
2022-07-28 12:28:19.627 [INF] RPCS: RPC server listening on [::]:9245
|
||||
```bash
|
||||
$ cd $GOPATH/src/github.com/btcsuite/btcd
|
||||
$ git pull
|
||||
$ GO111MODULE=on go install -v . ./cmd/...
|
||||
```
|
||||
|
||||
### Working with TLS (Default)
|
||||
## Getting Started
|
||||
|
||||
By default, **lbcd** runs RPC server with TLS enabled, and generates the `rpc.cert` and `rpc.key` under `${LBCDDIR}`, if not exist already.
|
||||
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.
|
||||
|
||||
To interact with the RPC server, a client has to either specify the `rpc.cert`, or disable the certification verification for TLS.
|
||||
#### Linux/BSD/POSIX/Source
|
||||
|
||||
Interact with **lbcd** RPC using `lbcctl`
|
||||
|
||||
``` sh
|
||||
$ ./lbcctl --rpccert "${LBCDDIR}/rpc.cert" getblockcount
|
||||
|
||||
# or disable the certificate verification
|
||||
$ ./lbcctl --skipverify getblockcount
|
||||
|
||||
1200062
|
||||
```bash
|
||||
$ ./btcd
|
||||
```
|
||||
|
||||
Interact with **lbcd** RPC using `curl`
|
||||
## IRC
|
||||
|
||||
``` sh
|
||||
$ curl --user rpcuser:rpcpass \
|
||||
--cacert "${LBCDDIR}/rpc.cert" \
|
||||
--data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockcount", "params": []}' \
|
||||
-H 'content-type: text/plain;' \
|
||||
https://127.0.0.1:9245/
|
||||
- irc.freenode.net
|
||||
- channel #btcd
|
||||
- [webchat](https://webchat.freenode.net/?channels=btcd)
|
||||
|
||||
# or disable the certificate verification
|
||||
$ curl --user rpcuser:rpcpass \
|
||||
--insecure \
|
||||
--data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockcount", "params": []}' \
|
||||
-H 'content-type: text/plain;' \
|
||||
https://127.0.0.1:9245/
|
||||
```
|
||||
## Issue Tracker
|
||||
|
||||
``` json
|
||||
{"jsonrpc":"1.0","result":1200062,"error":null,"id":"curltest"}
|
||||
```
|
||||
The [integrated github issue tracker](https://github.com/btcsuite/btcd/issues)
|
||||
is used for this project.
|
||||
|
||||
### Working without TLS
|
||||
## Documentation
|
||||
|
||||
TLS can be disabled using the `--notls` option:
|
||||
The documentation is a work-in-progress. It is located in the [docs](https://github.com/btcsuite/btcd/tree/master/docs) folder.
|
||||
|
||||
``` sh
|
||||
$ ./lbcd --notls
|
||||
```
|
||||
## Release Verification
|
||||
|
||||
``` sh
|
||||
$ ./lbcctl --notls getblockcount
|
||||
|
||||
1200062
|
||||
```
|
||||
|
||||
``` sh
|
||||
$ curl --user rpcuser:rpcpass \
|
||||
--data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockcount", "params": []}' \
|
||||
-H 'content-type: text/plain;' \
|
||||
http://127.0.0.1:9245/
|
||||
```
|
||||
|
||||
``` json
|
||||
{"jsonrpc":"1.0","result":1200062,"error":null,"id":"curltest"}
|
||||
```
|
||||
|
||||
## Using Snapshots (optional)
|
||||
|
||||
[Snapshots](https://snapshots.lbry.com/blockchain/) are created bi-weekly to help new users catch up current block height.
|
||||
|
||||
The snapshots are archived and compressed in [zstd](https://facebook.github.io/zstd/) format for it's compression ratio and speed.
|
||||
|
||||
Download the snapshot, and uncompress it:
|
||||
|
||||
``` sh
|
||||
time curl -O https://snapshots.lbry.com/blockchain/lbcd_snapshot_1199527_v0.22.105_2022-07-27.tar.zst
|
||||
zstd -d --stdout lbcd_snapshot_1199527_v0.22.105_2022-07-27.tar.zst | tar xf - -C "${LBCDDIR}"
|
||||
```
|
||||
|
||||
If preferred, a user can download and uncompress the snapshot on the fly:
|
||||
By the time the download is finished, the snapshots should be almost uncompressed already.
|
||||
|
||||
``` sh
|
||||
mkdir -p "${LBCDDIR}"
|
||||
|
||||
time curl https://snapshots.lbry.com/blockchain/lbcd_snapshot_1199527_v0.22.105_2022-07-27.tar.zst | zstd -d --stdout | tar xf - -C "${LBCDDIR}"
|
||||
|
||||
# % Total % Received % Xferd Average Speed Time Time Time Current
|
||||
# Dload Upload Total Spent Left Speed
|
||||
# 100 64.9G 100 64.9G 0 0 37.0M 0 0:29:49 0:29:49 --:--:-- 33.0M
|
||||
#
|
||||
# real 29m49.962s
|
||||
# user 6m53.710s
|
||||
# sys 8m56.545s
|
||||
```
|
||||
|
||||
## Working with RPCs
|
||||
|
||||
Using `lbcctl -l` to list available RPCs:
|
||||
|
||||
``` sh
|
||||
$ lbcctl -l
|
||||
|
||||
Chain Server Commands:
|
||||
addnode "addr" "add|remove|onetry"
|
||||
createrawtransaction [{"txid":"value","vout":n},...] {"address":amount,...} (locktime)
|
||||
debuglevel "levelspec"
|
||||
decoderawtransaction "hextx"
|
||||
decodescript "hexscript"
|
||||
deriveaddresses "descriptor" ({"value":value})
|
||||
fundrawtransaction "hextx" {"changeaddress":changeaddress,"changeposition":changeposition,"changetype":changetype,"includewatching":includewatching,"lockunspents":lockunspents,"feerate":feerate,"subtractfeefromoutputs":[subtractfeefromoutput,...],"replaceable":replaceable,"conftarget":conftarget,"estimatemode":estimatemode} (iswitness)
|
||||
generate numblocks
|
||||
|
||||
[skipped]
|
||||
|
||||
Wallet Server Commands (--wallet):
|
||||
addmultisigaddress nrequired ["key",...] ("account")
|
||||
addwitnessaddress "address"
|
||||
backupwallet "destination"
|
||||
createmultisig nrequired ["key",...]
|
||||
createnewaccount "account"
|
||||
createwallet "walletname" (disableprivatekeys=false blank=false passphrase="" avoidreuse=false)
|
||||
dumpprivkey "address"
|
||||
dumpwallet "filename"
|
||||
encryptwallet "passphrase"
|
||||
estimatefee numblocks
|
||||
estimatepriority numblocks
|
||||
estimatesmartfee conftarget (estimatemode="CONSERVATIVE")
|
||||
getaccount "address"
|
||||
getaccountaddress "account"
|
||||
getaddressesbyaccount "account"
|
||||
|
||||
[skipped]
|
||||
```
|
||||
|
||||
Using `lbcctl help rpcname` to show the RPC spec:
|
||||
|
||||
``` sh
|
||||
$ lbcctl help getblock
|
||||
|
||||
getblock "hash" (verbosity=1)
|
||||
|
||||
Returns information about a block given its hash.
|
||||
|
||||
Arguments:
|
||||
1. hash (string, required) The hash of the block
|
||||
2. verbosity (numeric, optional, default=1) Specifies whether the block data should be returned as a hex-encoded string (0), as parsed data with a slice of TXIDs (1), or as parsed data with parsed transaction data (2)
|
||||
|
||||
Result (verbosity=0):
|
||||
"value" (string) Hex-encoded bytes of the serialized block
|
||||
|
||||
Result (verbosity=1):
|
||||
{
|
||||
"getblockverboseresultbase": { (object)
|
||||
"hash": "value", (string) The hash of the block (same as provided)
|
||||
"confirmations": n, (numeric) The number of confirmations
|
||||
"strippedsize": n, (numeric) The size of the block without witness data
|
||||
"size": n, (numeric) The size of the block
|
||||
"weight": n, (numeric) The weight of the block
|
||||
"height": n, (numeric) The height of the block in the block chain
|
||||
"version": n, (numeric) The block version
|
||||
"versionHex": "value", (string) The block version in hexadecimal
|
||||
"merkleroot": "value", (string) Root hash of the merkle tree
|
||||
"time": n, (numeric) The block time in seconds since 1 Jan 1970 GMT
|
||||
"mediantime": n, (numeric) The median block time in seconds since 1 Jan 1970 GMT
|
||||
"nonce": n, (numeric) The block nonce
|
||||
"bits": "value", (string) The bits which represent the block difficulty
|
||||
"difficulty": n.nnn, (numeric) The proof-of-work difficulty as a multiple of the minimum difficulty
|
||||
"chainwork": "value", (string) Expected number of hashes required to produce the chain up to this block (in hex)
|
||||
"previousblockhash": "value", (string) The hash of the previous block
|
||||
"nextblockhash": "value", (string) The hash of the next block (only if there is one)
|
||||
"nameclaimroot": "value", (string) Root hash of the claim trie
|
||||
"nTx": n, (numeric) The number of transactions (aka, count of TX)
|
||||
},
|
||||
"tx": ["value",...], (array of string) The transaction hashes (only when verbosity=1)
|
||||
}
|
||||
```
|
||||
|
||||
## **lbcd** & **lbcwallet**
|
||||
|
||||
*Wallet* related functianlities and RPCs are provided by a separate programe - [**lbcwallet**](https://github.com/lbryio/lbcwallet).
|
||||
|
||||
Once setup, lbcwallet can serve wallet related RPCs as well as proxy lbcd RPCs to an assocated lbcd now.
|
||||
It's sufficient for user to connect just the **lbcwallet** instead of both.
|
||||
|
||||
``` mermaid
|
||||
sequenceDiagram
|
||||
actor C as lbcctl
|
||||
participant W as lbcwallet (port: 9244)
|
||||
participant D as lbcd (port: 9245)
|
||||
|
||||
rect rgb(200,200,200)
|
||||
Note over C,D: lbcctl getblockcount
|
||||
C ->>+ D: getblockcount
|
||||
D -->>- C: response
|
||||
end
|
||||
|
||||
rect rgb(200,200,200)
|
||||
Note over C,W: lbcctl --wallet balance
|
||||
C ->>+ W: getbalance
|
||||
W -->>- C: response
|
||||
end
|
||||
|
||||
rect rgb(200,200,200)
|
||||
Note over C,D: lbcctl --wallet getblockcount (lbcd RPC service proxied by lbcwallet)
|
||||
C ->>+ W: getblockcount
|
||||
W ->>+ D: getblockcount
|
||||
D -->>- W: response
|
||||
W -->>- C: response
|
||||
end
|
||||
```
|
||||
|
||||
While **lbcd** can run standalone as a full node, **lbcwallet** requires an associated **lbcd** instance for scanning and sync'ing block data.
|
||||
|
||||
``` mermaid
|
||||
sequenceDiagram
|
||||
participant W as lbcwallet (RPC port: 9244)
|
||||
participant D as lbcd (RPC port: 9245, P2P port: 9246)
|
||||
participant D2 as other lbcd node(s) (P2P port: 9246)
|
||||
|
||||
rect rgb(200,200,200)
|
||||
Note over W,D: Asynchronous websocket notifications
|
||||
W ->> D: subscribe to notifications
|
||||
D -->> W: notification
|
||||
D -->> W: notification
|
||||
end
|
||||
|
||||
rect rgb(200,200,200)
|
||||
Note over W,D: lbcd RPCs
|
||||
W ->>+ D: getblockheader
|
||||
D ->>- W: response
|
||||
end
|
||||
|
||||
rect rgb(200,200,200)
|
||||
Note over D,D2: P2P messages over port 9246
|
||||
D -->> D2: P2P message
|
||||
D2 -->> D: P2P message
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
## Data integrity
|
||||
|
||||
**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.
|
||||
|
||||
## Security
|
||||
|
||||
We take security seriously. Please contact [security](mailto:security@lbry.com) regarding any security issues.
|
||||
Our PGP key is [here](https://lbry.com/faq/pgp-key) if you need it.
|
||||
|
||||
We maintain a mailing list for notifications of upgrades, security issues,
|
||||
and soft/hard forks. To join, visit [fork list](https://lbry.com/forklist)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions to this project are welcome, encouraged, and compensated.
|
||||
The [integrated github issue tracker](https://github.com/lbryio/lbcd/issues)
|
||||
is used for this project. All pull requests will be considered.
|
||||
|
||||
<!-- ## Release Verification
|
||||
Please see our [documentation on the current build/verification
|
||||
process](https://github.com/lbryio/lbcd/tree/master/release) for all our
|
||||
process](https://github.com/btcsuite/btcd/tree/master/release) for all our
|
||||
releases for information on how to verify the integrity of published releases
|
||||
using our reproducible build system.
|
||||
-->
|
||||
|
||||
## License
|
||||
|
||||
lbcd is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||
btcd is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||
|
|
|
@ -23,8 +23,8 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// AddrManager provides a concurrency safe address manager for caching potential
|
||||
|
@ -45,7 +45,7 @@ type AddrManager struct {
|
|||
nTried int
|
||||
nNew int
|
||||
lamtx sync.Mutex
|
||||
localAddresses map[string]*LocalAddress
|
||||
localAddresses map[string]*localAddress
|
||||
version int
|
||||
}
|
||||
|
||||
|
@ -69,9 +69,9 @@ type serializedAddrManager struct {
|
|||
TriedBuckets [triedBucketCount][]string
|
||||
}
|
||||
|
||||
type LocalAddress struct {
|
||||
NA *wire.NetAddress
|
||||
Score AddressPriority
|
||||
type localAddress struct {
|
||||
na *wire.NetAddress
|
||||
score AddressPriority
|
||||
}
|
||||
|
||||
// AddressPriority type is used to describe the hierarchy of local address
|
||||
|
@ -176,9 +176,9 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddress) {
|
|||
// TODO: only update addresses periodically.
|
||||
// Update the last seen time and services.
|
||||
// note that to prevent causing excess garbage on getaddr
|
||||
// messages the netaddresses in addrmanager are *immutable*,
|
||||
// messages the netaddresses in addrmaanger are *immutable*,
|
||||
// if we need to change them then we replace the pointer with a
|
||||
// new copy so that we don't have to copy every NA for getaddr.
|
||||
// new copy so that we don't have to copy every na for getaddr.
|
||||
if netAddr.Timestamp.After(ka.na.Timestamp) ||
|
||||
(ka.na.Services&netAddr.Services) !=
|
||||
netAddr.Services {
|
||||
|
@ -186,9 +186,7 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddress) {
|
|||
naCopy := *ka.na
|
||||
naCopy.Timestamp = netAddr.Timestamp
|
||||
naCopy.AddService(netAddr.Services)
|
||||
ka.mtx.Lock()
|
||||
ka.na = &naCopy
|
||||
ka.mtx.Unlock()
|
||||
}
|
||||
|
||||
// If already in tried, we have nothing to do here.
|
||||
|
@ -755,7 +753,7 @@ func (a *AddrManager) HostToNetAddress(host string, port uint16, services wire.S
|
|||
// the relevant .onion address.
|
||||
func ipString(na *wire.NetAddress) string {
|
||||
if IsOnionCatTor(na) {
|
||||
// We know now that NA.IP is long enough.
|
||||
// We know now that na.IP is long enough.
|
||||
base32 := base32.StdEncoding.EncodeToString(na.IP[6:])
|
||||
return strings.ToLower(base32) + ".onion"
|
||||
}
|
||||
|
@ -859,11 +857,8 @@ func (a *AddrManager) Attempt(addr *wire.NetAddress) {
|
|||
return
|
||||
}
|
||||
// set last tried time to now
|
||||
now := time.Now()
|
||||
ka.mtx.Lock()
|
||||
ka.attempts++
|
||||
ka.lastattempt = now
|
||||
ka.mtx.Unlock()
|
||||
ka.lastattempt = time.Now()
|
||||
}
|
||||
|
||||
// Connected Marks the given address as currently connected and working at the
|
||||
|
@ -882,12 +877,10 @@ func (a *AddrManager) Connected(addr *wire.NetAddress) {
|
|||
// so.
|
||||
now := time.Now()
|
||||
if now.After(ka.na.Timestamp.Add(time.Minute * 20)) {
|
||||
// ka.NA is immutable, so replace it.
|
||||
// ka.na is immutable, so replace it.
|
||||
naCopy := *ka.na
|
||||
naCopy.Timestamp = time.Now()
|
||||
ka.mtx.Lock()
|
||||
ka.na = &naCopy
|
||||
ka.mtx.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -906,13 +899,11 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
|||
// ka.Timestamp is not updated here to avoid leaking information
|
||||
// about currently connected peers.
|
||||
now := time.Now()
|
||||
ka.mtx.Lock()
|
||||
ka.lastsuccess = now
|
||||
ka.lastattempt = now
|
||||
ka.attempts = 0
|
||||
ka.mtx.Unlock() // tried and refs synchronized via a.mtx
|
||||
|
||||
// move to tried set, optionally evicting other addresses if need.
|
||||
// move to tried set, optionally evicting other addresses if neeed.
|
||||
if ka.tried {
|
||||
return
|
||||
}
|
||||
|
@ -994,16 +985,14 @@ func (a *AddrManager) SetServices(addr *wire.NetAddress, services wire.ServiceFl
|
|||
|
||||
// Update the services if needed.
|
||||
if ka.na.Services != services {
|
||||
// ka.NA is immutable, so replace it.
|
||||
// ka.na is immutable, so replace it.
|
||||
naCopy := *ka.na
|
||||
naCopy.Services = services
|
||||
ka.mtx.Lock()
|
||||
ka.na = &naCopy
|
||||
ka.mtx.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// AddLocalAddress adds NA to the list of known local addresses to advertise
|
||||
// AddLocalAddress adds na to the list of known local addresses to advertise
|
||||
// with the given priority.
|
||||
func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPriority) error {
|
||||
if !IsRoutable(na) {
|
||||
|
@ -1015,13 +1004,13 @@ func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPrior
|
|||
|
||||
key := NetAddressKey(na)
|
||||
la, ok := a.localAddresses[key]
|
||||
if !ok || la.Score < priority {
|
||||
if !ok || la.score < priority {
|
||||
if ok {
|
||||
la.Score = priority + 1
|
||||
la.score = priority + 1
|
||||
} else {
|
||||
a.localAddresses[key] = &LocalAddress{
|
||||
NA: na,
|
||||
Score: priority,
|
||||
a.localAddresses[key] = &localAddress{
|
||||
na: na,
|
||||
score: priority,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1117,12 +1106,12 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
|
|||
var bestscore AddressPriority
|
||||
var bestAddress *wire.NetAddress
|
||||
for _, la := range a.localAddresses {
|
||||
reach := getReachabilityFrom(la.NA, remoteAddr)
|
||||
reach := getReachabilityFrom(la.na, remoteAddr)
|
||||
if reach > bestreach ||
|
||||
(reach == bestreach && la.Score > bestscore) {
|
||||
(reach == bestreach && la.score > bestscore) {
|
||||
bestreach = reach
|
||||
bestscore = la.Score
|
||||
bestAddress = la.NA
|
||||
bestscore = la.score
|
||||
bestAddress = la.na
|
||||
}
|
||||
}
|
||||
if bestAddress != nil {
|
||||
|
@ -1146,15 +1135,6 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
|
|||
return bestAddress
|
||||
}
|
||||
|
||||
// LocalAddresses returns the list of local addresses for our node.
|
||||
func (a *AddrManager) LocalAddresses() []*LocalAddress {
|
||||
var addrs []*LocalAddress
|
||||
for _, addr := range a.localAddresses {
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
// New returns a new bitcoin address manager.
|
||||
// Use Start to begin processing asynchronous address updates.
|
||||
func New(dataDir string, lookupFunc func(string) ([]net.IP, error)) *AddrManager {
|
||||
|
@ -1163,7 +1143,7 @@ func New(dataDir string, lookupFunc func(string) ([]net.IP, error)) *AddrManager
|
|||
lookupFunc: lookupFunc,
|
||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
quit: make(chan struct{}),
|
||||
localAddresses: make(map[string]*LocalAddress),
|
||||
localAddresses: make(map[string]*localAddress),
|
||||
version: serialisationVersion,
|
||||
}
|
||||
am.reset()
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// randAddr generates a *wire.NetAddress backed by a random IPv4/IPv6 address.
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/addrmgr"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/addrmgr"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// naTest is used to describe a test to be performed against the NetAddressKey
|
||||
|
@ -34,61 +34,61 @@ var someIP = "173.194.115.66"
|
|||
func addNaTests() {
|
||||
// IPv4
|
||||
// Localhost
|
||||
addNaTest("127.0.0.1", 9244, "127.0.0.1:9244")
|
||||
addNaTest("127.0.0.1", 9245, "127.0.0.1:9245")
|
||||
addNaTest("127.0.0.1", 8333, "127.0.0.1:8333")
|
||||
addNaTest("127.0.0.1", 8334, "127.0.0.1:8334")
|
||||
|
||||
// Class A
|
||||
addNaTest("1.0.0.1", 9244, "1.0.0.1:9244")
|
||||
addNaTest("2.2.2.2", 9245, "2.2.2.2:9245")
|
||||
addNaTest("27.253.252.251", 9246, "27.253.252.251:9246")
|
||||
addNaTest("123.3.2.1", 9247, "123.3.2.1:9247")
|
||||
addNaTest("1.0.0.1", 8333, "1.0.0.1:8333")
|
||||
addNaTest("2.2.2.2", 8334, "2.2.2.2:8334")
|
||||
addNaTest("27.253.252.251", 8335, "27.253.252.251:8335")
|
||||
addNaTest("123.3.2.1", 8336, "123.3.2.1:8336")
|
||||
|
||||
// Private Class A
|
||||
addNaTest("10.0.0.1", 9244, "10.0.0.1:9244")
|
||||
addNaTest("10.1.1.1", 9245, "10.1.1.1:9245")
|
||||
addNaTest("10.2.2.2", 9246, "10.2.2.2:9246")
|
||||
addNaTest("10.10.10.10", 9247, "10.10.10.10:9247")
|
||||
addNaTest("10.0.0.1", 8333, "10.0.0.1:8333")
|
||||
addNaTest("10.1.1.1", 8334, "10.1.1.1:8334")
|
||||
addNaTest("10.2.2.2", 8335, "10.2.2.2:8335")
|
||||
addNaTest("10.10.10.10", 8336, "10.10.10.10:8336")
|
||||
|
||||
// Class B
|
||||
addNaTest("128.0.0.1", 9244, "128.0.0.1:9244")
|
||||
addNaTest("129.1.1.1", 9245, "129.1.1.1:9245")
|
||||
addNaTest("180.2.2.2", 9246, "180.2.2.2:9246")
|
||||
addNaTest("191.10.10.10", 9247, "191.10.10.10:9247")
|
||||
addNaTest("128.0.0.1", 8333, "128.0.0.1:8333")
|
||||
addNaTest("129.1.1.1", 8334, "129.1.1.1:8334")
|
||||
addNaTest("180.2.2.2", 8335, "180.2.2.2:8335")
|
||||
addNaTest("191.10.10.10", 8336, "191.10.10.10:8336")
|
||||
|
||||
// Private Class B
|
||||
addNaTest("172.16.0.1", 9244, "172.16.0.1:9244")
|
||||
addNaTest("172.16.1.1", 9245, "172.16.1.1:9245")
|
||||
addNaTest("172.16.2.2", 9246, "172.16.2.2:9246")
|
||||
addNaTest("172.16.172.172", 9247, "172.16.172.172:9247")
|
||||
addNaTest("172.16.0.1", 8333, "172.16.0.1:8333")
|
||||
addNaTest("172.16.1.1", 8334, "172.16.1.1:8334")
|
||||
addNaTest("172.16.2.2", 8335, "172.16.2.2:8335")
|
||||
addNaTest("172.16.172.172", 8336, "172.16.172.172:8336")
|
||||
|
||||
// Class C
|
||||
addNaTest("193.0.0.1", 9244, "193.0.0.1:9244")
|
||||
addNaTest("200.1.1.1", 9245, "200.1.1.1:9245")
|
||||
addNaTest("205.2.2.2", 9246, "205.2.2.2:9246")
|
||||
addNaTest("223.10.10.10", 9247, "223.10.10.10:9247")
|
||||
addNaTest("193.0.0.1", 8333, "193.0.0.1:8333")
|
||||
addNaTest("200.1.1.1", 8334, "200.1.1.1:8334")
|
||||
addNaTest("205.2.2.2", 8335, "205.2.2.2:8335")
|
||||
addNaTest("223.10.10.10", 8336, "223.10.10.10:8336")
|
||||
|
||||
// Private Class C
|
||||
addNaTest("192.168.0.1", 9244, "192.168.0.1:9244")
|
||||
addNaTest("192.168.1.1", 9245, "192.168.1.1:9245")
|
||||
addNaTest("192.168.2.2", 9246, "192.168.2.2:9246")
|
||||
addNaTest("192.168.192.192", 9247, "192.168.192.192:9247")
|
||||
addNaTest("192.168.0.1", 8333, "192.168.0.1:8333")
|
||||
addNaTest("192.168.1.1", 8334, "192.168.1.1:8334")
|
||||
addNaTest("192.168.2.2", 8335, "192.168.2.2:8335")
|
||||
addNaTest("192.168.192.192", 8336, "192.168.192.192:8336")
|
||||
|
||||
// IPv6
|
||||
// Localhost
|
||||
addNaTest("::1", 9244, "[::1]:9244")
|
||||
addNaTest("fe80::1", 9245, "[fe80::1]:9245")
|
||||
addNaTest("::1", 8333, "[::1]:8333")
|
||||
addNaTest("fe80::1", 8334, "[fe80::1]:8334")
|
||||
|
||||
// Link-local
|
||||
addNaTest("fe80::1:1", 9244, "[fe80::1:1]:9244")
|
||||
addNaTest("fe91::2:2", 9245, "[fe91::2:2]:9245")
|
||||
addNaTest("fea2::3:3", 9246, "[fea2::3:3]:9246")
|
||||
addNaTest("feb3::4:4", 9247, "[feb3::4:4]:9247")
|
||||
addNaTest("fe80::1:1", 8333, "[fe80::1:1]:8333")
|
||||
addNaTest("fe91::2:2", 8334, "[fe91::2:2]:8334")
|
||||
addNaTest("fea2::3:3", 8335, "[fea2::3:3]:8335")
|
||||
addNaTest("feb3::4:4", 8336, "[feb3::4:4]:8336")
|
||||
|
||||
// Site-local
|
||||
addNaTest("fec0::1:1", 9244, "[fec0::1:1]:9244")
|
||||
addNaTest("fed1::2:2", 9245, "[fed1::2:2]:9245")
|
||||
addNaTest("fee2::3:3", 9246, "[fee2::3:3]:9246")
|
||||
addNaTest("fef3::4:4", 9247, "[fef3::4:4]:9247")
|
||||
addNaTest("fec0::1:1", 8333, "[fec0::1:1]:8333")
|
||||
addNaTest("fed1::2:2", 8334, "[fed1::2:2]:8334")
|
||||
addNaTest("fee2::3:3", 8335, "[fee2::3:3]:8335")
|
||||
addNaTest("fef3::4:4", 8336, "[fef3::4:4]:8336")
|
||||
}
|
||||
|
||||
func addNaTest(ip string, port uint16, want string) {
|
||||
|
@ -119,7 +119,7 @@ func TestAddAddressByIP(t *testing.T) {
|
|||
err error
|
||||
}{
|
||||
{
|
||||
someIP + ":9244",
|
||||
someIP + ":8333",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
|
@ -127,7 +127,7 @@ func TestAddAddressByIP(t *testing.T) {
|
|||
addrErr,
|
||||
},
|
||||
{
|
||||
someIP[:12] + ":9244",
|
||||
someIP[:12] + ":8333",
|
||||
fmtErr,
|
||||
},
|
||||
{
|
||||
|
@ -212,7 +212,7 @@ func TestAttempt(t *testing.T) {
|
|||
n := addrmgr.New("testattempt", lookupFunc)
|
||||
|
||||
// Add a new address and get it
|
||||
err := n.AddAddressByIP(someIP + ":9244")
|
||||
err := n.AddAddressByIP(someIP + ":8333")
|
||||
if err != nil {
|
||||
t.Fatalf("Adding address failed: %v", err)
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ func TestConnected(t *testing.T) {
|
|||
n := addrmgr.New("testconnected", lookupFunc)
|
||||
|
||||
// Add a new address and get it
|
||||
err := n.AddAddressByIP(someIP + ":9244")
|
||||
err := n.AddAddressByIP(someIP + ":8333")
|
||||
if err != nil {
|
||||
t.Fatalf("Adding address failed: %v", err)
|
||||
}
|
||||
|
@ -261,14 +261,14 @@ func TestNeedMoreAddresses(t *testing.T) {
|
|||
|
||||
var err error
|
||||
for i := 0; i < addrsToAdd; i++ {
|
||||
s := fmt.Sprintf("%d.%d.173.147:9244", i/128+60, i%128+60)
|
||||
s := fmt.Sprintf("%d.%d.173.147:8333", i/128+60, i%128+60)
|
||||
addrs[i], err = n.DeserializeNetAddress(s, wire.SFNodeNetwork)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to turn %s into an address: %v", s, err)
|
||||
}
|
||||
}
|
||||
|
||||
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 9244, 0)
|
||||
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
|
||||
|
||||
n.AddAddresses(addrs, srcAddr)
|
||||
numAddrs := n.NumAddresses()
|
||||
|
@ -289,14 +289,14 @@ func TestGood(t *testing.T) {
|
|||
|
||||
var err error
|
||||
for i := 0; i < addrsToAdd; i++ {
|
||||
s := fmt.Sprintf("%d.173.147.%d:9244", i/64+60, i%64+60)
|
||||
s := fmt.Sprintf("%d.173.147.%d:8333", i/64+60, i%64+60)
|
||||
addrs[i], err = n.DeserializeNetAddress(s, wire.SFNodeNetwork)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to turn %s into an address: %v", s, err)
|
||||
}
|
||||
}
|
||||
|
||||
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 9244, 0)
|
||||
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
|
||||
|
||||
n.AddAddresses(addrs, srcAddr)
|
||||
for _, addr := range addrs {
|
||||
|
@ -323,7 +323,7 @@ func TestGetAddress(t *testing.T) {
|
|||
}
|
||||
|
||||
// Add a new address and get it
|
||||
err := n.AddAddressByIP(someIP + ":9244")
|
||||
err := n.AddAddressByIP(someIP + ":8333")
|
||||
if err != nil {
|
||||
t.Fatalf("Adding address failed: %v", err)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
/*
|
||||
Package addrmgr implements concurrency safe Bitcoin address manager.
|
||||
|
||||
# Address Manager Overview
|
||||
Address Manager Overview
|
||||
|
||||
In order maintain the peer-to-peer Bitcoin network, there needs to be a source
|
||||
of addresses to connect to as nodes come and go. The Bitcoin protocol provides
|
||||
|
|
|
@ -7,7 +7,7 @@ package addrmgr
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
func TstKnownAddressIsBad(ka *KnownAddress) bool {
|
||||
|
|
|
@ -5,16 +5,14 @@
|
|||
package addrmgr
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// KnownAddress tracks information about a known network address that is used
|
||||
// to determine how viable an address is.
|
||||
type KnownAddress struct {
|
||||
mtx sync.RWMutex // na and lastattempt
|
||||
na *wire.NetAddress
|
||||
srcAddr *wire.NetAddress
|
||||
attempts int
|
||||
|
@ -27,28 +25,19 @@ type KnownAddress struct {
|
|||
// NetAddress returns the underlying wire.NetAddress associated with the
|
||||
// known address.
|
||||
func (ka *KnownAddress) NetAddress() *wire.NetAddress {
|
||||
ka.mtx.RLock()
|
||||
defer ka.mtx.RUnlock()
|
||||
return ka.na
|
||||
}
|
||||
|
||||
// LastAttempt returns the last time the known address was attempted.
|
||||
func (ka *KnownAddress) LastAttempt() time.Time {
|
||||
ka.mtx.RLock()
|
||||
defer ka.mtx.RUnlock()
|
||||
return ka.lastattempt
|
||||
}
|
||||
|
||||
// Services returns the services supported by the peer with the known address.
|
||||
func (ka *KnownAddress) Services() wire.ServiceFlag {
|
||||
ka.mtx.RLock()
|
||||
defer ka.mtx.RUnlock()
|
||||
return ka.na.Services
|
||||
}
|
||||
|
||||
// The unexported methods, chance and isBad, are used from within AddrManager
|
||||
// where KnownAddress field access is synchronized via it's own Mutex.
|
||||
|
||||
// chance returns the selection probability for a known address. The priority
|
||||
// depends upon how recently the address has been seen, how recently it was last
|
||||
// attempted and how often attempts to connect to it have failed.
|
||||
|
|
|
@ -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/lbryio/lbcd/addrmgr"
|
||||
"github.com/lbryio/lbcd/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"
|
||||
"net"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/addrmgr"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/addrmgr"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// TestIPTypes ensures the various functions which determine the type of an IP
|
||||
|
@ -39,7 +39,7 @@ func TestIPTypes(t *testing.T) {
|
|||
rfc4193, rfc4380, rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598,
|
||||
local, valid, routable bool) ipTest {
|
||||
nip := net.ParseIP(ip)
|
||||
na := *wire.NewNetAddressIPPort(nip, 9246, wire.SFNodeNetwork)
|
||||
na := *wire.NewNetAddressIPPort(nip, 8333, wire.SFNodeNetwork)
|
||||
test := ipTest{na, rfc1918, rfc2544, rfc3849, rfc3927, rfc3964, rfc4193, rfc4380,
|
||||
rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598, local, valid, routable}
|
||||
return test
|
||||
|
@ -192,7 +192,7 @@ func TestGroupKey(t *testing.T) {
|
|||
|
||||
for i, test := range tests {
|
||||
nip := net.ParseIP(test.ip)
|
||||
na := *wire.NewNetAddressIPPort(nip, 9246, wire.SFNodeNetwork)
|
||||
na := *wire.NewNetAddressIPPort(nip, 8333, wire.SFNodeNetwork)
|
||||
if key := addrmgr.GroupKey(&na); key != test.expected {
|
||||
t.Errorf("TestGroupKey #%d (%s): unexpected group key "+
|
||||
"- got '%s', want '%s'", i, test.name,
|
||||
|
|
|
@ -1,9 +1,30 @@
|
|||
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)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain)
|
||||
|
||||
### Bitcoin Chain Processing Overview
|
||||
Package blockchain implements bitcoin block handling and chain selection rules.
|
||||
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
|
||||
series of validation rules. The following list serves as a general outline of
|
||||
|
@ -36,4 +57,47 @@ is by no means exhaustive:
|
|||
transaction values
|
||||
- Run the transaction scripts to verify the spender is allowed to spend the
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/lbryio/lbcd/database"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// maybeAcceptBlock potentially accepts a block into the block chain and, if
|
||||
|
@ -84,11 +84,9 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
|||
// Notify the caller that the new block was accepted into the block
|
||||
// chain. The caller would typically want to react by relaying the
|
||||
// inventory to other peers.
|
||||
b.notificationSendLock.Lock()
|
||||
defer b.notificationSendLock.Unlock()
|
||||
b.chainLock.Unlock()
|
||||
defer b.chainLock.Lock()
|
||||
b.sendNotification(NTBlockAccepted, block)
|
||||
b.chainLock.Lock()
|
||||
|
||||
return isMainChain, nil
|
||||
}
|
||||
|
|
|
@ -1,29 +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"
|
||||
)
|
||||
|
||||
// BenchmarkIsCoinBase performs a simple benchmark against the IsCoinBase
|
||||
// function.
|
||||
func BenchmarkIsCoinBase(b *testing.B) {
|
||||
tx, _ := GetBlock100000().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, _ := GetBlock100000().Tx(1)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsCoinBaseTx(tx.MsgTx())
|
||||
}
|
||||
}
|
|
@ -10,10 +10,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// blockStatus is a bit field representing the validation state of the block.
|
||||
|
|
|
@ -8,18 +8,17 @@ package blockchain
|
|||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
||||
"github.com/lbryio/lbcd/claimtrie"
|
||||
"github.com/btcsuite/btcd/claimtrie"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -37,9 +36,8 @@ const (
|
|||
// from the block being located.
|
||||
//
|
||||
// For example, assume a block chain with a side chain as depicted below:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||
// \-> 16a -> 17a
|
||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||
// \-> 16a -> 17a
|
||||
//
|
||||
// The block locator for block 17a would be the hashes of blocks:
|
||||
// [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis]
|
||||
|
@ -118,12 +116,6 @@ type BlockChain struct {
|
|||
// fields in this struct below this point.
|
||||
chainLock sync.RWMutex
|
||||
|
||||
// notificationSendLock helps us only process one block at a time.
|
||||
// It's definitely a hack. DCRD has much better structure in this regard.
|
||||
// Without this you will get an error if you invalidate a block and then generate more right after.
|
||||
// Taken from https://github.com/gcash/bchd/pull/308
|
||||
notificationSendLock sync.Mutex
|
||||
|
||||
// These fields are related to the memory block index. They both have
|
||||
// their own locks, however they are often also protected by the chain
|
||||
// lock to help prevent logic races when blocks are being processed.
|
||||
|
@ -207,15 +199,6 @@ func (b *BlockChain) HaveBlock(hash *chainhash.Hash) (bool, error) {
|
|||
return exists || b.IsKnownOrphan(hash), nil
|
||||
}
|
||||
|
||||
// GetWarnings returns a bool for whether unknownRules
|
||||
// has been warned.
|
||||
func (b *BlockChain) GetWarnings() bool {
|
||||
b.chainLock.RLock()
|
||||
defer b.chainLock.RUnlock()
|
||||
|
||||
return b.unknownRulesWarned
|
||||
}
|
||||
|
||||
// IsKnownOrphan returns whether the passed hash is currently a known orphan.
|
||||
// Keep in mind that only a limited number of orphans are held onto for a
|
||||
// limited amount of time, so this function must not be used as an absolute
|
||||
|
@ -489,7 +472,7 @@ func (b *BlockChain) calcSequenceLock(node *blockNode, tx *btcutil.Tx, utxoView
|
|||
// LockTimeToSequence converts the passed relative locktime to a sequence
|
||||
// number in accordance to BIP-68.
|
||||
// See: https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
|
||||
// - (Compatibility)
|
||||
// * (Compatibility)
|
||||
func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 {
|
||||
// If we're expressing the relative lock time in blocks, then the
|
||||
// corresponding sequence number is simply the desired input age.
|
||||
|
@ -603,8 +586,7 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block,
|
|||
|
||||
// Handle LBRY Claim Scripts
|
||||
if b.claimTrie != nil {
|
||||
shouldFlush := current && b.chainParams.Net != wire.TestNet
|
||||
if err := b.ParseClaimScripts(block, node, view, shouldFlush); err != nil {
|
||||
if err := b.ParseClaimScripts(block, node, view, false, current); err != nil {
|
||||
return ruleError(ErrBadClaimTrie, err.Error())
|
||||
}
|
||||
}
|
||||
|
@ -691,11 +673,9 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block,
|
|||
// Notify the caller that the block was connected to the main chain.
|
||||
// The caller would typically want to react with actions such as
|
||||
// updating wallets.
|
||||
b.notificationSendLock.Lock()
|
||||
defer b.notificationSendLock.Unlock()
|
||||
b.chainLock.Unlock()
|
||||
defer b.chainLock.Lock()
|
||||
b.sendNotification(NTBlockConnected, block)
|
||||
b.chainLock.Lock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -793,10 +773,8 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
|
|||
return err
|
||||
}
|
||||
|
||||
if b.claimTrie != nil {
|
||||
if err = b.claimTrie.ResetHeight(node.parent.height); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = b.claimTrie.ResetHeight(node.parent.height); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prune fully spent entries and mark all entries in the view unmodified
|
||||
|
@ -818,11 +796,9 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
|
|||
// Notify the caller that the block was disconnected from the main
|
||||
// chain. The caller would typically want to react with actions such as
|
||||
// updating wallets.
|
||||
b.notificationSendLock.Lock()
|
||||
defer b.notificationSendLock.Unlock()
|
||||
b.chainLock.Unlock()
|
||||
defer b.chainLock.Lock()
|
||||
b.sendNotification(NTBlockDisconnected, block)
|
||||
b.chainLock.Lock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1006,7 +982,6 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
|||
err = b.checkConnectBlock(n, block, view, nil)
|
||||
if err != nil {
|
||||
if _, ok := err.(RuleError); ok {
|
||||
b.index.UnsetStatusFlags(n, statusValid)
|
||||
b.index.SetStatusFlags(n, statusValidateFailed)
|
||||
for de := e.Next(); de != nil; de = de.Next() {
|
||||
dn := de.Value.(*blockNode)
|
||||
|
@ -1108,8 +1083,8 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
|||
// a reorganization to become the main chain).
|
||||
//
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFFastAdd: Avoids several expensive transaction validation operations.
|
||||
// This is useful when using checkpoints.
|
||||
// - BFFastAdd: Avoids several expensive transaction validation operations.
|
||||
// This is useful when using checkpoints.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
||||
|
@ -1144,7 +1119,6 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
|||
if err == nil {
|
||||
b.index.SetStatusFlags(node, statusValid)
|
||||
} else if _, ok := err.(RuleError); ok {
|
||||
b.index.UnsetStatusFlags(node, statusValid)
|
||||
b.index.SetStatusFlags(node, statusValidateFailed)
|
||||
} else {
|
||||
return false, err
|
||||
|
@ -1179,7 +1153,6 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
|||
// that status of the block as invalid and flush the
|
||||
// index state to disk before returning with the error.
|
||||
if _, ok := err.(RuleError); ok {
|
||||
b.index.UnsetStatusFlags(node, statusValid)
|
||||
b.index.SetStatusFlags(
|
||||
node, statusValidateFailed,
|
||||
)
|
||||
|
@ -1250,8 +1223,8 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
|||
// isCurrent returns whether or not the chain believes it is current. Several
|
||||
// factors are used to guess, but the key factors that allow the chain to
|
||||
// believe it is current are:
|
||||
// - Latest block height is after the latest checkpoint (if enabled)
|
||||
// - Latest block has a timestamp newer than ~6 hours ago (as LBRY block time is one fourth of bitcoin)
|
||||
// - Latest block height is after the latest checkpoint (if enabled)
|
||||
// - 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).
|
||||
func (b *BlockChain) isCurrent() bool {
|
||||
|
@ -1274,8 +1247,8 @@ func (b *BlockChain) isCurrent() bool {
|
|||
// IsCurrent returns whether or not the chain believes it is current. Several
|
||||
// factors are used to guess, but the key factors that allow the chain to
|
||||
// believe it is current are:
|
||||
// - Latest block height is after the latest checkpoint (if enabled)
|
||||
// - Latest block has a timestamp newer than 24 hours ago
|
||||
// - Latest block height is after the latest checkpoint (if enabled)
|
||||
// - Latest block has a timestamp newer than 24 hours ago
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) IsCurrent() bool {
|
||||
|
@ -1375,57 +1348,6 @@ func (b *BlockChain) BlockHashByHeight(blockHeight int32) (*chainhash.Hash, erro
|
|||
return &node.hash, nil
|
||||
}
|
||||
|
||||
// BlockAttributes desribes a Block in relation to others on the main chain.
|
||||
type BlockAttributes struct {
|
||||
Height int32
|
||||
Confirmations int32
|
||||
MedianTime time.Time
|
||||
ChainWork *big.Int
|
||||
PrevHash *chainhash.Hash
|
||||
NextHash *chainhash.Hash
|
||||
}
|
||||
|
||||
// BlockAttributesByHash returns BlockAttributes for the block with the given hash
|
||||
// relative to other blocks in the main chain. A BestState snapshot describing
|
||||
// the main chain is also returned for convenience.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockAttributesByHash(hash *chainhash.Hash, prevHash *chainhash.Hash) (
|
||||
attrs *BlockAttributes, best *BestState, err error) {
|
||||
best = b.BestSnapshot()
|
||||
node := b.index.LookupNode(hash)
|
||||
if node == nil {
|
||||
str := fmt.Sprintf("block %s not found", hash)
|
||||
return nil, best, errNotInMainChain(str)
|
||||
}
|
||||
|
||||
attrs = &BlockAttributes{
|
||||
Height: node.height,
|
||||
Confirmations: 1 + best.Height - node.height,
|
||||
MedianTime: node.CalcPastMedianTime(),
|
||||
ChainWork: node.workSum,
|
||||
}
|
||||
if !b.bestChain.Contains(node) {
|
||||
attrs.Confirmations = -1
|
||||
}
|
||||
|
||||
// Populate prev block hash if there is one.
|
||||
if node.height > 0 {
|
||||
attrs.PrevHash = prevHash
|
||||
}
|
||||
|
||||
// Populate next block hash if there is one.
|
||||
if node.height < best.Height {
|
||||
nextHash, err := b.BlockHashByHeight(node.height + 1)
|
||||
if err != nil {
|
||||
return nil, best, err
|
||||
}
|
||||
attrs.NextHash = nextHash
|
||||
}
|
||||
|
||||
return attrs, best, nil
|
||||
}
|
||||
|
||||
// HeightRange returns a range of block hashes for the given start and end
|
||||
// heights. It is inclusive of the start height and exclusive of the end
|
||||
// height. The end height will be limited to the current main chain height.
|
||||
|
@ -1561,11 +1483,11 @@ func (b *BlockChain) IntervalBlockHashes(endHash *chainhash.Hash, interval int,
|
|||
//
|
||||
// In addition, there are two special cases:
|
||||
//
|
||||
// - When no locators are provided, the stop hash is treated as a request for
|
||||
// that block, so it will either return the node associated with the stop hash
|
||||
// if it is known, or nil if it is unknown
|
||||
// - When locators are provided, but none of them are known, nodes starting
|
||||
// after the genesis block will be returned
|
||||
// - When no locators are provided, the stop hash is treated as a request for
|
||||
// that block, so it will either return the node associated with the stop hash
|
||||
// if it is known, or nil if it is unknown
|
||||
// - When locators are provided, but none of them are known, nodes starting
|
||||
// after the genesis block will be returned
|
||||
//
|
||||
// This is primarily a helper function for the locateBlocks and locateHeaders
|
||||
// functions.
|
||||
|
@ -1649,11 +1571,11 @@ func (b *BlockChain) locateBlocks(locator BlockLocator, hashStop *chainhash.Hash
|
|||
//
|
||||
// In addition, there are two special cases:
|
||||
//
|
||||
// - When no locators are provided, the stop hash is treated as a request for
|
||||
// that block, so it will either return the stop hash itself if it is known,
|
||||
// or nil if it is unknown
|
||||
// - When locators are provided, but none of them are known, hashes starting
|
||||
// after the genesis block will be returned
|
||||
// - When no locators are provided, the stop hash is treated as a request for
|
||||
// that block, so it will either return the stop hash itself if it is known,
|
||||
// or nil if it is unknown
|
||||
// - When locators are provided, but none of them are known, hashes starting
|
||||
// after the genesis block will be returned
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) LocateBlocks(locator BlockLocator, hashStop *chainhash.Hash, maxHashes uint32) []chainhash.Hash {
|
||||
|
@ -1694,11 +1616,11 @@ func (b *BlockChain) locateHeaders(locator BlockLocator, hashStop *chainhash.Has
|
|||
//
|
||||
// In addition, there are two special cases:
|
||||
//
|
||||
// - When no locators are provided, the stop hash is treated as a request for
|
||||
// that header, so it will either return the header for the stop hash itself
|
||||
// if it is known, or nil if it is unknown
|
||||
// - When locators are provided, but none of them are known, headers starting
|
||||
// after the genesis block will be returned
|
||||
// - When no locators are provided, the stop hash is treated as a request for
|
||||
// that header, so it will either return the header for the stop hash itself
|
||||
// if it is known, or nil if it is unknown
|
||||
// - When locators are provided, but none of them are known, headers starting
|
||||
// after the genesis block will be returned
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Hash) []wire.BlockHeader {
|
||||
|
@ -1708,116 +1630,6 @@ func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Has
|
|||
return headers
|
||||
}
|
||||
|
||||
// InvalidateBlock takes a block hash and invalidates it.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) InvalidateBlock(hash *chainhash.Hash) error {
|
||||
b.chainLock.Lock()
|
||||
defer b.chainLock.Unlock()
|
||||
return b.invalidateBlock(hash)
|
||||
}
|
||||
|
||||
// invalidateBlock takes a block hash and invalidates it.
|
||||
func (b *BlockChain) invalidateBlock(hash *chainhash.Hash) error {
|
||||
node := b.index.LookupNode(hash)
|
||||
if node == nil {
|
||||
err := fmt.Errorf("block %s is not known", hash)
|
||||
return err
|
||||
}
|
||||
|
||||
// No need to invalidate if its already invalid.
|
||||
if node.status.KnownInvalid() {
|
||||
err := fmt.Errorf("block %s is already invalid", hash)
|
||||
return err
|
||||
}
|
||||
|
||||
if node.parent == nil {
|
||||
err := fmt.Errorf("block %s has no parent", hash)
|
||||
return err
|
||||
}
|
||||
|
||||
b.index.SetStatusFlags(node, statusValidateFailed)
|
||||
b.index.UnsetStatusFlags(node, statusValid)
|
||||
|
||||
detachNodes, attachNodes := b.getReorganizeNodes(node.parent)
|
||||
|
||||
err := b.reorganizeChain(detachNodes, attachNodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, e := 0, detachNodes.Front(); e != nil; i, e = i+1, e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
|
||||
b.index.SetStatusFlags(n, statusInvalidAncestor)
|
||||
b.index.UnsetStatusFlags(n, statusValid)
|
||||
}
|
||||
|
||||
if writeErr := b.index.flushToDB(); writeErr != nil {
|
||||
log.Warnf("Error flushing block index changes to disk: %v", writeErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReconsiderBlock takes a block hash and allows it to be revalidated.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) ReconsiderBlock(hash *chainhash.Hash) error {
|
||||
return b.reconsiderBlock(hash)
|
||||
}
|
||||
|
||||
// reconsiderBlock takes a block hash and allows it to be revalidated.
|
||||
func (b *BlockChain) reconsiderBlock(hash *chainhash.Hash) error {
|
||||
node := b.index.LookupNode(hash)
|
||||
if node == nil {
|
||||
err := fmt.Errorf("block %s is not known", hash)
|
||||
return err
|
||||
}
|
||||
|
||||
// No need to reconsider, it is already valid.
|
||||
if node.status.KnownValid() && !node.status.KnownInvalid() { // second clause works around old bug
|
||||
err := fmt.Errorf("block %s is already valid", hash)
|
||||
return err
|
||||
}
|
||||
|
||||
// Keep a reference to the first node in the chain of invalid
|
||||
// blocks so we can reprocess after status flags are updated.
|
||||
firstNode := node
|
||||
|
||||
// Find previous node to the point where the blocks are valid again.
|
||||
for n := node; n.status.KnownInvalid(); n = n.parent {
|
||||
b.index.UnsetStatusFlags(n, statusInvalidAncestor)
|
||||
b.index.UnsetStatusFlags(n, statusValidateFailed)
|
||||
|
||||
firstNode = n
|
||||
}
|
||||
|
||||
// do we need an rlock on chainstate for this section?
|
||||
var blk *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
blk, err = dbFetchBlockByNode(dbTx, firstNode)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process it all again. This will take care of the
|
||||
// orphans as well.
|
||||
_, _, err = b.ProcessBlock(blk, BFNoDupBlockCheck)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if writeErr := b.index.flushToDB(); writeErr != nil {
|
||||
log.Warnf("Error flushing block index changes to disk: %v", writeErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClaimTrie returns the claimTrie associated wit hthe chain.
|
||||
func (b *BlockChain) ClaimTrie() *claimtrie.ClaimTrie {
|
||||
return b.claimTrie
|
||||
|
@ -2069,7 +1881,7 @@ func rebuildMissingClaimTrieData(b *BlockChain, done <-chan struct{}) error {
|
|||
}
|
||||
|
||||
if h >= b.claimTrie.Height() {
|
||||
err = b.ParseClaimScripts(block, n, view, false)
|
||||
err = b.ParseClaimScripts(block, n, view, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,870 +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/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
)
|
||||
|
||||
// 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"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// TestErrNotInMainChain ensures the functions related to errNotInMainChain work
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
package blockchain
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
)
|
||||
|
||||
type ChainTip struct { // duplicate of btcjson.GetChainTipsResult to avoid circular reference
|
||||
Height int64
|
||||
Hash string
|
||||
BranchLen int64
|
||||
Status string
|
||||
}
|
||||
|
||||
// nodeHeightSorter implements sort.Interface to allow a slice of nodes to
|
||||
// be sorted by height in ascending order.
|
||||
type nodeHeightSorter []ChainTip
|
||||
|
||||
// Len returns the number of nodes in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s nodeHeightSorter) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Swap swaps the nodes at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s nodeHeightSorter) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Less returns whether the node with index i should sort before the node with
|
||||
// index j. It is part of the sort.Interface implementation.
|
||||
func (s nodeHeightSorter) Less(i, j int) bool {
|
||||
// To ensure stable order when the heights are the same, fall back to
|
||||
// sorting based on hash.
|
||||
if s[i].Height == s[j].Height {
|
||||
return strings.Compare(s[i].Hash, s[j].Hash) < 0
|
||||
}
|
||||
return s[i].Height < s[j].Height
|
||||
}
|
||||
|
||||
// ChainTips returns information, in JSON-RPC format, about all the currently
|
||||
// known chain tips in the block index.
|
||||
func (b *BlockChain) ChainTips() []ChainTip {
|
||||
// we need our current tip
|
||||
// we also need all of our orphans that aren't in the prevOrphans
|
||||
var results []ChainTip
|
||||
|
||||
tip := b.bestChain.Tip()
|
||||
results = append(results, ChainTip{
|
||||
Height: int64(tip.height),
|
||||
Hash: tip.hash.String(),
|
||||
BranchLen: 0,
|
||||
Status: "active",
|
||||
})
|
||||
|
||||
b.orphanLock.RLock()
|
||||
defer b.orphanLock.RUnlock()
|
||||
|
||||
notInBestChain := func(block *btcutil.Block) bool {
|
||||
node := b.bestChain.NodeByHeight(block.Height())
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
return node.hash.IsEqual(block.Hash())
|
||||
}
|
||||
|
||||
for hash, orphan := range b.orphans {
|
||||
if len(b.prevOrphans[hash]) > 0 {
|
||||
continue
|
||||
}
|
||||
fork := orphan.block
|
||||
for fork != nil && notInBestChain(fork) {
|
||||
fork = b.orphans[*fork.Hash()].block
|
||||
}
|
||||
|
||||
result := ChainTip{
|
||||
Height: int64(orphan.block.Height()),
|
||||
Hash: hash.String(),
|
||||
BranchLen: int64(orphan.block.Height() - fork.Height()),
|
||||
}
|
||||
|
||||
// Determine the status of the chain tip.
|
||||
//
|
||||
// active:
|
||||
// The current best chain tip.
|
||||
//
|
||||
// invalid:
|
||||
// The block or one of its ancestors is invalid.
|
||||
//
|
||||
// headers-only:
|
||||
// The block or one of its ancestors does not have the full block data
|
||||
// available which also means the block can't be validated or
|
||||
// connected.
|
||||
//
|
||||
// valid-fork:
|
||||
// The block is fully validated which implies it was probably part of
|
||||
// main chain at one point and was reorganized.
|
||||
//
|
||||
// valid-headers:
|
||||
// The full block data is available and the header is valid, but the
|
||||
// block was never validated which implies it was probably never part
|
||||
// of the main chain.
|
||||
tipStatus := b.index.LookupNode(&hash).status
|
||||
if tipStatus.KnownInvalid() {
|
||||
result.Status = "invalid"
|
||||
} else if !tipStatus.HaveData() {
|
||||
result.Status = "headers-only"
|
||||
} else if tipStatus.KnownValid() {
|
||||
result.Status = "valid-fork"
|
||||
} else {
|
||||
result.Status = "valid-headers"
|
||||
}
|
||||
|
||||
results = append(results, result)
|
||||
}
|
||||
|
||||
// Generate the results sorted by descending height.
|
||||
sort.Sort(sort.Reverse(nodeHeightSorter(results)))
|
||||
return results
|
||||
}
|
|
@ -36,13 +36,11 @@ func fastLog2Floor(n uint32) uint8 {
|
|||
// for comparing chains.
|
||||
//
|
||||
// For example, assume a block chain with a side chain as depicted below:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 4a -> 5a -> 6a
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 4a -> 5a -> 6a
|
||||
//
|
||||
// The chain view for the branch ending in 6a consists of:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> 3 -> 4a -> 5a -> 6a
|
||||
// genesis -> 1 -> 2 -> 3 -> 4a -> 5a -> 6a
|
||||
type chainView struct {
|
||||
mtx sync.Mutex
|
||||
nodes []*blockNode
|
||||
|
@ -260,14 +258,12 @@ func (c *chainView) next(node *blockNode) *blockNode {
|
|||
// view.
|
||||
//
|
||||
// For example, assume a block chain with a side chain as depicted below:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 4a -> 5a -> 6a
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 4a -> 5a -> 6a
|
||||
//
|
||||
// Further, assume the view is for the longer chain depicted above. That is to
|
||||
// say it consists of:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
//
|
||||
// Invoking this function with block node 5 would return block node 6 while
|
||||
// invoking it with block node 5a would return nil since that node is not part
|
||||
|
@ -325,14 +321,12 @@ func (c *chainView) findFork(node *blockNode) *blockNode {
|
|||
// the chain view. It will return nil if there is no common block.
|
||||
//
|
||||
// For example, assume a block chain with a side chain as depicted below:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 6a -> 7a
|
||||
// genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 6a -> 7a
|
||||
//
|
||||
// Further, assume the view is for the longer chain depicted above. That is to
|
||||
// say it consists of:
|
||||
//
|
||||
// genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8.
|
||||
// genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8.
|
||||
//
|
||||
// Invoking this function with block node 7a would return block node 5 while
|
||||
// invoking it with block node 7 would return itself since it is already part of
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// testNoncePrng provides a deterministic prng for the nonce in generated fake
|
||||
|
|
|
@ -8,10 +8,10 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// CheckpointConfirmations is the number of blocks before the end of the current
|
||||
|
@ -172,8 +172,7 @@ func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) {
|
|||
func isNonstandardTransaction(tx *btcutil.Tx) bool {
|
||||
// Check all of the output public key scripts for non-standard scripts.
|
||||
for _, txOut := range tx.MsgTx().TxOut {
|
||||
stripped := txscript.StripClaimScriptPrefix(txOut.PkScript)
|
||||
scriptClass := txscript.GetScriptClass(stripped)
|
||||
scriptClass := txscript.GetScriptClass(txOut.PkScript)
|
||||
if scriptClass == txscript.NonStandardTy {
|
||||
return true
|
||||
}
|
||||
|
@ -185,14 +184,14 @@ func isNonstandardTransaction(tx *btcutil.Tx) bool {
|
|||
// checkpoint candidate.
|
||||
//
|
||||
// The factors used to determine a good checkpoint are:
|
||||
// - The block must be in the main chain
|
||||
// - The block must be at least 'CheckpointConfirmations' blocks prior to the
|
||||
// current end of the main chain
|
||||
// - The timestamps for the blocks before and after the checkpoint must have
|
||||
// timestamps which are also before and after the checkpoint, respectively
|
||||
// (due to the median time allowance this is not always the case)
|
||||
// - The block must not contain any strange transaction such as those with
|
||||
// nonstandard scripts
|
||||
// - The block must be in the main chain
|
||||
// - The block must be at least 'CheckpointConfirmations' blocks prior to the
|
||||
// current end of the main chain
|
||||
// - The timestamps for the blocks before and after the checkpoint must have
|
||||
// timestamps which are also before and after the checkpoint, respectively
|
||||
// (due to the median time allowance this is not always the case)
|
||||
// - The block must not contain any strange transaction such as those with
|
||||
// nonstandard scripts
|
||||
//
|
||||
// The intent is that candidates are reviewed by a developer to make the final
|
||||
// decision and then manually added to the list of checkpoints for a network.
|
||||
|
|
|
@ -6,32 +6,17 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
||||
"github.com/lbryio/lbcd/claimtrie"
|
||||
"github.com/lbryio/lbcd/claimtrie/change"
|
||||
"github.com/lbryio/lbcd/claimtrie/node"
|
||||
"github.com/lbryio/lbcd/claimtrie/normalization"
|
||||
"github.com/btcsuite/btcd/claimtrie"
|
||||
"github.com/btcsuite/btcd/claimtrie/change"
|
||||
"github.com/btcsuite/btcd/claimtrie/node"
|
||||
)
|
||||
|
||||
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 {
|
||||
func (b *BlockChain) ParseClaimScripts(block *btcutil.Block, bn *blockNode, view *UtxoViewpoint,
|
||||
failOnHashMiss bool, shouldFlush bool) error {
|
||||
ht := block.Height()
|
||||
|
||||
for _, tx := range block.Transactions() {
|
||||
|
@ -44,7 +29,7 @@ func (b *BlockChain) ParseClaimScripts(block *btcutil.Block, bn *blockNode, view
|
|||
}
|
||||
}
|
||||
|
||||
err := b.claimTrie.AppendBlock(bn == nil)
|
||||
err := b.claimTrie.AppendBlock()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "in append block")
|
||||
}
|
||||
|
@ -54,11 +39,11 @@ func (b *BlockChain) ParseClaimScripts(block *btcutil.Block, bn *blockNode, view
|
|||
}
|
||||
|
||||
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)
|
||||
if bn.claimTrie != *hash {
|
||||
if failOnHashMiss {
|
||||
return errors.Errorf("height: %d, ct.MerkleHash: %s != node.ClaimTrie: %s", ht, *hash, bn.claimTrie)
|
||||
}
|
||||
node.LogOnce(fmt.Sprintf("\n\nHeight: %d, ct.MerkleHash: %s != node.ClaimTrie: %s, Error: %s", ht, *hash, bn.claimTrie, err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -80,30 +65,33 @@ func (h *handler) handleTxIns(ct *claimtrie.ClaimTrie) error {
|
|||
if e == nil {
|
||||
return errors.Errorf("missing input in view for %s", op.String())
|
||||
}
|
||||
cs, err := txscript.ExtractClaimScript(e.pkScript)
|
||||
if txscript.IsErrorCode(err, txscript.ErrNotClaimScript) {
|
||||
cs, closer, err := txscript.DecodeClaimScript(e.pkScript)
|
||||
if err == txscript.ErrNotClaimScript {
|
||||
closer()
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
closer()
|
||||
return err
|
||||
}
|
||||
|
||||
var id change.ClaimID
|
||||
name := cs.Name // name of the previous one (that we're now spending)
|
||||
name := cs.Name() // name of the previous one (that we're now spending)
|
||||
|
||||
switch cs.Opcode {
|
||||
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())
|
||||
h.spent[id.Key()] = node.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())
|
||||
copy(id[:], cs.ClaimID())
|
||||
h.spent[id.Key()] = node.NormalizeIfNecessary(name, ct.Height())
|
||||
err = ct.SpendClaim(name, op, id)
|
||||
case txscript.OP_SUPPORTCLAIM:
|
||||
copy(id[:], cs.ClaimID)
|
||||
copy(id[:], cs.ClaimID())
|
||||
err = ct.SpendSupport(name, op, id)
|
||||
}
|
||||
closer()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "handleTxIns")
|
||||
}
|
||||
|
@ -114,40 +102,44 @@ func (h *handler) handleTxIns(ct *claimtrie.ClaimTrie) error {
|
|||
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.ExtractClaimScript(txOut.PkScript)
|
||||
if txscript.IsErrorCode(err, txscript.ErrNotClaimScript) {
|
||||
cs, closer, err := txscript.DecodeClaimScript(txOut.PkScript)
|
||||
if err == txscript.ErrNotClaimScript {
|
||||
closer()
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
closer()
|
||||
return err
|
||||
}
|
||||
|
||||
var id change.ClaimID
|
||||
name := cs.Name
|
||||
name := cs.Name()
|
||||
amt := txOut.Value
|
||||
|
||||
switch cs.Opcode {
|
||||
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)
|
||||
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())
|
||||
copy(id[:], cs.ClaimID())
|
||||
normName := node.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()))
|
||||
closer()
|
||||
continue
|
||||
}
|
||||
|
||||
delete(h.spent, id.Key())
|
||||
err = ct.UpdateClaim(name, op, amt, id)
|
||||
}
|
||||
closer()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "handleTxOuts")
|
||||
}
|
||||
|
@ -164,13 +156,16 @@ func (b *BlockChain) GetNamesChangedInBlock(height int32) ([]string, error) {
|
|||
|
||||
func (b *BlockChain) GetClaimsForName(height int32, name string) (string, *node.Node, error) {
|
||||
|
||||
normalizedName := normalization.NormalizeIfNecessary([]byte(name), height)
|
||||
normalizedName := node.NormalizeIfNecessary([]byte(name), height)
|
||||
|
||||
b.chainLock.RLock()
|
||||
defer b.chainLock.RUnlock()
|
||||
|
||||
n, err := b.claimTrie.NodeAt(height, normalizedName)
|
||||
if err != nil {
|
||||
if n != nil {
|
||||
n.Close()
|
||||
}
|
||||
return string(normalizedName), nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
@ -15,13 +14,13 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
_ "github.com/lbryio/lbcd/database/ffldb"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"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 (
|
||||
|
@ -64,13 +63,13 @@ func isSupportedDbType(dbType string) bool {
|
|||
func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
|
||||
filename = filepath.Join("testdata/", filename)
|
||||
|
||||
var network = 0xd9b4bef9 // bitcoin's network ID
|
||||
var network = wire.MainNet
|
||||
var dr io.Reader
|
||||
var fi io.ReadCloser
|
||||
|
||||
fi, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return blocks, err
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasSuffix(filename, ".bz2") {
|
||||
|
@ -96,7 +95,7 @@ func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
|
|||
break
|
||||
}
|
||||
if rintbuf != uint32(network) {
|
||||
continue
|
||||
break
|
||||
}
|
||||
err = binary.Read(dr, binary.LittleEndian, &rintbuf)
|
||||
blocklen := rintbuf
|
||||
|
@ -106,20 +105,14 @@ func loadBlocks(filename string) (blocks []*btcutil.Block, err error) {
|
|||
// read block
|
||||
dr.Read(rbytes)
|
||||
|
||||
// inject claimtrie:
|
||||
tail := make([]byte, len(rbytes)-68)
|
||||
copy(tail, rbytes[68:])
|
||||
rbytes = append(rbytes[:68], bytes.Repeat([]byte{23}, chainhash.HashSize)...)
|
||||
rbytes = append(rbytes, tail...)
|
||||
|
||||
block, err = btcutil.NewBlockFromBytes(rbytes)
|
||||
if err != nil {
|
||||
return blocks, err
|
||||
return
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
|
||||
return blocks, err
|
||||
return
|
||||
}
|
||||
|
||||
// chainSetup is used to create a new db and chain instance with the genesis
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
package blockchain
|
||||
|
||||
import (
|
||||
"github.com/lbryio/lbcd/btcec"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -42,21 +42,18 @@ func HashToBig(hash *chainhash.Hash) *big.Int {
|
|||
// Like IEEE754 floating point, there are three basic components: the sign,
|
||||
// the exponent, and the mantissa. They are broken out as follows:
|
||||
//
|
||||
// - the most significant 8 bits represent the unsigned base 256 exponent
|
||||
// * the most significant 8 bits represent the unsigned base 256 exponent
|
||||
// * bit 23 (the 24th bit) represents the sign bit
|
||||
// * the least significant 23 bits represent the mantissa
|
||||
//
|
||||
// - bit 23 (the 24th bit) represents the sign bit
|
||||
//
|
||||
// - the least significant 23 bits represent the mantissa
|
||||
//
|
||||
// -------------------------------------------------
|
||||
// | Exponent | Sign | Mantissa |
|
||||
// -------------------------------------------------
|
||||
// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
|
||||
// -------------------------------------------------
|
||||
// -------------------------------------------------
|
||||
// | Exponent | Sign | Mantissa |
|
||||
// -------------------------------------------------
|
||||
// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
|
||||
// -------------------------------------------------
|
||||
//
|
||||
// The formula to calculate N is:
|
||||
//
|
||||
// N = (-1^sign) * mantissa * 256^(exponent-3)
|
||||
// N = (-1^sign) * mantissa * 256^(exponent-3)
|
||||
//
|
||||
// This compact form is only used in bitcoin to encode unsigned 256-bit numbers
|
||||
// which represent difficulty targets, thus there really is not a need for a
|
||||
|
@ -248,11 +245,10 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
|
|||
|
||||
// Get the block node at the previous retarget (targetTimespan days
|
||||
// worth of blocks).
|
||||
blocksBack := b.blocksPerRetarget
|
||||
if blocksBack > lastNode.height {
|
||||
blocksBack = lastNode.height
|
||||
firstNode := lastNode.RelativeAncestor(b.blocksPerRetarget)
|
||||
if lastNode.height == 0 {
|
||||
firstNode = lastNode
|
||||
}
|
||||
firstNode := lastNode.RelativeAncestor(blocksBack)
|
||||
if firstNode == nil {
|
||||
return 0, AssertError("unable to obtain previous retarget block")
|
||||
}
|
||||
|
|
|
@ -26,42 +26,42 @@ caller a high level of flexibility in how they want to react to certain events
|
|||
such as orphan blocks which need their parents requested and newly connected
|
||||
main chain blocks which might result in wallet updates.
|
||||
|
||||
# Bitcoin Chain Processing Overview
|
||||
Bitcoin Chain Processing Overview
|
||||
|
||||
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
|
||||
those rules to provide some intuition into what is going on under the hood, but
|
||||
is by no means exhaustive:
|
||||
|
||||
- Reject duplicate blocks
|
||||
- Perform a series of sanity checks on the block and its transactions such as
|
||||
verifying proof of work, timestamps, number and character of transactions,
|
||||
transaction amounts, script complexity, and merkle root calculations
|
||||
- Compare the block against predetermined checkpoints for expected timestamps
|
||||
and difficulty based on elapsed time since the checkpoint
|
||||
- Save the most recent orphan blocks for a limited time in case their parent
|
||||
blocks become available
|
||||
- Stop processing if the block is an orphan as the rest of the processing
|
||||
depends on the block's position within the block chain
|
||||
- Perform a series of more thorough checks that depend on the block's position
|
||||
within the block chain such as verifying block difficulties adhere to
|
||||
difficulty retarget rules, timestamps are after the median of the last
|
||||
several blocks, all transactions are finalized, checkpoint blocks match, and
|
||||
block versions are in line with the previous blocks
|
||||
- Determine how the block fits into the chain and perform different actions
|
||||
accordingly in order to ensure any side chains which have higher difficulty
|
||||
than the main chain become the new main chain
|
||||
- When a block is being connected to the main chain (either through
|
||||
reorganization of a side chain to the main chain or just extending the
|
||||
main chain), perform further checks on the block's transactions such as
|
||||
verifying transaction duplicates, script complexity for the combination of
|
||||
connected scripts, coinbase maturity, double spends, and connected
|
||||
transaction values
|
||||
- Run the transaction scripts to verify the spender is allowed to spend the
|
||||
coins
|
||||
- Insert the block into the block database
|
||||
- Reject duplicate blocks
|
||||
- Perform a series of sanity checks on the block and its transactions such as
|
||||
verifying proof of work, timestamps, number and character of transactions,
|
||||
transaction amounts, script complexity, and merkle root calculations
|
||||
- Compare the block against predetermined checkpoints for expected timestamps
|
||||
and difficulty based on elapsed time since the checkpoint
|
||||
- Save the most recent orphan blocks for a limited time in case their parent
|
||||
blocks become available
|
||||
- Stop processing if the block is an orphan as the rest of the processing
|
||||
depends on the block's position within the block chain
|
||||
- Perform a series of more thorough checks that depend on the block's position
|
||||
within the block chain such as verifying block difficulties adhere to
|
||||
difficulty retarget rules, timestamps are after the median of the last
|
||||
several blocks, all transactions are finalized, checkpoint blocks match, and
|
||||
block versions are in line with the previous blocks
|
||||
- Determine how the block fits into the chain and perform different actions
|
||||
accordingly in order to ensure any side chains which have higher difficulty
|
||||
than the main chain become the new main chain
|
||||
- When a block is being connected to the main chain (either through
|
||||
reorganization of a side chain to the main chain or just extending the
|
||||
main chain), perform further checks on the block's transactions such as
|
||||
verifying transaction duplicates, script complexity for the combination of
|
||||
connected scripts, coinbase maturity, double spends, and connected
|
||||
transaction values
|
||||
- Run the transaction scripts to verify the spender is allowed to spend the
|
||||
coins
|
||||
- Insert the block into the block database
|
||||
|
||||
# Errors
|
||||
Errors
|
||||
|
||||
Errors returned by this package are either the raw errors provided by underlying
|
||||
calls or of type blockchain.RuleError. This allows the caller to differentiate
|
||||
|
@ -70,12 +70,12 @@ violations through type assertions. In addition, callers can programmatically
|
|||
determine the specific rule violation by examining the ErrorCode field of the
|
||||
type asserted blockchain.RuleError.
|
||||
|
||||
# Bitcoin Improvement Proposals
|
||||
Bitcoin Improvement Proposals
|
||||
|
||||
This package includes spec changes outlined by the following BIPs:
|
||||
|
||||
BIP0016 (https://en.bitcoin.it/wiki/BIP_0016)
|
||||
BIP0030 (https://en.bitcoin.it/wiki/BIP_0030)
|
||||
BIP0034 (https://en.bitcoin.it/wiki/BIP_0034)
|
||||
BIP0016 (https://en.bitcoin.it/wiki/BIP_0016)
|
||||
BIP0030 (https://en.bitcoin.it/wiki/BIP_0030)
|
||||
BIP0034 (https://en.bitcoin.it/wiki/BIP_0034)
|
||||
*/
|
||||
package blockchain
|
||||
|
|
|
@ -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/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
_ "github.com/lbryio/lbcd/database/ffldb"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
)
|
||||
|
||||
// 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 9c89283ba0f3227f6c03b70216b9f665f0118d5e0fa729cedf4fb34d6a34f463
|
||||
}
|
||||
|
||||
// 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/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/blockchain/fullblocktests"
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
_ "github.com/lbryio/lbcd/database/ffldb"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
)
|
||||
|
||||
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",
|
||||
fullblocktests.FbRegressionNetParams)
|
||||
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
|
||||
==============
|
||||
|
||||
[![Build Status](https://github.com/lbryio/lbcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/lbryio/lbcd/actions)
|
||||
[![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)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/lbryio/lbcd/blockchain/fullblocktests)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain/fullblocktests)
|
||||
|
||||
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
|
||||
|
@ -20,7 +20,7 @@ of blocks that exercise the consensus validation rules.
|
|||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/lbryio/lbcd/blockchain/fullblocktests
|
||||
$ go get -u github.com/btcsuite/btcd/blockchain/fullblocktests
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
|
@ -18,20 +18,20 @@ import (
|
|||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/btcec"
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
// Intentionally defined here rather than using constants from codebase
|
||||
// to ensure consensus changes are detected.
|
||||
maxBlockSigOps = 20000
|
||||
maxBlockSize = 8000000
|
||||
maxBlockSize = 2000000
|
||||
minCoinbaseScriptLen = 2
|
||||
maxCoinbaseScriptLen = 100
|
||||
medianTimeBlocks = 11
|
||||
|
@ -342,8 +342,10 @@ func solveBlock(header *wire.BlockHeader) bool {
|
|||
return
|
||||
default:
|
||||
hdr.Nonce = i
|
||||
hash := hdr.BlockPoWHash()
|
||||
if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
|
||||
hash := hdr.BlockHash()
|
||||
if blockchain.HashToBig(&hash).Cmp(
|
||||
targetDifficulty) <= 0 {
|
||||
|
||||
results <- sbResult{true, i}
|
||||
return
|
||||
}
|
||||
|
@ -464,9 +466,9 @@ func createSpendTxForTx(tx *wire.MsgTx, fee btcutil.Amount) *wire.MsgTx {
|
|||
// - A coinbase that pays the required subsidy to an OP_TRUE script
|
||||
// - When a spendable output is provided:
|
||||
// - A transaction that spends from the provided output the following outputs:
|
||||
// - One that pays the inputs amount minus 1 atom to an OP_TRUE script
|
||||
// - One that contains an OP_RETURN output with a random uint64 in order to
|
||||
// ensure the transaction has a unique hash
|
||||
// - One that pays the inputs amount minus 1 atom to an OP_TRUE script
|
||||
// - One that contains an OP_RETURN output with a random uint64 in order to
|
||||
// ensure the transaction has a unique hash
|
||||
//
|
||||
// Additionally, if one or more munge functions are specified, they will be
|
||||
// invoked with the block prior to solving it. This provides callers with the
|
||||
|
@ -809,7 +811,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
|||
|
||||
// Create a test generator instance initialized with the genesis block
|
||||
// as the tip.
|
||||
g, err := makeTestGenerator(FbRegressionNetParams)
|
||||
g, err := makeTestGenerator(regressionNetParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1442,7 +1444,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
|||
// Keep incrementing the nonce until the hash treated as
|
||||
// a uint256 is higher than the limit.
|
||||
b46.Header.Nonce++
|
||||
blockHash := b46.Header.BlockPoWHash()
|
||||
blockHash := b46.BlockHash()
|
||||
hashNum := blockchain.HashToBig(&blockHash)
|
||||
if hashNum.Cmp(g.params.PowLimit) >= 0 {
|
||||
break
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// newHashFromStr converts the passed big-endian hex string into a
|
||||
|
@ -54,7 +54,6 @@ var (
|
|||
Version: 1,
|
||||
PrevBlock: *newHashFromStr("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
MerkleRoot: *newHashFromStr("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"),
|
||||
ClaimTrie: chainhash.Hash{1}, // EmptyTrieHash
|
||||
Timestamp: time.Unix(1296688602, 0), // 2011-02-02 23:16:42 +0000 UTC
|
||||
Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000]
|
||||
Nonce: 2,
|
||||
|
@ -84,25 +83,23 @@ var (
|
|||
LockTime: 0,
|
||||
}},
|
||||
}
|
||||
|
||||
regTestGenesisBlockHash = regTestGenesisBlock.BlockHash()
|
||||
)
|
||||
|
||||
// FbRegressionNetParams defines the network parameters for the regression test
|
||||
// regressionNetParams defines the network parameters for the regression test
|
||||
// network.
|
||||
//
|
||||
// NOTE: The test generator intentionally does not use the existing definitions
|
||||
// in the chaincfg package since the intent is to be able to generate known
|
||||
// good tests which exercise that code. Using the chaincfg parameters would
|
||||
// allow them to change out from under the tests potentially invalidating them.
|
||||
var FbRegressionNetParams = &chaincfg.Params{
|
||||
var regressionNetParams = &chaincfg.Params{
|
||||
Name: "regtest",
|
||||
Net: wire.TestNet,
|
||||
DefaultPort: "18444",
|
||||
|
||||
// Chain parameters
|
||||
GenesisBlock: ®TestGenesisBlock,
|
||||
GenesisHash: ®TestGenesisBlockHash,
|
||||
GenesisHash: newHashFromStr("5bec7567af40504e0994db3b573c186fffcc4edefe096ff2e58d00523bd7e8a6"),
|
||||
PowLimit: regressionPowLimit,
|
||||
PowLimitBits: 0x207fffff,
|
||||
CoinbaseMaturity: 100,
|
||||
|
@ -116,7 +113,6 @@ var FbRegressionNetParams = &chaincfg.Params{
|
|||
ReduceMinDifficulty: true,
|
||||
MinDiffReductionTime: time.Minute * 20, // TargetTimePerBlock * 2
|
||||
GenerateSupported: true,
|
||||
MinerConfirmationWindow: 1,
|
||||
|
||||
// Checkpoints ordered from oldest to newest.
|
||||
Checkpoints: nil,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
indexers
|
||||
========
|
||||
|
||||
[![Build Status](https://github.com/lbryio/lbcd/workflows/Build%20and%20Test/badge.svg)](https://github.com/lbryio/lbcd/actions)
|
||||
[![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)
|
||||
[![GoDoc](https://pkg.go.dev/github.com/lbryio/lbcd/blockchain/indexers?status.png)](https://pkg.go.dev/github.com/lbryio/lbcd/blockchain/indexers)
|
||||
[![GoDoc](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain/indexers?status.png)](https://pkg.go.dev/github.com/btcsuite/btcd/blockchain/indexers)
|
||||
|
||||
Package indexers implements optional block chain indexes.
|
||||
|
||||
|
@ -23,7 +23,7 @@ via an RPC interface.
|
|||
## Installation
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/lbryio/lbcd/blockchain/indexers
|
||||
$ go get -u github.com/btcsuite/btcd/blockchain/indexers
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
|
@ -9,13 +9,13 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// addrIndexBucket provides a mock address index database bucket by implementing
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/btcsuite/btclog"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// blockProgressLogger provides periodic logging for other services in order
|
||||
|
@ -27,9 +27,8 @@ type blockProgressLogger struct {
|
|||
|
||||
// newBlockProgressLogger returns a new block progress logger.
|
||||
// The progress message is templated as follows:
|
||||
//
|
||||
// {progressAction} {numProcessed} {blocks|block} in the last {timePeriod}
|
||||
// ({numTxs}, height {lastBlockHeight}, {lastBlockTimeStamp})
|
||||
// {progressAction} {numProcessed} {blocks|block} in the last {timePeriod}
|
||||
// ({numTxs}, height {lastBlockHeight}, {lastBlockTimeStamp})
|
||||
func newBlockProgressLogger(progressMessage string, logger btclog.Logger) *blockProgressLogger {
|
||||
return &blockProgressLogger{
|
||||
lastBlockLogTime: time.Now(),
|
||||
|
|
|
@ -7,14 +7,14 @@ package indexers
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/lbryio/lbcutil/gcs"
|
||||
"github.com/lbryio/lbcutil/gcs/builder"
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/gcs"
|
||||
"github.com/btcsuite/btcutil/gcs/builder"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -11,9 +11,9 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/lbryio/lbcd/blockchain"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -183,7 +183,7 @@ func (m *medianTime) AddTimeSample(sourceID string, timeVal time.Time) {
|
|||
// Warn if none of the time samples are close.
|
||||
if !remoteHasCloseTime {
|
||||
log.Warnf("Please check your date and time " +
|
||||
"are correct! lbcd will not work " +
|
||||
"are correct! btcd will not work " +
|
||||
"properly with an invalid time")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,9 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -87,7 +86,7 @@ func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.
|
|||
//
|
||||
// The above stored as a linear array is as follows:
|
||||
//
|
||||
// [h1 h2 h3 h4 h12 h34 root]
|
||||
// [h1 h2 h3 h4 h12 h34 root]
|
||||
//
|
||||
// As the above shows, the merkle root is always the last element in the array.
|
||||
//
|
||||
|
@ -228,20 +227,6 @@ func ValidateWitnessCommitment(blk *btcutil.Block) error {
|
|||
// coinbase transaction MUST have exactly one witness element within
|
||||
// its witness data and that element must be exactly
|
||||
// CoinbaseWitnessDataLen bytes.
|
||||
//
|
||||
// Some popular pool software, for example yiimp, uses pre-BIP0141
|
||||
// coinbase struture. In this case, we don't just accept it, but also
|
||||
// turn it into post-BIP0141 format.
|
||||
if len(coinbaseTx.MsgTx().TxIn[0].Witness) == 0 {
|
||||
log.Infof("pre-BIP0141 coinbase transaction detected. Height: %d", blk.Height())
|
||||
|
||||
var witnessNonce [CoinbaseWitnessDataLen]byte
|
||||
coinbaseTx.MsgTx().TxIn[0].Witness = wire.TxWitness{witnessNonce[:]}
|
||||
blk.MsgBlock().Transactions[0].TxIn[0].Witness = wire.TxWitness{witnessNonce[:]}
|
||||
|
||||
// Clear cached serialized block.
|
||||
blk.SetBytes(nil)
|
||||
}
|
||||
coinbaseWitness := coinbaseTx.MsgTx().TxIn[0].Witness
|
||||
if len(coinbaseWitness) != 1 {
|
||||
str := fmt.Sprintf("the coinbase transaction has %d items in "+
|
||||
|
|
|
@ -1,21 +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"
|
||||
)
|
||||
|
||||
// TestMerkle tests the BuildMerkleTreeStore API.
|
||||
func TestMerkle(t *testing.T) {
|
||||
block := GetBlock100000()
|
||||
merkles := BuildMerkleTreeStore(block.Transactions(), false)
|
||||
calculatedMerkleRoot := merkles[len(merkles)-1]
|
||||
wantMerkle := block.MsgBlock().Header.MerkleRoot
|
||||
if !wantMerkle.IsEqual(calculatedMerkleRoot) {
|
||||
t.Errorf("BuildMerkleTreeStore: merkle root mismatch - "+
|
||||
"got %v, want %v", calculatedMerkleRoot, wantMerkle)
|
||||
}
|
||||
}
|
|
@ -50,9 +50,9 @@ func (n NotificationType) String() string {
|
|||
// Notification defines notification that is sent to the caller via the callback
|
||||
// function provided during the call to New and consists of a notification type
|
||||
// as well as associated data that depends on the type as follows:
|
||||
// - NTBlockAccepted: *btcutil.Block
|
||||
// - NTBlockConnected: *btcutil.Block
|
||||
// - NTBlockDisconnected: *btcutil.Block
|
||||
// - NTBlockAccepted: *btcutil.Block
|
||||
// - NTBlockConnected: *btcutil.Block
|
||||
// - NTBlockDisconnected: *btcutil.Block
|
||||
type Notification struct {
|
||||
Type NotificationType
|
||||
Data interface{}
|
||||
|
|
|
@ -8,9 +8,9 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// BehaviorFlags is a bitmask defining tweaks to the normal behavior when
|
||||
|
@ -29,10 +29,6 @@ const (
|
|||
// not be performed.
|
||||
BFNoPoWCheck
|
||||
|
||||
// BFNoDupBlockCheck signals if the block should skip existence
|
||||
// checks.
|
||||
BFNoDupBlockCheck
|
||||
|
||||
// BFNone is a convenience value to specifically indicate no flags.
|
||||
BFNone BehaviorFlags = 0
|
||||
)
|
||||
|
@ -152,26 +148,24 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bo
|
|||
blockHash := block.Hash()
|
||||
log.Tracef("Processing block %v", blockHash)
|
||||
|
||||
if flags&BFNoDupBlockCheck != BFNoDupBlockCheck {
|
||||
// The block must not already exist in the main chain or side chains.
|
||||
exists, err := b.blockExists(blockHash)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if exists {
|
||||
str := fmt.Sprintf("already have block %v", blockHash)
|
||||
return false, false, ruleError(ErrDuplicateBlock, str)
|
||||
}
|
||||
// The block must not already exist in the main chain or side chains.
|
||||
exists, err := b.blockExists(blockHash)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if exists {
|
||||
str := fmt.Sprintf("already have block %v", blockHash)
|
||||
return false, false, ruleError(ErrDuplicateBlock, str)
|
||||
}
|
||||
|
||||
// The block must not already exist as an orphan.
|
||||
if _, exists := b.orphans[*blockHash]; exists {
|
||||
str := fmt.Sprintf("already have block (orphan) %v", blockHash)
|
||||
return false, false, ruleError(ErrDuplicateBlock, str)
|
||||
}
|
||||
// The block must not already exist as an orphan.
|
||||
if _, exists := b.orphans[*blockHash]; exists {
|
||||
str := fmt.Sprintf("already have block (orphan) %v", blockHash)
|
||||
return false, false, ruleError(ErrDuplicateBlock, str)
|
||||
}
|
||||
|
||||
// Perform preliminary sanity checks on the block and its transactions.
|
||||
err := checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags)
|
||||
err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ import (
|
|||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// txValidateItem holds a transaction along with which input to validate.
|
||||
|
@ -86,6 +86,7 @@ out:
|
|||
txIn.PreviousOutPoint, err, witness,
|
||||
sigScript, pkScript)
|
||||
err := ruleError(ErrScriptMalformed, str)
|
||||
vm.Close()
|
||||
v.sendResult(err)
|
||||
break out
|
||||
}
|
||||
|
@ -100,11 +101,13 @@ out:
|
|||
txIn.PreviousOutPoint, err, witness,
|
||||
sigScript, pkScript)
|
||||
err := ruleError(ErrScriptValidation, str)
|
||||
vm.Close()
|
||||
v.sendResult(err)
|
||||
break out
|
||||
}
|
||||
|
||||
// Validation succeeded.
|
||||
vm.Close()
|
||||
v.sendResult(nil)
|
||||
|
||||
case <-v.quitChan:
|
||||
|
|
|
@ -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/lbryio/lbcd/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
|
||||
}
|
||||
}
|
BIN
blockchain/testdata/blk_0_to_4.dat.bz2
vendored
Normal file
BIN
blockchain/testdata/blk_0_to_4.dat.bz2
vendored
Normal file
Binary file not shown.
BIN
blockchain/testdata/blk_3A.dat.bz2
vendored
Normal file
BIN
blockchain/testdata/blk_3A.dat.bz2
vendored
Normal file
Binary file not shown.
BIN
blockchain/testdata/blk_4A.dat.bz2
vendored
Normal file
BIN
blockchain/testdata/blk_4A.dat.bz2
vendored
Normal file
Binary file not shown.
BIN
blockchain/testdata/blk_5A.dat.bz2
vendored
Normal file
BIN
blockchain/testdata/blk_5A.dat.bz2
vendored
Normal file
Binary file not shown.
180
blockchain/testdata/reorgtest.hex
vendored
Normal file
180
blockchain/testdata/reorgtest.hex
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
File path: reorgTest/blk_0_to_4.dat
|
||||
|
||||
Block 0:
|
||||
f9beb4d9
|
||||
1d010000
|
||||
|
||||
01000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 3ba3edfd 7a7b12b2 7ac72c3e 67768f61 7fc81bc3 888a5132 3a9fb8aa
|
||||
4b1e5e4a 29ab5f49 ffff001d 1dac2b7c
|
||||
01
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff4d04ff ff001d01 04455468 65205469 6d657320 30332f4a
|
||||
616e2f32 30303920 4368616e 63656c6c 6f72206f 6e206272 696e6b20 6f662073
|
||||
65636f6e 64206261 696c6f75 7420666f 72206261 6e6b73ff ffffff01 00f2052a
|
||||
01000000 43410467 8afdb0fe 55482719 67f1a671 30b7105c d6a828e0 3909a679
|
||||
62e0ea1f 61deb649 f6bc3f4c ef38c4f3 5504e51e c112de5c 384df7ba 0b8d578a
|
||||
4c702b6b f11d5fac 00000000
|
||||
Block 1:
|
||||
f9beb4d9
|
||||
d4000000
|
||||
|
||||
01000000 6fe28c0a b6f1b372 c1a6a246 ae63f74f 931e8365 e15a089c 68d61900
|
||||
00000000 3bbd67ad e98fbbb7 0718cd80 f9e9acf9 3b5fae91 7bb2b41d 4c3bb82c
|
||||
77725ca5 81ad5f49 ffff001d 44e69904
|
||||
01
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff04722f 2e2bffff ffff0100 f2052a01 00000043 41046868
|
||||
0737c76d abb801cb 2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02
|
||||
b5ac9e8b 4c9f49be 5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ac00
|
||||
000000
|
||||
Block 2:
|
||||
f9beb4d9
|
||||
95010000
|
||||
|
||||
01000000 13ca7940 4c11c63e ca906bbd f190b751 2872b857 1b5143ae e8cb5737
|
||||
00000000 fc07c983 d7391736 0aeda657 29d0d4d3 2533eb84 76ee9d64 aa27538f
|
||||
9b4fc00a d9af5f49 ffff001d 630bea22
|
||||
02
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff04eb96 14e5ffff ffff0100 f2052a01 00000043 41046868
|
||||
0737c76d abb801cb 2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02
|
||||
b5ac9e8b 4c9f49be 5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ac00
|
||||
000000
|
||||
|
||||
01000000 0163451d 1002611c 1388d5ba 4ddfdf99 196a86b5 990fb5b0 dc786207
|
||||
4fdcb8ee d2000000 004a4930 46022100 3dde52c6 5e339f45 7fe1015e 70eed208
|
||||
872eb71e dd484c07 206b190e cb2ec3f8 02210011 c78dcfd0 3d43fa63 61242a33
|
||||
6291ba2a 8c1ef5bc d5472126 2468f2bf 8dee4d01 ffffffff 0200ca9a 3b000000
|
||||
001976a9 14cb2abd e8bccacc 32e893df 3a054b9e f7f227a4 ce88ac00 286bee00
|
||||
00000019 76a914ee 26c56fc1 d942be8d 7a24b2a1 001dd894 69398088 ac000000
|
||||
00
|
||||
Block 3:
|
||||
f9beb4d9
|
||||
96020000
|
||||
|
||||
01000000 7d338254 0506faab 0d4cf179 45dda023 49db51f9 6233f24c 28002258
|
||||
00000000 4806fe80 bf85931b 882ea645 77ca5a03 22bb8af2 3f277b20 55f160cd
|
||||
972c8e8b 31b25f49 ffff001d e8f0c653
|
||||
03
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff044abd 8159ffff ffff0100 f2052a01 00000043 4104b95c
|
||||
249d84f4 17e3e395 a1274254 28b54067 1cc15881 eb828c17 b722a53f c599e21c
|
||||
a5e56c90 f340988d 3933acc7 6beb832f d64cab07 8ddf3ce7 32923031 d1a8ac00
|
||||
000000
|
||||
|
||||
01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb
|
||||
bad253d3 77010000 008c4930 46022100 96ee0d02 b35fd61e 4960b44f f396f67e
|
||||
01fe17f9 de4e0c17 b6a963bd ab2b50a6 02210034 920d4daa 7e9f8abe 5675c931
|
||||
495809f9 0b9c1189 d05fbaf1 dd6696a5 b0d8f301 41046868 0737c76d abb801cb
|
||||
2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02 b5ac9e8b 4c9f49be
|
||||
5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ffff ffff0100 286bee00
|
||||
00000019 76a914c5 22664fb0 e55cdc5c 0cea73b4 aad97ec8 34323288 ac000000
|
||||
00
|
||||
|
||||
01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb
|
||||
bad253d3 77000000 008c4930 46022100 b08b922a c4bde411 1c229f92 9fe6eb6a
|
||||
50161f98 1f4cf47e a9214d35 bf74d380 022100d2 f6640327 e677a1e1 cc474991
|
||||
b9a48ba5 bd1e0c94 d1c8df49 f7b0193b 7ea4fa01 4104b95c 249d84f4 17e3e395
|
||||
a1274254 28b54067 1cc15881 eb828c17 b722a53f c599e21c a5e56c90 f340988d
|
||||
3933acc7 6beb832f d64cab07 8ddf3ce7 32923031 d1a8ffff ffff0100 ca9a3b00
|
||||
00000019 76a914c5 22664fb0 e55cdc5c 0cea73b4 aad97ec8 34323288 ac000000
|
||||
00
|
||||
|
||||
Block 4:
|
||||
f9beb4d9
|
||||
73010000
|
||||
|
||||
01000000 5da36499 06f35e09 9be42a1d 87b6dd42 11bc1400 6c220694 0807eaae
|
||||
00000000 48eeeaed 2d9d8522 e6201173 743823fd 4b87cd8a ca8e6408 ec75ca38
|
||||
302c2ff0 89b45f49 ffff001d 00530839
|
||||
02
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff04d41d 2213ffff ffff0100 f2052a01 00000043 4104678a
|
||||
fdb0fe55 48271967 f1a67130 b7105cd6 a828e039 09a67962 e0ea1f61 deb649f6
|
||||
bc3f4cef 38c4f355 04e51ec1 12de5c38 4df7ba0b 8d578a4c 702b6bf1 1d5fac00
|
||||
000000
|
||||
|
||||
01000000 0163451d 1002611c 1388d5ba 4ddfdf99 196a86b5 990fb5b0 dc786207
|
||||
4fdcb8ee d2000000 004a4930 46022100 8c8fd57b 48762135 8d8f3e69 19f33e08
|
||||
804736ff 83db47aa 248512e2 6df9b8ba 022100b0 c59e5ee7 bfcbfcd1 a4d83da9
|
||||
55fb260e fda7f42a 25522625 a3d6f2d9 1174a701 ffffffff 0100f205 2a010000
|
||||
001976a9 14c52266 4fb0e55c dc5c0cea 73b4aad9 7ec83432 3288ac00 000000
|
||||
|
||||
File path: reorgTest/blk_3A.dat
|
||||
Block 3A:
|
||||
f9beb4d9
|
||||
96020000
|
||||
|
||||
01000000 7d338254 0506faab 0d4cf179 45dda023 49db51f9 6233f24c 28002258
|
||||
00000000 5a15f573 1177a353 bdca7aab 20e16624 dfe90adc 70accadc 68016732
|
||||
302c20a7 31b25f49 ffff001d 6a901440
|
||||
03
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff04ad1b e7d5ffff ffff0100 f2052a01 00000043 4104ed83
|
||||
704c95d8 29046f1a c2780621 1132102c 34e9ac7f fa1b7111 0658e5b9 d1bdedc4
|
||||
16f5cefc 1db0625c d0c75de8 192d2b59 2d7e3b00 bcfb4a0e 860d880f d1fcac00
|
||||
000000
|
||||
|
||||
01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb
|
||||
bad253d3 77010000 008c4930 46022100 96ee0d02 b35fd61e 4960b44f f396f67e
|
||||
01fe17f9 de4e0c17 b6a963bd ab2b50a6 02210034 920d4daa 7e9f8abe 5675c931
|
||||
495809f9 0b9c1189 d05fbaf1 dd6696a5 b0d8f301 41046868 0737c76d abb801cb
|
||||
2204f57d be4e4579 e4f710cd 67dc1b42 27592c81 e9b5cf02 b5ac9e8b 4c9f49be
|
||||
5251056b 6a6d011e 4c37f6b6 d17ede6b 55faa235 19e2ffff ffff0100 286bee00
|
||||
00000019 76a914c5 22664fb0 e55cdc5c 0cea73b4 aad97ec8 34323288 ac000000
|
||||
00
|
||||
|
||||
01000000 01f287b5 e067e1cf 80f7da8a f89917b5 505094db d82412d9 35b665eb
|
||||
bad253d3 77000000 008c4930 46022100 9cc67ddd aa6f592a 6b2babd4 d6ff954f
|
||||
25a784cf 4fe4bb13 afb9f49b 08955119 022100a2 d99545b7 94080757 fcf2b563
|
||||
f2e91287 86332f46 0ec6b90f f085fb28 41a69701 4104b95c 249d84f4 17e3e395
|
||||
a1274254 28b54067 1cc15881 eb828c17 b722a53f c599e21c a5e56c90 f340988d
|
||||
3933acc7 6beb832f d64cab07 8ddf3ce7 32923031 d1a8ffff ffff0100 ca9a3b00
|
||||
00000019 76a914ee 26c56fc1 d942be8d 7a24b2a1 001dd894 69398088 ac000000
|
||||
00
|
||||
|
||||
File path: reorgTest/blk_4A.dat
|
||||
Block 4A:
|
||||
f9beb4d9
|
||||
d4000000
|
||||
|
||||
01000000 aae77468 2205667d 4f413a58 47cc8fe8 9795f1d5 645d5b24 1daf3c92
|
||||
00000000 361c9cde a09637a0 d0c05c3b 4e7a5d91 9edb184a 0a4c7633 d92e2ddd
|
||||
f04cb854 89b45f49 ffff001d 9e9aa1e8
|
||||
01
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff0401b8 f3eaffff ffff0100 f2052a01 00000043 4104678a
|
||||
fdb0fe55 48271967 f1a67130 b7105cd6 a828e039 09a67962 e0ea1f61 deb649f6
|
||||
bc3f4cef 38c4f355 04e51ec1 12de5c38 4df7ba0b 8d578a4c 702b6bf1 1d5fac00
|
||||
000000
|
||||
|
||||
File path: reorgTest/blk_5A.dat
|
||||
Block 5A:
|
||||
f9beb4d9
|
||||
73010000
|
||||
|
||||
01000000 ebc7d0de 9c31a71b 7f41d275 2c080ba4 11e1854b d45cb2cf 8c1e4624
|
||||
00000000 a607774b 79b8eb50 b52a5a32 c1754281 ec67f626 9561df28 57d1fe6a
|
||||
ea82c696 e1b65f49 ffff001d 4a263577
|
||||
02
|
||||
|
||||
01000000 01000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
00000000 00ffffff ff049971 0c7dffff ffff0100 f2052a01 00000043 4104678a
|
||||
fdb0fe55 48271967 f1a67130 b7105cd6 a828e039 09a67962 e0ea1f61 deb649f6
|
||||
bc3f4cef 38c4f355 04e51ec1 12de5c38 4df7ba0b 8d578a4c 702b6bf1 1d5fac00
|
||||
000000
|
||||
|
||||
01000000 0163451d 1002611c 1388d5ba 4ddfdf99 196a86b5 990fb5b0 dc786207
|
||||
4fdcb8ee d2000000 004a4930 46022100 8c8fd57b 48762135 8d8f3e69 19f33e08
|
||||
804736ff 83db47aa 248512e2 6df9b8ba 022100b0 c59e5ee7 bfcbfcd1 a4d83da9
|
||||
55fb260e fda7f42a 25522625 a3d6f2d9 1174a701 ffffffff 0100f205 2a010000
|
||||
001976a9 14c52266 4fb0e55c dc5c0cea 73b4aad9 7ec83432 3288ac00 000000
|
||||
|
|
@ -7,7 +7,7 @@ package blockchain
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// ThresholdState define the various threshold states used when voting on
|
||||
|
|
|
@ -7,7 +7,7 @@ package blockchain
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// TestThresholdStateStringer tests the stringized output for the
|
||||
|
|
|
@ -11,9 +11,9 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -232,25 +232,24 @@ func determineMainChainBlocks(blocksMap map[chainhash.Hash]*blockChainContext, t
|
|||
//
|
||||
// The legacy format is as follows:
|
||||
//
|
||||
// <version><height><header code><unspentness bitmap>[<compressed txouts>,...]
|
||||
// <version><height><header code><unspentness bitmap>[<compressed txouts>,...]
|
||||
//
|
||||
// Field Type Size
|
||||
// version VLQ variable
|
||||
// block height VLQ variable
|
||||
// header code VLQ variable
|
||||
// unspentness bitmap []byte variable
|
||||
// compressed txouts
|
||||
// compressed amount VLQ variable
|
||||
// compressed script []byte variable
|
||||
// Field Type Size
|
||||
// version VLQ variable
|
||||
// block height VLQ variable
|
||||
// header code VLQ variable
|
||||
// unspentness bitmap []byte variable
|
||||
// compressed txouts
|
||||
// compressed amount VLQ variable
|
||||
// compressed script []byte variable
|
||||
//
|
||||
// The serialized header code format is:
|
||||
//
|
||||
// bit 0 - containing transaction is a coinbase
|
||||
// bit 1 - output zero is unspent
|
||||
// bit 2 - output one is unspent
|
||||
// bits 3-x - number of bytes in unspentness bitmap. When both bits 1 and 2
|
||||
// are unset, it encodes N-1 since there must be at least one unspent
|
||||
// output.
|
||||
// bit 0 - containing transaction is a coinbase
|
||||
// bit 1 - output zero is unspent
|
||||
// bit 2 - output one is unspent
|
||||
// bits 3-x - number of bytes in unspentness bitmap. When both bits 1 and 2
|
||||
// are unset, it encodes N-1 since there must be at least one unspent
|
||||
// output.
|
||||
//
|
||||
// The rationale for the header code scheme is as follows:
|
||||
// - Transactions which only pay to a single output and a change output are
|
||||
|
@ -270,65 +269,65 @@ func determineMainChainBlocks(blocksMap map[chainhash.Hash]*blockChainContext, t
|
|||
// From tx in main blockchain:
|
||||
// Blk 1, 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098
|
||||
//
|
||||
// 010103320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52
|
||||
// <><><><------------------------------------------------------------------>
|
||||
// | | \--------\ |
|
||||
// | height | compressed txout 0
|
||||
// version header code
|
||||
// 010103320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52
|
||||
// <><><><------------------------------------------------------------------>
|
||||
// | | \--------\ |
|
||||
// | height | compressed txout 0
|
||||
// version header code
|
||||
//
|
||||
// - version: 1
|
||||
// - height: 1
|
||||
// - header code: 0x03 (coinbase, output zero unspent, 0 bytes of unspentness)
|
||||
// - unspentness: Nothing since it is zero bytes
|
||||
// - compressed txout 0:
|
||||
// - 0x32: VLQ-encoded compressed amount for 5000000000 (50 BTC)
|
||||
// - 0x04: special script type pay-to-pubkey
|
||||
// - 0x96...52: x-coordinate of the pubkey
|
||||
// - version: 1
|
||||
// - height: 1
|
||||
// - header code: 0x03 (coinbase, output zero unspent, 0 bytes of unspentness)
|
||||
// - unspentness: Nothing since it is zero bytes
|
||||
// - compressed txout 0:
|
||||
// - 0x32: VLQ-encoded compressed amount for 5000000000 (50 BTC)
|
||||
// - 0x04: special script type pay-to-pubkey
|
||||
// - 0x96...52: x-coordinate of the pubkey
|
||||
//
|
||||
// Example 2:
|
||||
// From tx in main blockchain:
|
||||
// Blk 113931, 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
|
||||
//
|
||||
// 0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58
|
||||
// <><----><><><------------------------------------------><-------------------------------------------->
|
||||
// | | | \-------------------\ | |
|
||||
// version | \--------\ unspentness | compressed txout 2
|
||||
// height header code compressed txout 0
|
||||
// 0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58
|
||||
// <><----><><><------------------------------------------><-------------------------------------------->
|
||||
// | | | \-------------------\ | |
|
||||
// version | \--------\ unspentness | compressed txout 2
|
||||
// height header code compressed txout 0
|
||||
//
|
||||
// - version: 1
|
||||
// - height: 113931
|
||||
// - header code: 0x0a (output zero unspent, 1 byte in unspentness bitmap)
|
||||
// - unspentness: [0x01] (bit 0 is set, so output 0+2 = 2 is unspent)
|
||||
// NOTE: It's +2 since the first two outputs are encoded in the header code
|
||||
// - compressed txout 0:
|
||||
// - 0x12: VLQ-encoded compressed amount for 20000000 (0.2 BTC)
|
||||
// - 0x00: special script type pay-to-pubkey-hash
|
||||
// - 0xe2...8a: pubkey hash
|
||||
// - compressed txout 2:
|
||||
// - 0x8009: VLQ-encoded compressed amount for 15000000 (0.15 BTC)
|
||||
// - 0x00: special script type pay-to-pubkey-hash
|
||||
// - 0xb8...58: pubkey hash
|
||||
// - version: 1
|
||||
// - height: 113931
|
||||
// - header code: 0x0a (output zero unspent, 1 byte in unspentness bitmap)
|
||||
// - unspentness: [0x01] (bit 0 is set, so output 0+2 = 2 is unspent)
|
||||
// NOTE: It's +2 since the first two outputs are encoded in the header code
|
||||
// - compressed txout 0:
|
||||
// - 0x12: VLQ-encoded compressed amount for 20000000 (0.2 BTC)
|
||||
// - 0x00: special script type pay-to-pubkey-hash
|
||||
// - 0xe2...8a: pubkey hash
|
||||
// - compressed txout 2:
|
||||
// - 0x8009: VLQ-encoded compressed amount for 15000000 (0.15 BTC)
|
||||
// - 0x00: special script type pay-to-pubkey-hash
|
||||
// - 0xb8...58: pubkey hash
|
||||
//
|
||||
// Example 3:
|
||||
// From tx in main blockchain:
|
||||
// Blk 338156, 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620
|
||||
//
|
||||
// 0193d06c100000108ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6
|
||||
// <><----><><----><-------------------------------------------------->
|
||||
// | | | \-----------------\ |
|
||||
// version | \--------\ unspentness |
|
||||
// height header code compressed txout 22
|
||||
// 0193d06c100000108ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6
|
||||
// <><----><><----><-------------------------------------------------->
|
||||
// | | | \-----------------\ |
|
||||
// version | \--------\ unspentness |
|
||||
// height header code compressed txout 22
|
||||
//
|
||||
// - version: 1
|
||||
// - height: 338156
|
||||
// - header code: 0x10 (2+1 = 3 bytes in unspentness bitmap)
|
||||
// NOTE: It's +1 since neither bit 1 nor 2 are set, so N-1 is encoded.
|
||||
// - unspentness: [0x00 0x00 0x10] (bit 20 is set, so output 20+2 = 22 is unspent)
|
||||
// NOTE: It's +2 since the first two outputs are encoded in the header code
|
||||
// - compressed txout 22:
|
||||
// - 0x8ba5b9e763: VLQ-encoded compressed amount for 366875659 (3.66875659 BTC)
|
||||
// - 0x01: special script type pay-to-script-hash
|
||||
// - 0x1d...e6: script hash
|
||||
// - version: 1
|
||||
// - height: 338156
|
||||
// - header code: 0x10 (2+1 = 3 bytes in unspentness bitmap)
|
||||
// NOTE: It's +1 since neither bit 1 nor 2 are set, so N-1 is encoded.
|
||||
// - unspentness: [0x00 0x00 0x10] (bit 20 is set, so output 20+2 = 22 is unspent)
|
||||
// NOTE: It's +2 since the first two outputs are encoded in the header code
|
||||
// - compressed txout 22:
|
||||
// - 0x8ba5b9e763: VLQ-encoded compressed amount for 366875659 (3.66875659 BTC)
|
||||
// - 0x01: special script type pay-to-script-hash
|
||||
// - 0x1d...e6: script hash
|
||||
func deserializeUtxoEntryV0(serialized []byte) (map[uint32]*UtxoEntry, error) {
|
||||
// Deserialize the version.
|
||||
//
|
||||
|
|
|
@ -7,11 +7,11 @@ package blockchain
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// txoFlags is a bitmask defining additional information and state for a
|
||||
|
|
|
@ -11,11 +11,11 @@ import (
|
|||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -334,8 +334,8 @@ func CheckTransactionSanity(tx *btcutil.Tx, enforceSoftFork bool) error {
|
|||
// target difficulty as claimed.
|
||||
//
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFNoPoWCheck: The check to ensure the block hash is less than the target
|
||||
// difficulty is not performed.
|
||||
// - BFNoPoWCheck: The check to ensure the block hash is less than the target
|
||||
// difficulty is not performed.
|
||||
func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags BehaviorFlags) error {
|
||||
// The target difficulty must be larger than zero.
|
||||
target := CompactToBig(header.Bits)
|
||||
|
@ -669,8 +669,8 @@ func checkSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int32) error {
|
|||
// which depend on its position within the block chain.
|
||||
//
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFFastAdd: All checks except those involving comparing the header against
|
||||
// the checkpoints are not performed.
|
||||
// - BFFastAdd: All checks except those involving comparing the header against
|
||||
// the checkpoints are not performed.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode *blockNode, flags BehaviorFlags) error {
|
||||
|
@ -748,8 +748,8 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
|||
// on its position within the block chain.
|
||||
//
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFFastAdd: The transaction are not checked to see if they are finalized
|
||||
// and the somewhat expensive BIP0034 validation is not performed.
|
||||
// - BFFastAdd: The transaction are not checked to see if they are finalized
|
||||
// and the somewhat expensive BIP0034 validation is not performed.
|
||||
//
|
||||
// The flags are also passed to checkBlockHeaderContext. See its documentation
|
||||
// for how the flags modify its behavior.
|
||||
|
|
|
@ -1,161 +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 (
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works
|
||||
// as expected.
|
||||
func TestCheckBlockSanity(t *testing.T) {
|
||||
powLimit := chaincfg.MainNetParams.PowLimit
|
||||
block := GetBlock100000()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var block100000Hex = "0000002024cbdc8644ee3983e66b003a0733891c069ca74c114c034c7b3e2e7ad7a12cd67e95e0555c0e056f6f2af538268ff9d21b420e529750d08eacb25c40f1322936637109b8a051157604c1c163cd39237687f6244b4e6d2b3a94e9d816babaecbb10c56058c811041b2b9c43000701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2003a086010410c56058081011314abf0100000d2f6e6f64655374726174756d2f000000000180354a6e0a0000001976a914b5e74e7cc9e1f480a6599895c92aab1401f870f188ac000000000100000002f1b75decc2c1c59c2178852822de412f574ad9796b65ac9092a79630d8109aaf000000006a47304402202f78ed3bf8dcadb6c17e289cd06e9c96b02c6f23aa1f446a4a7896a31cfd1e4702202862261e2eb59475ac91092c620b3cac5a831372bafc446d5ee450866040b532012103db4f3785354d84311fab7624c52784a61e3046d8f364463d327bdd96281b5b90feffffff987ee6b4bf95548d01e443683261dd0ffdcb2eb335b2f7119df0d41b60756b92010000006a47304402200c422c7560b6418d45443138bb957ec44eb293a639f4b2235a622205ca6cac370220759f15d5dc2543fd1aef80104c93427fcb025847bf895669025d1d82c62fbf6801210201864b998db5436990a0029fc3fb153c09e8c2689214b91c8ed68784995c8da0feffffff022bccfedd000000001976a914738f16132942a01d00cb9699bd058c4925aada3288ac1f4d030c000000001976a914c4292e757f5ff6a27c2f0a87d3a4aea5e46c275a88ac9f86010001000000015fbb26ad6d818186032baeef4d3f475dfe165c6da2d93411b8ec5f9f523cf1a4000000006a4730440220356999ad5a9f6f09b676f17dd4a2617a0af234722d68a50edc3768c983c0623d022056b4e5531608aeb0205fde9c44f741158da3bba1f4c3788c9fe79d36d43ea355012103509893a2a7c765d49ac9ff70126cb1af54871d70baba2c7e39ec9b4096289a9bfeffffff02389332fa080000001976a914f85e054405fbcedc2341cf8bf41ea989090587a288acf9321a41000000001976a914e85e90c048fdfbe1c2117a7132856ff4b39b470188ac9f86010001000000013508189b9bb61ac2aa536b905447b58f6c58c17cdef305240f566caa689d760a010000006a4730440220669a2b86e5abe58bae54829d3c271669540a9ad965c2fb79e8cc1fb609c0b60002202f958403d979138075cb53d8cb5ff6bb14e18d66dfdb6701c7d43a8ceeed0fa80121029935a602205a3fb72446064f3bc3a55ab9cd2e3459bf2ffdf80a48ab029d4b42feffffff02523c2f13000000001976a914c5b2ae398496f0f9ceaf81b83c28a27ddc890e3588ac211958f2000000001976a914ac65f1d16e5a2af37408b5d65406000a7ea143ca88ac9f8601000100000001bdd724322c555a21d5eb62d4aadbdc051663bcd4ec03f8d9005992f299783c21000000006a47304402205448141a2a788f73310025123bd24f5bee01dd8f48f18d7abc62d7c26465008902207ab46e6ddf6ba416decf3fbb97b6946a1428ea0a7c25a55cab47c47110d8e9ce0121029d6ff3b1235f2a08560b23dd4a08b14cc415b544801b885365736ea8ab1d3470feffffff029d104ccf000000001976a914999d5b0e3d5efcf601c711127b91841afbf5c37a88ace5c5a07f070000001976a9144aade372298eb387da9b6ac69d215a213e822f3f88ac9f86010001000000011658304d4ce796cd450228a10fdf647c6ea42295c9f5e1663df11481af1c884d010000006b483045022100a35d5d3ccde85b41559047d976ae6210b8e6ba5653c53aae1adc90048de0761002200d6bd6ebc6d73f97855f435c6fd595009ee71d23bb34870ab83ad53f67eeb22b012102d2f681ebfd1a570416d602986a47ca4254d8dedf2935b3f8c2ba55dcee8e98f4feffffff025ee913e6020000001976a91468076c9380d3c6b468ad1d6109c36770fb181e8f88acb165394f000000001976a9147ae970e81b3657cbb59df26517e372165807be0088ac9f86010001000000018f285109f78524a88ff328a4f94de2ac03224c50984b11c68adda192e8f78efa010000006b483045022100d77f2ac32dd6a3015f02f7115a13f617c57952fc5d53a33a87dc3fc00ffe1864022006455f74cff864b10424e445c530a59243f86d309dc92c5010ec5709e38471ab012102fdac7335b41edcd2846fc7e2166bb48312ee583ed6ff70fb5c27bcb2addaad86feffffff028b7a6d5c000000001976a914c65106d2e7ea4ec6aa8aa30ba4d11cfd1143123388ac5934c228000000001976a914d1c4d190b07edb972b91f33c36c6568b80358dd488ac9f860100"
|
||||
|
||||
// GetBlock100000 defines block 100,000 of the block chain. It is used to
|
||||
// test Block operations.
|
||||
func GetBlock100000() *btcutil.Block {
|
||||
var block100000Bytes, _ = hex.DecodeString(block100000Hex)
|
||||
var results, _ = btcutil.NewBlockFromBytes(block100000Bytes)
|
||||
return results
|
||||
}
|
|
@ -7,7 +7,7 @@ package blockchain
|
|||
import (
|
||||
"math"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -7,9 +7,9 @@ package blockchain
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -15,11 +15,10 @@ import (
|
|||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
|
||||
"github.com/lbryio/lbcd/blockchain/indexers"
|
||||
"github.com/lbryio/lbcd/claimtrie/param"
|
||||
"github.com/lbryio/lbcd/database"
|
||||
"github.com/lbryio/lbcd/limits"
|
||||
"github.com/lbryio/lbcd/version"
|
||||
"github.com/btcsuite/btcd/blockchain/indexers"
|
||||
"github.com/btcsuite/btcd/claimtrie/param"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/limits"
|
||||
|
||||
"github.com/felixge/fgprof"
|
||||
)
|
||||
|
@ -65,7 +64,7 @@ func btcdMain(serverChan chan<- *server) error {
|
|||
defer btcdLog.Info("Shutdown complete")
|
||||
|
||||
// Show version at startup.
|
||||
btcdLog.Infof("Version %s", version.Full())
|
||||
btcdLog.Infof("Version %s", version())
|
||||
|
||||
// Enable http profiling server if requested.
|
||||
if cfg.Profile != "" {
|
||||
|
@ -92,25 +91,6 @@ func btcdMain(serverChan chan<- *server) error {
|
|||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
// Write memory profile if requested.
|
||||
if cfg.MemProfile != "" {
|
||||
f, err := os.Create(cfg.MemProfile + ".heap")
|
||||
if err != nil {
|
||||
btcdLog.Errorf("Unable to create mem profile: %v", err)
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
defer pprof.Lookup("heap").WriteTo(f, 0)
|
||||
|
||||
f, err = os.Create(cfg.MemProfile + ".allocs")
|
||||
if err != nil {
|
||||
btcdLog.Errorf("Unable to create mem profile: %v", err)
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
defer pprof.Lookup("allocs").WriteTo(f, 0)
|
||||
}
|
||||
|
||||
// Perform upgrades to btcd as new versions require it.
|
||||
if err := doUpgrades(); err != nil {
|
||||
btcdLog.Errorf("%v", err)
|
||||
|
@ -203,6 +183,34 @@ func btcdMain(serverChan chan<- *server) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// removeRegressionDB removes the existing regression test database if running
|
||||
// in regression test mode and it already exists.
|
||||
func removeRegressionDB(dbPath string) error {
|
||||
// Don't do anything if not in regression test mode.
|
||||
if !cfg.RegressionTest {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove the old regression test database if it already exists.
|
||||
fi, err := os.Stat(dbPath)
|
||||
if err == nil {
|
||||
btcdLog.Infof("Removing regression test database from '%s'", dbPath)
|
||||
if fi.IsDir() {
|
||||
err := os.RemoveAll(dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := os.Remove(dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbPath returns the path to the block database given a database type.
|
||||
func blockDbPath(dbType string) string {
|
||||
// The database name is based on the database type.
|
||||
|
@ -269,6 +277,11 @@ func loadBlockDB() (database.DB, error) {
|
|||
|
||||
// The database name is based on the database type.
|
||||
dbPath := blockDbPath(cfg.DbType)
|
||||
|
||||
// The regression test is special in that it needs a clean database for
|
||||
// each run, so remove it now if it already exists.
|
||||
removeRegressionDB(dbPath)
|
||||
|
||||
btcdLog.Infof("Loading block database from '%s'", dbPath)
|
||||
db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net)
|
||||
if err != nil {
|
||||
|
@ -300,9 +313,7 @@ func main() {
|
|||
// limits the garbage collector from excessively overallocating during
|
||||
// bursts. This value was arrived at with the help of profiling live
|
||||
// usage.
|
||||
if _, ok := os.LookupEnv("GOGC"); !ok {
|
||||
debug.SetGCPercent(10)
|
||||
}
|
||||
debug.SetGCPercent(10)
|
||||
|
||||
// Up some limits.
|
||||
if err := limits.SetLimits(); err != nil {
|
|
@ -1,11 +1,68 @@
|
|||
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)
|
||||
[![GoDoc](https://pkg.go.dev/github.com/btcsuite/btcd/btcec?status.png)](https://pkg.go.dev/github.com/btcsuite/btcd/btcec)
|
||||
|
||||
btcec implements elliptic curve cryptography needed for working with
|
||||
Package 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
|
||||
standard crypto/ecdsa packages provided with go. A comprehensive suite of test
|
||||
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
|
||||
signficantly diverged since then.
|
||||
signficantly diverged since then. The btcsuite developers original is licensed
|
||||
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.
|
||||
|
||||
|
|
|
@ -527,7 +527,7 @@ type baseMultTest struct {
|
|||
x, y string
|
||||
}
|
||||
|
||||
// TODO: add more test vectors
|
||||
//TODO: add more test vectors
|
||||
var s256BaseMultTests = []baseMultTest{
|
||||
{
|
||||
"AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522",
|
||||
|
@ -556,7 +556,7 @@ var s256BaseMultTests = []baseMultTest{
|
|||
},
|
||||
}
|
||||
|
||||
// TODO: test different curves as well?
|
||||
//TODO: test different curves as well?
|
||||
func TestBaseMult(t *testing.T) {
|
||||
s256 := S256()
|
||||
for i, e := range s256BaseMultTests {
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/lbryio/lbcd/btcec"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// This example demonstrates signing a message with a secp256k1 private key that
|
||||
|
|
|
@ -125,30 +125,27 @@ var (
|
|||
// the arithmetic needed for elliptic curve operations.
|
||||
//
|
||||
// The following depicts the internal representation:
|
||||
//
|
||||
// -----------------------------------------------------------------
|
||||
// | n[9] | n[8] | ... | n[0] |
|
||||
// | 32 bits available | 32 bits available | ... | 32 bits available |
|
||||
// | 22 bits for value | 26 bits for value | ... | 26 bits for value |
|
||||
// | 10 bits overflow | 6 bits overflow | ... | 6 bits overflow |
|
||||
// | Mult: 2^(26*9) | Mult: 2^(26*8) | ... | Mult: 2^(26*0) |
|
||||
// -----------------------------------------------------------------
|
||||
// -----------------------------------------------------------------
|
||||
// | n[9] | n[8] | ... | n[0] |
|
||||
// | 32 bits available | 32 bits available | ... | 32 bits available |
|
||||
// | 22 bits for value | 26 bits for value | ... | 26 bits for value |
|
||||
// | 10 bits overflow | 6 bits overflow | ... | 6 bits overflow |
|
||||
// | Mult: 2^(26*9) | Mult: 2^(26*8) | ... | Mult: 2^(26*0) |
|
||||
// -----------------------------------------------------------------
|
||||
//
|
||||
// For example, consider the number 2^49 + 1. It would be represented as:
|
||||
//
|
||||
// n[0] = 1
|
||||
// n[1] = 2^23
|
||||
// n[2..9] = 0
|
||||
// n[0] = 1
|
||||
// n[1] = 2^23
|
||||
// n[2..9] = 0
|
||||
//
|
||||
// The full 256-bit value is then calculated by looping i from 9..0 and
|
||||
// doing sum(n[i] * 2^(26i)) like so:
|
||||
//
|
||||
// n[9] * 2^(26*9) = 0 * 2^234 = 0
|
||||
// n[8] * 2^(26*8) = 0 * 2^208 = 0
|
||||
// ...
|
||||
// n[1] * 2^(26*1) = 2^23 * 2^26 = 2^49
|
||||
// n[0] * 2^(26*0) = 1 * 2^0 = 1
|
||||
// Sum: 0 + 0 + ... + 2^49 + 1 = 2^49 + 1
|
||||
// n[9] * 2^(26*9) = 0 * 2^234 = 0
|
||||
// n[8] * 2^(26*8) = 0 * 2^208 = 0
|
||||
// ...
|
||||
// n[1] * 2^(26*1) = 2^23 * 2^26 = 2^49
|
||||
// n[0] * 2^(26*0) = 1 * 2^0 = 1
|
||||
// Sum: 0 + 0 + ... + 2^49 + 1 = 2^49 + 1
|
||||
type fieldVal struct {
|
||||
n [10]uint32
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
// 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
|
||||
// tables used to accelerate operations.
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
@ -18,7 +17,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/lbryio/lbcd/btcec"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
// This file is ignored during the regular build due to the following build tag.
|
||||
// This build tag is set during go generate.
|
||||
//go:build gensecp256k1
|
||||
// +build gensecp256k1
|
||||
|
||||
package btcec
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
//go:rm -f gensecp256k1.go; generate go run -tags gensecp256k1 genprecomps.go
|
||||
//go:generate go run -tags gensecp256k1 genprecomps.go
|
||||
|
||||
// loadS256BytePoints decompresses and deserializes the pre-computed byte points
|
||||
// used to accelerate scalar base multiplication for the secp256k1 curve. This
|
||||
|
|
|
@ -353,10 +353,6 @@ func recoverKeyFromSignature(curve *KoblitzCurve, sig *Signature, msg []byte,
|
|||
// step to prevent the jacobian conversion back and forth.
|
||||
Qx, Qy := curve.Add(sRx, sRy, minuseGx, minuseGy)
|
||||
|
||||
if Qx.Sign() == 0 && Qy.Sign() == 0 {
|
||||
return nil, errors.New("point (Qx, Qy) equals the point at infinity")
|
||||
}
|
||||
|
||||
return &PublicKey{
|
||||
Curve: curve,
|
||||
X: Qx,
|
||||
|
|
|
@ -549,12 +549,6 @@ var recoveryTests = []struct {
|
|||
sig: "0100b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f00b940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549",
|
||||
err: fmt.Errorf("invalid square root"),
|
||||
},
|
||||
{
|
||||
// Point at infinity recovered
|
||||
msg: "6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9",
|
||||
sig: "0079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9",
|
||||
err: fmt.Errorf("point (Qx, Qy) equals the point at infinity"),
|
||||
},
|
||||
{
|
||||
// Low R and S values.
|
||||
msg: "ba09edc1275a285fb27bfe82c4eea240a907a0dbaf9e55764b8f318c37d5974f",
|
||||
|
|
|
@ -1,8 +1,70 @@
|
|||
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)
|
||||
[![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
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/btcjson"
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
)
|
||||
|
||||
// TestBtcdExtCmds tests all of the btcd extended commands marshal and unmarshal
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/btcjson"
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
)
|
||||
|
||||
// TestBtcdExtCustomResults ensures any results that have custom marshalling
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/btcjson"
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
)
|
||||
|
||||
// TestBtcWalletExtCmds tests all of the btcwallet extended commands marshal and
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// AddNodeSubCmd defines the type used in the addnode JSON-RPC command for the
|
||||
|
@ -48,15 +48,6 @@ func NewAddNodeCmd(addr string, subCmd AddNodeSubCmd) *AddNodeCmd {
|
|||
}
|
||||
}
|
||||
|
||||
// ClearBannedCmd defines the clearbanned JSON-RPC command.
|
||||
type ClearBannedCmd struct{}
|
||||
|
||||
// NewClearBannedCmd returns a new instance which can be used to issue an clearbanned
|
||||
// JSON-RPC command.
|
||||
func NewClearBannedCmd() *ClearBannedCmd {
|
||||
return &ClearBannedCmd{}
|
||||
}
|
||||
|
||||
// TransactionInput represents the inputs to a transaction. Specifically a
|
||||
// transaction hash and output number pair.
|
||||
type TransactionInput struct {
|
||||
|
@ -67,7 +58,7 @@ type TransactionInput struct {
|
|||
// CreateRawTransactionCmd defines the createrawtransaction JSON-RPC command.
|
||||
type CreateRawTransactionCmd struct {
|
||||
Inputs []TransactionInput
|
||||
Outputs map[string]interface{} `jsonrpcusage:"{\"address\":amount, \"data\":\"hex\", ...}"`
|
||||
Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In BTC
|
||||
LockTime *int64
|
||||
}
|
||||
|
||||
|
@ -76,7 +67,7 @@ type CreateRawTransactionCmd struct {
|
|||
//
|
||||
// Amounts are in BTC. Passing in nil and the empty slice as inputs is equivalent,
|
||||
// both gets interpreted as the empty slice.
|
||||
func NewCreateRawTransactionCmd(inputs []TransactionInput, outputs map[string]interface{},
|
||||
func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64,
|
||||
lockTime *int64) *CreateRawTransactionCmd {
|
||||
// to make sure we're serializing this to the empty list and not null, we
|
||||
// explicitly initialize the list
|
||||
|
@ -85,7 +76,7 @@ func NewCreateRawTransactionCmd(inputs []TransactionInput, outputs map[string]in
|
|||
}
|
||||
return &CreateRawTransactionCmd{
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
Amounts: amounts,
|
||||
LockTime: lockTime,
|
||||
}
|
||||
}
|
||||
|
@ -659,7 +650,7 @@ func NewGetRawMempoolCmd(verbose *bool) *GetRawMempoolCmd {
|
|||
// Core even though it really should be a bool.
|
||||
type GetRawTransactionCmd struct {
|
||||
Txid string
|
||||
Verbose *bool `jsonrpcdefault:"false"`
|
||||
Verbose *int `jsonrpcdefault:"0"`
|
||||
}
|
||||
|
||||
// NewGetRawTransactionCmd returns a new instance which can be used to issue a
|
||||
|
@ -667,7 +658,7 @@ type GetRawTransactionCmd struct {
|
|||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetRawTransactionCmd(txHash string, verbose *bool) *GetRawTransactionCmd {
|
||||
func NewGetRawTransactionCmd(txHash string, verbose *int) *GetRawTransactionCmd {
|
||||
return &GetRawTransactionCmd{
|
||||
Txid: txHash,
|
||||
Verbose: verbose,
|
||||
|
@ -766,15 +757,6 @@ func NewInvalidateBlockCmd(blockHash string) *InvalidateBlockCmd {
|
|||
}
|
||||
}
|
||||
|
||||
// ListBannedCmd defines the listbanned JSON-RPC command.
|
||||
type ListBannedCmd struct{}
|
||||
|
||||
// NewListBannedCmd returns a new instance which can be used to issue a listbanned
|
||||
// JSON-RPC command.
|
||||
func NewListBannedCmd() *ListBannedCmd {
|
||||
return &ListBannedCmd{}
|
||||
}
|
||||
|
||||
// PingCmd defines the ping JSON-RPC command.
|
||||
type PingCmd struct{}
|
||||
|
||||
|
@ -921,39 +903,6 @@ func NewBitcoindSendRawTransactionCmd(hexTx string, maxFeeRate int32) *SendRawTr
|
|||
}
|
||||
}
|
||||
|
||||
// SetBanSubCmd defines the type used in the setban JSON-RPC command for the
|
||||
// sub command field.
|
||||
type SetBanSubCmd string
|
||||
|
||||
const (
|
||||
// SBAdd indicates the specified host should be added as a persistent
|
||||
// peer.
|
||||
SBAdd SetBanSubCmd = "add"
|
||||
|
||||
// SBRemove indicates the specified peer should be removed.
|
||||
SBRemove SetBanSubCmd = "remove"
|
||||
)
|
||||
|
||||
// SetBanCmd defines the setban JSON-RPC command.
|
||||
type SetBanCmd struct {
|
||||
Addr string
|
||||
SubCmd SetBanSubCmd `jsonrpcusage:"\"add|remove\""`
|
||||
BanTime *int `jsonrpcdefault:"0"`
|
||||
Absolute *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewSetBanCmd returns a new instance which can be used to issue an setban
|
||||
// JSON-RPC command.
|
||||
func NewSetBanCmd(addr string, subCmd SetBanSubCmd, banTime *int,
|
||||
absolute *bool) *SetBanCmd {
|
||||
return &SetBanCmd{
|
||||
Addr: addr,
|
||||
SubCmd: subCmd,
|
||||
BanTime: banTime,
|
||||
Absolute: absolute,
|
||||
}
|
||||
}
|
||||
|
||||
// SetGenerateCmd defines the setgenerate JSON-RPC command.
|
||||
type SetGenerateCmd struct {
|
||||
Generate bool
|
||||
|
@ -1131,9 +1080,6 @@ func init() {
|
|||
MustRegisterCmd("getnetworkhashps", (*GetNetworkHashPSCmd)(nil), flags)
|
||||
MustRegisterCmd("getnodeaddresses", (*GetNodeAddressesCmd)(nil), flags)
|
||||
MustRegisterCmd("getpeerinfo", (*GetPeerInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("listbanned", (*ListBannedCmd)(nil), flags)
|
||||
MustRegisterCmd("setban", (*SetBanCmd)(nil), flags)
|
||||
MustRegisterCmd("clearbanned", (*ClearBannedCmd)(nil), flags)
|
||||
MustRegisterCmd("getrawmempool", (*GetRawMempoolCmd)(nil), flags)
|
||||
MustRegisterCmd("getrawtransaction", (*GetRawTransactionCmd)(nil), flags)
|
||||
MustRegisterCmd("gettxout", (*GetTxOutCmd)(nil), flags)
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/btcjson"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// TestChainSvrCmds tests all of the chain server commands marshal and unmarshal
|
||||
|
@ -52,13 +52,13 @@ func TestChainSvrCmds(t *testing.T) {
|
|||
txInputs := []btcjson.TransactionInput{
|
||||
{Txid: "123", Vout: 1},
|
||||
}
|
||||
txOutputs := map[string]interface{}{"456": .0123}
|
||||
return btcjson.NewCreateRawTransactionCmd(txInputs, txOutputs, nil)
|
||||
amounts := map[string]float64{"456": .0123}
|
||||
return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, nil)
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123}],"id":1}`,
|
||||
unmarshalled: &btcjson.CreateRawTransactionCmd{
|
||||
Inputs: []btcjson.TransactionInput{{Txid: "123", Vout: 1}},
|
||||
Outputs: map[string]interface{}{"456": .0123},
|
||||
Amounts: map[string]float64{"456": .0123},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -67,13 +67,13 @@ func TestChainSvrCmds(t *testing.T) {
|
|||
return btcjson.NewCmd("createrawtransaction", `[]`, `{"456":0.0123}`)
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
txOutputs := map[string]interface{}{"456": .0123}
|
||||
return btcjson.NewCreateRawTransactionCmd(nil, txOutputs, nil)
|
||||
amounts := map[string]float64{"456": .0123}
|
||||
return btcjson.NewCreateRawTransactionCmd(nil, amounts, nil)
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[],{"456":0.0123}],"id":1}`,
|
||||
unmarshalled: &btcjson.CreateRawTransactionCmd{
|
||||
Inputs: []btcjson.TransactionInput{},
|
||||
Outputs: map[string]interface{}{"456": .0123},
|
||||
Amounts: map[string]float64{"456": .0123},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -86,35 +86,16 @@ func TestChainSvrCmds(t *testing.T) {
|
|||
txInputs := []btcjson.TransactionInput{
|
||||
{Txid: "123", Vout: 1},
|
||||
}
|
||||
txOutputs := map[string]interface{}{"456": .0123}
|
||||
return btcjson.NewCreateRawTransactionCmd(txInputs, txOutputs, btcjson.Int64(12312333333))
|
||||
amounts := map[string]float64{"456": .0123}
|
||||
return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, btcjson.Int64(12312333333))
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123},12312333333],"id":1}`,
|
||||
unmarshalled: &btcjson.CreateRawTransactionCmd{
|
||||
Inputs: []btcjson.TransactionInput{{Txid: "123", Vout: 1}},
|
||||
Outputs: map[string]interface{}{"456": .0123},
|
||||
Amounts: map[string]float64{"456": .0123},
|
||||
LockTime: btcjson.Int64(12312333333),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "createrawtransaction with data",
|
||||
newCmd: func() (interface{}, error) {
|
||||
return btcjson.NewCmd("createrawtransaction", `[{"txid":"123","vout":1}]`,
|
||||
`{"data":"6a134920616d204672616374616c456e6372797074"}`)
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
txInputs := []btcjson.TransactionInput{
|
||||
{Txid: "123", Vout: 1},
|
||||
}
|
||||
txOutputs := map[string]interface{}{"data": "6a134920616d204672616374616c456e6372797074"}
|
||||
return btcjson.NewCreateRawTransactionCmd(txInputs, txOutputs, nil)
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"data":"6a134920616d204672616374616c456e6372797074"}],"id":1}`,
|
||||
unmarshalled: &btcjson.CreateRawTransactionCmd{
|
||||
Inputs: []btcjson.TransactionInput{{Txid: "123", Vout: 1}},
|
||||
Outputs: map[string]interface{}{"data": "6a134920616d204672616374616c456e6372797074"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fundrawtransaction - empty opts",
|
||||
newCmd: func() (i interface{}, e error) {
|
||||
|
@ -891,21 +872,21 @@ func TestChainSvrCmds(t *testing.T) {
|
|||
marshalled: `{"jsonrpc":"1.0","method":"getrawtransaction","params":["123"],"id":1}`,
|
||||
unmarshalled: &btcjson.GetRawTransactionCmd{
|
||||
Txid: "123",
|
||||
Verbose: btcjson.Bool(false),
|
||||
Verbose: btcjson.Int(0),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getrawtransaction optional",
|
||||
newCmd: func() (interface{}, error) {
|
||||
return btcjson.NewCmd("getrawtransaction", "123", true)
|
||||
return btcjson.NewCmd("getrawtransaction", "123", 1)
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
return btcjson.NewGetRawTransactionCmd("123", btcjson.Bool(true))
|
||||
return btcjson.NewGetRawTransactionCmd("123", btcjson.Int(1))
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"getrawtransaction","params":["123",true],"id":1}`,
|
||||
marshalled: `{"jsonrpc":"1.0","method":"getrawtransaction","params":["123",1],"id":1}`,
|
||||
unmarshalled: &btcjson.GetRawTransactionCmd{
|
||||
Txid: "123",
|
||||
Verbose: btcjson.Bool(true),
|
||||
Verbose: btcjson.Int(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
|
||||
|
@ -25,7 +25,7 @@ type GetBlockHeaderVerboseResult struct {
|
|||
Version int32 `json:"version"`
|
||||
VersionHex string `json:"versionHex"`
|
||||
MerkleRoot string `json:"merkleroot"`
|
||||
ClaimTrie string `json:"nameclaimroot,omitempty"`
|
||||
ClaimTrie string `json:"claimtrie"`
|
||||
Time int64 `json:"time"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Bits string `json:"bits"`
|
||||
|
@ -35,60 +35,35 @@ type GetBlockHeaderVerboseResult struct {
|
|||
}
|
||||
|
||||
// GetBlockStatsResult models the data from the getblockstats command.
|
||||
// Pointers are used instead of values to allow for optional fields.
|
||||
type GetBlockStatsResult struct {
|
||||
AverageFee *int64 `json:"avgfee,omitempty"`
|
||||
AverageFeeRate *int64 `json:"avgfeerate,omitempty"`
|
||||
AverageTxSize *int64 `json:"avgtxsize,omitempty"`
|
||||
FeeratePercentiles *[]int64 `json:"feerate_percentiles,omitempty"`
|
||||
Hash *string `json:"blockhash,omitempty"`
|
||||
Height *int64 `json:"height,omitempty"`
|
||||
Ins *int64 `json:"ins,omitempty"`
|
||||
MaxFee *int64 `json:"maxfee,omitempty"`
|
||||
MaxFeeRate *int64 `json:"maxfeerate,omitempty"`
|
||||
MaxTxSize *int64 `json:"maxtxsize,omitempty"`
|
||||
MedianFee *int64 `json:"medianfee,omitempty"`
|
||||
MedianTime *int64 `json:"mediantime,omitempty"`
|
||||
MedianTxSize *int64 `json:"mediantxsize,omitempty"`
|
||||
MinFee *int64 `json:"minfee,omitempty"`
|
||||
MinFeeRate *int64 `json:"minfeerate,omitempty"`
|
||||
MinTxSize *int64 `json:"mintxsize,omitempty"`
|
||||
Outs *int64 `json:"outs,omitempty"`
|
||||
SegWitTotalSize *int64 `json:"swtotal_size,omitempty"`
|
||||
SegWitTotalWeight *int64 `json:"swtotal_weight,omitempty"`
|
||||
SegWitTxs *int64 `json:"swtxs,omitempty"`
|
||||
Subsidy *int64 `json:"subsidy,omitempty"`
|
||||
Time *int64 `json:"time,omitempty"`
|
||||
TotalOut *int64 `json:"total_out,omitempty"`
|
||||
TotalSize *int64 `json:"total_size,omitempty"`
|
||||
TotalWeight *int64 `json:"total_weight,omitempty"`
|
||||
TotalFee *int64 `json:"totalfee,omitempty"`
|
||||
Txs *int64 `json:"txs,omitempty"`
|
||||
UTXOIncrease *int64 `json:"utxo_increase,omitempty"`
|
||||
UTXOSizeIncrease *int64 `json:"utxo_size_inc,omitempty"`
|
||||
}
|
||||
|
||||
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"`
|
||||
MedianTime int64 `json:"mediantime"`
|
||||
Nonce uint32 `json:"nonce"`
|
||||
Bits string `json:"bits"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
ChainWork string `json:"chainwork"`
|
||||
PreviousHash string `json:"previousblockhash,omitempty"`
|
||||
NextHash string `json:"nextblockhash,omitempty"`
|
||||
|
||||
ClaimTrie string `json:"nameclaimroot,omitempty"`
|
||||
TxCount int `json:"nTx"` // For backwards compatibility only
|
||||
AverageFee int64 `json:"avgfee"`
|
||||
AverageFeeRate int64 `json:"avgfeerate"`
|
||||
AverageTxSize int64 `json:"avgtxsize"`
|
||||
FeeratePercentiles []int64 `json:"feerate_percentiles"`
|
||||
Hash string `json:"blockhash"`
|
||||
Height int64 `json:"height"`
|
||||
Ins int64 `json:"ins"`
|
||||
MaxFee int64 `json:"maxfee"`
|
||||
MaxFeeRate int64 `json:"maxfeerate"`
|
||||
MaxTxSize int64 `json:"maxtxsize"`
|
||||
MedianFee int64 `json:"medianfee"`
|
||||
MedianTime int64 `json:"mediantime"`
|
||||
MedianTxSize int64 `json:"mediantxsize"`
|
||||
MinFee int64 `json:"minfee"`
|
||||
MinFeeRate int64 `json:"minfeerate"`
|
||||
MinTxSize int64 `json:"mintxsize"`
|
||||
Outs int64 `json:"outs"`
|
||||
SegWitTotalSize int64 `json:"swtotal_size"`
|
||||
SegWitTotalWeight int64 `json:"swtotal_weight"`
|
||||
SegWitTxs int64 `json:"swtxs"`
|
||||
Subsidy int64 `json:"subsidy"`
|
||||
Time int64 `json:"time"`
|
||||
TotalOut int64 `json:"total_out"`
|
||||
TotalSize int64 `json:"total_size"`
|
||||
TotalWeight int64 `json:"total_weight"`
|
||||
Txs int64 `json:"txs"`
|
||||
UTXOIncrease int64 `json:"utxo_increase"`
|
||||
UTXOSizeIncrease int64 `json:"utxo_size_inc"`
|
||||
}
|
||||
|
||||
// GetBlockVerboseResult models the data from the getblock command when the
|
||||
|
@ -98,8 +73,24 @@ type GetBlockVerboseResultBase struct {
|
|||
// 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.
|
||||
type GetBlockVerboseResult struct {
|
||||
GetBlockVerboseResultBase
|
||||
Tx []string `json:"tx"`
|
||||
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"`
|
||||
ClaimTrie string `json:"claimTrie"`
|
||||
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
|
||||
|
@ -109,8 +100,23 @@ type GetBlockVerboseResult struct {
|
|||
// 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.
|
||||
type GetBlockVerboseTxResult struct {
|
||||
GetBlockVerboseResultBase
|
||||
Tx []TxRawResult `json:"tx"`
|
||||
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"`
|
||||
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.
|
||||
|
@ -292,10 +298,6 @@ type GetBlockTemplateResult struct {
|
|||
// Block proposal from BIP 0023.
|
||||
Capabilities []string `json:"capabilities,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
|
||||
|
@ -327,27 +329,13 @@ type GetMempoolEntryResult struct {
|
|||
WTxId string `json:"wtxid"`
|
||||
Fees MempoolFees `json:"fees"`
|
||||
Depends []string `json:"depends"`
|
||||
SpentBy []string `json:"spentby"`
|
||||
}
|
||||
|
||||
// GetChainTipsResult models the data returns from the getchaintips command.
|
||||
type GetChainTipsResult struct {
|
||||
Height int64 `json:"height"`
|
||||
Hash string `json:"hash"`
|
||||
BranchLen int64 `json:"branchlen"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// GetMempoolInfoResult models the data returned from the getmempoolinfo
|
||||
// command.
|
||||
type GetMempoolInfoResult struct {
|
||||
Size int64 `json:"size"` // Current tx count
|
||||
Bytes int64 `json:"bytes"` // Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted
|
||||
Usage int64 `json:"usage"` // Total memory usage for the mempool
|
||||
TotalFee float64 `json:"total_fee"` // Total fees for the mempool in LBC, ignoring modified fees through prioritizetransaction
|
||||
MemPoolMinFee float64 `json:"mempoolminfee"` // Minimum fee rate in LBC/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee
|
||||
MinRelayTxFee float64 `json:"minrelaytxfee"` // Current minimum relay fee for transactions
|
||||
UnbroadcastCount int64 `json:"unbroadcastcount"` // Current number of transactions that haven't passed initial broadcast yet
|
||||
Size int64 `json:"size"`
|
||||
Bytes int64 `json:"bytes"`
|
||||
}
|
||||
|
||||
// NetworksResult models the networks data from the getnetworkinfo command.
|
||||
|
@ -723,15 +711,6 @@ type InfoChainResult struct {
|
|||
Errors string `json:"errors"`
|
||||
}
|
||||
|
||||
// ListBannedResult models the data returned from the listbanned command.
|
||||
type ListBannedResult struct {
|
||||
Address string `json:"address"`
|
||||
BanCreated int64 `json:"ban_created"`
|
||||
BannedUntil int64 `json:"banned_until"`
|
||||
BanDuration int64 `json:"ban_duration"`
|
||||
TimeRemaining int64 `json:"time_remaining"`
|
||||
}
|
||||
|
||||
// TxRawResult models the data from the getrawtransaction command.
|
||||
type TxRawResult struct {
|
||||
Hex string `json:"hex"`
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"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
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/btcjson"
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
)
|
||||
|
||||
// TestChainSvrWsCmds tests all of the chain server websocket-specific commands
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/btcjson"
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
)
|
||||
|
||||
// TestChainSvrWsNtfns tests all of the chain server websocket-specific
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbcd/btcjson"
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
)
|
||||
|
||||
// TestChainSvrWsResults ensures any results that have custom marshalling
|
||||
|
|
|
@ -55,12 +55,11 @@ type GetClaimsForNameBySeqCmd struct {
|
|||
}
|
||||
|
||||
type GetClaimsForNameResult struct {
|
||||
Hash string `json:"hash"`
|
||||
Height int32 `json:"height"`
|
||||
LastTakeoverHeight int32 `json:"lasttakeoverheight"`
|
||||
NormalizedName string `json:"normalizedname"`
|
||||
Claims []ClaimResult `json:"claims"`
|
||||
// UnclaimedSupports []SupportResult `json:"supportswithoutclaim"` how would this work with other constraints?
|
||||
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 {
|
||||
|
@ -81,7 +80,6 @@ type ClaimResult struct {
|
|||
Sequence int32 `json:"sequence"`
|
||||
Height int32 `json:"height"`
|
||||
ValidAtHeight int32 `json:"validatheight"`
|
||||
Amount int64 `json:"amount"`
|
||||
EffectiveAmount int64 `json:"effectiveamount"`
|
||||
Supports []SupportResult `json:"supports,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
|
|
|
@ -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/lbryio/lbcd/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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -134,16 +134,6 @@ func UnmarshalCmd(r *Request) (interface{}, error) {
|
|||
// Unmarshal the parameter into the struct field.
|
||||
concreteVal := rvf.Addr().Interface()
|
||||
if err := json.Unmarshal(r.Params[i], &concreteVal); err != nil {
|
||||
// Parse Integer into Bool for compatibility with lbrycrd.
|
||||
if rvf.Kind() == reflect.Ptr &&
|
||||
rvf.Elem().Type().Kind() == reflect.Bool {
|
||||
boolInt, errBoolInt := strconv.Atoi(string(r.Params[i]))
|
||||
if errBoolInt == nil {
|
||||
rvf.Elem().SetBool(boolInt != 0)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// The most common error is the wrong type, so
|
||||
// explicitly detect that error and make it nicer.
|
||||
fieldName := strings.ToLower(rt.Field(i).Name)
|
||||
|
|
|
@ -1,674 +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/lbryio/lbcd/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.0")},
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnmarshalCmdBoolParams tests the parsing of boolean paramers of the UnmarshalCmd function.
|
||||
func TestUnmarshalCmdBoolParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
txid := []byte(`"ab91c149aff2b37a4a1856e9935ea623c973f47886d032ed7511ad8ca37855bb"`)
|
||||
tests := []struct {
|
||||
name string
|
||||
request btcjson.Request
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
name: "parse true",
|
||||
request: btcjson.Request{
|
||||
Jsonrpc: btcjson.RpcVersion1,
|
||||
Method: "getrawtransaction",
|
||||
Params: []json.RawMessage{txid, []byte("true")},
|
||||
ID: nil,
|
||||
},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "parse false",
|
||||
request: btcjson.Request{
|
||||
Jsonrpc: btcjson.RpcVersion1,
|
||||
Method: "getrawtransaction",
|
||||
Params: []json.RawMessage{txid, []byte("false")},
|
||||
ID: nil,
|
||||
},
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "parse integer 0 to false",
|
||||
request: btcjson.Request{
|
||||
Jsonrpc: btcjson.RpcVersion1,
|
||||
Method: "getrawtransaction",
|
||||
Params: []json.RawMessage{txid, []byte("0")},
|
||||
ID: nil,
|
||||
},
|
||||
expect: false,
|
||||
},
|
||||
{
|
||||
name: "parse integer 1 to true",
|
||||
request: btcjson.Request{
|
||||
Jsonrpc: btcjson.RpcVersion1,
|
||||
Method: "getrawtransaction",
|
||||
Params: []json.RawMessage{txid, []byte("1")},
|
||||
ID: nil,
|
||||
},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
name: "parse integer 100 to true",
|
||||
request: btcjson.Request{
|
||||
Jsonrpc: btcjson.RpcVersion1,
|
||||
Method: "getrawtransaction",
|
||||
Params: []json.RawMessage{txid, []byte("100")},
|
||||
ID: nil,
|
||||
},
|
||||
expect: true,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
cmd, err := btcjson.UnmarshalCmd(&test.request)
|
||||
if err != nil {
|
||||
t.Errorf("Test #%d (%s) error - got %T (%v)", i, test.name,
|
||||
err, err)
|
||||
continue
|
||||
}
|
||||
cc := cmd.(*btcjson.GetRawTransactionCmd)
|
||||
verbose := *cc.Verbose
|
||||
if verbose != test.expect {
|
||||
t.Errorf("Test #%d (%s) got %t, want %v", i, test.name,
|
||||
verbose, test.expect)
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
/*
|
||||
Package btcjson provides primitives for working with the bitcoin JSON-RPC API.
|
||||
|
||||
# Overview
|
||||
Overview
|
||||
|
||||
When communicating via the JSON-RPC protocol, all of the commands need to be
|
||||
marshalled to and from the the wire in the appropriate format. This package
|
||||
|
@ -14,7 +14,7 @@ provides data structures and primitives to ease this process.
|
|||
In addition, it also provides some additional features such as custom command
|
||||
registration, command categorization, and reflection-based help generation.
|
||||
|
||||
# JSON-RPC Protocol Overview
|
||||
JSON-RPC Protocol Overview
|
||||
|
||||
This information is not necessary in order to use this package, but it does
|
||||
provide some intuition into what the marshalling and unmarshalling that is
|
||||
|
@ -47,39 +47,39 @@ with it) doesn't always follow the spec and will sometimes return an error
|
|||
string in the result field with a null error for certain commands. However,
|
||||
for the most part, the error field will be set as described on failure.
|
||||
|
||||
# Marshalling and Unmarshalling
|
||||
Marshalling and Unmarshalling
|
||||
|
||||
Based upon the discussion above, it should be easy to see how the types of this
|
||||
package map into the required parts of the protocol
|
||||
|
||||
- Request Objects (type Request)
|
||||
- Commands (type <Foo>Cmd)
|
||||
- Notifications (type <Foo>Ntfn)
|
||||
- Commands (type <Foo>Cmd)
|
||||
- Notifications (type <Foo>Ntfn)
|
||||
- Response Objects (type Response)
|
||||
- Result (type <Foo>Result)
|
||||
- Result (type <Foo>Result)
|
||||
|
||||
To simplify the marshalling of the requests and responses, the MarshalCmd and
|
||||
MarshalResponse functions are provided. They return the raw bytes ready to be
|
||||
sent across the wire.
|
||||
|
||||
Unmarshalling a received Request object is a two step process:
|
||||
1. Unmarshal the raw bytes into a Request struct instance via json.Unmarshal
|
||||
2. Use UnmarshalCmd on the Result field of the unmarshalled Request to create
|
||||
a concrete command or notification instance with all struct fields set
|
||||
accordingly
|
||||
1) Unmarshal the raw bytes into a Request struct instance via json.Unmarshal
|
||||
2) Use UnmarshalCmd on the Result field of the unmarshalled Request to create
|
||||
a concrete command or notification instance with all struct fields set
|
||||
accordingly
|
||||
|
||||
This approach is used since it provides the caller with access to the additional
|
||||
fields in the request that are not part of the command such as the ID.
|
||||
|
||||
Unmarshalling a received Response object is also a two step process:
|
||||
1. Unmarhsal the raw bytes into a Response struct instance via json.Unmarshal
|
||||
2. Depending on the ID, unmarshal the Result field of the unmarshalled
|
||||
Response to create a concrete type instance
|
||||
1) Unmarhsal the raw bytes into a Response struct instance via json.Unmarshal
|
||||
2) Depending on the ID, unmarshal the Result field of the unmarshalled
|
||||
Response to create a concrete type instance
|
||||
|
||||
As above, this approach is used since it provides the caller with access to the
|
||||
fields in the response such as the ID and Error.
|
||||
|
||||
# Command Creation
|
||||
Command Creation
|
||||
|
||||
This package provides two approaches for creating a new command. This first,
|
||||
and preferred, method is to use one of the New<Foo>Cmd functions. This allows
|
||||
|
@ -93,7 +93,7 @@ obviously, run-time which means any mistakes won't be found until the code is
|
|||
actually executed. However, it is quite useful for user-supplied commands
|
||||
that are intentionally dynamic.
|
||||
|
||||
# Custom Command Registration
|
||||
Custom Command Registration
|
||||
|
||||
The command handling of this package is built around the concept of registered
|
||||
commands. This is true for the wide variety of commands already provided by the
|
||||
|
@ -104,7 +104,7 @@ function for this purpose.
|
|||
A list of all registered methods can be obtained with the RegisteredCmdMethods
|
||||
function.
|
||||
|
||||
# Command Inspection
|
||||
Command Inspection
|
||||
|
||||
All registered commands are registered with flags that identify information such
|
||||
as whether the command applies to a chain server, wallet server, or is a
|
||||
|
@ -112,7 +112,7 @@ notification along with the method name to use. These flags can be obtained
|
|||
with the MethodUsageFlags flags, and the method can be obtained with the
|
||||
CmdMethod function.
|
||||
|
||||
# Help Generation
|
||||
Help Generation
|
||||
|
||||
To facilitate providing consistent help to users of the RPC server, this package
|
||||
exposes the GenerateHelp and function which uses reflection on registered
|
||||
|
@ -122,7 +122,7 @@ generate the final help text.
|
|||
In addition, the MethodUsageText function is provided to generate consistent
|
||||
one-line usage for registered commands and notifications using reflection.
|
||||
|
||||
# Errors
|
||||
Errors
|
||||
|
||||
There are 2 distinct type of errors supported by this package:
|
||||
|
||||
|
|
|
@ -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/lbryio/lbcd/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
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue