mirror of
https://github.com/btcsuite/btcd.git
synced 2025-02-24 06:47:59 +01:00
351 lines
13 KiB
Go
351 lines
13 KiB
Go
|
package integration
|
||
|
|
||
|
import (
|
||
|
"encoding/hex"
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/btcsuite/btcd/btcjson"
|
||
|
"github.com/btcsuite/btcd/btcutil"
|
||
|
"github.com/btcsuite/btcd/chaincfg"
|
||
|
"github.com/btcsuite/btcd/integration/rpctest"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
func getBlockFromString(t *testing.T, hexStr string) *btcutil.Block {
|
||
|
t.Helper()
|
||
|
|
||
|
serializedBlock, err := hex.DecodeString(hexStr)
|
||
|
if err != nil {
|
||
|
t.Fatalf("couldn't decode hex string of %s", hexStr)
|
||
|
}
|
||
|
|
||
|
block, err := btcutil.NewBlockFromBytes(serializedBlock)
|
||
|
if err != nil {
|
||
|
t.Fatalf("couldn't make a new block from bytes. "+
|
||
|
"Decoded hex string: %s", hexStr)
|
||
|
}
|
||
|
|
||
|
return block
|
||
|
}
|
||
|
|
||
|
// compareMultipleChainTips checks that all the expected chain tips are included in got chain tips and
|
||
|
// verifies that the got chain tip matches the expected chain tip.
|
||
|
func compareMultipleChainTips(t *testing.T, gotChainTips, expectedChainTips []*btcjson.GetChainTipsResult) error {
|
||
|
if len(gotChainTips) != len(expectedChainTips) {
|
||
|
return fmt.Errorf("Expected %d chaintips but got %d", len(expectedChainTips), len(gotChainTips))
|
||
|
}
|
||
|
|
||
|
gotChainTipsMap := make(map[string]btcjson.GetChainTipsResult)
|
||
|
for _, gotChainTip := range gotChainTips {
|
||
|
gotChainTipsMap[gotChainTip.Hash] = *gotChainTip
|
||
|
}
|
||
|
|
||
|
for _, expectedChainTip := range expectedChainTips {
|
||
|
gotChainTip, found := gotChainTipsMap[expectedChainTip.Hash]
|
||
|
if !found {
|
||
|
return fmt.Errorf("Couldn't find expected chaintip with hash %s", expectedChainTip.Hash)
|
||
|
}
|
||
|
|
||
|
require.Equal(t, gotChainTip, *expectedChainTip)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func TestGetChainTips(t *testing.T) {
|
||
|
// block1Hex is a block that builds on top of the regtest genesis block.
|
||
|
// Has blockhash of "36c056247e8c0589f6307995e4e13acf2b2b79cad9ecd5a4eeab2131ed0ecde5".
|
||
|
block1Hex := "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf18891" +
|
||
|
"0f71881025ae0d41ce8748b79ac40e5f3197af3bb83a594def7943aff0fce504c638ea6d63f" +
|
||
|
"fff7f2000000000010200000000010100000000000000000000000000000000000000000000" +
|
||
|
"00000000000000000000ffffffff025100ffffffff0200f2052a010000001600149b0f9d020" +
|
||
|
"8b3b425246e16830562a63bf1c701180000000000000000266a24aa21a9ede2f61c3f71d1de" +
|
||
|
"fd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000" +
|
||
|
"000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block2Hex is a block that builds on top of block1Hex.
|
||
|
// Has blockhash of "664b51334782a4ad16e8471b530dcd0027c75b8c25187b41dfc85ecd353295c6".
|
||
|
block2Hex := "00000020e5cd0eed3121abeea4d5ecd9ca792b2bcf3ae1e4957930f689058c7e2456c0" +
|
||
|
"362a78a11b875d31af2ea493aa5b6b623e0d481f11e69f7147ab974be9da087f3e24696f63f" +
|
||
|
"fff7f2001000000010200000000010100000000000000000000000000000000000000000000" +
|
||
|
"00000000000000000000ffffffff025200ffffffff0200f2052a0100000016001470fea1feb" +
|
||
|
"4969c1f237753ae29c0217c6637835c0000000000000000266a24aa21a9ede2f61c3f71d1de" +
|
||
|
"fd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000" +
|
||
|
"000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block3Hex is a block that builds on top of block2Hex.
|
||
|
// Has blockhash of "17a5c5cb90ecde5a46dd195d434eea46b653e35e4517070eade429db3ac83944".
|
||
|
block3Hex := "00000020c6953235cd5ec8df417b18258c5bc72700cd0d531b47e816ada4824733514b" +
|
||
|
"66c3ad4d567a36c20df07ea0b7fce1e4b4ee5be3eaf0b946b0ae73f3a74d47f0cf99696f63f" +
|
||
|
"fff7f2000000000010200000000010100000000000000000000000000000000000000000000" +
|
||
|
"00000000000000000000ffffffff025300ffffffff0200f2052a010000001600140e835869b" +
|
||
|
"154f647d11376634b5e8c785e7d21060000000000000000266a24aa21a9ede2f61c3f71d1de" +
|
||
|
"fd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000" +
|
||
|
"000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block4Hex is a block that builds on top of block3Hex.
|
||
|
// Has blockhash of "7b357f3073c4397d6d069a32a09141c32560f3c62233ca138eb5e03c5991f45c".
|
||
|
block4Hex := "000000204439c83adb29e4ad0e0717455ee353b646ea4e435d19dd465adeec90cbc5a5" +
|
||
|
"17ab639a5dd622e90f5f9feffc1c7c28f47a2caf85c21d7dd52cd223a7164619e37a6a6f63f" +
|
||
|
"fff7f2004000000010200000000010100000000000000000000000000000000000000000000" +
|
||
|
"00000000000000000000ffffffff025400ffffffff0200f2052a01000000160014a157c74b4" +
|
||
|
"42a3e11b45cf5273f8c0c032c5a40ed0000000000000000266a24aa21a9ede2f61c3f71d1de" +
|
||
|
"fd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000" +
|
||
|
"000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block2aHex is a block that builds on top of block1Hex.
|
||
|
// Has blockhash of "5181a4e34cc23ed95c69749dedf4cc7ebd659243bc1683372f8940c8cd8f9b68".
|
||
|
block2aHex := "00000020e5cd0eed3121abeea4d5ecd9ca792b2bcf3ae1e4957930f689058c7e2456c" +
|
||
|
"036f7d4ebe524260c9b6c2b5e3d105cad0b7ddfaeaa29971363574fc1921a3f2f7ad66b6f63" +
|
||
|
"ffff7f200100000001020000000001010000000000000000000000000000000000000000000" +
|
||
|
"000000000000000000000ffffffff025200ffffffff0200f2052a0100000016001466fca22d" +
|
||
|
"0e4679d119ea1e127c984746a1f7e66c0000000000000000266a24aa21a9ede2f61c3f71d1d" +
|
||
|
"efd3fa999dfa36953755c690689799962b48bebd836974e8cf9012000000000000000000000" +
|
||
|
"0000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block3aHex is a block that builds on top of block2aHex.
|
||
|
// Has blockhash of "0b0216936d1a5c01362256d06a9c9a2b13768fa2f2748549a71008af36dd167f".
|
||
|
block3aHex := "00000020689b8fcdc840892f378316bc439265bd7eccf4ed9d74695cd93ec24ce3a48" +
|
||
|
"15161a430ce5cae955b1254b753bc95854d942947855d3ae59002de9773b7fe65fdf16b6f63" +
|
||
|
"ffff7f200100000001020000000001010000000000000000000000000000000000000000000" +
|
||
|
"000000000000000000000ffffffff025300ffffffff0200f2052a0100000016001471da0afb" +
|
||
|
"883c228b18af6bd0cabc471aebe8d1750000000000000000266a24aa21a9ede2f61c3f71d1d" +
|
||
|
"efd3fa999dfa36953755c690689799962b48bebd836974e8cf9012000000000000000000000" +
|
||
|
"0000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block4aHex is a block that builds on top of block3aHex.
|
||
|
// Has blockhash of "65a00a026eaa83f6e7a7f4a920faa090f3f9d3565a56df2362db2ab2fa14ccec".
|
||
|
block4aHex := "000000207f16dd36af0810a7498574f2a28f76132b9a9c6ad0562236015c1a6d93160" +
|
||
|
"20b951fa5ee5072d88d6aef9601999307dbd8d96dad067b80bfe04afe81c7a8c21beb706f63" +
|
||
|
"ffff7f200000000001020000000001010000000000000000000000000000000000000000000" +
|
||
|
"000000000000000000000ffffffff025400ffffffff0200f2052a01000000160014fd1f118c" +
|
||
|
"95a712b8adef11c3cc0643bcb6b709f10000000000000000266a24aa21a9ede2f61c3f71d1d" +
|
||
|
"efd3fa999dfa36953755c690689799962b48bebd836974e8cf9012000000000000000000000" +
|
||
|
"0000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block5aHex is a block that builds on top of block4aHex.
|
||
|
// Has blockhash of "5c8814bc034a4c37fa5ccdc05e09b45a771bd7505d68092f21869a912737ee10".
|
||
|
block5aHex := "00000020eccc14fab22adb6223df565a56d3f9f390a0fa20a9f4a7e7f683aa6e020aa" +
|
||
|
"0656331bd4fcd3db611de7fbf72ef3dff0b85b244b5a983d5c0270e728214f67f9aaa766f63" +
|
||
|
"ffff7f200600000001020000000001010000000000000000000000000000000000000000000" +
|
||
|
"000000000000000000000ffffffff025500ffffffff0200f2052a0100000016001438335896" +
|
||
|
"ad1d087e3541436a5b293c0d23ad27e60000000000000000266a24aa21a9ede2f61c3f71d1d" +
|
||
|
"efd3fa999dfa36953755c690689799962b48bebd836974e8cf9012000000000000000000000" +
|
||
|
"0000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// block4bHex is a block that builds on top of block3aHex.
|
||
|
// Has blockhash of "130458e795cc46f2759195e92737426fb0ada2a07f98434551ffb7500b23c161".
|
||
|
block4bHex := "000000207f16dd36af0810a7498574f2a28f76132b9a9c6ad0562236015c1a6d93160" +
|
||
|
"20b14f9ce93d0144c383fea72f408b06b268a1523a029b825a1edfa15b367f6db2cfd7d6f63" +
|
||
|
"ffff7f200200000001020000000001010000000000000000000000000000000000000000000" +
|
||
|
"000000000000000000000ffffffff025400ffffffff0200f2052a0100000016001405b5ba2d" +
|
||
|
"1e549c4c84a623de3575948d3ef8a27f0000000000000000266a24aa21a9ede2f61c3f71d1d" +
|
||
|
"efd3fa999dfa36953755c690689799962b48bebd836974e8cf9012000000000000000000000" +
|
||
|
"0000000000000000000000000000000000000000000000000000"
|
||
|
|
||
|
// Set up regtest chain.
|
||
|
r, err := rpctest.New(&chaincfg.RegressionNetParams, nil, nil, "")
|
||
|
if err != nil {
|
||
|
t.Fatal("TestGetChainTips fail. Unable to create primary harness: ", err)
|
||
|
}
|
||
|
if err := r.SetUp(true, 0); err != nil {
|
||
|
t.Fatalf("TestGetChainTips fail. Unable to setup test chain: %v", err)
|
||
|
}
|
||
|
defer r.TearDown()
|
||
|
|
||
|
// Immediately call getchaintips after setting up regtest.
|
||
|
gotChainTips, err := r.Client.GetChainTips()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
// We expect a single genesis block.
|
||
|
expectedChainTips := []*btcjson.GetChainTipsResult{
|
||
|
{
|
||
|
Height: 0,
|
||
|
Hash: chaincfg.RegressionNetParams.GenesisHash.String(),
|
||
|
BranchLen: 0,
|
||
|
Status: "active",
|
||
|
},
|
||
|
}
|
||
|
err = compareMultipleChainTips(t, gotChainTips, expectedChainTips)
|
||
|
if err != nil {
|
||
|
t.Fatalf("TestGetChainTips fail. Error: %v", err)
|
||
|
}
|
||
|
|
||
|
// Submit 4 blocks.
|
||
|
//
|
||
|
// Our chain view looks like so:
|
||
|
// (genesis block) -> 1 -> 2 -> 3 -> 4
|
||
|
blockStrings := []string{block1Hex, block2Hex, block3Hex, block4Hex}
|
||
|
for _, blockString := range blockStrings {
|
||
|
block := getBlockFromString(t, blockString)
|
||
|
err = r.Client.SubmitBlock(block, nil)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gotChainTips, err = r.Client.GetChainTips()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expectedChainTips = []*btcjson.GetChainTipsResult{
|
||
|
{
|
||
|
Height: 4,
|
||
|
Hash: getBlockFromString(t, blockStrings[len(blockStrings)-1]).Hash().String(),
|
||
|
BranchLen: 0,
|
||
|
Status: "active",
|
||
|
},
|
||
|
}
|
||
|
err = compareMultipleChainTips(t, gotChainTips, expectedChainTips)
|
||
|
if err != nil {
|
||
|
t.Fatalf("TestGetChainTips fail. Error: %v", err)
|
||
|
}
|
||
|
|
||
|
// Submit 2 blocks that don't build on top of the current active tip.
|
||
|
//
|
||
|
// Our chain view looks like so:
|
||
|
// (genesis block) -> 1 -> 2 -> 3 -> 4 (active)
|
||
|
// \ -> 2a -> 3a (valid-fork)
|
||
|
blockStrings = []string{block2aHex, block3aHex}
|
||
|
for _, blockString := range blockStrings {
|
||
|
block := getBlockFromString(t, blockString)
|
||
|
err = r.Client.SubmitBlock(block, nil)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gotChainTips, err = r.Client.GetChainTips()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expectedChainTips = []*btcjson.GetChainTipsResult{
|
||
|
{
|
||
|
Height: 4,
|
||
|
Hash: getBlockFromString(t, block4Hex).Hash().String(),
|
||
|
BranchLen: 0,
|
||
|
Status: "active",
|
||
|
},
|
||
|
{
|
||
|
Height: 3,
|
||
|
Hash: getBlockFromString(t, block3aHex).Hash().String(),
|
||
|
BranchLen: 2,
|
||
|
Status: "valid-fork",
|
||
|
},
|
||
|
}
|
||
|
err = compareMultipleChainTips(t, gotChainTips, expectedChainTips)
|
||
|
if err != nil {
|
||
|
t.Fatalf("TestGetChainTips fail. Error: %v", err)
|
||
|
}
|
||
|
|
||
|
// Submit a single block that don't build on top of the current active tip.
|
||
|
//
|
||
|
// Our chain view looks like so:
|
||
|
// (genesis block) -> 1 -> 2 -> 3 -> 4 (active)
|
||
|
// \ -> 2a -> 3a -> 4a (valid-fork)
|
||
|
block := getBlockFromString(t, block4aHex)
|
||
|
err = r.Client.SubmitBlock(block, nil)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
gotChainTips, err = r.Client.GetChainTips()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expectedChainTips = []*btcjson.GetChainTipsResult{
|
||
|
{
|
||
|
Height: 4,
|
||
|
Hash: getBlockFromString(t, block4Hex).Hash().String(),
|
||
|
BranchLen: 0,
|
||
|
Status: "active",
|
||
|
},
|
||
|
{
|
||
|
Height: 4,
|
||
|
Hash: getBlockFromString(t, block4aHex).Hash().String(),
|
||
|
BranchLen: 3,
|
||
|
Status: "valid-fork",
|
||
|
},
|
||
|
}
|
||
|
err = compareMultipleChainTips(t, gotChainTips, expectedChainTips)
|
||
|
if err != nil {
|
||
|
t.Fatalf("TestGetChainTips fail. Error: %v", err)
|
||
|
}
|
||
|
|
||
|
// Submit a single block that changes the active branch to 5a.
|
||
|
//
|
||
|
// Our chain view looks like so:
|
||
|
// (genesis block) -> 1 -> 2 -> 3 -> 4 (valid-fork)
|
||
|
// \ -> 2a -> 3a -> 4a -> 5a (active)
|
||
|
block = getBlockFromString(t, block5aHex)
|
||
|
err = r.Client.SubmitBlock(block, nil)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
gotChainTips, err = r.Client.GetChainTips()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expectedChainTips = []*btcjson.GetChainTipsResult{
|
||
|
{
|
||
|
Height: 4,
|
||
|
Hash: getBlockFromString(t, block4Hex).Hash().String(),
|
||
|
BranchLen: 3,
|
||
|
Status: "valid-fork",
|
||
|
},
|
||
|
{
|
||
|
Height: 5,
|
||
|
Hash: getBlockFromString(t, block5aHex).Hash().String(),
|
||
|
BranchLen: 0,
|
||
|
Status: "active",
|
||
|
},
|
||
|
}
|
||
|
err = compareMultipleChainTips(t, gotChainTips, expectedChainTips)
|
||
|
if err != nil {
|
||
|
t.Fatalf("TestGetChainTips fail. Error: %v", err)
|
||
|
}
|
||
|
|
||
|
// Submit a single block that builds on top of 3a.
|
||
|
//
|
||
|
// Our chain view looks like so:
|
||
|
// (genesis block) -> 1 -> 2 -> 3 -> 4 (valid-fork)
|
||
|
// \ -> 2a -> 3a -> 4a -> 5a (active)
|
||
|
// \ -> 4b (valid-fork)
|
||
|
block = getBlockFromString(t, block4bHex)
|
||
|
err = r.Client.SubmitBlock(block, nil)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
gotChainTips, err = r.Client.GetChainTips()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expectedChainTips = []*btcjson.GetChainTipsResult{
|
||
|
{
|
||
|
Height: 4,
|
||
|
Hash: getBlockFromString(t, block4Hex).Hash().String(),
|
||
|
BranchLen: 3,
|
||
|
Status: "valid-fork",
|
||
|
},
|
||
|
{
|
||
|
Height: 5,
|
||
|
Hash: getBlockFromString(t, block5aHex).Hash().String(),
|
||
|
BranchLen: 0,
|
||
|
Status: "active",
|
||
|
},
|
||
|
{
|
||
|
Height: 4,
|
||
|
Hash: getBlockFromString(t, block4bHex).Hash().String(),
|
||
|
BranchLen: 1,
|
||
|
Status: "valid-fork",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
err = compareMultipleChainTips(t, gotChainTips, expectedChainTips)
|
||
|
if err != nil {
|
||
|
t.Fatalf("TestGetChainTips fail. Error: %v", err)
|
||
|
}
|
||
|
}
|