Skip to content

Commit

Permalink
reservation timestamp check (#990)
Browse files Browse the repository at this point in the history
  • Loading branch information
hopeyen authored Dec 12, 2024
1 parent 06e88b3 commit 7b12ebf
Show file tree
Hide file tree
Showing 23 changed files with 184 additions and 109 deletions.
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

0 comments on commit 7b12ebf

Please sign in to comment.