-
Notifications
You must be signed in to change notification settings - Fork 35
/
deal.go
248 lines (206 loc) · 6.88 KB
/
deal.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
package market
import (
"bytes"
"encoding/json"
"fmt"
"io"
"unicode/utf8"
addr "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
acrypto "github.com/filecoin-project/go-state-types/crypto"
mh "github.com/multiformats/go-multihash"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
"github.com/ipfs/go-cid"
)
var PieceCIDPrefix = cid.Prefix{
Version: 1,
Codec: cid.FilCommitmentUnsealed,
MhType: mh.SHA2_256_TRUNC254_PADDED,
MhLength: 32,
}
type DealState struct {
SectorStartEpoch abi.ChainEpoch // -1 if not yet included in proven sector
LastUpdatedEpoch abi.ChainEpoch // -1 if deal state never updated
SlashEpoch abi.ChainEpoch // -1 if deal never slashed
}
// The DealLabel is a kinded union of string or byte slice.
// It serializes to a CBOR string or CBOR byte string depending on which form it takes.
// The zero value is serialized as an empty CBOR string (maj type 3).
type DealLabel struct {
bs []byte
notString bool
}
// Zero value of DealLabel is canonical EmptyDealLabel
var EmptyDealLabel = DealLabel{}
func NewLabelFromString(s string) (DealLabel, error) {
if len(s) > DealMaxLabelSize {
return EmptyDealLabel, xerrors.Errorf("provided string is too large to be a label (%d), max length (%d)", len(s), DealMaxLabelSize)
}
if !utf8.ValidString(s) {
return EmptyDealLabel, xerrors.Errorf("provided string is invalid utf8")
}
return DealLabel{
bs: []byte(s),
notString: false,
}, nil
}
func NewLabelFromBytes(b []byte) (DealLabel, error) {
if len(b) > DealMaxLabelSize {
return EmptyDealLabel, xerrors.Errorf("provided bytes are too large to be a label (%d), max length (%d)", len(b), DealMaxLabelSize)
}
return DealLabel{
bs: b,
notString: true,
}, nil
}
func (label DealLabel) IsString() bool {
return !label.notString
}
func (label DealLabel) IsBytes() bool {
return label.notString
}
func (label DealLabel) ToString() (string, error) {
if !label.IsString() {
return "", xerrors.Errorf("label is not string")
}
return string(label.bs), nil
}
func (label DealLabel) ToBytes() ([]byte, error) {
if !label.IsBytes() {
return nil, xerrors.Errorf("label is not bytes")
}
return label.bs, nil
}
func (label DealLabel) Length() int {
return len(label.bs)
}
func (l DealLabel) Equals(o DealLabel) bool {
return bytes.Equal(l.bs, o.bs) && l.notString == o.notString
}
func (label *DealLabel) MarshalCBOR(w io.Writer) error {
scratch := make([]byte, 9)
// nil *DealLabel counts as EmptyLabel
// on chain structures should never have a pointer to a DealLabel but the case is included for completeness
if label == nil {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, 0); err != nil {
return err
}
_, err := io.WriteString(w, string(""))
return err
}
if uint64(len(label.bs)) > cbg.ByteArrayMaxLen {
return xerrors.Errorf("label is too long to marshal (%d), max allowed (%d)", len(label.bs), cbg.ByteArrayMaxLen)
}
majorType := byte(cbg.MajByteString)
if label.IsString() {
majorType = cbg.MajTextString
}
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, majorType, uint64(len(label.bs))); err != nil {
return err
}
_, err := w.Write(label.bs)
return err
}
func (label *DealLabel) UnmarshalCBOR(br io.Reader) error {
if label == nil {
return xerrors.Errorf("cannot unmarshal into nil pointer")
}
// reset fields
label.bs = nil
scratch := make([]byte, 8)
maj, length, err := cbg.CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
if maj != cbg.MajTextString && maj != cbg.MajByteString {
return fmt.Errorf("unexpected major tag (%d) when unmarshaling DealLabel: only textString (%d) or byteString (%d) expected", maj, cbg.MajTextString, cbg.MajByteString)
}
if length > cbg.ByteArrayMaxLen {
return fmt.Errorf("label was too long (%d), max allowed (%d)", length, cbg.ByteArrayMaxLen)
}
buf := make([]byte, length)
_, err = io.ReadAtLeast(br, buf, int(length))
if err != nil {
return err
}
label.bs = buf
label.notString = maj != cbg.MajTextString
if !label.notString && !utf8.ValidString(string(buf)) {
return fmt.Errorf("label string not valid utf8")
}
return nil
}
func (label DealLabel) MarshalJSON() ([]byte, error) {
if !label.IsString() {
return json.Marshal("")
}
str, err := label.ToString()
if err != nil {
return nil, xerrors.Errorf("failed to convert to string: %w", err)
}
return json.Marshal(str)
}
func (label *DealLabel) UnmarshalJSON(b []byte) error {
var str string
if err := json.Unmarshal(b, &str); err != nil {
return xerrors.Errorf("failed to unmarshal string: %w", err)
}
newLabel, err := NewLabelFromString(str)
if err != nil {
return xerrors.Errorf("failed to create label from string: %w", err)
}
*label = newLabel
return nil
}
// Note: Deal Collateral is only released and returned to clients and miners
// when the storage deal stops counting towards power. In the current iteration,
// it will be released when the sector containing the storage deals expires,
// even though some storage deals can expire earlier than the sector does.
// Collaterals are denominated in PerEpoch to incur a cost for self dealing or
// minimal deals that last for a long time.
// Note: ClientCollateralPerEpoch may not be needed and removed pending future confirmation.
// There will be a Minimum value for both client and provider deal collateral.
type DealProposal struct {
PieceCID cid.Cid `checked:"true"` // Checked in validateDeal, CommP
PieceSize abi.PaddedPieceSize
VerifiedDeal bool
Client addr.Address
Provider addr.Address
// Label is an arbitrary client chosen label to apply to the deal
Label DealLabel
// Nominal start epoch. Deal payment is linear between StartEpoch and EndEpoch,
// with total amount StoragePricePerEpoch * (EndEpoch - StartEpoch).
// Storage deal must appear in a sealed (proven) sector no later than StartEpoch,
// otherwise it is invalid.
StartEpoch abi.ChainEpoch
EndEpoch abi.ChainEpoch
StoragePricePerEpoch abi.TokenAmount
ProviderCollateral abi.TokenAmount
ClientCollateral abi.TokenAmount
}
// ClientDealProposal is a DealProposal signed by a client
type ClientDealProposal struct {
Proposal DealProposal
ClientSignature acrypto.Signature
}
func (p *DealProposal) Duration() abi.ChainEpoch {
return p.EndEpoch - p.StartEpoch
}
func (p *DealProposal) TotalStorageFee() abi.TokenAmount {
return big.Mul(p.StoragePricePerEpoch, big.NewInt(int64(p.Duration())))
}
func (p *DealProposal) ClientBalanceRequirement() abi.TokenAmount {
return big.Add(p.ClientCollateral, p.TotalStorageFee())
}
func (p *DealProposal) ProviderBalanceRequirement() abi.TokenAmount {
return p.ProviderCollateral
}
func (p *DealProposal) Cid() (cid.Cid, error) {
buf := new(bytes.Buffer)
if err := p.MarshalCBOR(buf); err != nil {
return cid.Undef, err
}
return abi.CidBuilder.Sum(buf.Bytes())
}