Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

primitives: Add initial proof-of-work funcs. #2788

Merged
merged 9 commits into from
Dec 4, 2021

Conversation

davecgh
Copy link
Member

@davecgh davecgh commented Nov 7, 2021

This requires #2787.

This introduces a primitives package that currently only contains functions related to proof of work along with comprehensive tests, benchmarks, full documentation and a skeleton README.md.

These functions are all the semantic equivalents of their respective functions in blockchain/standalone updated to use, accept, and return the new uint256 type instead of stdlib big integers.

Specifically, the following functions are updated and added along with their associated tests and benchmarks:

  • DiffBitsToUint256 (equiv to standalone.CompactToBig)
  • Uint256ToDiffBits (equiv to standalone.BigToCompact)
  • CalcWork (equiv to standalone.CalcWork)
  • HashToUint256 (equiv to standalone.HashToBig)
  • CheckProofOfWorkRange (equiv to standalone.CheckProofOfWorkRange)
  • CheckProofOfWork (equiv to standalone.CheckProofOfWork)

It is split into multiple commits to ease the review process and each commit further describes the relevant changes.

A few key points:

The primary motivation for renaming the functions to specifically call out difficulty bits instead of the shorter "Compact" is to prevent future ambiguity with Satoshi-style variable length compact integers that will ultimately also be available from the package.

For legacy reasons, even though the difficulty bits encoding is only used in Decred to encode unsigned 256-bit integers which represent difficulty targets, the encoding is capable of representing negative numbers as well as numbers much larger than the maximum value of an unsigned 256-bit integer. Therefore, in order to enable proper error detection and stay consistent with legacy code, DiffBitsToUint256 returns additional flags to indicate the aforementioned conditions.

The original calculation that CalcWork performed (and therefore is used in consensus and must stay the same) involves a dividend of 2^256 which is not directly representable by a uint256, so this implementation retains the same semantics by transforming the calculation as described in detail by the comments in the code.

Benchmarks

The following is a comparison between the existing implementations in the blockchain/standalone module with big integers (old) and the new implementation (new) averaging 10 runs each:

Name Old Time/Op New Time/Op Delta
DiffBitsToUint256 158ns ± 1% 4ns ± 1% -97.35%
Uint256ToDiffBits 151ns ± 2% 8ns ± 4% -94.82%
CalcWork 638ns ± 1% 59ns ± 1% -90.78%
HashToUint256 127ns ± 3% 3ns ± 1% -97.68%
CheckProofOfWork 314ns ± 1% 43ns ± 0% -86.42%
Name Old Allocs/Op New Allocs/Op Delta
DiffBitsToUint256 3.00 ± 0% 0.00 -100.00%
Uint256ToDiffBits 2.00 ± 0% 0.00 -100.00%
CalcWork 7.00 ± 0% 0.00 -100.00%
HashToUint256 2.00 ± 0% 0.00 -100.00%
CheckProofOfWork 5.00 ± 0% 0.00 -100.00%

This is work towards #2786.

@davecgh davecgh added non-forking consensus Changes that involve modifying consensus code without causing any forking changes. optimization labels Nov 7, 2021
@davecgh davecgh force-pushed the primitives_pow branch 7 times, most recently from 8fa898c to 2debfc9 Compare November 12, 2021 02:20
Copy link
Member Author

@davecgh davecgh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit title typo rimitives: Add check proof of work. .

internal/staging/primitives/pow.go Outdated Show resolved Hide resolved
@davecgh
Copy link
Member Author

davecgh commented Nov 13, 2021

Commit title typo rimitives: Add check proof of work. .

Corrected.

@davecgh davecgh force-pushed the primitives_pow branch 5 times, most recently from 8b581ad to eeea7b9 Compare November 20, 2021 00:36
Copy link
Member

@rstaudt2 rstaudt2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I confirmed that the behavior is the same as the corresponding functions in blockchain/standalone. I just had a few minor comments inline.

internal/staging/primitives/README.md Outdated Show resolved Hide resolved
internal/staging/primitives/README.md Outdated Show resolved Hide resolved
internal/staging/primitives/pow.go Outdated Show resolved Hide resolved
internal/staging/primitives/pow_test.go Show resolved Hide resolved
Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

benchmarks
$ go test -bench .
goos: linux
goarch: amd64
pkg: github.com/decred/dcrd/internal/staging/primitives
cpu: AMD Ryzen 9 3900XT 12-Core Processor           
BenchmarkDiffBitsToUint256-24           332246042                3.625 ns/op           0 B/op          0 allocs/op
BenchmarkUint256ToDiffBits-24           220453808                5.322 ns/op           0 B/op          0 allocs/op
BenchmarkCalcWork-24                    28234624                41.63 ns/op            0 B/op          0 allocs/op
BenchmarkHashToUint256-24               500613214                2.060 ns/op           0 B/op          0 allocs/op
BenchmarkCheckProofOfWork-24            39382293                29.22 ns/op            0 B/op          0 allocs/op
PASS
ok      github.com/decred/dcrd/internal/staging/primitives      8.405s

