-
Notifications
You must be signed in to change notification settings - Fork 200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[payments] disperser client local metering #793
Conversation
0758b5d
to
6972c65
Compare
887827c
to
e3a3eda
Compare
24cc85d
to
256cd70
Compare
e3a3eda
to
cf39a09
Compare
c81aad0
to
5c33742
Compare
32e61dd
to
072e007
Compare
45a72b2
to
4218aba
Compare
835f1be
to
76e1aad
Compare
api/clients/accountant.go
Outdated
// binUsage := a.binUsages[0] + dataLength | ||
a.usageLock.Lock() | ||
defer a.usageLock.Unlock() | ||
a.binUsages[0] += dataLength |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way that the accountant is doing bin accounting looks to me like it might be incorrect; in particular, it might not line up with the server, since the transitions are happening at different points on the client than on the server.
I have another idea for how we could possibly do this. Will follow up!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated to use the wrapped bin array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really good! I'll continue reviewing after the discussed changes to the accountant!
disperser/apiserver/server.go
Outdated
@@ -285,7 +285,7 @@ func (s *DispersalServer) disperseBlob(ctx context.Context, blob *core.Blob, aut | |||
|
|||
// If paymentHeader is not empty, we use the meterer, otherwise we use the ratelimiter if the ratelimiter is available | |||
if paymentHeader != nil && *paymentHeader != (core.PaymentMetadata{}) { | |||
err := s.meterer.MeterRequest(ctx, *blob, *paymentHeader) | |||
err := s.meterer.MeterRequest(ctx, *paymentHeader, uint(blobSize), blob.GetQuorumNumbers()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs to be converted from byte length to symbol length (
Line 10 in db3da32
func GetBlobLength(blobSize uint) uint { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank for the link, I've updated to use the conversion
disperser/apiserver/payment_test.go
Outdated
pk := "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeb" | ||
signer := auth.NewPaymentSigner(pk) | ||
|
||
dataLength := len(data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to the server.go
comment, I believe we should be using the number of symbols, here, correct?
313a2cf
to
e20e9b6
Compare
c317303
to
928ab5e
Compare
6797360
to
c446db2
Compare
api/clients/accountant.go
Outdated
return &a | ||
} | ||
|
||
// accountant calculates and records payment information |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: BlobPaymentInfo calculates...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we also document what's being returned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated the comments
api/clients/accountant.go
Outdated
binRecords []BinRecord | ||
usageLock sync.Mutex | ||
cumulativePayment *big.Int | ||
stopRotation chan struct{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outdated concept, removed:)
api/clients/disperser_client.go
Outdated
} | ||
|
||
var _ DisperserClient = &disperserClient{} | ||
|
||
// DisperserClient maintains a single underlying grpc connection to the disperser server, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we keep this doc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for taking it out, I'm not sure what happened, putting it back in
func (c *disperserClient) DispersePaidBlob(ctx context.Context, data []byte, quorums []uint8) (*disperser.BlobStatus, []byte, error) { | ||
return nil, nil, api.NewErrorInternal("not implemented") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep returning this error if accountant is not configured?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good, added it
api/clients/disperser_client.go
Outdated
return nil, nil, fmt.Errorf("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 %w", err) | ||
} | ||
|
||
header, signature, err := c.accountant.AccountBlob(ctx, uint64(len(data)), quorums) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check nil pointer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the inner level SignBlobPayment(protoPaymentHeader)
should error out if the header is nil so I don't expect this to happen, but added it for additional safety
api/clients/accountant.go
Outdated
Usage uint64 | ||
} | ||
|
||
func NewAccountant(reservation core.ActiveReservation, onDemand core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, paymentSigner core.PaymentSigner) *Accountant { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any validations needed here?
Also, should we be passing in pointers to the structs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a lot of what's going on here will be replaced by #835 . binRecords and cumulativePayment should change more frequently compared to the global parameters, and no one else needs access to the structs. would you help me understand what we will get by using pointers here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing by values will make copies of the structs. Unless you're modifying the input structs, it's more efficient to pass by pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New accountant would modify the input structs in later functions. Since there's no other use for the input params after being passed in, I thought it would be good for the accountant to take ownership of the params and imagined a garbage collector. Now I realize I'm thinking in rust 😅 gonna update this to be more efficient
api/clients/accountant.go
Outdated
"github.com/Layr-Labs/eigenda/core/meterer" | ||
) | ||
|
||
type IAccountant interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we call the interface Accountant
and the struct accountant
and only export the interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is https://github.com/Layr-Labs/eigenda/pull/847/files a fix to this? I think it looks good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah that one does something similar to PaymentSigner
, not to Accountant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made similar changes to accountant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove the I
prefix now?
api/clients/eigenda_client.go
Outdated
@@ -298,6 +304,125 @@ func (m *EigenDAClient) putBlob(ctx context.Context, rawData []byte, resultChan | |||
} | |||
} | |||
|
|||
// PaidPutBlob behaves like PutBlob but with authenticated payment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this set of methods are copy paste of the existing set, except calling DispersePaidBlob
instead of DisperseBlobAuthenticated
. Could we integrate this to the existing method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, did a similar refactoring that we did on the server side; if there's a setting on "PaymentSignerPrivateKeyHex", then we call out to DispersePaidBlob
, otherwise DisperseBlobAuthenticated
. lmk if this is okay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
api/clients/accountant.go
Outdated
// then on-demand if the reservation is not available. The returned values are | ||
// bin index for reservation payments and cumulative payment for on-demand payments, | ||
// and both fields are used to create the payment header and signature | ||
func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint64, quorumNumbers []uint8) (uint32, *big.Int, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic in this function is pretty coupled with the core/meterer library. It might be worth considering whether there are ways to encapsulate the core logic and reuse it across these contexts. Maybe an improvement for later.
api/clients/accountant.go
Outdated
incrementRequired := big.NewInt(int64(a.PaymentCharged(uint(numSymbols)))) | ||
a.cumulativePayment.Add(a.cumulativePayment, incrementRequired) | ||
if a.cumulativePayment.Cmp(a.onDemand.CumulativePayment) <= 0 { | ||
if err := QuorumCheck(quorumNumbers, []uint8{0, 1}); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guessing these parameters will be read from the disperser endpoint in the next PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't intend on reading this from disperser endpoint. Everything else made sense to, but the quorum numbers were not included in the next PR because I was expecting them to stay as 0 and 1. alternatively, I could mark them as a hard coded constant and/or add to accountant struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think either of these is fine. In the event that this set increased, it seems like it wouldn't be a breaking change for clients still using {0,1}. Maybe just a hard-coded constant for legibility would be good!
api/clients/accountant.go
Outdated
} | ||
protoPaymentHeader := pm.ConvertToProtoPaymentHeader() | ||
|
||
signature, err := a.paymentSigner.SignBlobPayment(protoPaymentHeader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this would have been a previous PR, but why does SignBlobPayment
take the proto struct instead of the core golang struct here, out of curiosity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uhhhhh I think it was because DispersePaidBlobRequest
gave me the proto struct. This is a good question, I thought about it a bit and added a conversion function (proto->golang) so we are now using the local struct
api/clients/accountant.go
Outdated
} | ||
|
||
func (a *Accountant) GetRelativeBinRecord(index uint32) *BinRecord { | ||
relativeIndex := index % 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm convinced that 3 should always work here. But is there any reason to make this configurable? Maybe a hard coded constant would be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure 🤔 maybe a client wants more insights into the past and use a bigger number? I added a config to EigenDA proxy client and the accountant will take the maximum between the setting and the minimum number of bins (hardcoded to 3)
@@ -683,6 +685,22 @@ func newTestServer(transactor core.Writer, testName string) *apiserver.Dispersal | |||
panic("failed to make initial query to the on-chain state") | |||
} | |||
|
|||
mockState.On("GetPricePerSymbol").Return(uint32(encoding.BYTES_PER_SYMBOL), nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little confused about why you decided to use encoding.BYTES_PER_SYMBOL
for this. 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ha uhhh this is because the previous tests was made on the basis of charging per byte instead of per symbol, and I was lazy to redo the calculation after the blob size conversion, so I just scaled the price 😅
3267b20
to
e0369f3
Compare
e0369f3
to
150582d
Compare
api/clients/accountant.go
Outdated
return 0, big.NewInt(0), fmt.Errorf("neither reservation nor on-demand payment is available") | ||
} | ||
|
||
// accountant provides and records payment information |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: AccountBlob provides...
api/clients/disperser_client.go
Outdated
@@ -78,6 +78,8 @@ type disperserClient struct { | |||
// instead of a real network connection for eg. | |||
conn *grpc.ClientConn | |||
client disperser_rpc.DisperserClient | |||
|
|||
accountant Accountant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it would be okay to leave as is, but removing for clarity
Why are these changes needed?
Disperser client integrated with payments metering with inabox integration tests
Checks