-
Notifications
You must be signed in to change notification settings - Fork 104
/
Copy pathcontracts.go
292 lines (258 loc) · 9.46 KB
/
contracts.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
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
package calypso
import (
"fmt"
"strings"
"go.dedis.ch/cothority/v3"
"go.dedis.ch/cothority/v3/byzcoin"
"go.dedis.ch/cothority/v3/darc"
"go.dedis.ch/onet/v3"
"go.dedis.ch/onet/v3/log"
"go.dedis.ch/onet/v3/network"
"go.dedis.ch/protobuf"
"golang.org/x/xerrors"
)
// ContractWriteID references a write contract system-wide.
const ContractWriteID = "calypsoWrite"
// ContractWrite represents one calypso write instance.
type ContractWrite struct {
byzcoin.BasicContract
Write
}
// String returns a human readable string representation of the Write data
func (w Write) String() string {
out := new(strings.Builder)
out.WriteString("- Write:\n")
fmt.Fprintf(out, "-- Data: %s\n", w.Data)
fmt.Fprintf(out, "-- U: %s\n", w.U)
fmt.Fprintf(out, "-- Ubar: %s\n", w.Ubar)
fmt.Fprintf(out, "-- E: %s\n", w.E)
fmt.Fprintf(out, "-- F: %s\n", w.F)
fmt.Fprintf(out, "-- C: %s\n", w.C)
fmt.Fprintf(out, "-- ExtraData: %s\n", w.ExtraData)
fmt.Fprintf(out, "-- LTSID: %s\n", w.LTSID)
fmt.Fprintf(out, "-- Cost: %x\n", w.Cost)
return out.String()
}
func contractWriteFromBytes(in []byte) (byzcoin.Contract, error) {
c := &ContractWrite{}
err := protobuf.DecodeWithConstructors(in, &c.Write, network.DefaultConstructors(cothority.Suite))
return c, cothority.ErrorOrNil(err, "couldn't unmarshal write")
}
// Spawn is used to create a new write- or read-contract. The read-contract is
// created by the write-instance, because the creation of a new read-instance is
// protected by the write-contract's darc.
func (c ContractWrite) Spawn(rst byzcoin.ReadOnlyStateTrie, inst byzcoin.Instruction, coins []byzcoin.Coin) (sc []byzcoin.StateChange, cout []byzcoin.Coin, err error) {
cout = coins
var darcID darc.ID
_, _, _, darcID, err = rst.GetValues(inst.InstanceID.Slice())
if err != nil {
err = xerrors.Errorf("getting values: %v", err)
return
}
switch inst.Spawn.ContractID {
case ContractWriteID:
w := inst.Spawn.Args.Search("write")
if w == nil || len(w) == 0 {
err = xerrors.New("need a write request in 'write' argument")
return
}
err = protobuf.DecodeWithConstructors(w, &c.Write, network.DefaultConstructors(cothority.Suite))
if err != nil {
err = xerrors.New("couldn't unmarshal write: " + err.Error())
return
}
if d := inst.Spawn.Args.Search("darcID"); d != nil {
darcID = d
}
if err = c.Write.CheckProof(cothority.Suite, darcID); err != nil {
err = xerrors.Errorf("proof of write failed: %v", err)
return
}
var cid string
_, _, cid, _, err = rst.GetValues(c.Write.LTSID[:])
if err != nil {
err = xerrors.Errorf("couldn't find the corresponding LTSID: %v",
err)
return
}
if cid != ContractLongTermSecretID {
err = xerrors.Errorf("given LTSID points to wrong contract: %s",
cid)
return
}
instID, err := inst.DeriveIDArg("", "preID")
if err != nil {
return nil, nil, xerrors.Errorf(
"couldn't get ID for instance: %v", err)
}
log.Lvlf3("Successfully verified write request and will store in %x", instID)
sc = append(sc, byzcoin.NewStateChange(byzcoin.Create, instID, ContractWriteID, w, darcID))
case ContractReadID:
var rd Read
r := inst.Spawn.Args.Search("read")
if r == nil || len(r) == 0 {
return nil, nil, xerrors.New("need a read argument")
}
err = protobuf.DecodeWithConstructors(r, &rd, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, nil, xerrors.Errorf("passed read argument is invalid: %v", err)
}
if !rd.Write.Equal(inst.InstanceID) {
return nil, nil, xerrors.New("the read request doesn't reference this write-instance")
}
if c.Cost.Value > 0 {
for i, coin := range cout {
if coin.Name.Equal(c.Cost.Name) {
err := coin.SafeSub(c.Cost.Value)
if err != nil {
return nil, nil, xerrors.Errorf("couldn't pay for read request: %v", err)
}
cout[i] = coin
break
}
}
}
instID, err := inst.DeriveIDArg("", "preID")
if err != nil {
return nil, nil, xerrors.Errorf(
"couldn't get ID for instance: %v", err)
}
sc = byzcoin.StateChanges{byzcoin.NewStateChange(byzcoin.Create,
instID, ContractReadID, r, darcID)}
default:
err = xerrors.New("can only spawn writes and reads")
}
return
}
// Invoke supports the following command:
// - update - it takes a 'data' and/or 'extraData' argument that is used to
// update the data and/or extradata part of the write structure.
func (c *ContractWrite) Invoke(rst byzcoin.ReadOnlyStateTrie,
inst byzcoin.Instruction, cin []byzcoin.Coin) ([]byzcoin.StateChange,
[]byzcoin.Coin, error) {
_, _, _, darcID, err := rst.GetValues(inst.InstanceID.Slice())
if err != nil {
return nil, nil, err
}
update := false
switch inst.Invoke.Command {
case "update":
data := inst.Invoke.Args.Search("data")
if data != nil {
c.Data = data
update = true
}
extraData := inst.Invoke.Args.Search("extraData")
if extraData != nil {
c.ExtraData = extraData
update = true
}
default:
return nil, nil, xerrors.New("only know 'update' command")
}
if !update {
return nil, nil, xerrors.New("neither data nor extraData update")
}
var ciBuf []byte
ciBuf, err = protobuf.Encode(&c.Write)
if err != nil {
return nil, nil, err
}
return []byzcoin.StateChange{byzcoin.NewStateChange(byzcoin.Update,
inst.InstanceID, ContractWriteID, ciBuf, darcID)}, cin, nil
}
// ContractReadID references a read contract system-wide.
const ContractReadID = "calypsoRead"
// ContractRead represents one read contract.
type ContractRead struct {
byzcoin.BasicContract
Read
}
func contractReadFromBytes(in []byte) (byzcoin.Contract, error) {
return nil, xerrors.New("calypso read instances are never instantiated")
}
// ContractLongTermSecretID is the contract ID for updating the LTS roster.
var ContractLongTermSecretID = "longTermSecret"
type contractLTS struct {
byzcoin.BasicContract
LtsInstanceInfo LtsInstanceInfo
}
func contractLTSFromBytes(in []byte) (byzcoin.Contract, error) {
c := &contractLTS{}
err := protobuf.DecodeWithConstructors(in, &c.LtsInstanceInfo, network.DefaultConstructors(cothority.Suite))
return c, cothority.ErrorOrNil(err, "couldn't unmarshal LtsInfo")
}
func (c *contractLTS) Spawn(rst byzcoin.ReadOnlyStateTrie, inst byzcoin.Instruction, coins []byzcoin.Coin) ([]byzcoin.StateChange, []byzcoin.Coin, error) {
var darcID darc.ID
_, _, _, darcID, err := rst.GetValues(inst.InstanceID.Slice())
if err != nil {
return nil, nil, xerrors.Errorf("getting values: %v", err)
}
if inst.Spawn.ContractID != ContractLongTermSecretID {
return nil, nil, xerrors.New("can only spawn long-term-secret instances")
}
infoBuf := inst.Spawn.Args.Search("lts_instance_info")
if infoBuf == nil || len(infoBuf) == 0 {
return nil, nil, xerrors.New("need a lts_instance_info argument")
}
var info LtsInstanceInfo
err = protobuf.DecodeWithConstructors(infoBuf, &info, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, nil, xerrors.Errorf("passed lts_instance_info argument is invalid: %v", err)
}
return byzcoin.StateChanges{byzcoin.NewStateChange(byzcoin.Create, inst.DeriveID(""), ContractLongTermSecretID, infoBuf, darcID)}, coins, nil
}
func (c *contractLTS) Invoke(rst byzcoin.ReadOnlyStateTrie, inst byzcoin.Instruction, coins []byzcoin.Coin) ([]byzcoin.StateChange, []byzcoin.Coin, error) {
var darcID darc.ID
curBuf, _, _, darcID, err := rst.GetValues(inst.InstanceID.Slice())
if err != nil {
return nil, nil, xerrors.Errorf("getting values: %v", err)
}
if inst.Invoke.Command != "reshare" {
return nil, nil, xerrors.New("can only reshare long-term secrets")
}
infoBuf := inst.Invoke.Args.Search("lts_instance_info")
if infoBuf == nil || len(infoBuf) == 0 {
return nil, nil, xerrors.New("need a lts_instance_info argument")
}
var curInfo, newInfo LtsInstanceInfo
err = protobuf.DecodeWithConstructors(infoBuf, &newInfo, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, nil, xerrors.Errorf("passed lts_instance_info argument is invalid: %v", err)
}
err = protobuf.DecodeWithConstructors(curBuf, &curInfo, network.DefaultConstructors(cothority.Suite))
if err != nil {
return nil, nil, xerrors.Errorf("current info is invalid: %v", err)
}
// Verify the intersection between new roster and the old one. There must be
// at least a threshold of nodes in the intersection.
n := len(curInfo.Roster.List)
overlap := intersectRosters(&curInfo.Roster, &newInfo.Roster)
thr := n - (n-1)/3
if overlap < thr {
return nil, nil, xerrors.New("new roster does not overlap enough with current roster")
}
return byzcoin.StateChanges{byzcoin.NewStateChange(byzcoin.Update, inst.InstanceID, ContractLongTermSecretID, infoBuf, darcID)}, coins, nil
}
func intersectRosters(r1, r2 *onet.Roster) int {
res := 0
for _, x := range r2.List {
if i, _ := r1.Search(x.ID); i != -1 {
res++
}
}
return res
}
// VerifyInstruction uses a specific verification based on attr in the case it
// is a read spawn. This will check if any makeAttInterpreter has been
// registered in the service and apply them.
func (c ContractWrite) VerifyInstruction(rst byzcoin.ReadOnlyStateTrie, inst byzcoin.Instruction, ctxHash []byte) error {
if inst.GetType() == byzcoin.SpawnType && inst.Spawn.ContractID == ContractReadID {
evalAttr := darc.AttrInterpreters{}
for _, makeAttrInterpreterWrapper := range readMakeAttrInterpreter {
evalAttr[makeAttrInterpreterWrapper.name] = makeAttrInterpreterWrapper.interpreter(c, rst, inst)
}
return inst.VerifyWithOption(rst, ctxHash, &byzcoin.VerificationOptions{EvalAttr: evalAttr})
}
return inst.VerifyWithOption(rst, ctxHash, nil)
}