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
|
package seal
import (
"bytes"
"crypto/ed25519"
"crypto/sha512"
"encoding/base32"
"encoding/json"
"errors"
"fmt"
"log/slog"
"time"
)
type Type int
const (
TypHighest = iota
TypSecondHighest
)
type Duration time.Duration
func (d *Duration) UnmarshalJSON(data []byte) error {
data = bytes.Trim(data, `"`)
dur, e := time.ParseDuration(string(data))
if e != nil {
return e
}
*d = Duration(dur)
return nil
}
func (d Duration) MarshalJSON() ([]byte, error) {
return []byte((time.Duration(d)).String()), nil
}
// Auction describes the asset of an auction and other
// relevant meta-data
type Description struct {
Start time.Time `json:"start"`
End time.Time `json:"end"`
Timeout Duration `json:"timeout"` // Timeout per round by which all responses must have arrived
Bitlength uint8 `json:"bitlength"` // Length of the price encoding
Currency string `json:"currency"`
Type Type `json:"type"`
Seller ed25519.PublicKey `json:"seller"` // Public key of the Seller
}
// The SignedAuction contains an Auction and the signature,
// signed by the seller's public key off the SHA512 hash of
// the normalized JSON-object.
type SignedDescription struct {
Description
SellerSignature []byte
}
func (sd *SignedDescription) Verify() (bool, error) {
// TODO: need to normalize this encoding
buf := &bytes.Buffer{}
e := json.NewEncoder(buf).Encode(sd.Description)
if e != nil {
return false, e
}
r := ed25519.Verify(sd.Seller, buf.Bytes(), sd.SellerSignature)
return r, nil
}
func (d *Description) Hash() (hash string, e error) {
buf := &bytes.Buffer{}
e = json.NewEncoder(buf).Encode(d)
if e != nil {
return "", e
}
h := sha512.Sum512(buf.Bytes())
return base32.StdEncoding.EncodeToString(h[:]), nil
}
func (d *Description) validate() error {
if d == nil {
return fmt.Errorf("description is nil")
}
if d.Bitlength > MAXBITLENGTH {
return fmt.Errorf("invalid BitLength in description: %d", d.Bitlength)
}
return nil
}
type Message []byte
type Auction interface {
Start() error
Cancel() error
Message(msg Message) error
}
type Result struct {
Price uint32
Winner string
Error error
}
type auction struct {
description *Description
// non nil if run by an bidder, via Join()
bidder Bidder
// non nil if run by an observer, via Observe()
observer Observer
// The commitments we received from the bidders.
bidders map[string][]*Commitment
// sorted list of the bidders.
bidder_ids []string
// Stage 1 data per round
stage1 []*Stage
log *slog.Logger
}
// Start must be called by the bidder or observer,
// when they receive a start signal from the dashboard
func (a *auction) Start() error {
return fmt.Errorf("auction.Start not fully implemented")
}
func (a *auction) Cancel() error {
return errors.New("Cancel not implemented")
}
// Message is called by the bidder's or observer's code
// whenever a message came in for the auction via the dashboard
// or other means of communication.
func (a *auction) Message(msg Message) error {
return fmt.Errorf("Auction.Received not implemented")
}
const MAXBITLENGTH = 64
// Bidder is the interface that the Auction engine uses to interact
type Bidder interface {
Broadcast(Message) error
Result(Result)
Start() error
}
func Join(
bidder Bidder,
description *Description,
options ...Option) (Auction, error) {
if bidder == nil {
return nil, fmt.Errorf("missing bidder")
}
if e := description.validate(); e != nil {
return nil, e
}
a := &auction{
description: description,
bidder: bidder,
log: slog.Default(),
}
for _, opt := range options {
opt(a)
}
return a, nil
}
// Observer
type Observer interface {
Result(Result)
Start()
}
func Observe(
observer Observer,
description *Description,
options ...Option) (Auction, error) {
if observer == nil {
return nil, fmt.Errorf("missing observer")
}
if e := description.validate(); e != nil {
return nil, e
}
a := &auction{
description: description,
observer: observer,
log: slog.Default(),
}
for _, opt := range options {
opt(a)
}
return a, nil
}
|