Skip to content
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

Merged
merged 17 commits into from
Nov 2, 2024

Conversation

hopeyen
Copy link
Contributor

@hopeyen hopeyen commented Oct 8, 2024

Why are these changes needed?

Disperser client integrated with payments metering with inabox integration tests

Checks

  • I've made sure the lint is passing in this PR.
  • I've made sure the tests are passing. Note that there might be a few flaky tests, in that case, please comment that they are not relevant.
  • I've checked the new test coverage and the coverage percentage didn't drop.
  • Testing Strategy
    • Unit tests
    • Integration tests
    • This PR is not tested :(

@hopeyen hopeyen changed the base branch from master to hope/disperser-server-metering October 8, 2024 03:52
@hopeyen hopeyen changed the title disperser client metering disperser client local metering Oct 8, 2024
@hopeyen hopeyen changed the title disperser client local metering [payments] disperser client local metering Oct 8, 2024
@hopeyen hopeyen force-pushed the hope/disperser-server-metering branch from 0758b5d to 6972c65 Compare October 11, 2024 17:29
@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch from 887827c to e3a3eda Compare October 11, 2024 18:42
@hopeyen hopeyen force-pushed the hope/disperser-server-metering branch from 24cc85d to 256cd70 Compare October 21, 2024 19:20
@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch from e3a3eda to cf39a09 Compare October 22, 2024 02:32
@hopeyen hopeyen force-pushed the hope/disperser-server-metering branch from c81aad0 to 5c33742 Compare October 22, 2024 21:56
@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch 2 times, most recently from 32e61dd to 072e007 Compare October 23, 2024 20:39
@hopeyen hopeyen force-pushed the hope/disperser-server-metering branch 5 times, most recently from 45a72b2 to 4218aba Compare October 25, 2024 00:17
@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch 3 times, most recently from 835f1be to 76e1aad Compare October 25, 2024 21:35
// binUsage := a.binUsages[0] + dataLength
a.usageLock.Lock()
defer a.usageLock.Unlock()
a.binUsages[0] += dataLength
Copy link
Collaborator

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!

Copy link
Contributor Author

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

Copy link
Collaborator

@mooselumph mooselumph left a 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!

@@ -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())
Copy link
Collaborator

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 (

func GetBlobLength(blobSize uint) uint {
)

Copy link
Contributor Author

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

pk := "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeb"
signer := auth.NewPaymentSigner(pk)

dataLength := len(data)
Copy link
Collaborator

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?

@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch from 313a2cf to e20e9b6 Compare October 29, 2024 01:48
@hopeyen hopeyen force-pushed the hope/disperser-server-metering branch from c317303 to 928ab5e Compare October 29, 2024 16:33
@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch from 6797360 to c446db2 Compare October 30, 2024 08:50
@hopeyen hopeyen changed the base branch from hope/disperser-server-metering to master October 30, 2024 08:51
return &a
}

// accountant calculates and records payment information
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: BlobPaymentInfo calculates...

Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated the comments

binRecords []BinRecord
usageLock sync.Mutex
cumulativePayment *big.Int
stopRotation chan struct{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outdated concept, removed:)

}

var _ DisperserClient = &disperserClient{}

// DisperserClient maintains a single underlying grpc connection to the disperser server,
Copy link
Contributor

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?

Copy link
Contributor Author

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")
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good, added it

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check nil pointer?

Copy link
Contributor Author

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

Usage uint64
}

func NewAccountant(reservation core.ActiveReservation, onDemand core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, paymentSigner core.PaymentSigner) *Accountant {
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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

Copy link
Contributor Author

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

"github.com/Layr-Labs/eigenda/core/meterer"
)

type IAccountant interface {
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Contributor

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?

@@ -298,6 +304,125 @@ func (m *EigenDAClient) putBlob(ctx context.Context, rawData []byte, resultChan
}
}

// PaidPutBlob behaves like PutBlob but with authenticated payment.
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Collaborator

@mooselumph mooselumph left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

// 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) {
Copy link
Collaborator

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.

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 {
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

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!

}
protoPaymentHeader := pm.ConvertToProtoPaymentHeader()

signature, err := a.paymentSigner.SignBlobPayment(protoPaymentHeader)
Copy link
Collaborator

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?

Copy link
Contributor Author

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

}

func (a *Accountant) GetRelativeBinRecord(index uint32) *BinRecord {
relativeIndex := index % 3
Copy link
Collaborator

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?

Copy link
Contributor Author

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)
Copy link
Collaborator

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. 😅

Copy link
Contributor Author

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 😅

@hopeyen hopeyen requested a review from ian-shim October 31, 2024 18:21
@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch 13 times, most recently from 3267b20 to e0369f3 Compare November 1, 2024 13:46
@hopeyen hopeyen force-pushed the hope/disperser-client-metering branch from e0369f3 to 150582d Compare November 1, 2024 14:27
return 0, big.NewInt(0), fmt.Errorf("neither reservation nor on-demand payment is available")
}

// accountant provides and records payment information
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: AccountBlob provides...

@@ -78,6 +78,8 @@ type disperserClient struct {
// instead of a real network connection for eg.
conn *grpc.ClientConn
client disperser_rpc.DisperserClient

accountant Accountant
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be removed?

Copy link
Contributor Author

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

@hopeyen hopeyen merged commit 7af65ea into master Nov 2, 2024
9 checks passed
@hopeyen hopeyen deleted the hope/disperser-client-metering branch November 2, 2024 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants