-
Notifications
You must be signed in to change notification settings - Fork 1
/
skeleton.go
111 lines (91 loc) · 2.33 KB
/
skeleton.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package protocol
import (
"context"
"errors"
"time"
"github.com/sunvim/dogesyncer/protocol/proto"
"github.com/sunvim/dogesyncer/types"
)
const (
defaultBodyFetchTimeout = time.Second * 10
)
var (
errNilHeaderResponse = errors.New("header response is nil")
errInvalidHeaderSequence = errors.New("invalid header sequence")
errHeaderBodyMismatch = errors.New("requested body and header mismatch")
)
func getHeaders(clt proto.V1Client, req *proto.GetHeadersRequest) ([]*types.Header, error) {
resp, err := clt.GetHeaders(context.Background(), req)
if err != nil {
return nil, err
}
headers := make([]*types.Header, len(resp.Objs))
for index, obj := range resp.Objs {
if obj == nil || obj.Spec == nil {
// this nil header comes from a faulty node, reject all blocks of it.
return nil, errNilHeaderResponse
}
header := &types.Header{}
if err := header.UnmarshalRLP(obj.Spec.Value); err != nil {
return nil, err
}
headers[index] = header
}
return headers, nil
}
type skeleton struct {
blocks []*types.Block
skip int64
amount int64
}
// getBlocksFromPeer fetches the blocks from the peer,
// from the specified block number (including)
func (s *skeleton) getBlocksFromPeer(
peerClient proto.V1Client,
initialBlockNum uint64,
) error {
// Fetch the headers from the peer
headers, err := getHeaders(
peerClient,
&proto.GetHeadersRequest{
Number: int64(initialBlockNum),
Skip: s.skip,
Amount: s.amount,
},
)
if err != nil {
return err
}
// Make sure the number sequences match up
for i := 1; i < len(headers); i++ {
if headers[i].Number-headers[i-1].Number != 1 {
return errInvalidHeaderSequence
}
}
// Construct the body request
headerHashes := make([]types.Hash, len(headers))
for index, header := range headers {
headerHashes[index] = header.Hash
}
getBodiesContext, cancelFn := context.WithTimeout(
context.Background(),
defaultBodyFetchTimeout,
)
defer cancelFn()
// Grab the block bodies
bodies, err := getBodies(getBodiesContext, peerClient, headerHashes)
if err != nil {
return err
}
if len(bodies) != len(headers) {
return errHeaderBodyMismatch
}
s.blocks = make([]*types.Block, len(headers))
for index, body := range bodies {
s.blocks[index] = &types.Block{
Header: headers[index],
Transactions: body.Transactions,
}
}
return nil
}