Skip to content

Commit a36ddb9

Browse files
authored
[v2] disperser server payments api (#902)
1 parent f3a9c52 commit a36ddb9

File tree

18 files changed

+1097
-352
lines changed

18 files changed

+1097
-352
lines changed

api/clients/accountant.go

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ import (
1313
"github.com/Layr-Labs/eigenda/core/meterer"
1414
)
1515

16-
var minNumBins uint32 = 3
1716
var requiredQuorums = []uint8{0, 1}
1817

1918
type Accountant interface {
20-
AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, []byte, error)
19+
AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, error)
2120
}
2221

2322
var _ Accountant = &accountant{}
2423

2524
type accountant struct {
2625
// on-chain states
26+
accountID string
2727
reservation *core.ActiveReservation
2828
onDemand *core.OnDemandPayment
2929
reservationWindow uint32
@@ -36,16 +36,16 @@ type accountant struct {
3636
usageLock sync.Mutex
3737
cumulativePayment *big.Int
3838

39-
paymentSigner core.PaymentSigner
40-
numBins uint32
39+
// number of bins in the circular accounting, restricted by minNumBins which is 3
40+
numBins uint32
4141
}
4242

4343
type BinRecord struct {
4444
Index uint32
4545
Usage uint64
4646
}
4747

48-
func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, paymentSigner core.PaymentSigner, numBins uint32) *accountant {
48+
func NewAccountant(accountID string, reservation *core.ActiveReservation, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, numBins uint32) *accountant {
4949
//TODO: client storage; currently every instance starts fresh but on-chain or a small store makes more sense
5050
// Also client is currently responsible for supplying network params, we need to add RPC in order to be automatic
5151
// There's a subsequent PR that handles populating the accountant with on-chain state from the disperser
@@ -54,15 +54,15 @@ func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandP
5454
binRecords[i] = BinRecord{Index: uint32(i), Usage: 0}
5555
}
5656
a := accountant{
57+
accountID: accountID,
5758
reservation: reservation,
5859
onDemand: onDemand,
5960
reservationWindow: reservationWindow,
6061
pricePerSymbol: pricePerSymbol,
6162
minNumSymbols: minNumSymbols,
6263
binRecords: binRecords,
6364
cumulativePayment: big.NewInt(0),
64-
paymentSigner: paymentSigner,
65-
numBins: max(numBins, minNumBins),
65+
numBins: max(numBins, uint32(meterer.MinNumBins)),
6666
}
6767
// TODO: add a routine to refresh the on-chain state occasionally?
6868
return &a
@@ -116,26 +116,20 @@ func (a *accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint64, quo
116116
}
117117

118118
// AccountBlob accountant provides and records payment information
119-
func (a *accountant) AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, []byte, error) {
119+
func (a *accountant) AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, error) {
120120
binIndex, cumulativePayment, err := a.BlobPaymentInfo(ctx, numSymbols, quorums)
121121
if err != nil {
122-
return nil, nil, err
122+
return nil, err
123123
}
124124

125-
accountID := a.paymentSigner.GetAccountID()
126125
pm := &core.PaymentMetadata{
127-
AccountID: accountID,
126+
AccountID: a.accountID,
128127
BinIndex: binIndex,
129128
CumulativePayment: cumulativePayment,
130129
}
131130
protoPaymentHeader := pm.ConvertToProtoPaymentHeader()
132131

133-
signature, err := a.paymentSigner.SignBlobPayment(pm)
134-
if err != nil {
135-
return nil, nil, err
136-
}
137-
138-
return protoPaymentHeader, signature, nil
132+
return protoPaymentHeader, nil
139133
}
140134

141135
// TODO: PaymentCharged and SymbolsCharged copied from meterer, should be refactored

api/clients/accountant_test.go

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"time"
1010

