Skip to content

Commit

Permalink
feat(SPV-760): handle insertion of genesis block for chosen chain net…
Browse files Browse the repository at this point in the history
… during db migration (#244)
  • Loading branch information
kuba-4chain authored May 9, 2024
1 parent c0277cd commit 9da8e48
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 91 deletions.
4 changes: 1 addition & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ func main() {
hs := service.NewServices(service.Dept{
Repositories: repo,
Peers: peers,
Params: config.ActiveNetParams.Params,
AdminToken: cfg.HTTP.AuthToken,
Logger: log,
Config: cfg,
Expand Down Expand Up @@ -150,8 +149,7 @@ func main() {
var p2pServer P2PServer

if cfg.P2P.Experimental {
chainParams := config.ActiveNetParams.Params
p2pServer = p2pexp.NewServer(cfg.P2P, chainParams, hs.Headers, hs.Chains, log)
p2pServer = p2pexp.NewServer(cfg.P2P, hs.Headers, hs.Chains, log)
} else {
p2pServer, err = p2p.NewServer(hs, peers, cfg.P2P, log)
if err != nil {
Expand Down
11 changes: 7 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ const (

var version = "should-be-overridden-by-setDefaults"

var Lookup func(string) ([]net.IP, error)
var Dial func(string, string, time.Duration) (net.Conn, error)
var Checkpoints []chaincfg.Checkpoint
var TimeSource MedianTimeSource
var (
Lookup func(string) ([]net.IP, error)
Dial func(string, string, time.Duration) (net.Conn, error)
Checkpoints []chaincfg.Checkpoint
TimeSource MedianTimeSource
)

// DbEngine database engine.
type DbEngine string
Expand Down Expand Up @@ -125,6 +127,7 @@ type P2PConfig struct {
DefaultConnectTimeout time.Duration `mapstructure:"default_connect_timeout" description:"The default connection timeout"`
UserAgentName string `mapstructure:"user_agent_name" description:"The name that should be used during announcement of the client on the p2p network"`
UserAgentVersion string `mapstructure:"user_agent_version" description:"By default will be equal to application version, but can be overridden for development purposes"`
ChainNetType NetworkType `mapstructure:"chain_net_type" description:"Chain Network Type (mainnet, testnet, regtest, simnet), mainnet by default"`
Experimental bool `mapstructure:"experimental" description:"Turns on a new (highly experimental) way of getting headers with the usage of /internal/transports/p2p instead of /transports/p2p"`
}

Expand Down
1 change: 1 addition & 0 deletions config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func getP2PDefaults() *P2PConfig {
DisableCheckpoints: false,
UserAgentName: ApplicationName,
UserAgentVersion: Version(),
ChainNetType: MainNet,
Experimental: false,
}
}
Expand Down
50 changes: 24 additions & 26 deletions config/p2p_network_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,31 @@ import (
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg"
)

// ActiveNetParams is a pointer to the parameters specific to the
// currently active bitcoin network.
var ActiveNetParams = updatedMainNetParams(mainNetParams)
type NetworkType string

// params is used to group parameters for various networks such as the main
// network and test networks.
type params struct {
*chaincfg.Params
rpcPort string
}

// mainNetParams contains parameters specific to the main network
// (wire.MainNet). NOTE: The RPC port is intentionally different than the
// reference implementation because bsvd does not handle wallet requests. The
// separate wallet process listens on the well-known port and forwards requests
// it does not handle on to bsvd. This approach allows the wallet process
// to emulate the full reference implementation RPC API.
var mainNetParams = params{
Params: &chaincfg.MainNetParams,
rpcPort: "8334",
}
const (
MainNet NetworkType = "mainnet"
RegTestNet NetworkType = "regtest"
TestNet NetworkType = "testnet"
SimulationNet NetworkType = "simnet"
)

var mainNetDNSSeeds = []chaincfg.DNSSeed{
{Host: "seed-nodes.bsvb.tech", HasFiltering: true},
func (c *P2PConfig) GetNetParams() *chaincfg.Params {
switch c.ChainNetType {
case MainNet:
return &chaincfg.MainNetParams
case RegTestNet:
return &chaincfg.RegressionNetParams
case TestNet:
return &chaincfg.TestNet3Params
case SimulationNet:
return &chaincfg.SimNetParams
default:
return &chaincfg.MainNetParams
}
}

func updatedMainNetParams(p params) *params {
p.DNSSeeds = mainNetDNSSeeds
return &p
}
// ActiveNetParams is a pointer to the parameters specific to the
// currently active bitcoin network.
// TODO: remove this after switching to new p2p server.
var ActiveNetParams = &chaincfg.MainNetParams
4 changes: 4 additions & 0 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ func Init(cfg *config.AppConfig, log *zerolog.Logger) (*sqlx.DB, error) {
if err := importHeaders(adapter, cfg, &dbLog); err != nil {
return nil, err
}
} else {
if err := insertGenesisBlock(adapter, cfg, &dbLog); err != nil {
return nil, err
}
}

return adapter.getDBx(), nil
Expand Down
48 changes: 48 additions & 0 deletions database/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package database

import (
"context"
"time"

"github.com/bitcoin-sv/block-headers-service/config"
"github.com/bitcoin-sv/block-headers-service/database/sql"
"github.com/bitcoin-sv/block-headers-service/domains"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg/chainhash"
"github.com/bitcoin-sv/block-headers-service/internal/wire"
"github.com/bitcoin-sv/block-headers-service/repository/dto"
"github.com/rs/zerolog"
)

func insertGenesisBlock(db dbAdapter, cfg *config.AppConfig, log *zerolog.Logger) error {
hRepository := sql.NewHeadersDb(db.getDBx(), log)
netParams := cfg.P2P.GetNetParams()

genesis := createGenesisHeaderBlock(netParams.GenesisBlock.Header)

err := hRepository.Create(context.Background(), genesis)
if err != nil {
return err
}

return nil
}

// CreateGenesisHeaderBlock create filled genesis block based on the chosen chain net header block.
func createGenesisHeaderBlock(genesisBlockHeader wire.BlockHeader) dto.DbBlockHeader {
longestChain := domains.LongestChain
genesisBlock := dto.DbBlockHeader{
Hash: genesisBlockHeader.BlockHash().String(),
Height: 0,
Version: 1,
PreviousBlock: chainhash.Hash{}.String(), // 0000000000000000000000000000000000000000000000000000000000000000
MerkleRoot: genesisBlockHeader.MerkleRoot.String(), // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
Timestamp: time.Unix(genesisBlockHeader.Timestamp.Unix(), 0),
Bits: genesisBlockHeader.Bits,
Nonce: genesisBlockHeader.Nonce,
State: longestChain.String(),
Chainwork: domains.CalculateWork(genesisBlockHeader.Bits).BigInt().String(),
CumulatedWork: domains.CalculateWork(genesisBlockHeader.Bits).BigInt().String(),
}

return genesisBlock
}
21 changes: 0 additions & 21 deletions domains/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"math/big"
"time"

"github.com/bitcoin-sv/block-headers-service/internal/chaincfg"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg/chainhash"
)

Expand Down Expand Up @@ -188,26 +187,6 @@ func (bh *BlockHeader) WrapWithHeaderState() BlockHeaderState {
return model
}

// CreateGenesisHeaderBlock create filled genesis block.
func CreateGenesisHeaderBlock() BlockHeader {
// Create a new node from the genesis block and set it as the best node.
genesisBlock := BlockHeader{
Hash: chaincfg.GenesisHash,
Height: 0,
Version: 1,
PreviousBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
MerkleRoot: chaincfg.GenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 18:15:05 +0000 UTC
Bits: 0x1d00ffff,
Nonce: 0x7c2bac1d,
State: LongestChain,
Chainwork: big.NewInt(4295032833),
CumulatedWork: big.NewInt(4295032833),
}

return genesisBlock
}

// FastLog2Floor calculates the floor of the base-2 logarithm of an input 32-bit
// unsigned integer using a bitwise algorithm that masks off decreasingly lower-order bits
// of the integer until it reaches the highest order bit, and returns the resulting integer value.
Expand Down
20 changes: 17 additions & 3 deletions internal/tests/fixtures/chain_fixtures.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package fixtures

import (
"time"

"github.com/bitcoin-sv/block-headers-service/domains"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg/chainhash"
)

Expand All @@ -12,10 +15,21 @@ func StartingChain() (db []domains.BlockHeader, tip *domains.BlockHeader) {
}

func startingChain() headerChainFixture {
genesisBlock := domains.CreateGenesisHeaderBlock()
return []domains.BlockHeader{
genesisBlock,
genesisHeader := chaincfg.MainNetParams.GenesisBlock.Header
genesisBlock := domains.BlockHeader{
Hash: genesisHeader.BlockHash(),
Height: 0,
Version: 1,
PreviousBlock: chainhash.Hash{},
MerkleRoot: genesisHeader.MerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
Timestamp: time.Unix(genesisHeader.Timestamp.Unix(), 0),
Bits: genesisHeader.Bits,
Nonce: genesisHeader.Nonce,
State: domains.LongestChain,
Chainwork: domains.CalculateWork(genesisHeader.Bits).BigInt(),
CumulatedWork: domains.CalculateWork(genesisHeader.Bits).BigInt(),
}
return []domains.BlockHeader{genesisBlock}
}

// LongestChain creates mocked the longest chain entries (containing Genesis Block and 4 first blocks).
Expand Down
3 changes: 1 addition & 2 deletions internal/tests/testapp/test_bhs.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (p *TestBlockHeaderService) When() *When {

// NewTestBlockHeaderService Start block headers service for testing reason.
func NewTestBlockHeaderService(t *testing.T, ops ...bhsOpt) (*TestBlockHeaderService, Cleanup) {
//override arguments otherwise all flags provided to go test command will be parsed by LoadConfig
// override arguments otherwise all flags provided to go test command will be parsed by LoadConfig
os.Args = []string{""}

viper.Reset()
Expand Down Expand Up @@ -95,7 +95,6 @@ func NewTestBlockHeaderService(t *testing.T, ops ...bhsOpt) (*TestBlockHeaderSer
hs := service.NewServices(service.Dept{
Repositories: repo.ToDomainRepo(),
Peers: nil,
Params: config.ActiveNetParams.Params,
AdminToken: cfg.HTTP.AuthToken,
Logger: &testLog,
Config: cfg,
Expand Down
7 changes: 3 additions & 4 deletions internal/transports/p2p/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,15 @@ type server struct {
}

func NewServer(
config *config.P2PConfig,
chainParams *chaincfg.Params,
p2pConfig *config.P2PConfig,
headersService service.Headers,
chainService service.Chains,
log *zerolog.Logger,
) *server {
serverLogger := log.With().Str("service", "p2p-experimental").Logger()
server := &server{
config: config,
chainParams: chainParams,
config: p2pConfig,
chainParams: p2pConfig.GetNetParams(),
headersService: headersService,
chainService: chainService,
log: &serverLogger,
Expand Down
12 changes: 0 additions & 12 deletions service/header_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,18 +419,6 @@ func (hs *HeaderService) CountHeaders() int {
return count
}

// InsertGenesisHeaderInDatabase adds a genesis header (height=0) to db.
func (hs *HeaderService) InsertGenesisHeaderInDatabase() error {
genesisBlock := domains.CreateGenesisHeaderBlock()
if hs.repo.Headers.GenesisExists() {
return nil
}

err := hs.repo.Headers.AddHeaderToDatabase(genesisBlock)

return err
}

// GetTips returns slice with current tips.
func (hs *HeaderService) GetTips() ([]*domains.BlockHeader, error) {
return hs.repo.Headers.GetAllTips()
Expand Down
5 changes: 1 addition & 4 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package service
import (
"github.com/bitcoin-sv/block-headers-service/config"
"github.com/bitcoin-sv/block-headers-service/domains"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg/chainhash"
"github.com/bitcoin-sv/block-headers-service/internal/wire"
"github.com/bitcoin-sv/block-headers-service/notification"
Expand All @@ -29,7 +28,6 @@ type Headers interface {
GetTip() *domains.BlockHeader
GetTipHeight() int32
CountHeaders() int
InsertGenesisHeaderInDatabase() error
GetHeaderByHash(hash string) (*domains.BlockHeader, error)
GetHeadersByHeight(height int, count int) ([]*domains.BlockHeader, error)
GetMerkleRootsConfirmations(request []domains.MerkleRootConfirmationRequestItem) ([]*domains.MerkleRootConfirmation, error)
Expand Down Expand Up @@ -65,7 +63,6 @@ type Services struct {
type Dept struct {
Peers map[*peerpkg.Peer]*peerpkg.PeerSyncState
Repositories *repository.Repositories
Params *chaincfg.Params
AdminToken string
Logger *zerolog.Logger
Config *config.AppConfig
Expand All @@ -88,7 +85,7 @@ func NewServices(d Dept) *Services {
func newChainService(d Dept, notifier *notification.Notifier) Chains {
return NewChainsService(
d.Repositories,
d.Params,
d.Config.P2P.GetNetParams(),
d.Logger,
DefaultBlockHasher(),
notifier,
Expand Down
10 changes: 6 additions & 4 deletions transports/http/endpoints/api/headers/header_endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"testing"

"github.com/bitcoin-sv/block-headers-service/domains"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg"
"github.com/bitcoin-sv/block-headers-service/internal/chaincfg/chainhash"
"github.com/bitcoin-sv/block-headers-service/internal/tests/assert"
"github.com/bitcoin-sv/block-headers-service/internal/tests/fixtures"
"github.com/bitcoin-sv/block-headers-service/internal/tests/testapp"
Expand Down Expand Up @@ -258,16 +260,16 @@ func TestGetCommonAncestor(t *testing.T) {
// given
bhs, cleanup := testapp.NewTestBlockHeaderService(t, testapp.WithLongestChain(), testapp.WithApiAuthorizationDisabled())
defer cleanup()
genesis := domains.CreateGenesisHeaderBlock()
genesis := chaincfg.MainNetParams.GenesisBlock.Header
expected_response := headers.BlockHeaderResponse{
Hash: genesis.Hash.String(),
Hash: genesis.BlockHash().String(),
Version: genesis.Version,
PreviousBlock: genesis.PreviousBlock.String(),
PreviousBlock: chainhash.Hash{}.String(),
MerkleRoot: genesis.MerkleRoot.String(),
Timestamp: uint32(genesis.Timestamp.Unix()),
DifficultyTarget: genesis.Bits,
Nonce: genesis.Nonce,
Work: genesis.Chainwork.String(),
Work: domains.CalculateWork(genesis.Bits).BigInt().String(),
}
expected_result := struct {
code int
Expand Down
11 changes: 3 additions & 8 deletions transports/p2p/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,8 @@ func (s *server) peerHandler() {
}

// Add peers discovered through DNS to the address manager.
s.log.Info().Msgf("[Server] configs.ActiveNetParams.Params: %#v", pretty.Formatter(config.ActiveNetParams.Params))
connmgr.SeedFromDNS(config.ActiveNetParams.Params, defaultRequiredServices,
s.log.Info().Msgf("[Server] configs.ActiveNetParams.Params: %#v", pretty.Formatter(config.ActiveNetParams))
connmgr.SeedFromDNS(config.ActiveNetParams, defaultRequiredServices,
s.p2pConfig.BsvdLookup, func(addrs []*wire.NetAddress) {
// Bitcoind uses a lookup of the dns seeder here. This
// is rather strange since the values looked up by the
Expand Down Expand Up @@ -819,11 +819,6 @@ func newServer(chainParams *chaincfg.Params, services *service.Services,
return nil, errors.New("no valid listen address")
}

initErr := services.Headers.InsertGenesisHeaderInDatabase()
if initErr != nil {
return nil, initErr
}

s := server{
startupTime: time.Now().Unix(),
chainParams: chainParams,
Expand Down Expand Up @@ -879,7 +874,7 @@ func newServer(chainParams *chaincfg.Params, services *service.Services,
// NewServer creates and return p2p server.
func NewServer(services *service.Services, peers map[*peer.Peer]*peer.PeerSyncState, p2pCfg *config.P2PConfig, log *zerolog.Logger) (*server, error) {
serverLogger := log.With().Str("service", "p2p").Logger()
server, err := newServer(config.ActiveNetParams.Params, services, peers, p2pCfg, &serverLogger)
server, err := newServer(config.ActiveNetParams, services, peers, p2pCfg, &serverLogger)
if err != nil {
serverLogger.Error().Msgf("Unable to start server: %v", err)
return nil, err
Expand Down

0 comments on commit 9da8e48

Please sign in to comment.