Skip to content

Commit f1ca2ca

Browse files
committed
feat: support for fetching headers from Handshake peer
Signed-off-by: Aurora Gaffney <aurora@blinklabs.io>
1 parent b62fa08 commit f1ca2ca

File tree

4 files changed

+239
-3
lines changed

4 files changed

+239
-3
lines changed

internal/handshake/block.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import (
1212
"io"
1313
)
1414

15+
const (
16+
BlockHeaderSize = 236
17+
)
18+
1519
type Block struct {
1620
Header BlockHeader
1721
Transactions []Transaction
@@ -66,9 +70,30 @@ type BlockHeader struct {
6670
Mask [32]byte
6771
}
6872

73+
func NewBlockHeaderFromReader(r io.Reader) (*BlockHeader, error) {
74+
// Read entire input into a bytes.Buffer
75+
tmpData, err := io.ReadAll(r)
76+
if err != nil {
77+
return nil, err
78+
}
79+
buf := bytes.NewBuffer(tmpData)
80+
// Decode block header
81+
var tmpBlockHeader BlockHeader
82+
if err := tmpBlockHeader.Decode(buf); err != nil {
83+
return nil, err
84+
}
85+
return &tmpBlockHeader, err
86+
}
87+
6988
func (h *BlockHeader) Decode(r io.Reader) error {
7089
if err := binary.Read(r, binary.LittleEndian, h); err != nil {
7190
return err
7291
}
7392
return nil
7493
}
94+
95+
func (h *BlockHeader) Encode() []byte {
96+
buf := new(bytes.Buffer)
97+
_ = binary.Write(buf, binary.LittleEndian, h)
98+
return buf.Bytes()
99+
}

internal/handshake/protocol/messages.go

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"fmt"
1414
"math"
1515
"net"
16+
17+
"github.com/blinklabs-io/cdnsd/internal/handshake"
1618
)
1719

1820
// Message types
@@ -36,6 +38,14 @@ const (
3638
messageMaxPayloadLength = 8 * 1000 * 1000
3739
)
3840