1111
"github.com/Layr-Labs/eigenda/core"
12-
"github.com/Layr-Labs/eigenda/core/auth"
1312
"github.com/Layr-Labs/eigenda/core/meterer"
1413
"github.com/ethereum/go-ethereum/crypto"
1514
"github.com/stretchr/testify/assert"
@@ -34,9 +33,8 @@ func TestNewAccountant(t *testing.T) {
3433

3534
privateKey1, err := crypto.GenerateKey()
3635
assert.NoError(t, err)
37-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
38-
assert.NoError(t, err)
39-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
36+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
37+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
4038

4139
assert.NotNil(t, accountant)
4240
assert.Equal(t, reservation, accountant.reservation)
@@ -65,15 +63,14 @@ func TestAccountBlob_Reservation(t *testing.T) {
6563

6664
privateKey1, err := crypto.GenerateKey()
6765
assert.NoError(t, err)
68-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
69-
assert.NoError(t, err)
70-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
66+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
67+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
7168

7269
ctx := context.Background()
7370
symbolLength := uint64(500)
7471
quorums := []uint8{0, 1}
7572

76-
header, _, err := accountant.AccountBlob(ctx, symbolLength, quorums)
73+
header, err := accountant.AccountBlob(ctx, symbolLength, quorums)
7774
metadata := core.ConvertPaymentHeader(header)
7875

7976
assert.NoError(t, err)
@@ -83,7 +80,7 @@ func TestAccountBlob_Reservation(t *testing.T) {
8380

8481
symbolLength = uint64(700)
8582

86-
header, _, err = accountant.AccountBlob(ctx, symbolLength, quorums)
83+
header, err = accountant.AccountBlob(ctx, symbolLength, quorums)
8784
metadata = core.ConvertPaymentHeader(header)
8885

8986
assert.NoError(t, err)
@@ -92,7 +89,7 @@ func TestAccountBlob_Reservation(t *testing.T) {
9289
assert.Equal(t, isRotation([]uint64{1200, 0, 200}, mapRecordUsage(accountant.binRecords)), true)
9390

9491
// Second call should use on-demand payment
95-
header, _, err = accountant.AccountBlob(ctx, 300, quorums)
92+
header, err = accountant.AccountBlob(ctx, 300, quorums)
9693
metadata = core.ConvertPaymentHeader(header)
9794

9895
assert.NoError(t, err)
@@ -117,15 +114,14 @@ func TestAccountBlob_OnDemand(t *testing.T) {
117114

118115
privateKey1, err := crypto.GenerateKey()
119116
assert.NoError(t, err)
120-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
121-
assert.NoError(t, err)
122-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
117+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
118+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
123119

124120
ctx := context.Background()
125121
numSymbols := uint64(1500)
126122
quorums := []uint8{0, 1}
127123

128-
header, _, err := accountant.AccountBlob(ctx, numSymbols, quorums)
124+
header, err := accountant.AccountBlob(ctx, numSymbols, quorums)
129125
assert.NoError(t, err)
130126

131127
metadata := core.ConvertPaymentHeader(header)
@@ -147,15 +143,14 @@ func TestAccountBlob_InsufficientOnDemand(t *testing.T) {
147143

148144
privateKey1, err := crypto.GenerateKey()
149145
assert.NoError(t, err)
150-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
151-
assert.NoError(t, err)
152-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
146+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
147+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
153148

154149
ctx := context.Background()
155150
numSymbols := uint64(2000)
156151
quorums := []uint8{0, 1}
157152

158-
_, _, err = accountant.AccountBlob(ctx, numSymbols, quorums)
153+
_, err = accountant.AccountBlob(ctx, numSymbols, quorums)
159154
assert.Contains(t, err.Error(), "neither reservation nor on-demand payment is available")
160155
}
161156

@@ -176,37 +171,36 @@ func TestAccountBlobCallSeries(t *testing.T) {
176171

177172
privateKey1, err := crypto.GenerateKey()
178173
assert.NoError(t, err)
179-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
180-
assert.NoError(t, err)
181-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
174+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
175+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
182176

183177
ctx := context.Background()
184178
quorums := []uint8{0, 1}
185179
now := time.Now().Unix()
186180

187181
// First call: Use reservation
188-
header, _, err := accountant.AccountBlob(ctx, 800, quorums)
182+
header, err := accountant.AccountBlob(ctx, 800, quorums)
189183
metadata := core.ConvertPaymentHeader(header)
190184
assert.NoError(t, err)
191185
assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex)
192186
assert.Equal(t, big.NewInt(0), metadata.CumulativePayment)
193187

194188
// Second call: Use remaining reservation + overflow
195-
header, _, err = accountant.AccountBlob(ctx, 300, quorums)
189+
header, err = accountant.AccountBlob(ctx, 300, quorums)
196190
metadata = core.ConvertPaymentHeader(header)
197191
assert.NoError(t, err)
198192
assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex)
199193
assert.Equal(t, big.NewInt(0), metadata.CumulativePayment)
200194

201195
// Third call: Use on-demand
202-
header, _, err = accountant.AccountBlob(ctx, 500, quorums)
196+
header, err = accountant.AccountBlob(ctx, 500, quorums)
203197
metadata = core.ConvertPaymentHeader(header)
204198
assert.NoError(t, err)
205199
assert.Equal(t, uint32(0), header.BinIndex)
206200
assert.Equal(t, big.NewInt(500), metadata.CumulativePayment)
207201

208202
// Fourth call: Insufficient on-demand
209-
_, _, err = accountant.AccountBlob(ctx, 600, quorums)
203+
_, err = accountant.AccountBlob(ctx, 600, quorums)
210204
assert.Error(t, err)
211205
assert.Contains(t, err.Error(), "neither reservation nor on-demand payment is available")
212206
}
@@ -225,30 +219,30 @@ func TestAccountBlob_BinRotation(t *testing.T) {
225219
reservationWindow := uint32(1) // Set to 1 second for testing
226220
pricePerSymbol := uint32(1)
227221
minNumSymbols := uint32(100)
222+
228223
privateKey1, err := crypto.GenerateKey()
229224
assert.NoError(t, err)
230-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
231-
assert.NoError(t, err)
232-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
225+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
226+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
233227

234228
ctx := context.Background()
235229
quorums := []uint8{0, 1}
236230

237231
// First call
238-
_, _, err = accountant.AccountBlob(ctx, 800, quorums)
232+
_, err = accountant.AccountBlob(ctx, 800, quorums)
239233
assert.NoError(t, err)
240234
assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.binRecords)), true)
241235

242236
// next reservation duration
243237
time.Sleep(1000 * time.Millisecond)
244238

245239
// Second call
246-
_, _, err = accountant.AccountBlob(ctx, 300, quorums)
240+
_, err = accountant.AccountBlob(ctx, 300, quorums)
247241
assert.NoError(t, err)
248242
assert.Equal(t, isRotation([]uint64{800, 300, 0}, mapRecordUsage(accountant.binRecords)), true)
249243

250244
// Third call
251-
_, _, err = accountant.AccountBlob(ctx, 500, quorums)
245+
_, err = accountant.AccountBlob(ctx, 500, quorums)
252246
assert.NoError(t, err)
253247
assert.Equal(t, isRotation([]uint64{800, 800, 0}, mapRecordUsage(accountant.binRecords)), true)
254248
}
@@ -270,9 +264,8 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) {
270264

271265
privateKey1, err := crypto.GenerateKey()
272266
assert.NoError(t, err)
273-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
274-
assert.NoError(t, err)
275-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
267+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
268+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
276269

277270
ctx := context.Background()
278271
quorums := []uint8{0, 1}
@@ -285,7 +278,7 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) {
285278
defer wg.Done()
286279
// for j := 0; j < 5; j++ {
287280
// fmt.Println("request ", i)
288-
_, _, err := accountant.AccountBlob(ctx, 100, quorums)
281+
_, err := accountant.AccountBlob(ctx, 100, quorums)
289282
assert.NoError(t, err)
290283
time.Sleep(500 * time.Millisecond)
291284
// }
@@ -317,30 +310,30 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) {
317310

318311
privateKey1, err := crypto.GenerateKey()
319312
assert.NoError(t, err)
320-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
321-
assert.NoError(t, err)
322-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
313+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
314+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
315+
323316
ctx := context.Background()
324317
quorums := []uint8{0, 1}
325318
now := time.Now().Unix()
326319

327320
// Okay reservation
328-
header, _, err := accountant.AccountBlob(ctx, 800, quorums)
321+
header, err := accountant.AccountBlob(ctx, 800, quorums)
329322
assert.NoError(t, err)
330323
assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex)
331324
metadata := core.ConvertPaymentHeader(header)
332325
assert.Equal(t, big.NewInt(0), metadata.CumulativePayment)
333326
assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.binRecords)), true)
334327

