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

reservation timestamp check #990

Merged
merged 5 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions api/clients/accountant.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var requiredQuorums = []uint8{0, 1}
type Accountant struct {
// on-chain states
accountID string
reservation *core.ActiveReservation
reservation *core.ReservedPayment
onDemand *core.OnDemandPayment
reservationWindow uint32
pricePerSymbol uint32
Expand All @@ -39,7 +39,7 @@ type BinRecord struct {
Usage uint64
}

func NewAccountant(accountID string, reservation *core.ActiveReservation, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, numBins uint32) *Accountant {
func NewAccountant(accountID string, reservation *core.ReservedPayment, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, numBins uint32) *Accountant {
//TODO: client storage; currently every instance starts fresh but on-chain or a small store makes more sense
// Also client is currently responsible for supplying network params, we need to add RPC in order to be automatic
// There's a subsequent PR that handles populating the accountant with on-chain state from the disperser
Expand All @@ -65,7 +65,7 @@ func NewAccountant(accountID string, reservation *core.ActiveReservation, onDema
// BlobPaymentInfo calculates and records payment information. The accountant
// will attempt to use the active reservation first and check for quorum settings,
// 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,
// reservation period 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) {
now := time.Now().Unix()
Expand Down
18 changes: 9 additions & 9 deletions api/clients/accountant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const numBins = uint32(3)
const salt = uint32(0)

func TestNewAccountant(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 100,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down Expand Up @@ -48,7 +48,7 @@ func TestNewAccountant(t *testing.T) {
}

func TestAccountBlob_Reservation(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 200,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down Expand Up @@ -96,7 +96,7 @@ func TestAccountBlob_Reservation(t *testing.T) {
}

func TestAccountBlob_OnDemand(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 200,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down Expand Up @@ -130,7 +130,7 @@ func TestAccountBlob_OnDemand(t *testing.T) {
}

func TestAccountBlob_InsufficientOnDemand(t *testing.T) {
reservation := &core.ActiveReservation{}
reservation := &core.ReservedPayment{}
onDemand := &core.OnDemandPayment{
CumulativePayment: big.NewInt(500),
}
Expand All @@ -152,7 +152,7 @@ func TestAccountBlob_InsufficientOnDemand(t *testing.T) {
}

func TestAccountBlobCallSeries(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 200,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down Expand Up @@ -200,7 +200,7 @@ func TestAccountBlobCallSeries(t *testing.T) {
}

func TestAccountBlob_BinRotation(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 1000,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down Expand Up @@ -242,7 +242,7 @@ func TestAccountBlob_BinRotation(t *testing.T) {
}

func TestConcurrentBinRotationAndAccountBlob(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 1000,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down Expand Up @@ -284,7 +284,7 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) {
}

func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 200,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down Expand Up @@ -332,7 +332,7 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) {
}

func TestAccountBlob_ReservationOverflowReset(t *testing.T) {
reservation := &core.ActiveReservation{
reservation := &core.ReservedPayment{
SymbolsPerSecond: 1000,
StartTimestamp: 100,
EndTimestamp: 200,
Expand Down
2 changes: 1 addition & 1 deletion api/docs/disperser_v2.html
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ <h3 id="disperser.v2.GetPaymentStateRequest">GetPaymentStateRequest</h3>
<td><a href="#bytes">bytes</a></td>
<td></td>
<td><p>Signature over the account ID
TODO: sign over a bin index or a nonce to mitigate signature replay attacks </p></td>
TODO: sign over a reservation period or a nonce to mitigate signature replay attacks </p></td>
</tr>

</tbody>
Expand Down
2 changes: 1 addition & 1 deletion api/docs/disperser_v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ GetPaymentStateRequest contains parameters to query the payment state of an acco
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| account_id | [string](#string) | | |
| signature | [bytes](#bytes) | | Signature over the account ID TODO: sign over a bin index or a nonce to mitigate signature replay attacks |
| signature | [bytes](#bytes) | | Signature over the account ID TODO: sign over a reservation period or a nonce to mitigate signature replay attacks |



Expand Down
2 changes: 1 addition & 1 deletion api/docs/eigenda-protos.html
Original file line number Diff line number Diff line change
Expand Up @@ -2303,7 +2303,7 @@ <h3 id="disperser.v2.GetPaymentStateRequest">GetPaymentStateRequest</h3>
<td><a href="#bytes">bytes</a></td>
<td></td>
<td><p>Signature over the account ID
TODO: sign over a bin index or a nonce to mitigate signature replay attacks </p></td>
TODO: sign over a reservation period or a nonce to mitigate signature replay attacks </p></td>
</tr>

</tbody>
Expand Down
2 changes: 1 addition & 1 deletion api/docs/eigenda-protos.md
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ GetPaymentStateRequest contains parameters to query the payment state of an acco
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| account_id | [string](#string) | | |
| signature | [bytes](#bytes) | | Signature over the account ID TODO: sign over a bin index or a nonce to mitigate signature replay attacks |
| signature | [bytes](#bytes) | | Signature over the account ID TODO: sign over a reservation period or a nonce to mitigate signature replay attacks |



Expand Down
2 changes: 1 addition & 1 deletion api/grpc/disperser/v2/disperser_v2.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/proto/disperser/v2/disperser_v2.proto
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ message BlobCommitmentReply {
message GetPaymentStateRequest {
string account_id = 1;
// Signature over the account ID
// TODO: sign over a bin index or a nonce to mitigate signature replay attacks
// TODO: sign over a reservation period or a nonce to mitigate signature replay attacks
bytes signature = 2;
}

Expand Down
8 changes: 4 additions & 4 deletions core/chainio.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ type Reader interface {
// GetAllVersionedBlobParams returns the blob version parameters for all blob versions at the given block number.
GetAllVersionedBlobParams(ctx context.Context) (map[uint16]*BlobVersionParameters, error)

// GetActiveReservations returns active reservations (end timestamp > current timestamp)
GetActiveReservations(ctx context.Context, accountIDs []gethcommon.Address) (map[gethcommon.Address]*ActiveReservation, error)
// GetReservedPayments returns active reservations (end timestamp > current timestamp)
GetReservedPayments(ctx context.Context, accountIDs []gethcommon.Address) (map[gethcommon.Address]*ReservedPayment, error)

// GetActiveReservationByAccount returns active reservation by account ID
GetActiveReservationByAccount(ctx context.Context, accountID gethcommon.Address) (*ActiveReservation, error)
// GetReservedPaymentByAccount returns active reservation by account ID
GetReservedPaymentByAccount(ctx context.Context, accountID gethcommon.Address) (*ReservedPayment, error)

// GetOnDemandPayments returns all on-demand payments
GetOnDemandPayments(ctx context.Context, accountIDs []gethcommon.Address) (map[gethcommon.Address]*OnDemandPayment, error)
Expand Down
29 changes: 19 additions & 10 deletions core/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,24 +604,33 @@ func ConvertToPaymentMetadata(ph *commonpb.PaymentHeader) *PaymentMetadata {
}
}

// OperatorInfo contains information about an operator which is stored on the blockchain state,
// corresponding to a particular quorum
type ActiveReservation struct {
SymbolsPerSecond uint64 // reserve number of symbols per second
//TODO: we are not using start and end timestamp, add check or remove
StartTimestamp uint64 // Unix timestamp that's valid for basically eternity
EndTimestamp uint64
// ReservedPayment contains information the onchain state about a reserved payment
type ReservedPayment struct {
// reserve number of symbols per second
SymbolsPerSecond uint64
// reservation activation timestamp
StartTimestamp uint64
// reservation expiration timestamp
EndTimestamp uint64

QuorumNumbers []uint8 // allowed quorums
QuorumSplits []byte // ordered mapping of quorum number to payment split; on-chain validation should ensure split <= 100
// allowed quorums
QuorumNumbers []uint8
// ordered mapping of quorum number to payment split; on-chain validation should ensure split <= 100
QuorumSplits []byte
}

type OnDemandPayment struct {
CumulativePayment *big.Int // Total amount deposited by the user
// Total amount deposited by the user
CumulativePayment *big.Int
}

type BlobVersionParameters struct {
CodingRate uint32
MaxNumOperators uint32
NumChunks uint32
}

// IsActive returns true if the reservation is active at the given timestamp
func (ar *ReservedPayment) IsActive(currentTimestamp uint64) bool {
return ar.StartTimestamp <= currentTimestamp && ar.EndTimestamp >= currentTimestamp
}
62 changes: 62 additions & 0 deletions core/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,65 @@ func TestChunksData(t *testing.T) {
assert.EqualError(t, err, "unsupported chunk encoding format: 3")
}
}

func TestReservedPayment_IsActive(t *testing.T) {
tests := []struct {
name string
reservedPayment core.ReservedPayment
currentTimestamp uint64
wantActive bool
}{
{
name: "active - current time in middle of range",
reservedPayment: core.ReservedPayment{
StartTimestamp: 100,
EndTimestamp: 200,
},
currentTimestamp: 150,
wantActive: true,
},
{
name: "active - current time at start",
reservedPayment: core.ReservedPayment{
StartTimestamp: 100,
EndTimestamp: 200,
},
currentTimestamp: 100,
wantActive: true,
},
{
name: "active - current time at end",
reservedPayment: core.ReservedPayment{
StartTimestamp: 100,
EndTimestamp: 200,
},
currentTimestamp: 200,
wantActive: true,
},
{
name: "inactive - current time before start",
reservedPayment: core.ReservedPayment{
StartTimestamp: 100,
EndTimestamp: 200,
},
currentTimestamp: 99,
wantActive: false,
},
{
name: "inactive - current time after end",
reservedPayment: core.ReservedPayment{
StartTimestamp: 100,
EndTimestamp: 200,
},
currentTimestamp: 201,
wantActive: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isActive := tt.reservedPayment.IsActive(tt.currentTimestamp)
assert.Equal(t, tt.wantActive, isActive)
})
}
}
10 changes: 5 additions & 5 deletions core/eth/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -690,11 +690,11 @@ func (t *Reader) GetAllVersionedBlobParams(ctx context.Context) (map[uint16]*cor
return res, nil
}

func (t *Reader) GetActiveReservations(ctx context.Context, accountIDs []gethcommon.Address) (map[gethcommon.Address]*core.ActiveReservation, error) {
func (t *Reader) GetReservedPayments(ctx context.Context, accountIDs []gethcommon.Address) (map[gethcommon.Address]*core.ReservedPayment, error) {
if t.bindings.PaymentVault == nil {
return nil, errors.New("payment vault not deployed")
}
reservationsMap := make(map[gethcommon.Address]*core.ActiveReservation)
reservationsMap := make(map[gethcommon.Address]*core.ReservedPayment)
reservations, err := t.bindings.PaymentVault.GetReservations(&bind.CallOpts{
Context: ctx,
}, accountIDs)
Expand All @@ -704,7 +704,7 @@ func (t *Reader) GetActiveReservations(ctx context.Context, accountIDs []gethcom

// since reservations are returned in the same order as the accountIDs, we can directly map them
for i, reservation := range reservations {
res, err := ConvertToActiveReservation(reservation)
res, err := ConvertToReservedPayment(reservation)
if err != nil {
t.logger.Warn("failed to get active reservation", "account", accountIDs[i], "err", err)
continue
Expand All @@ -716,7 +716,7 @@ func (t *Reader) GetActiveReservations(ctx context.Context, accountIDs []gethcom
return reservationsMap, nil
}

func (t *Reader) GetActiveReservationByAccount(ctx context.Context, accountID gethcommon.Address) (*core.ActiveReservation, error) {
func (t *Reader) GetReservedPaymentByAccount(ctx context.Context, accountID gethcommon.Address) (*core.ReservedPayment, error) {
if t.bindings.PaymentVault == nil {
return nil, errors.New("payment vault not deployed")
}
Expand All @@ -726,7 +726,7 @@ func (t *Reader) GetActiveReservationByAccount(ctx context.Context, accountID ge
if err != nil {
return nil, err
}
return ConvertToActiveReservation(reservation)
return ConvertToReservedPayment(reservation)
}

func (t *Reader) GetOnDemandPayments(ctx context.Context, accountIDs []gethcommon.Address) (map[gethcommon.Address]*core.OnDemandPayment, error) {
Expand Down
6 changes: 3 additions & 3 deletions core/eth/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,14 @@ func isZeroValuedReservation(reservation paymentvault.IPaymentVaultReservation)
len(reservation.QuorumSplits) == 0
}

// ConvertToActiveReservation converts a upstream binding data structure to local definition.
// ConvertToReservedPayment converts a upstream binding data structure to local definition.
// Returns an error if the input reservation is zero-valued.
func ConvertToActiveReservation(reservation paymentvault.IPaymentVaultReservation) (*core.ActiveReservation, error) {
func ConvertToReservedPayment(reservation paymentvault.IPaymentVaultReservation) (*core.ReservedPayment, error) {
if isZeroValuedReservation(reservation) {
return nil, fmt.Errorf("reservation is not a valid active reservation")
}

return &core.ActiveReservation{
return &core.ReservedPayment{
SymbolsPerSecond: reservation.SymbolsPerSecond,
StartTimestamp: reservation.StartTimestamp,
EndTimestamp: reservation.EndTimestamp,
Expand Down
Loading
Loading