41+
type UnsupportedMessageTypeError struct {
42+
MessageType uint8
43+
}
44+
45+
func (e UnsupportedMessageTypeError) Error() string {
46+
return fmt.Sprintf("unsupported message type: %d", e.MessageType)
47+
}
48+
3949
type Message interface {
4050
Encode() []byte
4151
Decode([]byte) error
@@ -69,8 +79,12 @@ func decodeMessage(header *msgHeader, payload []byte) (Message, error) {
6979
ret = &MsgGetAddr{}
7080
case MessageAddr:
7181
ret = &MsgAddr{}
82+
case MessageGetHeaders:
83+
ret = &MsgGetHeaders{}
84+
case MessageHeaders:
85+
ret = &MsgHeaders{}
7286
default:
73-
return nil, fmt.Errorf("unsupported message type: %d", header.MessageType)
87+
return nil, UnsupportedMessageTypeError{MessageType: header.MessageType}
7488
}
7589
if err := ret.Decode(payload); err != nil {
7690
return nil, fmt.Errorf("decode message: %w", err)
@@ -326,9 +340,76 @@ func (m *MsgAddr) Decode(data []byte) error {
326340
return nil
327341
}
328342

329-
type MsgGetHeaders struct{}
343+
type MsgGetHeaders struct {
344+
Locator [][32]byte
345+
StopHash [32]byte
346+
}
347+
348+
func (m *MsgGetHeaders) Encode() []byte {
349+
buf := new(bytes.Buffer)
350+
locatorCount := writeUvarint(uint64(len(m.Locator)))
351+
_, _ = buf.Write(locatorCount)
352+
for _, loc := range m.Locator {
353+
_, _ = buf.Write(loc[:])
354+
}
355+
_, _ = buf.Write(m.StopHash[:])
356+
return buf.Bytes()
357+
}
330358

331-
type MsgHeaders struct{}
359+
func (m *MsgGetHeaders) Decode(data []byte) error {
360+
count, bytesRead, err := readUvarint(data)
361+
if err != nil {
362+
return err
363+
}
364+
data = data[bytesRead:]
365+
if len(data) != int(((count * 32) + 32)) { // nolint:gosec
366+
return errors.New("invalid payload length")
367+
}
368+
m.Locator = make([][32]byte, count)
369+
for i := range count {
370+
loc := data[0:32]
371+
m.Locator[i] = [32]byte(loc)
372+
data = data[32:]
373+
}
374+
m.StopHash = [32]byte(data[0:32])
375+
return nil
376+
}
377+
378+
type MsgHeaders struct {
379+
Headers []*handshake.BlockHeader
380+
}
381+
382+
func (m *MsgHeaders) Encode() []byte {
383+
buf := new(bytes.Buffer)
384+
headerCount := writeUvarint(uint64(len(m.Headers)))
385+
_, _ = buf.Write(headerCount)
386+
for _, header := range m.Headers {
387+
_, _ = buf.Write(header.Encode())
388+
}
389+
return buf.Bytes()
390+
}
391+
392+
func (m *MsgHeaders) Decode(data []byte) error {
393+
count, bytesRead, err := readUvarint(data)
394+
if err != nil {
395+
return err
396+
}
397+
data = data[bytesRead:]
398+
if len(data) != int(count*handshake.BlockHeaderSize) { // nolint:gosec
399+
return errors.New("invalid payload length")
400+
}
401+
m.Headers = make([]*handshake.BlockHeader, count)
402+
for i := range count {
403+
tmpReader := bytes.NewReader(data[0:handshake.BlockHeaderSize])
404+
tmpHeader, err := handshake.NewBlockHeaderFromReader(tmpReader)
405+
if err != nil {
406+
return err
407+
}
408+
m.Headers[i] = tmpHeader
409+
data = data[handshake.BlockHeaderSize:]
410+
}
411+
return nil
412+
}
332413

333414
type MsgSendHeaders struct{}
334415

internal/handshake/protocol/messages_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"net"
1212
"reflect"
1313
"testing"
14+
15+
"github.com/blinklabs-io/cdnsd/internal/handshake"
1416
)
1517

1618
func TestMsgVersionEncodeDecode(t *testing.T) {
@@ -116,3 +118,102 @@ func TestMsgAddrEncodeDecode(t *testing.T) {
116118
}
117119
}
118120
}
121+
122+
func TestMsgGetHeadersEncodeDecode(t *testing.T) {
123+
testDefs := []struct {
124+
message Message
125+
binaryHex string
126+
}{
127+
// Generated by our own code
128+
{
129+
binaryHex: "015b6ef2d3c1f3cdcadfd9a030ba1811efdd17740f14e166489760741d075992e00000000000000000000000000000000000000000000000000000000000000000",
130+
message: &MsgGetHeaders{
131+
Locator: [][32]byte{
132+
func() [32]byte {
133+
foo, _ := hex.DecodeString("5b6ef2d3c1f3cdcadfd9a030ba1811efdd17740f14e166489760741d075992e0")
134+
return [32]byte(foo)
135+
}(),
136+
},
137+
StopHash: [32]byte{},
138+
},
139+
},
140+
}
141+
for _, testDef := range testDefs {
142+
binaryData, err := hex.DecodeString(testDef.binaryHex)
143+
if err != nil {
144+
t.Fatalf("unexpected error decoding hex: %s", err)
145+
}
146+
testMsg := new(MsgGetHeaders)
147+
if err := testMsg.Decode(binaryData); err != nil {
148+
t.Fatalf("unexpected error decoding message: %s", err)
149+
}
150+
if !reflect.DeepEqual(testMsg, testDef.message) {
151+
t.Fatalf("did not get expected message after decode:\n got: %#v\n wanted: %#v", testMsg, testDef.message)
152+
}
153+
testEncoded := testMsg.Encode()
154+
testEncodedHex := hex.EncodeToString(testEncoded)
155+
if testEncodedHex != testDef.binaryHex {
156+
t.Fatalf("did not get expected binary hex after encode:\n got: %s\n wanted: %s", testEncodedHex, testDef.binaryHex)
157+
}
158+
}
159+
}
160+
161+
func TestMsgHeadersEncodeDecode(t *testing.T) {
162+
testDefs := []struct {
163+
message Message
164+
binaryHex string
165+
}{
166+
// Modified (truncated) from data captured from hsd
167+
{
168+
binaryHex: "0232bb5134a541385e000000005b6ef2d3c1f3cdcadfd9a030ba1811efdd17740f14e166489760741d075992e00000000000000000000000000000000000000000000000000000000000000000000000150000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059919422c20530ece2b328adf63ec3f35a10e79375731687a81dfa7cd83a24e728b17095216d5e211ba1f61031416a51efca54eacb8c9059440c4671b0625bbe00000000ffff001c0000000000000000000000000000000000000000000000000000000000000000032a8839c741385e000000000000000000a5e40e8ba291bd7e8649747fa7fb8a7af39f5bacdb7433cd2f59710000000000000000000000000000000000000000000000000000000000000000000000060000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038180556e54cd70fd131130e154be4569945a6526d45add9ade6dbdfa34f54e25d1d56a37915cd1337b007806116ff623d973bec006280154472a6677eacbe0700000000ffff001c0000000000000000000000000000000000000000000000000000000000000000",
169+
message: &MsgHeaders{
170+
Headers: []*handshake.BlockHeader{
171+
&handshake.BlockHeader{
172+
Nonce: 0x3451bb32,
173+
Time: 0x5e3841a5,
174+
PrevBlock: [32]uint8{0x5b, 0x6e, 0xf2, 0xd3, 0xc1, 0xf3, 0xcd, 0xca, 0xdf, 0xd9, 0xa0, 0x30, 0xba, 0x18, 0x11, 0xef, 0xdd, 0x17, 0x74, 0xf, 0x14, 0xe1, 0x66, 0x48, 0x97, 0x60, 0x74, 0x1d, 0x7, 0x59, 0x92, 0xe0},
175+
NameRoot: [32]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
176+
ExtraNonce: [24]uint8{0x0, 0x0, 0x0, 0x15, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
177+
ReservedRoot: [32]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
178+
WitnessRoot: [32]uint8{0x59, 0x91, 0x94, 0x22, 0xc2, 0x5, 0x30, 0xec, 0xe2, 0xb3, 0x28, 0xad, 0xf6, 0x3e, 0xc3, 0xf3, 0x5a, 0x10, 0xe7, 0x93, 0x75, 0x73, 0x16, 0x87, 0xa8, 0x1d, 0xfa, 0x7c, 0xd8, 0x3a, 0x24, 0xe7},
179+
MerkleRoot: [32]uint8{0x28, 0xb1, 0x70, 0x95, 0x21, 0x6d, 0x5e, 0x21, 0x1b, 0xa1, 0xf6, 0x10, 0x31, 0x41, 0x6a, 0x51, 0xef, 0xca, 0x54, 0xea, 0xcb, 0x8c, 0x90, 0x59, 0x44, 0xc, 0x46, 0x71, 0xb0, 0x62, 0x5b, 0xbe},
180+
Version: 0x0,
181+
Bits: 0x1c00ffff,
182+
Mask: [32]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
183+
},
184+
&handshake.BlockHeader{
185+
Nonce: 0x39882a03,
186+
Time: 0x5e3841c7,
187+
PrevBlock: [32]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0xa5, 0xe4, 0xe, 0x8b, 0xa2, 0x91, 0xbd, 0x7e, 0x86, 0x49, 0x74, 0x7f, 0xa7, 0xfb, 0x8a, 0x7a, 0xf3, 0x9f, 0x5b, 0xac, 0xdb, 0x74, 0x33, 0xcd, 0x2f, 0x59, 0x71},
188+
NameRoot: [32]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
189+
ExtraNonce: [24]uint8{0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
190+
ReservedRoot: [32]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
191+
WitnessRoot: [32]uint8{0x38, 0x18, 0x5, 0x56, 0xe5, 0x4c, 0xd7, 0xf, 0xd1, 0x31, 0x13, 0xe, 0x15, 0x4b, 0xe4, 0x56, 0x99, 0x45, 0xa6, 0x52, 0x6d, 0x45, 0xad, 0xd9, 0xad, 0xe6, 0xdb, 0xdf, 0xa3, 0x4f, 0x54, 0xe2},
192+
MerkleRoot: [32]uint8{0x5d, 0x1d, 0x56, 0xa3, 0x79, 0x15, 0xcd, 0x13, 0x37, 0xb0, 0x7, 0x80, 0x61, 0x16, 0xff, 0x62, 0x3d, 0x97, 0x3b, 0xec, 0x0, 0x62, 0x80, 0x15, 0x44, 0x72, 0xa6, 0x67, 0x7e, 0xac, 0xbe, 0x7},
193+
Version: 0x0,
194+
Bits: 0x1c00ffff,
195+
Mask: [32]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
196+
},
197+
},
198+
},
199+
},
200+
}
201+
for _, testDef := range testDefs {
202+
binaryData, err := hex.DecodeString(testDef.binaryHex)
203+
if err != nil {
204+
t.Fatalf("unexpected error decoding hex: %s", err)
205+
}
206+
testMsg := new(MsgHeaders)
207+
if err := testMsg.Decode(binaryData); err != nil {
208+
t.Fatalf("unexpected error decoding message: %s", err)
209+
}
210+
if !reflect.DeepEqual(testMsg, testDef.message) {
211+
t.Fatalf("did not get expected message after decode:\n got: %#v\n wanted: %#v", testMsg, testDef.message)
212+
}
213+
testEncoded := testMsg.Encode()
214+
testEncodedHex := hex.EncodeToString(testEncoded)
215+
if testEncodedHex != testDef.binaryHex {
216+
t.Fatalf("did not get expected binary hex after encode:\n got: %s\n wanted: %s", testEncodedHex, testDef.binaryHex)
217+
}
218+
}
219+
}

internal/handshake/protocol/peer.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"io"
1414
"net"
1515
"time"
16+
17+
"github.com/blinklabs-io/cdnsd/internal/handshake"
1618
)
1719

