Skip to content

Commit 6059c6a

Browse files
authored
[v2] payments inabox (#991)
1 parent 94318c2 commit 6059c6a

File tree

7 files changed

+104
-85
lines changed

7 files changed

+104
-85
lines changed

api/clients/v2/disperser_client.go

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package clients
33
import (
44
"context"
55
"fmt"
6-
"math/big"
76
"sync"
87

98
"github.com/Layr-Labs/eigenda/api"
@@ -139,16 +138,11 @@ func (c *disperserClient) DisperseBlob(
139138
return nil, [32]byte{}, api.NewErrorInternal("uninitialized signer for authenticated dispersal")
140139
}
141140

142-
// TODO(hopeyen): uncomment this after the accountant is implemented
143-
// if c.accountant == nil {
144-
// return nil, [32]byte{}, api.NewErrorInternal("uninitialized accountant for paid dispersal; make sure to call PopulateAccountant after creating the client")
145-
// }
146-
147-
// symbolLength := encoding.GetBlobLengthPowerOf2(uint(len(data)))
148-
// payment, err := c.accountant.AccountBlob(ctx, uint32(symbolLength), quorums, salt)
149-
// if err != nil {
150-
// return nil, [32]byte{}, fmt.Errorf("error accounting blob: %w", err)
151-
// }
141+
symbolLength := encoding.GetBlobLengthPowerOf2(uint(len(data)))
142+
payment, err := c.accountant.AccountBlob(ctx, uint32(symbolLength), quorums, salt)
143+
if err != nil {
144+
return nil, [32]byte{}, fmt.Errorf("error accounting blob: %w", err)
145+
}
152146

153147
if len(quorums) == 0 {
154148
return nil, [32]byte{}, api.NewErrorInvalidArg("quorum numbers must be provided")
@@ -187,26 +181,18 @@ func (c *disperserClient) DisperseBlob(
187181
}
188182
}
189183

190-
var payment core.PaymentMetadata
191-
accountId, err := c.signer.GetAccountID()
192-
if err != nil {
193-
return nil, [32]byte{}, api.NewErrorInvalidArg(fmt.Sprintf("please configure signer key if you want to use authenticated endpoint %v", err))
194-
}
195-
payment.AccountID = accountId
196-
payment.ReservationPeriod = 0
197-
payment.CumulativePayment = big.NewInt(0)
198184
blobHeader := &corev2.BlobHeader{
199185
BlobVersion: blobVersion,
200186
BlobCommitments: blobCommitments,
201187
QuorumNumbers: quorums,
202-
PaymentMetadata: payment,
203-
}
204-
// TODO(hopeyen): uncomment this and replace the payment metadata for authentication
205-
// sig, err := c.signer.SignBlobRequest(blobHeader)
206-
// if err != nil {
207-
// return nil, [32]byte{}, fmt.Errorf("error signing blob request: %w", err)
208-
// }
209-
// blobHeader.Signature = sig
188+
PaymentMetadata: *payment,
189+
}
190+
191+
sig, err := c.signer.SignBlobRequest(blobHeader)
192+
if err != nil {
193+
return nil, [32]byte{}, fmt.Errorf("error signing blob request: %w", err)
194+
}
195+
blobHeader.Signature = sig
210196
blobHeaderProto, err := blobHeader.ToProtobuf()
211197
if err != nil {
212198
return nil, [32]byte{}, fmt.Errorf("error converting blob header to protobuf: %w", err)

contracts/script/SetUpEigenDA.s.sol

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {StakeRegistry} from "eigenlayer-middleware/StakeRegistry.sol";
1010
import {IIndexRegistry} from "eigenlayer-middleware/interfaces/IIndexRegistry.sol";
1111

1212
import {EigenDAServiceManager} from "../src/core/EigenDAServiceManager.sol";
13+
import {PaymentVault} from "../src/payments/PaymentVault.sol";
14+
import {IPaymentVault} from "../src/interfaces/IPaymentVault.sol";
1315
import {EigenDAHasher} from "../src/libraries/EigenDAHasher.sol";
1416
import {EigenDADeployer} from "./EigenDADeployer.s.sol";
1517
import {EigenLayerUtils} from "./EigenLayerUtils.s.sol";
@@ -19,6 +21,21 @@ import "forge-std/Test.sol";
1921
import "forge-std/Script.sol";
2022
import "forge-std/StdJson.sol";
2123

24+
25+
// Helper function to create single-element arrays
26+
function toArray(address element) pure returns (address[] memory) {
27+
address[] memory arr = new address[](1);
28+
arr[0] = element;
29+
return arr;
30+
}
31+
32+
function toArray(uint256 element) pure returns (uint256[] memory) {
33+
uint256[] memory arr = new uint256[](1);
34+
arr[0] = element;
35+
return arr;
36+
}
37+
38+
2239
// # To load the variables in the .env file
2340
// source .env
2441
// # To deploy and verify our contract
@@ -112,8 +129,7 @@ contract SetupEigenDA is EigenDADeployer, EigenLayerUtils {
112129
}
113130

114131
vm.startBroadcast();
115-
116-
// Allocate eth to stakers and operators
132+
// Allocate eth to stakers, operators, dispserser clients
117133
_allocate(
118134
IERC20(address(0)),
119135
stakers,
@@ -156,6 +172,22 @@ contract SetupEigenDA is EigenDADeployer, EigenLayerUtils {
156172
delegation.registerAsOperator(IDelegationManager.OperatorDetails(earningsReceiver, delegationApprover, stakerOptOutWindowBlocks), metadataURI);
157173
}
158174

175+
176+
// Register Reservations for client as the eigenDACommunityMultisig
177+
IPaymentVault.Reservation memory reservation = IPaymentVault.Reservation({
178+
symbolsPerSecond: 452198,
179+
startTimestamp: uint64(block.timestamp),
180+
endTimestamp: uint64(block.timestamp + 1000000000),
181+
quorumNumbers: hex"0001",
182+
quorumSplits: hex"3232"
183+
});
184+
address clientAddress = address(0x641691973c98dFe68b07Ee3613E270406285DFE8);
185+
vm.startBroadcast(msg.sender);
186+
paymentVault.setReservation(clientAddress, reservation);
187+
// Deposit OnDemand
188+
paymentVault.depositOnDemand{value: 0.1 ether}(clientAddress);
189+
vm.stopBroadcast();
190+
159191
// Deposit stakers into EigenLayer and delegate to operators
160192
for (uint256 i = 0; i < stakerPrivateKeys.length; i++) {
161193
vm.startBroadcast(stakerPrivateKeys[i]);

core/meterer/onchain_state_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var (
2727
func TestRefreshOnchainPaymentState(t *testing.T) {
2828
mockState := &mock.MockOnchainPaymentState{}
2929
ctx := context.Background()
30-
mockState.On("RefreshOnchainPaymentState", testifymock.Anything, testifymock.Anything).Return(nil)
30+
mockState.On("RefreshOnchainPaymentState", testifymock.Anything).Return(nil)
3131

3232
err := mockState.RefreshOnchainPaymentState(ctx)
3333
assert.NoError(t, err)

disperser/apiserver/disperse_blob_v2.go

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"math/big"
78
"time"
89

910
"github.com/Layr-Labs/eigenda/api"
1011
pb "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
12+
"github.com/Layr-Labs/eigenda/core"
1113
corev2 "github.com/Layr-Labs/eigenda/core/v2"
1214
"github.com/Layr-Labs/eigenda/disperser/common"
1315
dispv2 "github.com/Layr-Labs/eigenda/disperser/common/v2"
@@ -107,6 +109,19 @@ func (s *DispersalServerV2) validateDispersalRequest(ctx context.Context, req *p
107109
return api.NewErrorInvalidArg("blob header must contain commitments")
108110
}
109111

112+
blobHeader, err := corev2.BlobHeaderFromProtobuf(blobHeaderProto)
113+
if err != nil {
114+
return api.NewErrorInvalidArg(fmt.Sprintf("invalid blob header: %s", err.Error()))
115+
}
116+
117+
if blobHeader.PaymentMetadata == (core.PaymentMetadata{}) {
118+
return api.NewErrorInvalidArg("payment metadata is required")
119+
}
120+
121+
if len(blobHeader.PaymentMetadata.AccountID) == 0 || blobHeader.PaymentMetadata.ReservationPeriod == 0 || blobHeader.PaymentMetadata.CumulativePayment == nil {
122+
return api.NewErrorInvalidArg("invalid payment metadata")
123+
}
124+
110125
if len(blobHeaderProto.GetQuorumNumbers()) == 0 {
111126
return api.NewErrorInvalidArg("blob header must contain at least one quorum number")
112127
}
@@ -122,7 +137,7 @@ func (s *DispersalServerV2) validateDispersalRequest(ctx context.Context, req *p
122137
}
123138

124139
// validate every 32 bytes is a valid field element
125-
_, err := rs.ToFrArray(data)
140+
_, err = rs.ToFrArray(data)
126141
if err != nil {
127142
s.logger.Error("failed to convert a 32bytes as a field element", "err", err)
128143
return api.NewErrorInvalidArg("encountered an error to convert a 32-bytes into a valid field element, please use the correct format where every 32bytes(big-endian) is less than 21888242871839275222246405745257275088548364400416034343698204186575808495617")
@@ -132,38 +147,25 @@ func (s *DispersalServerV2) validateDispersalRequest(ctx context.Context, req *p
132147
return api.NewErrorInvalidArg(fmt.Sprintf("invalid blob version %d; valid blob versions are: %v", blobHeaderProto.GetVersion(), onchainState.BlobVersionParameters.Keys()))
133148
}
134149

135-
blobHeader, err := corev2.BlobHeaderFromProtobuf(blobHeaderProto)
136-
if err != nil {
137-
return api.NewErrorInvalidArg(fmt.Sprintf("invalid blob header: %s", err.Error()))
150+
if err = s.authenticator.AuthenticateBlobRequest(blobHeader); err != nil {
151+
return api.NewErrorInvalidArg(fmt.Sprintf("authentication failed: %s", err.Error()))
138152
}
139-
// TODO(ian-shim): enable this check for authentication
140-
// if blobHeader.PaymentMetadata == nil {
141-
// return api.NewErrorInvalidArg("payment metadata is required")
142-
// }
143-
// if err = s.authenticator.AuthenticateBlobRequest(blobHeader); err != nil {
144-
// return api.NewErrorInvalidArg(fmt.Sprintf("authentication failed: %s", err.Error()))
145-
// }
146-
147-
// TODO(ian-shim): enable this check when we have payment metadata + authentication in disperser client
148-
// if len(blobHeader.PaymentMetadata.AccountID) == 0 || (blobHeader.PaymentMetadata.ReservationPeriod == 0 && blobHeader.PaymentMetadata.CumulativePayment == nil) {
149-
// return api.NewErrorInvalidArg("invalid payment metadata")
150-
// }
151153

152154
// handle payments and check rate limits
153-
// reservationPeriod := blobHeaderProto.GetPaymentHeader().GetReservationPeriod()
154-
// cumulativePayment := new(big.Int).SetBytes(blobHeaderProto.GetPaymentHeader().GetCumulativePayment())
155-
// accountID := blobHeaderProto.GetPaymentHeader().GetAccountId()
156-
157-
// paymentHeader := core.PaymentMetadata{
158-
// AccountID: accountID,
159-
// ReservationPeriod: reservationPeriod,
160-
// CumulativePayment: cumulativePayment,
161-
// }
162-
163-
// err := s.meterer.MeterRequest(ctx, paymentHeader, blobLength, blobHeader.QuorumNumbers)
164-
// if err != nil {
165-
// return api.NewErrorResourceExhausted(err.Error())
166-
// }
155+
reservationPeriod := blobHeaderProto.GetPaymentHeader().GetReservationPeriod()
156+
cumulativePayment := new(big.Int).SetBytes(blobHeaderProto.GetPaymentHeader().GetCumulativePayment())
157+
accountID := blobHeaderProto.GetPaymentHeader().GetAccountId()
158+
159+
paymentHeader := core.PaymentMetadata{
160+
AccountID: accountID,
161+
ReservationPeriod: reservationPeriod,
162+
CumulativePayment: cumulativePayment,
163+
}
164+
165+
err = s.meterer.MeterRequest(ctx, paymentHeader, blobLength, blobHeader.QuorumNumbers)
166+
if err != nil {
167+
return api.NewErrorResourceExhausted(err.Error())
168+
}
167169

168170
commitments, err := s.prover.GetCommitmentsForPaddedLength(data)
169171
if err != nil {

disperser/apiserver/server_v2.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaym
269269

270270
// validate the signature
271271
if err := s.authenticator.AuthenticatePaymentStateRequest(req.GetSignature(), req.GetAccountId()); err != nil {
272+
s.logger.Debug("failed to validate signature", "err", err, "accountID", accountID)
272273
return nil, api.NewErrorInvalidArg(fmt.Sprintf("authentication failed: %s", err.Error()))
273274
}
274275
// on-chain global payment parameters

disperser/apiserver/server_v2_test.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,14 @@ func TestV2DisperseBlob(t *testing.T) {
107107
assert.Greater(t, blobMetadata.RequestedAt, uint64(now.UnixNano()))
108108
assert.Equal(t, blobMetadata.RequestedAt, blobMetadata.UpdatedAt)
109109

110-
// Try dispersing the same blob
110+
// Try dispersing the same blob; if payment is different, blob will be considered as a differernt blob
111+
// payment will cause failure before commitment check
111112
reply, err = c.DispersalServerV2.DisperseBlob(ctx, &pbv2.DisperseBlobRequest{
112113
Data: data,
113114
BlobHeader: blobHeaderProto,
114115
})
115116
assert.Nil(t, reply)
116-
assert.ErrorContains(t, err, "blob already exists")
117+
assert.ErrorContains(t, err, "payment already exists")
117118
}
118119

119120
func TestV2DisperseBlobRequestValidation(t *testing.T) {
@@ -212,9 +213,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) {
212213
Data: data,
213214
BlobHeader: invalidReqProto,
214215
})
215-
// TODO(hopeyen); re-enable this validation after adding signature verification
216-
// assert.ErrorContains(t, err, "authentication failed")
217-
assert.NoError(t, err)
216+
assert.ErrorContains(t, err, "authentication failed")
218217

219218
// request with invalid payment metadata
220219
invalidReqProto = &pbcommonv2.BlobHeader{
@@ -237,9 +236,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) {
237236
Data: data,
238237
BlobHeader: invalidReqProto,
239238
})
240-
// TODO(ian-shim): re-enable this validation after fixing the payment metadata validation
241-
// assert.ErrorContains(t, err, "invalid payment metadata")
242-
assert.NoError(t, err)
239+
assert.ErrorContains(t, err, "invalid payment metadata")
243240

244241
// request with invalid commitment
245242
invalidCommitment := commitmentProto

inabox/deploy/localstack.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -135,30 +135,31 @@ func DeployResources(
135135
return err
136136
}
137137

138+
fmt.Println("Creating v2 tables")
138139
if v2MetadataTableName != "" {
139140
// Create v2 metadata table
140141
_, err = test_utils.CreateTable(context.Background(), cfg, v2MetadataTableName, blobstorev2.GenerateTableSchema(v2MetadataTableName, 10, 10))
141142
if err != nil {
142143
return err
143144
}
144-
}
145145

146-
v2PaymentName := "e2e_v2_"
147-
// create payment related tables
148-
err = meterer.CreateReservationTable(cfg, v2PaymentName+"reservation")
149-
if err != nil {
150-
fmt.Println("err", err)
151-
return err
152-
}
153-
err = meterer.CreateOnDemandTable(cfg, v2PaymentName+"ondemand")
154-
if err != nil {
155-
fmt.Println("err", err)
156-
return err
157-
}
158-
err = meterer.CreateGlobalReservationTable(cfg, v2PaymentName+"global_reservation")
159-
if err != nil {
160-
fmt.Println("err", err)
161-
return err
146+
v2PaymentName := "e2e_v2_"
147+
// create payment related tables
148+
err = meterer.CreateReservationTable(cfg, v2PaymentName+"reservation")
149+
if err != nil {
150+
fmt.Println("err", err)
151+
return err
152+
}
153+
err = meterer.CreateOnDemandTable(cfg, v2PaymentName+"ondemand")
154+
if err != nil {
155+
fmt.Println("err", err)
156+
return err
157+
}
158+
err = meterer.CreateGlobalReservationTable(cfg, v2PaymentName+"global_reservation")
159+
if err != nil {
160+
fmt.Println("err", err)
161+
return err
162+
}
162163
}
163164

164165
return err

0 commit comments

Comments
 (0)