diff --git a/api/clients/accountant.go b/api/clients/accountant.go index d07d8d436..619a1f578 100644 --- a/api/clients/accountant.go +++ b/api/clients/accountant.go @@ -13,6 +13,8 @@ import ( "github.com/Layr-Labs/eigenda/core/meterer" ) +var minNumBins uint32 = 3 + type IAccountant interface { AccountBlob(ctx context.Context, data []byte, quorums []uint8) (uint32, uint64, error) } @@ -32,6 +34,7 @@ type accountant struct { cumulativePayment *big.Int paymentSigner core.PaymentSigner + numBins uint32 } type BinRecord struct { @@ -39,7 +42,7 @@ type BinRecord struct { Usage uint64 } -func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, paymentSigner core.PaymentSigner) *accountant { +func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, paymentSigner core.PaymentSigner, 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 @@ -52,6 +55,7 @@ func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandP binRecords: []BinRecord{{Index: 0, Usage: 0}, {Index: 1, Usage: 0}, {Index: 2, Usage: 0}}, cumulativePayment: big.NewInt(0), paymentSigner: paymentSigner, + numBins: max(numBins, minNumBins), } // TODO: add a routine to refresh the on-chain state occasionally? return &a @@ -119,7 +123,7 @@ func (a *accountant) AccountBlob(ctx context.Context, numSymbols uint64, quorums } protoPaymentHeader := pm.ConvertToProtoPaymentHeader() - signature, err := a.paymentSigner.SignBlobPayment(protoPaymentHeader) + signature, err := a.paymentSigner.SignBlobPayment(pm) if err != nil { return nil, nil, err } @@ -144,7 +148,7 @@ func (a *accountant) SymbolsCharged(numSymbols uint) uint32 { } func (a *accountant) GetRelativeBinRecord(index uint32) *BinRecord { - relativeIndex := index % 3 + relativeIndex := index % a.numBins if a.binRecords[relativeIndex].Index != uint32(index) { a.binRecords[relativeIndex] = BinRecord{ Index: uint32(index), diff --git a/api/clients/accountant_test.go b/api/clients/accountant_test.go index 0e2a26ec2..2f74ce0d2 100644 --- a/api/clients/accountant_test.go +++ b/api/clients/accountant_test.go @@ -16,6 +16,8 @@ import ( "github.com/stretchr/testify/assert" ) +const numBins = uint32(3) + func TestNewAccountant(t *testing.T) { reservation := core.ActiveReservation{ SymbolsPerSec: 100, @@ -34,7 +36,7 @@ func TestNewAccountant(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) assert.NotNil(t, accountant) assert.Equal(t, reservation, accountant.reservation) @@ -64,7 +66,7 @@ func TestAccountBlob_Reservation(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() symbolLength := uint64(500) @@ -115,7 +117,7 @@ func TestAccountBlob_OnDemand(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() numSymbols := uint64(1500) @@ -144,7 +146,7 @@ func TestAccountBlob_InsufficientOnDemand(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() numSymbols := uint64(2000) @@ -172,7 +174,7 @@ func TestAccountBlobCallSeries(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() quorums := []uint8{0, 1} @@ -222,7 +224,7 @@ func TestAccountBlob_BinRotation(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() quorums := []uint8{0, 1} @@ -265,7 +267,7 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() quorums := []uint8{0, 1} @@ -311,7 +313,7 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() quorums := []uint8{0, 1} now := time.Now().Unix() @@ -358,7 +360,7 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) paymentSigner := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner) + accountant := NewAccountant(&reservation, &onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) ctx := context.Background() quorums := []uint8{0, 1} diff --git a/api/clients/config.go b/api/clients/config.go index ffcbe7b9b..00b5e2c03 100644 --- a/api/clients/config.go +++ b/api/clients/config.go @@ -55,6 +55,9 @@ type EigenDAClientConfig struct { // if set to "", will result in a non-paying client and cannot disperse paid blobs. PaymentSignerPrivateKeyHex string + // Payment number of bins indicate how many bins are kept at all times; the minimum is set to 3. + PaymentNumBins uint32 + // Whether to disable TLS for an insecure connection when connecting to a local EigenDA disperser instance. DisableTLS bool diff --git a/api/clients/eigenda_client.go b/api/clients/eigenda_client.go index 1d0226a68..d068427a5 100644 --- a/api/clients/eigenda_client.go +++ b/api/clients/eigenda_client.go @@ -117,7 +117,7 @@ func NewEigenDAClient(log log.Logger, config EigenDAClientConfig) (*EigenDAClien } // a subsequent PR contains updates to fill in payment state - accountant := NewAccountant(&core.ActiveReservation{}, &core.OnDemandPayment{}, 0, 0, 0, paymentSigner) + accountant := NewAccountant(&core.ActiveReservation{}, &core.OnDemandPayment{}, 0, 0, 0, paymentSigner, config.PaymentNumBins) disperserClient := NewDisperserClient(disperserConfig, signer, accountant) diff --git a/core/auth.go b/core/auth.go index 349f5b1af..964d3fdcd 100644 --- a/core/auth.go +++ b/core/auth.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" - commonpb "github.com/Layr-Labs/eigenda/api/grpc/common" geth "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -51,6 +50,7 @@ func VerifySignature(message []byte, accountAddr geth.Address, sig []byte) error } type PaymentSigner interface { - SignBlobPayment(header *commonpb.PaymentHeader) ([]byte, error) + // SignBlobPayment(header *commonpb.PaymentHeader) ([]byte, error) + SignBlobPayment(header *PaymentMetadata) ([]byte, error) GetAccountID() string } diff --git a/core/auth/payment_signer.go b/core/auth/payment_signer.go index 0d873e75f..7568e2cc3 100644 --- a/core/auth/payment_signer.go +++ b/core/auth/payment_signer.go @@ -5,7 +5,6 @@ import ( "fmt" "log" - commonpb "github.com/Layr-Labs/eigenda/api/grpc/common" "github.com/Layr-Labs/eigenda/core" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -31,9 +30,10 @@ func NewPaymentSigner(privateKeyHex string) *PaymentSigner { } // SignBlobPayment signs the payment header and returns the signature -func (s *PaymentSigner) SignBlobPayment(header *commonpb.PaymentHeader) ([]byte, error) { - header.AccountId = s.GetAccountID() - pm := core.ConvertPaymentHeader(header) +// func (s *PaymentSigner) SignBlobPayment(header *commonpb.PaymentHeader) ([]byte, error) { +func (s *PaymentSigner) SignBlobPayment(pm *core.PaymentMetadata) ([]byte, error) { + // header.AccountId = s.GetAccountID() + // pm := core.ConvertPaymentHeader(header) hash, err := pm.Hash() if err != nil { return nil, fmt.Errorf("failed to hash payment header: %v", err) @@ -53,7 +53,7 @@ func NewNoopPaymentSigner() *NoopPaymentSigner { return &NoopPaymentSigner{} } -func (s *NoopPaymentSigner) SignBlobPayment(header *commonpb.PaymentHeader) ([]byte, error) { +func (s *NoopPaymentSigner) SignBlobPayment(header *core.PaymentMetadata) ([]byte, error) { return nil, fmt.Errorf("noop signer cannot sign blob payment header") } @@ -62,9 +62,8 @@ func (s *NoopPaymentSigner) GetAccountID() string { } // VerifyPaymentSignature verifies the signature against the payment metadata -func VerifyPaymentSignature(paymentHeader *commonpb.PaymentHeader, paymentSignature []byte) bool { - pm := core.ConvertPaymentHeader(paymentHeader) - hash, err := pm.Hash() +func VerifyPaymentSignature(paymentHeader *core.PaymentMetadata, paymentSignature []byte) bool { + hash, err := paymentHeader.Hash() if err != nil { return false } @@ -76,7 +75,7 @@ func VerifyPaymentSignature(paymentHeader *commonpb.PaymentHeader, paymentSignat } recoveredAddress := crypto.PubkeyToAddress(*recoveredPubKey) - accountId := common.HexToAddress(paymentHeader.AccountId) + accountId := common.HexToAddress(paymentHeader.AccountID) if recoveredAddress != accountId { log.Printf("Signature address %s does not match account id %s\n", recoveredAddress.Hex(), accountId.Hex()) return false diff --git a/core/auth/payment_signer_test.go b/core/auth/payment_signer_test.go index 4ba7c872a..1db00ed06 100644 --- a/core/auth/payment_signer_test.go +++ b/core/auth/payment_signer_test.go @@ -2,9 +2,10 @@ package auth_test import ( "encoding/hex" + "math/big" "testing" - commonpb "github.com/Layr-Labs/eigenda/api/grpc/common" + "github.com/Layr-Labs/eigenda/core" "github.com/Layr-Labs/eigenda/core/auth" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" @@ -19,10 +20,10 @@ func TestPaymentSigner(t *testing.T) { signer := auth.NewPaymentSigner(privateKeyHex) t.Run("SignBlobPayment", func(t *testing.T) { - header := &commonpb.PaymentHeader{ + header := &core.PaymentMetadata{ + AccountID: "", BinIndex: 1, - CumulativePayment: []byte{0x01, 0x02, 0x03}, - AccountId: "", + CumulativePayment: big.NewInt(1), } signature, err := signer.SignBlobPayment(header) @@ -35,10 +36,10 @@ func TestPaymentSigner(t *testing.T) { }) t.Run("VerifyPaymentSignature_InvalidSignature", func(t *testing.T) { - header := &commonpb.PaymentHeader{ + header := &core.PaymentMetadata{ BinIndex: 1, - CumulativePayment: []byte{0x01, 0x02, 0x03}, - AccountId: "", + CumulativePayment: big.NewInt(1), + AccountID: "", } // Create an invalid signature @@ -48,10 +49,10 @@ func TestPaymentSigner(t *testing.T) { }) t.Run("VerifyPaymentSignature_ModifiedHeader", func(t *testing.T) { - header := &commonpb.PaymentHeader{ + header := &core.PaymentMetadata{ BinIndex: 1, - CumulativePayment: []byte{0x01, 0x02, 0x03}, - AccountId: "", + CumulativePayment: big.NewInt(1), + AccountID: "", } signature, err := signer.SignBlobPayment(header) diff --git a/core/data.go b/core/data.go index 970d76746..e4e990e62 100644 --- a/core/data.go +++ b/core/data.go @@ -572,7 +572,7 @@ func ConvertPaymentHeader(header *commonpb.PaymentHeader) *PaymentMetadata { } } -// Hash returns the Keccak256 hash of the PaymentMetadata +// ConvertToProtoPaymentHeader converts a PaymentMetadata to a protobuf payment header func (pm *PaymentMetadata) ConvertToProtoPaymentHeader() *commonpb.PaymentHeader { return &commonpb.PaymentHeader{ AccountId: pm.AccountID, @@ -581,6 +581,15 @@ func (pm *PaymentMetadata) ConvertToProtoPaymentHeader() *commonpb.PaymentHeader } } +// ConvertToProtoPaymentHeader converts a PaymentMetadata to a protobuf payment header +func ConvertToPaymentMetadata(ph *commonpb.PaymentHeader) *PaymentMetadata { + return &PaymentMetadata{ + AccountID: ph.AccountId, + BinIndex: ph.BinIndex, + CumulativePayment: new(big.Int).SetBytes(ph.CumulativePayment), + } +} + // OperatorInfo contains information about an operator which is stored on the blockchain state, // corresponding to a particular quorum type ActiveReservation struct { diff --git a/disperser/apiserver/payment_test.go b/disperser/apiserver/payment_test.go index 23ac774ed..c04938089 100644 --- a/disperser/apiserver/payment_test.go +++ b/disperser/apiserver/payment_test.go @@ -14,7 +14,6 @@ import ( "github.com/Layr-Labs/eigenda/encoding" "github.com/Layr-Labs/eigenda/encoding/utils/codec" - pbcommon "github.com/Layr-Labs/eigenda/api/grpc/common" pb "github.com/Layr-Labs/eigenda/api/grpc/disperser" "github.com/Layr-Labs/eigenda/core" "github.com/stretchr/testify/assert" @@ -60,17 +59,17 @@ func TestDispersePaidBlob(t *testing.T) { symbolLength := encoding.GetBlobLength(uint(len(data))) // disperse on-demand payment for i := 1; i < 3; i++ { - pm := pbcommon.PaymentHeader{ - AccountId: signer.GetAccountID(), + pm := &core.PaymentMetadata{ + AccountID: signer.GetAccountID(), BinIndex: 0, - CumulativePayment: big.NewInt(int64(int(symbolLength) * i * encoding.BYTES_PER_SYMBOL)).Bytes(), + CumulativePayment: big.NewInt(int64(int(symbolLength) * i * encoding.BYTES_PER_SYMBOL)), } - sig, err := signer.SignBlobPayment(&pm) + sig, err := signer.SignBlobPayment(pm) assert.NoError(t, err) reply, err := dispersalServer.DispersePaidBlob(ctx, &pb.DispersePaidBlobRequest{ Data: data, QuorumNumbers: quorums, - PaymentHeader: &pm, + PaymentHeader: pm.ConvertToProtoPaymentHeader(), PaymentSignature: sig, }) assert.NoError(t, err) @@ -79,17 +78,17 @@ func TestDispersePaidBlob(t *testing.T) { } // exceeded payment limit - pm := pbcommon.PaymentHeader{ - AccountId: signer.GetAccountID(), + pm := &core.PaymentMetadata{ + AccountID: signer.GetAccountID(), BinIndex: 0, - CumulativePayment: big.NewInt(int64(symbolLength*3)*encoding.BYTES_PER_SYMBOL - 1).Bytes(), + CumulativePayment: big.NewInt(int64(symbolLength*3)*encoding.BYTES_PER_SYMBOL - 1), } - sig, err := signer.SignBlobPayment(&pm) + sig, err := signer.SignBlobPayment(pm) assert.NoError(t, err) _, err = dispersalServer.DispersePaidBlob(ctx, &pb.DispersePaidBlobRequest{ Data: data, QuorumNumbers: quorums, - PaymentHeader: &pm, + PaymentHeader: pm.ConvertToProtoPaymentHeader(), PaymentSignature: sig, }) assert.Error(t, err) @@ -99,17 +98,17 @@ func TestDispersePaidBlob(t *testing.T) { // TODO: somehow meterer is not defined as a method or field in dispersalServer; reservationWindow we set was 1 for i := 0; i < 2; i++ { binIndex := meterer.GetBinIndex(uint64(time.Now().Unix()), 1) - pm = pbcommon.PaymentHeader{ - AccountId: signer.GetAccountID(), + pm := &core.PaymentMetadata{ + AccountID: signer.GetAccountID(), BinIndex: binIndex, - CumulativePayment: big.NewInt(0).Bytes(), + CumulativePayment: big.NewInt(0), } - sig, err = signer.SignBlobPayment(&pm) + sig, err = signer.SignBlobPayment(pm) assert.NoError(t, err) reply, err := dispersalServer.DispersePaidBlob(ctx, &pb.DispersePaidBlobRequest{ Data: data, QuorumNumbers: []uint32{1}, - PaymentHeader: &pm, + PaymentHeader: pm.ConvertToProtoPaymentHeader(), PaymentSignature: sig, }) assert.NoError(t, err) @@ -118,33 +117,33 @@ func TestDispersePaidBlob(t *testing.T) { } binIndex := meterer.GetBinIndex(uint64(time.Now().Unix()), 1) - pm = pbcommon.PaymentHeader{ - AccountId: signer.GetAccountID(), + pm = &core.PaymentMetadata{ + AccountID: signer.GetAccountID(), BinIndex: binIndex, - CumulativePayment: big.NewInt(0).Bytes(), + CumulativePayment: big.NewInt(0), } - sig, err = signer.SignBlobPayment(&pm) + sig, err = signer.SignBlobPayment(pm) assert.NoError(t, err) _, err = dispersalServer.DispersePaidBlob(ctx, &pb.DispersePaidBlobRequest{ Data: data, QuorumNumbers: []uint32{1}, - PaymentHeader: &pm, + PaymentHeader: pm.ConvertToProtoPaymentHeader(), PaymentSignature: sig, }) assert.Contains(t, err.Error(), "bin has already been filled") // invalid bin index binIndex = meterer.GetBinIndex(uint64(time.Now().Unix())/2, 1) - pm = pbcommon.PaymentHeader{ - AccountId: signer.GetAccountID(), + pm = &core.PaymentMetadata{ + AccountID: signer.GetAccountID(), BinIndex: binIndex, - CumulativePayment: big.NewInt(0).Bytes(), + CumulativePayment: big.NewInt(0), } - sig, err = signer.SignBlobPayment(&pm) + sig, err = signer.SignBlobPayment(pm) _, err = dispersalServer.DispersePaidBlob(ctx, &pb.DispersePaidBlobRequest{ Data: data, QuorumNumbers: []uint32{1}, - PaymentHeader: &pm, + PaymentHeader: pm.ConvertToProtoPaymentHeader(), PaymentSignature: sig, }) assert.Contains(t, err.Error(), "invalid bin index for reservation") diff --git a/disperser/apiserver/server.go b/disperser/apiserver/server.go index 0aa240d53..0e1db6156 100644 --- a/disperser/apiserver/server.go +++ b/disperser/apiserver/server.go @@ -330,7 +330,7 @@ func (s *DispersalServer) DispersePaidBlob(ctx context.Context, req *pb.Disperse cumulativePayment := new(big.Int).SetBytes(req.PaymentHeader.CumulativePayment) //todo: before disperse blob, validate the signature signature := req.PaymentSignature - if !auth.VerifyPaymentSignature(req.GetPaymentHeader(), signature) { + if !auth.VerifyPaymentSignature(core.ConvertToPaymentMetadata(req.GetPaymentHeader()), signature) { return nil, api.NewErrorInvalidArg("payment signature is invalid") } if err != nil { @@ -1116,7 +1116,7 @@ func (s *DispersalServer) validatePaidRequestAndGetBlob(ctx context.Context, req seenQuorums := make(map[uint8]struct{}) // TODO: validate payment signature against payment metadata - if !auth.VerifyPaymentSignature(req.GetPaymentHeader(), req.GetPaymentSignature()) { + if !auth.VerifyPaymentSignature(core.ConvertToPaymentMetadata(req.GetPaymentHeader()), req.GetPaymentSignature()) { return nil, fmt.Errorf("payment signature is invalid") } // Unlike regular blob dispersal request validation, there's no check with required quorums diff --git a/inabox/tests/integration_test.go b/inabox/tests/integration_test.go index 1f311ea39..cd4404ad8 100644 --- a/inabox/tests/integration_test.go +++ b/inabox/tests/integration_test.go @@ -45,7 +45,7 @@ var _ = Describe("Inabox Integration", func() { Hostname: "localhost", Port: "32003", Timeout: 10 * time.Second, - }, signer, clients.NewAccountant(&core.ActiveReservation{}, &core.OnDemandPayment{}, 60, 128, 128, paymentSigner)) + }, signer, clients.NewAccountant(&core.ActiveReservation{}, &core.OnDemandPayment{}, 60, 128, 128, paymentSigner, 3)) Expect(disp).To(Not(BeNil())) diff --git a/inabox/tests/payment_test.go b/inabox/tests/payment_test.go index b70913ebe..b0d245139 100644 --- a/inabox/tests/payment_test.go +++ b/inabox/tests/payment_test.go @@ -43,7 +43,7 @@ var _ = Describe("Inabox Integration", func() { Hostname: "localhost", Port: "32003", Timeout: 10 * time.Second, - }, signer, clients.NewAccountant(&core.ActiveReservation{}, &core.OnDemandPayment{}, 60, 128, 128, paymentSigner)) + }, signer, clients.NewAccountant(&core.ActiveReservation{}, &core.OnDemandPayment{}, 60, 128, 128, paymentSigner, 3)) Expect(disp).To(Not(BeNil()))