-
Notifications
You must be signed in to change notification settings - Fork 291
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
primitives: Add check proof of work.
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.
- Loading branch information
Showing
4 changed files
with
386 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright (c) 2021 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package primitives | ||
|
||
// ErrorKind identifies a kind of error. It has full support for errors.Is and | ||
// errors.As, so the caller can directly check against an error kind when | ||
// determining the reason for an error. | ||
type ErrorKind string | ||
|
||
// Error satisfies the error interface and prints human-readable errors. | ||
func (e ErrorKind) Error() string { | ||
return string(e) | ||
} | ||
|
||
// These constants are used to identify a specific RuleError. | ||
const ( | ||
// ErrUnexpectedDifficulty indicates specified bits do not align with | ||
// the expected value either because it doesn't match the calculated | ||
// value based on difficulty rules or it is out of the valid range. | ||
ErrUnexpectedDifficulty = ErrorKind("ErrUnexpectedDifficulty") | ||
|
||
// ErrHighHash indicates the block does not hash to a value which is | ||
// lower than the required target difficultly. | ||
ErrHighHash = ErrorKind("ErrHighHash") | ||
) | ||
|
||
// RuleError identifies a rule violation. It has full support for errors.Is | ||
// and errors.As, so the caller can ascertain the specific reason for the | ||
// error by checking the underlying error. | ||
type RuleError struct { | ||
Description string | ||
Err error | ||
} | ||
|
||
// Error satisfies the error interface and prints human-readable errors. | ||
func (e RuleError) Error() string { | ||
return e.Description | ||
} | ||
|
||
// Unwrap returns the underlying wrapped error. | ||
func (e RuleError) Unwrap() error { | ||
return e.Err | ||
} | ||
|
||
// ruleError creates a RuleError given a set of arguments. | ||
func ruleError(kind ErrorKind, desc string) RuleError { | ||
return RuleError{Err: kind, Description: desc} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// Copyright (c) 2021 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package primitives | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
"testing" | ||
) | ||
|
||
// TestErrorKindStringer tests the stringized output for the ErrorKind type. | ||
func TestErrorKindStringer(t *testing.T) { | ||
tests := []struct { | ||
in ErrorKind | ||
want string | ||
}{ | ||
{ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"}, | ||
{ErrHighHash, "ErrHighHash"}, | ||
} | ||
|
||
for i, test := range tests { | ||
result := test.in.Error() | ||
if result != test.want { | ||
t.Errorf("#%d: got: %s want: %s", i, result, test.want) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
// TestRuleError tests the error output for the RuleError type. | ||
func TestRuleError(t *testing.T) { | ||
t.Parallel() | ||
|
||
tests := []struct { | ||
in RuleError | ||
want string | ||
}{{ | ||
RuleError{Description: "some error"}, | ||
"some error", | ||
}, { | ||
RuleError{Description: "human-readable error"}, | ||
"human-readable error", | ||
}} | ||
|
||
for i, test := range tests { | ||
result := test.in.Error() | ||
if result != test.want { | ||
t.Errorf("#%d: got: %s want: %s", i, result, test.want) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
// TestErrorKindIsAs ensures both ErrorKind and RuleError can be identified as | ||
// being a specific error kind via errors.Is and unwrapped via errors.As. | ||
func TestErrorKindIsAs(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
err error | ||
target error | ||
wantMatch bool | ||
wantAs ErrorKind | ||
}{{ | ||
name: "ErrUnexpectedDifficulty == ErrUnexpectedDifficulty", | ||
err: ErrUnexpectedDifficulty, | ||
target: ErrUnexpectedDifficulty, | ||
wantMatch: true, | ||
wantAs: ErrUnexpectedDifficulty, | ||
}, { | ||
name: "RuleError.ErrUnexpectedDifficulty == ErrUnexpectedDifficulty", | ||
err: ruleError(ErrUnexpectedDifficulty, ""), | ||
target: ErrUnexpectedDifficulty, | ||
wantMatch: true, | ||
wantAs: ErrUnexpectedDifficulty, | ||
}, { | ||
name: "ErrHighHash != ErrUnexpectedDifficulty", | ||
err: ErrHighHash, | ||
target: ErrUnexpectedDifficulty, | ||
wantMatch: false, | ||
wantAs: ErrHighHash, | ||
}, { | ||
name: "RuleError.ErrHighHash != ErrUnexpectedDifficulty", | ||
err: ruleError(ErrHighHash, ""), | ||
target: ErrUnexpectedDifficulty, | ||
wantMatch: false, | ||
wantAs: ErrHighHash, | ||
}, { | ||
name: "ErrHighHash != RuleError.ErrUnexpectedDifficulty", | ||
err: ErrHighHash, | ||
target: ruleError(ErrUnexpectedDifficulty, ""), | ||
wantMatch: false, | ||
wantAs: ErrHighHash, | ||
}, { | ||
name: "RuleError.ErrHighHash != RuleError.ErrUnexpectedDifficulty", | ||
err: ruleError(ErrHighHash, ""), | ||
target: ruleError(ErrUnexpectedDifficulty, ""), | ||
wantMatch: false, | ||
wantAs: ErrHighHash, | ||
}, { | ||
name: "RuleError.ErrHighHash != io.EOF", | ||
err: ruleError(ErrHighHash, ""), | ||
target: io.EOF, | ||
wantMatch: false, | ||
wantAs: ErrHighHash, | ||
}} | ||
|
||
for _, test := range tests { | ||
// Ensure the error matches or not depending on the expected result. | ||
result := errors.Is(test.err, test.target) | ||
if result != test.wantMatch { | ||
t.Errorf("%s: incorrect error identification -- got %v, want %v", | ||
test.name, result, test.wantMatch) | ||
continue | ||
} | ||
|
||
// Ensure the underlying error kind can be unwrapped is and is the | ||
// expected kind. | ||
var kind ErrorKind | ||
if !errors.As(test.err, &kind) { | ||
t.Errorf("%s: unable to unwrap to error kind", test.name) | ||
continue | ||
} | ||
if kind != test.wantAs { | ||
t.Errorf("%s: unexpected unwrapped error kind -- got %v, want %v", | ||
test.name, kind, test.wantAs) | ||
continue | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.