Skip to content
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
1 change: 1 addition & 0 deletions rpc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ func (h *Handler) MethodsV0_10() ([]jsonrpc.Method, string) {
Params: []jsonrpc.Parameter{
{Name: "finality_status", Optional: true},
{Name: "sender_address", Optional: true},
{Name: "tags", Optional: true},
},
Handler: h.rpcv10Handler.SubscribeNewTransactions,
},
Expand Down
199 changes: 199 additions & 0 deletions rpc/v10/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
rpcv10 "github.com/NethermindEth/juno/rpc/v10"
rpcv6 "github.com/NethermindEth/juno/rpc/v6"
rpcv9 "github.com/NethermindEth/juno/rpc/v9"
adaptfeeder "github.com/NethermindEth/juno/starknetdata/feeder"
"github.com/NethermindEth/juno/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -890,3 +891,201 @@ func nilToOne(f *felt.Felt) *felt.Felt {
}
return f
}

func TestBlockWithTxsWithResponseFlags(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)

network := &utils.Sepolia
client := feeder.NewTestClient(t, network)
gw := adaptfeeder.New(client)

block, err := gw.BlockByNumber(t.Context(), 4072139)
require.NoError(t, err)
require.NotNil(t, block)
require.Greater(t, len(block.Transactions), 0)

// Count invoke v3 transactions with proof_facts and total invoke v3 transactions
var invokeV3WithProofFactsCount int
var invokeV3Count int
for _, tx := range block.Transactions {
if invokeTx, ok := tx.(*core.InvokeTransaction); ok {
if invokeTx.Version != nil && invokeTx.Version.Is(3) {
invokeV3Count++
if invokeTx.ProofFacts != nil {
invokeV3WithProofFactsCount++
}
}
}
}
require.Greater(
t,
invokeV3WithProofFactsCount,
0,
"Block should contain at least one invoke v3 transaction with proof_facts",
)

mockReader := mocks.NewMockReader(mockCtrl)
mockSyncReader := mocks.NewMockSyncReader(mockCtrl)

blockID := rpcv9.BlockIDFromNumber(block.Header.Number)
mockReader.EXPECT().BlockByNumber(block.Header.Number).Return(block, nil).AnyTimes()
mockReader.EXPECT().Network().Return(network).AnyTimes()
mockReader.EXPECT().L1Head().Return(core.L1Head{}, nil).AnyTimes()

mockReader.EXPECT().BlockCommitmentsByNumber(block.Header.Number).Return(&core.BlockCommitments{
TransactionCommitment: &felt.Zero,
EventCommitment: &felt.Zero,
ReceiptCommitment: &felt.Zero,
StateDiffCommitment: &felt.Zero,
}, nil).AnyTimes()
mockReader.EXPECT().StateUpdateByNumber(block.Header.Number).Return(&core.StateUpdate{
StateDiff: &core.StateDiff{},
}, nil).AnyTimes()

handler := rpcv10.New(mockReader, mockSyncReader, nil, utils.NewNopZapLogger())

t.Run("WithResponseFlag", func(t *testing.T) {
responseFlags := rpcv10.ResponseFlags{IncludeProofFacts: true}
blockWithTxs, rpcErr := handler.BlockWithTxs(&blockID, responseFlags)
require.Nil(t, rpcErr)
require.NotNil(t, blockWithTxs)

// Verify total number of transactions is the same
require.Equal(t, len(block.Transactions), len(blockWithTxs.Transactions))

// Count transactions with proof_facts in response
var txsWithProofFactsCount int
for _, tx := range blockWithTxs.Transactions {
if tx.ProofFacts != nil {
txsWithProofFactsCount++
}
}

// Verify number of transactions with proof_facts matches expected
require.Equal(
t,
invokeV3WithProofFactsCount,
txsWithProofFactsCount,
"Number of transactions with proof_facts should match",
)
})

t.Run("WithoutResponseFlag", func(t *testing.T) {
blockWithTxs, rpcErr := handler.BlockWithTxs(&blockID, rpcv10.ResponseFlags{})
require.Nil(t, rpcErr)
require.NotNil(t, blockWithTxs)

// Verify total number of transactions is the same
require.Equal(t, len(block.Transactions), len(blockWithTxs.Transactions))

// Verify no transactions have proof_facts when flag is not set
for _, tx := range blockWithTxs.Transactions {
require.Nil(t, tx.ProofFacts, "proof_facts should not be included when flag is not set")
}
})
}

func TestBlockWithReceiptsWithResponseFlags(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)

network := &utils.Sepolia
client := feeder.NewTestClient(t, network)
gw := adaptfeeder.New(client)

block, err := gw.BlockByNumber(t.Context(), 4072139)
require.NoError(t, err)
require.NotNil(t, block)
require.Greater(t, len(block.Transactions), 0)
require.Equal(
t,
len(block.Transactions),
len(block.Receipts),
"Block should have receipts for all transactions",
)

