mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-07 14:46:44 +00:00
[DEV-149] Add test case for CVE-2018-17144 (Bitcoin DoS/Double Spend bug) (#77)
* [DEV-149] Add test case for CVE-2018-17144 (Bitcoin DoS/Double Spend bug) * [DEV-149] change t.Errorf + return to t.Fatalf * [DEV-149] fix malformed blocks * [DEV-149] change test blocks to use simnet genesis
This commit is contained in:
parent
45e0e6707b
commit
030469f035
@ -1,18 +1,19 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bou.ke/monkey"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"bou.ke/monkey"
|
||||||
"github.com/daglabs/btcd/dagconfig"
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
"github.com/daglabs/btcd/database"
|
"github.com/daglabs/btcd/database"
|
||||||
"github.com/daglabs/btcd/util"
|
"github.com/daglabs/btcd/util"
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMaybeAcceptBlockErrors(t *testing.T) {
|
func TestMaybeAcceptBlockErrors(t *testing.T) {
|
||||||
// Create a new database and DAG instance to run tests against.
|
// Create a new database and DAG instance to run tests against.
|
||||||
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", &dagconfig.MainNetParams)
|
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", &dagconfig.SimNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestMaybeAcceptBlockErrors: Failed to setup DAG instance: %v", err)
|
t.Fatalf("TestMaybeAcceptBlockErrors: Failed to setup DAG instance: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bouk/monkey"
|
|
||||||
"github.com/daglabs/btcd/dagconfig"
|
|
||||||
"github.com/daglabs/btcd/database"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"bou.ke/monkey"
|
||||||
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
|
"github.com/daglabs/btcd/database"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAncestorErrors(t *testing.T) {
|
func TestAncestorErrors(t *testing.T) {
|
||||||
@ -21,7 +22,7 @@ func TestAncestorErrors(t *testing.T) {
|
|||||||
|
|
||||||
func TestFlushToDBErrors(t *testing.T) {
|
func TestFlushToDBErrors(t *testing.T) {
|
||||||
// Create a new database and DAG instance to run tests against.
|
// Create a new database and DAG instance to run tests against.
|
||||||
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", &dagconfig.MainNetParams)
|
dag, teardownFunc, err := DAGSetup("TestFlushToDBErrors", &dagconfig.MainNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("TestFlushToDBErrors: Failed to setup DAG instance: %s", err)
|
t.Fatalf("TestFlushToDBErrors: Failed to setup DAG instance: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -36,18 +36,16 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
for _, file := range testFiles {
|
for _, file := range testFiles {
|
||||||
blockTmp, err := loadBlocks(file)
|
blockTmp, err := loadBlocks(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error loading file: %v\n", err)
|
t.Fatalf("Error loading file: %v\n", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
blocks = append(blocks, blockTmp...)
|
blocks = append(blocks, blockTmp...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new database and chain instance to run tests against.
|
// Create a new database and chain instance to run tests against.
|
||||||
dag, teardownFunc, err := DAGSetup("haveblock",
|
dag, teardownFunc, err := DAGSetup("haveblock",
|
||||||
&dagconfig.MainNetParams)
|
&dagconfig.SimNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to setup chain instance: %v", err)
|
t.Fatalf("Failed to setup chain instance: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer teardownFunc()
|
defer teardownFunc()
|
||||||
|
|
||||||
@ -58,16 +56,15 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
for i := 1; i < len(blocks); i++ {
|
for i := 1; i < len(blocks); i++ {
|
||||||
isOrphan, err := dag.ProcessBlock(blocks[i], BFNone)
|
isOrphan, err := dag.ProcessBlock(blocks[i], BFNone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ProcessBlock fail on block %v: %v\n", i, err)
|
t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if isOrphan {
|
if isOrphan {
|
||||||
t.Errorf("ProcessBlock incorrectly returned block %v "+
|
t.Fatalf("ProcessBlock incorrectly returned block %v "+
|
||||||
"is an orphan\n", i)
|
"is an orphan\n", i)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test a block with related parents
|
||||||
testFiles = []string{
|
testFiles = []string{
|
||||||
"blk_3C.dat",
|
"blk_3C.dat",
|
||||||
}
|
}
|
||||||
@ -75,35 +72,60 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
for _, file := range testFiles {
|
for _, file := range testFiles {
|
||||||
blockTmp, err := loadBlocks(file)
|
blockTmp, err := loadBlocks(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error loading file: %v\n", err)
|
t.Fatalf("Error loading file: %v\n", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
blocks = append(blocks, blockTmp...)
|
blocks = append(blocks, blockTmp...)
|
||||||
}
|
}
|
||||||
isOrphan, err := dag.ProcessBlock(blocks[6], BFNone)
|
isOrphan, err := dag.ProcessBlock(blocks[6], BFNone)
|
||||||
|
|
||||||
// Block 3c should fail to connect since its parents are related. (It points to 1 and 2, and 1 is the parent of 2)
|
// Block 3C should fail to connect since its parents are related. (It points to 1 and 2, and 1 is the parent of 2)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("ProcessBlock for block 3c has no error when expected to have an error\n")
|
t.Fatalf("ProcessBlock for block 3C has no error when expected to have an error\n")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if isOrphan {
|
if isOrphan {
|
||||||
t.Errorf("ProcessBlock incorrectly returned block 3c " +
|
t.Fatalf("ProcessBlock incorrectly returned block 3C " +
|
||||||
|
"is an orphan\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a block with the same input twice
|
||||||
|
testFiles = []string{
|
||||||
|
"blk_3D.dat",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range testFiles {
|
||||||
|
blockTmp, err := loadBlocks(file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error loading file: %v\n", err)
|
||||||
|
}
|
||||||
|
blocks = append(blocks, blockTmp...)
|
||||||
|
}
|
||||||
|
isOrphan, err = dag.ProcessBlock(blocks[7], BFNone)
|
||||||
|
|
||||||
|
// Block 3D should fail to connect since it has a transaction with the same input twice
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("ProcessBlock for block 3D has no error when expected to have an error\n")
|
||||||
|
}
|
||||||
|
rErr, ok := err.(RuleError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("ProcessBlock for block 3D expected a RuleError, but got something else\n")
|
||||||
|
}
|
||||||
|
if !ok || rErr.ErrorCode != ErrDuplicateTxInputs {
|
||||||
|
t.Fatalf("ProcessBlock for block 3D expected error code %s but got %s\n", ErrDuplicateTxInputs, rErr.ErrorCode)
|
||||||
|
}
|
||||||
|
if isOrphan {
|
||||||
|
t.Fatalf("ProcessBlock incorrectly returned block 3D " +
|
||||||
"is an orphan\n")
|
"is an orphan\n")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert an orphan block.
|
// Insert an orphan block.
|
||||||
isOrphan, err = dag.ProcessBlock(util.NewBlock(&Block100000),
|
isOrphan, err = dag.ProcessBlock(util.NewBlock(&Block100000),
|
||||||
BFNone)
|
BFNone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unable to process block: %v", err)
|
t.Fatalf("Unable to process block: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if !isOrphan {
|
if !isOrphan {
|
||||||
t.Errorf("ProcessBlock indicated block is an not orphan when " +
|
t.Fatalf("ProcessBlock indicated block is an not orphan when " +
|
||||||
"it should be\n")
|
"it should be\n")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -111,13 +133,13 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
// Genesis block should be present.
|
// Genesis block should be present.
|
||||||
{hash: dagconfig.MainNetParams.GenesisHash.String(), want: true},
|
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
|
||||||
|
|
||||||
// Block 3b should be present (as a second child of Block 2).
|
// Block 3b should be present (as a second child of Block 2).
|
||||||
{hash: "00000093c8f2ab3444502da0754fc8149d738701aef9b2e0f32f32c078039295", want: true},
|
{hash: "00cd35debc62fd60b6fbda1925894db5996c02bcd575a4130fdb4d6071537152", want: true},
|
||||||
|
|
||||||
// Block 100000 should be present (as an orphan).
|
// Block 100000 should be present (as an orphan).
|
||||||
{hash: "000000e46b5f4f7bfecff77f2f30f2ab90d08e3c5a55784080f97689bcd92786", want: true},
|
{hash: "66cdaddc8884c99ccc46c2f34f579903a223cc12b44c239938af47ee0c7193b4", want: true},
|
||||||
|
|
||||||
// Random hashes should not be available.
|
// Random hashes should not be available.
|
||||||
{hash: "123", want: false},
|
{hash: "123", want: false},
|
||||||
@ -126,19 +148,16 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
hash, err := daghash.NewHashFromStr(test.hash)
|
hash, err := daghash.NewHashFromStr(test.hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Fatalf("NewHashFromStr: %v", err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := dag.HaveBlock(hash)
|
result, err := dag.HaveBlock(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("HaveBlock #%d unexpected error: %v", i, err)
|
t.Fatalf("HaveBlock #%d unexpected error: %v", i, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if result != test.want {
|
if result != test.want {
|
||||||
t.Errorf("HaveBlock #%d got %v want %v", i, result,
|
t.Fatalf("HaveBlock #%d got %v want %v", i, result,
|
||||||
test.want)
|
test.want)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -745,7 +764,7 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new database and dag instance to run tests against.
|
// Create a new database and dag instance to run tests against.
|
||||||
dag, teardownFunc, err := DAGSetup("testErrorThroughPatching", &dagconfig.MainNetParams)
|
dag, teardownFunc, err := DAGSetup("testErrorThroughPatching", &dagconfig.SimNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to setup dag instance: %v", err)
|
t.Fatalf("Failed to setup dag instance: %v", err)
|
||||||
}
|
}
|
||||||
@ -759,7 +778,12 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
|
|||||||
|
|
||||||
err = nil
|
err = nil
|
||||||
for i := 1; i < len(blocks); i++ {
|
for i := 1; i < len(blocks); i++ {
|
||||||
_, err = dag.ProcessBlock(blocks[i], BFNone)
|
var isOrphan bool
|
||||||
|
isOrphan, err = dag.ProcessBlock(blocks[i], BFNone)
|
||||||
|
if isOrphan {
|
||||||
|
t.Fatalf("ProcessBlock incorrectly returned block %v "+
|
||||||
|
"is an orphan\n", i)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,11 @@ func TestNotifications(t *testing.T) {
|
|||||||
t.Fatalf("Error loading file: %v\n", err)
|
t.Fatalf("Error loading file: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new database and chain instance to run tests against.
|
// Create a new database and dag instance to run tests against.
|
||||||
chain, teardownFunc, err := DAGSetup("notifications",
|
dag, teardownFunc, err := DAGSetup("notifications",
|
||||||
&dagconfig.MainNetParams)
|
&dagconfig.SimNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to setup chain instance: %v", err)
|
t.Fatalf("Failed to setup dag instance: %v", err)
|
||||||
}
|
}
|
||||||
defer teardownFunc()
|
defer teardownFunc()
|
||||||
|
|
||||||
@ -36,10 +36,14 @@ func TestNotifications(t *testing.T) {
|
|||||||
// times.
|
// times.
|
||||||
const numSubscribers = 3
|
const numSubscribers = 3
|
||||||
for i := 0; i < numSubscribers; i++ {
|
for i := 0; i < numSubscribers; i++ {
|
||||||
chain.Subscribe(callback)
|
dag.Subscribe(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = chain.ProcessBlock(blocks[1], BFNone)
|
isOrphan, err := dag.ProcessBlock(blocks[1], BFNone)
|
||||||
|
if isOrphan {
|
||||||
|
t.Fatalf("ProcessBlock incorrectly returned block " +
|
||||||
|
"is an orphan\n")
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ProcessBlock fail on block 1: %v\n", err)
|
t.Fatalf("ProcessBlock fail on block 1: %v\n", err)
|
||||||
}
|
}
|
||||||
|
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3A.dat
vendored
BIN
blockdag/testdata/blk_3A.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3B.dat
vendored
BIN
blockdag/testdata/blk_3B.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3C.dat
vendored
BIN
blockdag/testdata/blk_3C.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3D.dat
vendored
Normal file
BIN
blockdag/testdata/blk_3D.dat
vendored
Normal file
Binary file not shown.
@ -435,7 +435,7 @@ func checkBlockParentsOrder(header *wire.BlockHeader) error {
|
|||||||
sortedHashes = append(sortedHashes, hash)
|
sortedHashes = append(sortedHashes, hash)
|
||||||
}
|
}
|
||||||
sort.Slice(sortedHashes, func(i, j int) bool {
|
sort.Slice(sortedHashes, func(i, j int) bool {
|
||||||
return daghash.Less(&sortedHashes[j], &sortedHashes[i])
|
return daghash.Less(&sortedHashes[i], &sortedHashes[j])
|
||||||
})
|
})
|
||||||
if !daghash.AreEqual(header.PrevBlocks, sortedHashes) {
|
if !daghash.AreEqual(header.PrevBlocks, sortedHashes) {
|
||||||
return ruleError(ErrWrongParentsOrder, "block parents are not ordered by hash")
|
return ruleError(ErrWrongParentsOrder, "block parents are not ordered by hash")
|
||||||
|
@ -68,7 +68,7 @@ func TestSequenceLocksActive(t *testing.T) {
|
|||||||
func TestCheckConnectBlockTemplate(t *testing.T) {
|
func TestCheckConnectBlockTemplate(t *testing.T) {
|
||||||
// Create a new database and chain instance to run tests against.
|
// Create a new database and chain instance to run tests against.
|
||||||
dag, teardownFunc, err := DAGSetup("checkconnectblocktemplate",
|
dag, teardownFunc, err := DAGSetup("checkconnectblocktemplate",
|
||||||
&dagconfig.MainNetParams)
|
&dagconfig.SimNetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to setup chain instance: %v", err)
|
t.Errorf("Failed to setup chain instance: %v", err)
|
||||||
return
|
return
|
||||||
@ -190,9 +190,9 @@ func TestCheckBlockSanity(t *testing.T) {
|
|||||||
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
|
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
|
||||||
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
|
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
|
||||||
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
|
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
|
||||||
Timestamp: time.Unix(0x5b7142b4, 0), // 2018-07-19 15:11:10 +0000 UTC
|
Timestamp: time.Unix(0x5bbc64c9, 0), // 2018-10-09 08:17:35 +0000 UTC
|
||||||
Bits: 0x1e00ffff, // 503382015
|
Bits: 0x1e00ffff, // 503382015
|
||||||
Nonce: 0xa05433ab, // 2148280951
|
Nonce: 0xe00edcf9, // 3759070457
|
||||||
},
|
},
|
||||||
Transactions: []*wire.MsgTx{
|
Transactions: []*wire.MsgTx{
|
||||||
{
|
{
|
||||||
@ -551,17 +551,17 @@ var Block100000 = wire.MsgBlock{
|
|||||||
NumPrevBlocks: 2,
|
NumPrevBlocks: 2,
|
||||||
PrevBlocks: []daghash.Hash{
|
PrevBlocks: []daghash.Hash{
|
||||||
[32]byte{ // Make go vet happy.
|
[32]byte{ // Make go vet happy.
|
||||||
0xa5, 0x60, 0xe4, 0x59, 0x61, 0x5d, 0xbb, 0x4e,
|
0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95,
|
||||||
0xae, 0x33, 0x40, 0x2a, 0xc0, 0xb8, 0x8a, 0xb6,
|
0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3,
|
||||||
0x53, 0xc5, 0x11, 0xe3, 0x35, 0xd0, 0xa6, 0x1e,
|
0x46, 0x11, 0x89, 0x6b, 0x82, 0x1a, 0x68, 0x3b,
|
||||||
0x0a, 0xc4, 0x06, 0x96, 0x97, 0x00, 0x00, 0x00,
|
0x7a, 0x4e, 0xde, 0xfe, 0x2c, 0x00, 0x00, 0x00,
|
||||||
}, // MainNet genesis
|
},
|
||||||
[32]byte{ // Make go vet happy.
|
[32]byte{ // Make go vet happy.
|
||||||
0x2a, 0x9a, 0x2a, 0xe4, 0x3b, 0xc1, 0x6b, 0x55,
|
0x4b, 0xb0, 0x75, 0x35, 0xdf, 0xd5, 0x8e, 0x0b,
|
||||||
0x06, 0x39, 0xc2, 0xe6, 0xbb, 0x4a, 0xec, 0xeb,
|
0x3c, 0xd6, 0x4f, 0xd7, 0x15, 0x52, 0x80, 0x87,
|
||||||
0x7a, 0x49, 0x7c, 0xe9, 0xf3, 0x60, 0x20, 0x7d,
|
0x2a, 0x04, 0x71, 0xbc, 0xf8, 0x30, 0x95, 0x52,
|
||||||
0x63, 0xc7, 0x26, 0x89, 0xf6, 0x00, 0x00, 0x00,
|
0x6a, 0xce, 0x0e, 0x38, 0xc6, 0x00, 0x00, 0x00,
|
||||||
}, // SimNet genesis
|
},
|
||||||
},
|
},
|
||||||
MerkleRoot: daghash.Hash([32]byte{ // Make go vet happy.
|
MerkleRoot: daghash.Hash([32]byte{ // Make go vet happy.
|
||||||
0xc0, 0x92, 0x53, 0x8f, 0x6f, 0xf7, 0xf5, 0x24,
|
0xc0, 0x92, 0x53, 0x8f, 0x6f, 0xf7, 0xf5, 0x24,
|
||||||
@ -569,9 +569,9 @@ var Block100000 = wire.MsgBlock{
|
|||||||
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
|
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
|
||||||
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
|
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
|
||||||
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
|
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
|
||||||
Timestamp: time.Unix(0x5bb9f3b4, 0), // 2018-07-19 15:11:10 +0000 UTC
|
Timestamp: time.Unix(0x5bbc7588, 0), // 2018-10-09 07:49:35 +0000 UTC
|
||||||
Bits: 0x1e00ffff, // 503382015
|
Bits: 0x207fffff, // 503382015
|
||||||
Nonce: 0x203602e3, // 2148280951
|
Nonce: 0xdffffff9,
|
||||||
},
|
},
|
||||||
Transactions: []*wire.MsgTx{
|
Transactions: []*wire.MsgTx{
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user