1820
const (
@@ -116,6 +118,12 @@ func (p *Peer) receiveMessage() (Message, error) {
116118
}
117119
msg, err := decodeMessage(header, payload)
118120
if err != nil {
121+
// Discard unsupported messages and try to get another message
122+
// This is a bit of a hack
123+
var unsupportedErr UnsupportedMessageTypeError
124+
if errors.As(err, &unsupportedErr) {
125+
return p.receiveMessage()
126+
}
119127
return nil, err
120128
}
121129
return msg, nil
@@ -186,3 +194,24 @@ func (p *Peer) GetPeers() ([]NetAddress, error) {
186194
}
187195
return msgAddr.Peers, nil
188196
}
197+
198+
// GetHeaders requests a list of headers from the network peer
199+
func (p *Peer) GetHeaders(locator [][32]byte, stopHash [32]byte) ([]*handshake.BlockHeader, error) {
200+
getHeadersMsg := &MsgGetHeaders{
201+
Locator: locator,
202+
StopHash: stopHash,
203+
}
204+
if err := p.sendMessage(MessageGetHeaders, getHeadersMsg); err != nil {
205+
return nil, err
206+
}
207+
// Wait for Headers response
208+
msg, err := p.receiveMessage()
209+
if err != nil {
210+
return nil, err
211+
}
212+
msgHeaders, ok := msg.(*MsgHeaders)
213+
if !ok {
214+
return nil, fmt.Errorf("unexpected message: %T", msg)
215+
}
216+
return msgHeaders.Headers, nil
217+
}

0 commit comments

Comments
 (0)