btcd/integration/getchaintips_test.go

351 lines
13 KiB
Go
Raw Normal View History

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)
}
}