335328
// Second call: Allow one overflow
336-
header, _, err = accountant.AccountBlob(ctx, 500, quorums)
329+
header, err = accountant.AccountBlob(ctx, 500, quorums)
337330
assert.NoError(t, err)
338331
metadata = core.ConvertPaymentHeader(header)
339332
assert.Equal(t, big.NewInt(0), metadata.CumulativePayment)
340333
assert.Equal(t, isRotation([]uint64{1300, 0, 300}, mapRecordUsage(accountant.binRecords)), true)
341334

342335
// Third call: Should use on-demand payment
343-
header, _, err = accountant.AccountBlob(ctx, 200, quorums)
336+
header, err = accountant.AccountBlob(ctx, 200, quorums)
344337
assert.NoError(t, err)
345338
assert.Equal(t, uint32(0), header.BinIndex)
346339
metadata = core.ConvertPaymentHeader(header)
@@ -365,20 +358,19 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) {
365358

366359
privateKey1, err := crypto.GenerateKey()
367360
assert.NoError(t, err)
368-
paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes()))
369-
assert.NoError(t, err)
370-
accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins)
361+
accountId := hex.EncodeToString(privateKey1.D.Bytes())
362+
accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins)
371363

372364
ctx := context.Background()
373365
quorums := []uint8{0, 1}
374366

375367
// full reservation
376-
_, _, err = accountant.AccountBlob(ctx, 1000, quorums)
368+
_, err = accountant.AccountBlob(ctx, 1000, quorums)
377369
assert.NoError(t, err)
378370
assert.Equal(t, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.binRecords)), true)
379371

380372
// no overflow
381-
header, _, err := accountant.AccountBlob(ctx, 500, quorums)
373+
header, err := accountant.AccountBlob(ctx, 500, quorums)
382374
assert.NoError(t, err)
383375
assert.Equal(t, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.binRecords)), true)
384376
metadata := core.ConvertPaymentHeader(header)
@@ -388,7 +380,7 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) {
388380
time.Sleep(time.Duration(reservationWindow) * time.Second)
389381

390382
// Third call: Should use new bin and allow overflow again
391-
_, _, err = accountant.AccountBlob(ctx, 500, quorums)
383+
_, err = accountant.AccountBlob(ctx, 500, quorums)
392384
assert.NoError(t, err)
393385
assert.Equal(t, isRotation([]uint64{1000, 500, 0}, mapRecordUsage(accountant.binRecords)), true)
394386
}

0 commit comments

Comments
 (0)