// Count invoke v3 transactions with proof_facts
var invokeV3WithProofFactsCount int
for _, tx := range block.Transactions {
if invokeTx, ok := tx.(*core.InvokeTransaction); ok {
if invokeTx.Version != nil && invokeTx.Version.Is(3) && invokeTx.ProofFacts != nil {
invokeV3WithProofFactsCount++
}
}
}
require.Greater(
t,
invokeV3WithProofFactsCount, 0,
"Block should contain at least one invoke v3 transaction with proof_facts",
)

// Count all transactions
totalTxCount := len(block.Transactions)

mockReader := mocks.NewMockReader(mockCtrl)
mockSyncReader := mocks.NewMockSyncReader(mockCtrl)

blockID := rpcv9.BlockIDFromNumber(block.Header.Number)
mockReader.EXPECT().BlockByNumber(block.Header.Number).Return(block, nil).AnyTimes()
mockReader.EXPECT().Network().Return(network).AnyTimes()
mockReader.EXPECT().L1Head().Return(core.L1Head{}, nil).AnyTimes()

mockReader.EXPECT().BlockCommitmentsByNumber(block.Header.Number).Return(&core.BlockCommitments{
TransactionCommitment: &felt.Zero,
EventCommitment: &felt.Zero,
ReceiptCommitment: &felt.Zero,
StateDiffCommitment: &felt.Zero,
}, nil).AnyTimes()
mockReader.EXPECT().StateUpdateByNumber(block.Header.Number).Return(&core.StateUpdate{
StateDiff: &core.StateDiff{},
}, nil).AnyTimes()

handler := rpcv10.New(mockReader, mockSyncReader, nil, utils.NewNopZapLogger())

t.Run("WithResponseFlag", func(t *testing.T) {
responseFlags := rpcv10.ResponseFlags{IncludeProofFacts: true}
blockWithReceipts, rpcErr := handler.BlockWithReceipts(&blockID, responseFlags)
require.Nil(t, rpcErr)
require.NotNil(t, blockWithReceipts)

// Verify total number of transactions is the same
require.Equal(t, totalTxCount, len(blockWithReceipts.Transactions))

// Count transactions with proof_facts in response
var txsWithProofFactsCount int
for _, txWithReceipt := range blockWithReceipts.Transactions {
if txWithReceipt.Transaction.ProofFacts != nil {
txsWithProofFactsCount++
}
}

// Verify number of transactions with proof_facts matches expected
require.Equal(
t,
invokeV3WithProofFactsCount,
txsWithProofFactsCount,
"Number of transactions with proof_facts should match",
)
})

t.Run("WithoutResponseFlag", func(t *testing.T) {
t.Run("WithoutResponseFlag", func(t *testing.T) {
blockWithReceipts, rpcErr := handler.BlockWithReceipts(&blockID, rpcv10.ResponseFlags{})
require.Nil(t, rpcErr)
require.NotNil(t, blockWithReceipts)

// Verify total number of transactions is the same
require.Equal(t, len(block.Transactions), len(blockWithReceipts.Transactions))

// Verify no transactions have proof_facts when flag is not set
for _, tx := range blockWithReceipts.Transactions {
require.Nil(
t,
tx.Transaction.ProofFacts,
"proof_facts should not be included when flag is not set",
)
}
})
})
}
24 changes: 24 additions & 0 deletions rpc/v10/response_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,27 @@ func (r *ResponseFlags) UnmarshalJSON(data []byte) error {

return nil
}

type SubscriptionTags struct {
IncludeProofFacts bool
}

func (r *SubscriptionTags) UnmarshalJSON(data []byte) error {
var flags []string
if err := json.Unmarshal(data, &flags); err != nil {
return err
}

*r = SubscriptionTags{}

for _, flag := range flags {
switch flag {
case "INCLUDE_PROOF_FACTS":
r.IncludeProofFacts = true
default:
return fmt.Errorf("unknown flag: %s", flag)
}
}

return nil
}
54 changes: 54 additions & 0 deletions rpc/v10/response_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,57 @@ func TestResponseFlags_UnmarshalJSON(t *testing.T) {
})
}
}

func TestSubscriptionTags_UnmarshalJSON(t *testing.T) {
t.Parallel()

tests := []struct {
name string
json string
expected rpcv10.SubscriptionTags
expectedError string
}{
{
name: "empty array",
json: `[]`,
expected: rpcv10.SubscriptionTags{IncludeProofFacts: false},
},
{
name: "array with INCLUDE_PROOF_FACTS",
json: `["INCLUDE_PROOF_FACTS"]`,
expected: rpcv10.SubscriptionTags{IncludeProofFacts: true},
},
{
name: "array with unknown flag and valid flag",
json: `["INCLUDE_PROOF_FACTS", "UNKNOWN_FLAG"]`,
expectedError: "unknown flag: UNKNOWN_FLAG",
},
{
name: "case sensitive",
json: `["include_proof_facts"]`,
expectedError: "unknown flag: include_proof_facts",
},
{
name: "invalid JSON",
json: `{"not": "an array"}`,
expectedError: "cannot unmarshal",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var tags rpcv10.SubscriptionTags
err := json.Unmarshal([]byte(tt.json), &tags)

if tt.expectedError != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.expectedError)
return
}

require.NoError(t, err)
require.Equal(t, tt.expected, tags)
})
}
}
Loading
Loading