primitives: Add work calc from diff bits.
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.
This commit is contained in:
parent
3e7115cf64
commit
ecf6c6313e
@ -141,3 +141,50 @@ func Uint256ToDiffBits(n *uint256.Uint256) uint32 {
|
||||
const isNegative = false
|
||||
return uint256ToDiffBits(n, isNegative)
|
||||
}
|
||||
|
||||
// CalcWork calculates a work value from difficulty bits. Decred increases the
|
||||
// difficulty for generating a block by decreasing the value which the generated
|
||||
// hash must be less than. This difficulty target is stored in each block
|
||||
// header using a compact representation as described in the documentation for
|
||||
// DiffBitsToUint256. The main chain is selected by choosing the chain that has
|
||||
// the most proof of work (highest difficulty). Since a lower target difficulty
|
||||
// value equates to higher actual difficulty, the work value which will be
|
||||
// accumulated must be the inverse of the difficulty. For legacy reasons, the
|
||||
// result is zero when the difficulty is zero. Finally, to avoid really small
|
||||
// floating point numbers, the result multiplies the numerator by 2^256 and adds
|
||||
// 1 to the denominator.
|
||||
func CalcWork(diffBits uint32) uint256.Uint256 {
|
||||
// Return a work value of zero if the passed difficulty bits represent a
|
||||
// negative number, a number that overflows a uint256, or zero. Note this
|
||||
// should not happen in practice with valid blocks, but an invalid block
|
||||
// could trigger it.
|
||||
diff, isNegative, overflows := DiffBitsToUint256(diffBits)
|
||||
if isNegative || overflows || diff.IsZero() {
|
||||
return uint256.Uint256{}
|
||||
}
|
||||
|
||||
// The goal is to calculate 2^256 / (diff+1), where diff > 0 using a
|
||||
// fixed-precision uint256.
|
||||
//
|
||||
// Since 2^256 can't be represented by a uint256, the calc is performed as
|
||||
// follows:
|
||||
//
|
||||
// Notice:
|
||||
// work = (2^256 / (diff+1))
|
||||
// => work = ((2^256-diff-1) / (diff+1))+1
|
||||
//
|
||||
// Next, observe that 2^256-diff-1 is the one's complement of diff as a
|
||||
// uint256 which is equivalent to the bitwise not. Also, of special note is
|
||||
// the case when diff = 2^256-1 because (2^256-1)+1 ≡ 0 (mod 2^256) and
|
||||
// thus would result in division by zero when working with a uint256. The
|
||||
// original calculation would produce 1 in that case, so the resulting
|
||||
// piecewise function is:
|
||||
//
|
||||
// {work = 1 , where diff = 2^256-1
|
||||
// {work = (^diff / (diff+1))+1, where 0 < diff < 2^256-1
|
||||
//
|
||||
// However, a difficulty target of 2^256 - 1 is impossible to encode in the
|
||||
// difficulty bits, so it is safe to ignore that case.
|
||||
divisor := new(uint256.Uint256).SetUint64(1).Add(&diff)
|
||||
return *diff.Not().Div(divisor).AddUint64(1)
|
||||
}
|
||||
|
||||
@ -209,3 +209,49 @@ func TestUint256ToDiffBits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCalcWork ensures calculating a work value from a compact target
|
||||
// difficulty produces the correct results.
|
||||
func TestCalcWork(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string // test description
|
||||
input uint32 // target difficulty bits to test
|
||||
want string // expected uint256
|
||||
}{{
|
||||
name: "mainnet block 1",
|
||||
input: 0x1b01ffff,
|
||||
want: "0000000000000000000000000000000000000000000000000000800040002000",
|
||||
}, {
|
||||
name: "mainnet block 288",
|
||||
input: 0x1b01330e,
|
||||
want: "0000000000000000000000000000000000000000000000000000d56f2dcbe105",
|
||||
}, {
|
||||
name: "higher diff (exponent 24)",
|
||||
input: 0x185fb28a,
|
||||
want: "000000000000000000000000000000000000000000000002acd33ddd458512da",
|
||||
}, {
|
||||
name: "zero",
|
||||
input: 0,
|
||||
want: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
}, {
|
||||
name: "max uint256",
|
||||
input: 0x2100ffff,
|
||||
want: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
}, {
|
||||
name: "negative target difficulty",
|
||||
input: 0x1810000,
|
||||
want: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
}}
|
||||
|
||||
for _, test := range tests {
|
||||
want := hexToUint256(test.want)
|
||||
result := CalcWork(test.input)
|
||||
if !result.Eq(want) {
|
||||
t.Errorf("%q: mismatched result -- got %x, want %x", test.name,
|
||||
result, want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user