-
Notifications
You must be signed in to change notification settings - Fork 1
/
rep_replication.go
117 lines (108 loc) · 3.38 KB
/
rep_replication.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
112
113
114
115
116
117
package pb
import (
"errors"
"fmt"
"strings"
"github.com/ipfs/go-cid"
)
const header = "rtrade-replication v0"
// NewReplication creates a new Replication file, with the correct header
func NewReplication() *Replication {
return &Replication{
Header: header,
}
}
// Valid checks if this is an Valid Replication file, with:
// - correct header (if strict, then no unknown extensions),
// - at least one CID,
// - if strict, then equal or more servers than required for replication, and
// - replication factor greater than 1.
// reports the first error found.
//
// If strict, then invalid CIDs, Servers, and unknown protocol extensions are also not allowed.
func (r *Replication) Valid(strict bool) error {
if _, err := r.GetHeaderString(strict); err != nil {
return err
}
if strict && haveUnrecognized(r) {
return errors.New("found unrecognized data")
}
if CIDs, err := r.GetCIDs(); (strict || len(CIDs) == 0) && err != nil {
return err
}
as := ServerSourcesToAddrInfos(r.Servers)
if len(as) < int(r.GetReplicationFactor()) {
return fmt.Errorf("not enough servers list %v of %v", len(as), r.GetReplicationFactor())
}
if strict {
for _, a := range as {
if _, err := a.GetID(); err != nil {
return err
}
if _, err := a.GetMultiAddrs(); err != nil {
return err
}
}
}
if r.GetReplicationFactor() < 2 {
return fmt.Errorf("replication factor of %v is too low", r.GetReplicationFactor())
}
return nil
}
// GetHeaderString returns the full header string.
// Returns an error if header is not valid.
//
// If strict, then unknown extensions are not allowed.
func (r *Replication) GetHeaderString(strict bool) (string, error) {
if r == nil {
return "", fmt.Errorf(`can not get header from nil replication`)
}
if strict {
if r.Header != header {
return "", fmt.Errorf(`header "%s" does not match "%s"`, r.Header, header)
}
} else if !strings.HasPrefix(r.Header, header) {
return "", fmt.Errorf(`header "%s" does not start with "%s"`, r.Header, header)
}
return r.Header, nil
}
// GetCIDs returns the list of CIDs of this replication.
// If any errors are encountered during decoding, the first error is returned,
// and the returned slice will only contain good CIDs.
// An Error is always returned if there are no CIDs in this replication.
func (r *Replication) GetCIDs() ([]cid.Cid, error) {
cs := make([]cid.Cid, 0, len(r.CidsBytes))
var firstError error
if len(r.CidsBytes) == 0 {
return nil, errors.New("no CIDs found")
}
for _, bs := range r.CidsBytes {
c, err := cid.Cast(bs)
if err != nil {
if firstError == nil {
firstError = err
}
continue
}
cs = append(cs, c)
}
return cs, firstError
}
// AddCIDs adds CIDs to this replication, requires v1 or above, then sort and remove duplicates
func (r *Replication) AddCIDs(cids ...cid.Cid) error {
startLen := len(r.CidsBytes)
for _, c := range cids {
if _, err := cid.Cast(c.Bytes()); err != nil { // this "recasting" is to make sure we have a valid cid
r.CidsBytes = r.CidsBytes[:startLen] // don't add any cids if we return an error
return err
}
if c.Version() < 1 {
// future: maybe upgrade deprecated versions instead
r.CidsBytes = r.CidsBytes[:startLen] // don't add any cids if we return an error
return fmt.Errorf("CID version %v is not allowed", c.Version())
}
r.CidsBytes = append(r.CidsBytes, c.Bytes())
}
r.CidsBytes = uniqueOrderedByteSlices(r.CidsBytes)
return nil
}