-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathchannels.go
More file actions
188 lines (149 loc) · 4.7 KB
/
channels.go
File metadata and controls
188 lines (149 loc) · 4.7 KB
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package channels
import (
"bytes"
"fmt"
"sync"
envelope "github.com/tokenized/envelope/pkg/golang/envelope/base"
envelopeV1 "github.com/tokenized/envelope/pkg/golang/envelope/v1"
"github.com/tokenized/pkg/bitcoin"
"github.com/pkg/errors"
)
var (
ErrNotChannels = errors.New("Not Channels")
ErrInvalidMessage = errors.New("Invalid Message")
ErrUnsupportedVersion = errors.New("Unsupported Version")
ErrUnsupportedProtocol = errors.New("Unsupported Protocol")
ErrNotSupported = errors.New("Not Supported")
ErrRemainingProtocols = errors.New("Remaining Protocols")
ErrParseDidntConsumeProtocol = errors.New("Parse Didn't Consume Protocol")
)
// Message is implemented by all channels protocol message types. It is used to identify the
// specific channels protocol for the message.
// ChannelsMessages must also implement either Writer or Wrapper though this can't be enforced at
// compile time.
type Message interface {
ProtocolID() envelope.ProtocolID
}
// Writer is implemented by Channels messages that can't wrap other message types. It returns the
// envelope protocol IDs and payload.
type Writer interface {
Message
Write() (envelope.Data, error)
}
// Wrapper is implemented by Channels messages that can wrap other message types. For instance a
// signature message can be wrapped around a another message. It returns the envelope protocol IDs
// and payload.
type Wrapper interface {
Message
Wrap(payload envelope.Data) (envelope.Data, error)
}
type PeerChannel struct {
BaseURL string `bsor:"1" json:"base_url"`
ID string `bsor:"2" json:"id"`
ReadToken string `bsor:"3" json:"read_token,omitempty"`
WriteToken string `bsor:"4" json:"write_token,omitempty"`
}
type PeerChannels []PeerChannel
type Protocol interface {
ProtocolID() envelope.ProtocolID
Parse(payload envelope.Data) (Message, envelope.Data, error)
ResponseCodeToString(code uint32) string // convert Response.Code to a string
}
type Protocols struct {
Protocols []Protocol
lock sync.RWMutex
}
func NewProtocols(protocols ...Protocol) *Protocols {
result := &Protocols{}
for _, protocol := range protocols {
result.Protocols = append(result.Protocols, protocol)
}
return result
}
func (ps *Protocols) AddProtocols(protocols ...Protocol) {
ps.lock.Lock()
defer ps.lock.Unlock()
for _, protocol := range protocols {
ps.Protocols = append(ps.Protocols, protocol)
}
}
func (ps *Protocols) GetProtocol(protocolID envelope.ProtocolID) Protocol {
ps.lock.RLock()
defer ps.lock.RUnlock()
for _, protocol := range ps.Protocols {
if bytes.Equal(protocol.ProtocolID(), protocolID) {
return protocol
}
}
return nil
}
func (ps *Protocols) ResponseCodeToString(protocolID envelope.ProtocolID, code uint32) string {
if code == 0 {
return protocolID.String() + ":parse"
}
ps.lock.RLock()
defer ps.lock.RUnlock()
for _, protocol := range ps.Protocols {
if bytes.Equal(protocol.ProtocolID(), protocolID) {
return protocolID.String() + ":" + protocol.ResponseCodeToString(code)
}
}
return protocolID.String() + fmt.Sprintf(":unknown(%d)", code)
}
func (ps *Protocols) Parse(script bitcoin.Script) (Message, []Wrapper, error) {
payload, err := envelopeV1.Parse(bytes.NewReader(script))
if err != nil {
return nil, nil, errors.Wrap(err, "envelope")
}
var wrappers []Wrapper
for {
protocol := ps.GetProtocol(payload.ProtocolIDs[0])
if protocol == nil {
return nil, wrappers, errors.Wrap(ErrUnsupportedProtocol,
payload.ProtocolIDs[0].String())
}
msg, newPayload, err := protocol.Parse(payload)
if err != nil {
return nil, nil, errors.Wrapf(err, "parse: %s", protocol.ProtocolID())
}
if len(newPayload.ProtocolIDs) == 0 {
return msg, wrappers, nil
}
if wrapper, ok := msg.(Wrapper); ok {
wrappers = append(wrappers, wrapper)
} else {
return nil, nil, errors.Wrapf(ErrRemainingProtocols, "%s", newPayload.ProtocolIDs)
}
if len(payload.ProtocolIDs) == len(newPayload.ProtocolIDs) {
return nil, nil, errors.Wrapf(ErrParseDidntConsumeProtocol, "%s", newPayload.ProtocolIDs)
}
payload = newPayload
}
}
func WrapEnvelope(msg Writer, wrappers ...Wrapper) (envelope.Data, error) {
var payload envelope.Data
var err error
if msg != nil {
payload, err = msg.Write()
if err != nil {
return payload, errors.Wrap(err, "write")
}
}
for _, wrapper := range wrappers {
if wrapper == nil {
continue
}
payload, err = wrapper.Wrap(payload)
if err != nil {
return payload, errors.Wrap(err, "reply to")
}
}
return payload, nil
}
func Wrap(msg Writer, wrappers ...Wrapper) (bitcoin.Script, error) {
data, err := WrapEnvelope(msg, wrappers...)
if err != nil {
return nil, errors.Wrap(err, "envelope")
}
return envelopeV1.Wrap(data).Script()
}