forked from rubblelabs/ripple
/
path.go
142 lines (124 loc) · 2.88 KB
/
path.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package data
import (
"encoding/json"
"fmt"
"hash/crc32"
"strings"
)
type pathEntry uint8
const (
PATH_BOUNDARY pathEntry = 0xFF
PATH_END pathEntry = 0x00
PATH_ACCOUNT pathEntry = 0x01
PATH_REDEEM pathEntry = 0x02
PATH_CURRENCY pathEntry = 0x10
PATH_ISSUER pathEntry = 0x20
)
// PathElem represents one link in a path.
type PathElem struct {
Account *Account
Currency *Currency
Issuer *Account
}
func newPathElem(s string) (PathElem, error) {
var err error
pe := PathElem{}
parts := strings.Split(s, "/")
switch {
case len(parts) == 1:
pe.Account, err = NewAccountFromAddress(parts[0])
if err != nil {
return pe, err
}
case len(parts) == 2:
pe.Currency = &Currency{}
*pe.Currency, err = NewCurrency(parts[0])
if err != nil {
return pe, err
}
pe.Issuer, err = NewAccountFromAddress(parts[1])
if err != nil {
return pe, err
}
default:
return pe, fmt.Errorf("Bad PathElem: %s", s)
}
return pe, nil
}
// Path represents a single path of liquidity that a transaction may use.
type Path []PathElem
// NewPath accepts a path consisting of hops delimited by "=>" where each hop
// is either "<currency>/<issuer>" or "<account>". Whitespace around the delimiter
// is acceptable and is ignored.
func NewPath(s string) (Path, error) {
p := Path{}
for _, part := range strings.Split(s, "=>") {
pe, err := newPathElem(strings.TrimSpace(part))
if err != nil {
return nil, err
}
p = append(p, pe)
}
return p, nil
}
// PathSet represents a collection of possible paths that a transaction may use.
type PathSet []Path
func (p PathElem) pathEntry() pathEntry {
var entry pathEntry
if p.Account != nil {
entry |= PATH_ACCOUNT
}
if p.Currency != nil {
entry |= PATH_CURRENCY
}
if p.Issuer != nil {
entry |= PATH_ISSUER
}
return entry
}
func (p Path) Signature() (uint32, error) {
checksum := crc32.NewIEEE()
for _, path := range p {
b := append(path.Account.Bytes(), append(path.Currency.Bytes(), path.Issuer.Bytes()...)...)
if _, err := checksum.Write(b); err != nil {
return 0, err
}
}
return checksum.Sum32(), nil
}
func (p Path) String() string {
var s []string
for _, path := range p {
s = append(s, path.String())
}
return strings.Join(s, " => ")
}
func (p PathElem) String() string {
var s []string
if p.Account != nil {
s = append(s, p.Account.String())
}
if p.Currency != nil {
s = append(s, p.Currency.String())
}
if p.Issuer != nil {
s = append(s, p.Issuer.String())
}
return strings.Join(s, "/")
}
func (p PathElem) MarshalJSON() ([]byte, error) {
typ := p.pathEntry()
return json.Marshal(struct {
Account *Account `json:"account,omitempty"`
Currency *Currency `json:"currency,omitempty"`
Issuer *Account `json:"issuer,omitempty"`
Type pathEntry `json:"type"`
TypeHex string `json:"type_hex"`
}{
p.Account,
p.Currency,
p.Issuer,
typ,
fmt.Sprintf("%016X", uint64(typ)),
})
}