Centralise handshake response validation so outbound sync checks both network identity and minimum peer build version through the p2p layer. Also record HTLC key images as spent during block processing, matching the HF1 input semantics and preventing those spends from being omitted from chain state. Co-Authored-By: Charon <charon@lethean.io>
117 lines
3 KiB
Go
117 lines
3 KiB
Go
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
|
//
|
|
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package blockchain
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"time"
|
|
|
|
coreerr "dappco.re/go/core/log"
|
|
|
|
"dappco.re/go/core/blockchain/chain"
|
|
"dappco.re/go/core/blockchain/config"
|
|
"dappco.re/go/core/blockchain/p2p"
|
|
levin "dappco.re/go/core/p2p/node/levin"
|
|
)
|
|
|
|
func runChainSyncLoop(ctx context.Context, blockchain *chain.Chain, chainConfig *config.ChainConfig, hardForks []config.HardFork, seed string) {
|
|
opts := chain.SyncOptions{
|
|
VerifySignatures: false,
|
|
Forks: hardForks,
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
}
|
|
|
|
if err := runChainSyncOnce(ctx, blockchain, chainConfig, opts, seed); err != nil {
|
|
log.Printf("sync: %v (retrying in 10s)", err)
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(10 * time.Second):
|
|
}
|
|
continue
|
|
}
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(30 * time.Second):
|
|
}
|
|
}
|
|
}
|
|
|
|
func runChainSyncOnce(ctx context.Context, blockchain *chain.Chain, chainConfig *config.ChainConfig, opts chain.SyncOptions, seed string) error {
|
|
conn, err := net.DialTimeout("tcp", seed, 10*time.Second)
|
|
if err != nil {
|
|
return coreerr.E("runChainSyncOnce", fmt.Sprintf("dial %s", seed), err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
levinConn := levin.NewConnection(conn)
|
|
|
|
var peerIDBytes [8]byte
|
|
rand.Read(peerIDBytes[:])
|
|
peerID := binary.LittleEndian.Uint64(peerIDBytes[:])
|
|
|
|
localHeight, _ := blockchain.Height()
|
|
|
|
handshakeReq := p2p.HandshakeRequest{
|
|
NodeData: p2p.NodeData{
|
|
NetworkID: chainConfig.NetworkID,
|
|
PeerID: peerID,
|
|
LocalTime: time.Now().Unix(),
|
|
MyPort: 0,
|
|
},
|
|
PayloadData: p2p.CoreSyncData{
|
|
CurrentHeight: localHeight,
|
|
ClientVersion: config.ClientVersion,
|
|
NonPruningMode: true,
|
|
},
|
|
}
|
|
payload, err := p2p.EncodeHandshakeRequest(&handshakeReq)
|
|
if err != nil {
|
|
return coreerr.E("runChainSyncOnce", "encode handshake", err)
|
|
}
|
|
if err := levinConn.WritePacket(p2p.CommandHandshake, payload, true); err != nil {
|
|
return coreerr.E("runChainSyncOnce", "write handshake", err)
|
|
}
|
|
|
|
hdr, data, err := levinConn.ReadPacket()
|
|
if err != nil {
|
|
return coreerr.E("runChainSyncOnce", "read handshake", err)
|
|
}
|
|
if hdr.Command != uint32(p2p.CommandHandshake) {
|
|
return coreerr.E("runChainSyncOnce", fmt.Sprintf("unexpected command %d", hdr.Command), nil)
|
|
}
|
|
|
|
var handshakeResp p2p.HandshakeResponse
|
|
if err := handshakeResp.Decode(data); err != nil {
|
|
return coreerr.E("runChainSyncOnce", "decode handshake", err)
|
|
}
|
|
|
|
if err := p2p.ValidateHandshakeResponse(&handshakeResp, chainConfig.NetworkID, chainConfig.IsTestnet); err != nil {
|
|
return coreerr.E("runChainSyncOnce", "validate handshake", err)
|
|
}
|
|
|
|
localSync := p2p.CoreSyncData{
|
|
CurrentHeight: localHeight,
|
|
ClientVersion: config.ClientVersion,
|
|
NonPruningMode: true,
|
|
}
|
|
p2pConn := chain.NewLevinP2PConn(levinConn, handshakeResp.PayloadData.CurrentHeight, localSync)
|
|
|
|
return blockchain.P2PSync(ctx, p2pConn, opts)
|
|
}
|