internal/staging/primitives/pow.go Outdated Show resolved Hide resolved
internal/staging/primitives/pow.go Show resolved Hide resolved
internal/staging/primitives/pow_test.go Show resolved Hide resolved
internal/staging/primitives/pow_test.go Show resolved Hide resolved
internal/staging/primitives/pow.go Outdated Show resolved Hide resolved
internal/staging/primitives/README.md Outdated Show resolved Hide resolved
This implements code for converting between difficulty bits and the new
uint256 type along with associated tests.

The functions are the semantic equivalents of CompactToBig and
BigToCompact from blockchain/standalone updated to use the new uint256
type instead of stdlib big integers and renamed to DiffBitsToUint256 and
Uint256ToDiffBits, respectively.

The primary motivation for renaming the functions to specifically call
out difficulty bits instead of the shorter "Compact" is to prevent
future ambiguity with Satoshi-style variable length compact integers
that will ultimately also be available from the package.

Note that for legacy reasons, even though the difficulty bits encoding
is only used in Decred to encode unsigned 256-bit integers which
represent difficulty targets, the encoding is capable of representing
negative numbers as well as numbers much larger than the maximum value
of an unsigned 256-bit integer.  Therefore, in order to enable proper
error detection and stay consistent with legacy code, DiffBitsToUint256
returns additional flags to indicate the aforementioned conditions.
The following is a comparison between the existing implementations in
the blockchain/standalone module with big integers (old) and the new
implementations (new) averaging 10 runs each:

name                old time/op     new time/op     delta
------------------------------------------------------------------------------
DiffBitsToUint256   158ns ± 1%      4ns ± 1%        -97.35%  (p=0.000 n=10+10)
Uint256ToDiffBits   151ns ± 2%      8ns ± 4%        -94.82%  (p=0.000 n=10+10)

name                      old allocs/op   new allocs/op   delta
-------------------------------------------------------------------------------
DiffBitsToUint256   3.00 ± 0%       0.00            -100.00%  (p=0.000 n=10+10)
Uint256ToDiffBits   2.00 ± 0%       0.00            -100.00%  (p=0.000 n=10+10)
This implements code for calculating a uint256 work value from
difficulty bits along with associated tests.

The function is the semantic equivalent of CalcWork from
blockchain/standalone updated to use and return the new uint256 type
instead of stdlib big integers.

Note that the original calculation involves a dividend of 2^256 which is
not directly representable by a uint256, so this implementation retains
the same semantics by transforming the calculation as described in
detail by the comments.
The following is a comparison between the existing implementation in the
blockchain/standalone module with big integers (old) and the new
implementation (new) averaging 10 runs each:

name       old time/op     new time/op     delta
---------------------------------------------------------------------
CalcWork   638ns ± 1%      59ns ± 1%       -90.78%  (p=0.000 n=10+10)

name       old allocs/op   new allocs/op   delta
----------------------------------------------------------------------
CalcWork   7.00 ± 0%       0.00            -100.00%  (p=0.000 n=10+10)
This implements a function to convert a hash to the new uint256 type
along with associated tests.

The function is the semantic equivalent of HashToBig from
blockchain/standalone updated to use and return the new uint256 type
instead of stdlib big integers.
The following is a comparison between the existing implementation in the
blockchain/standalone module with big integers (old) and the new
implementation (new) averaging 10 runs each:

name            old time/op     new time/op     delta
--------------------------------------------------------------------------
HashToUint256   127ns ± 3%      3ns ± 1%        -97.68%  (p=0.000 n=10+10)

name            old allocs/op   new allocs/op   delta
---------------------------------------------------------------------------
HashToUint256   2.00 ± 0%       0.00            -100.00%  (p=0.000 n=10+10)
This implements functions to verify a block hash is less than the target
difficulty represented by given difficulty bits and that said difficulty
is in min/max range per a proof-of-work limit along with associated
tests.

It also introduces error infrastructure consistent with the rest of the
code.

The functions are the semantic equivalent of the functions of the same
names from blockchain/standalone updated to use and accept the new
uint256 type instead of stdlib big integers.
The following is a comparison between the existing implementation in the
blockchain/standalone module with big integers (old) and the new
implementation (new) averaging 10 runs each:

name               old time/op     new time/op     delta
-----------------------------------------------------------------------------
CheckProofOfWork   314ns ± 1%      43ns ± 0%       -86.42%  (p=0.000 n=10+10)

name               old allocs/op   new allocs/op   delta
------------------------------------------------------------------------------
CheckProofOfWork   5.00 ± 0%       0.00            -100.00%  (p=0.000 n=10+10)
@davecgh davecgh merged commit 92ccbee into decred:master Dec 4, 2021
@davecgh davecgh deleted the primitives_pow branch December 4, 2021 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
non-forking consensus Changes that involve modifying consensus code without causing any forking changes. optimization
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants