Skip to content

Commit

Permalink
Implement P2P Receipt Handler and Adapter (#1352)
Browse files Browse the repository at this point in the history
  • Loading branch information
IronGauntlets authored Oct 30, 2023
1 parent d1be96d commit 7aba064
Show file tree
Hide file tree
Showing 3 changed files with 298 additions and 15 deletions.
96 changes: 96 additions & 0 deletions adapters/core2p2p/receipt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package core2p2p

import (
"github.com/NethermindEth/juno/core"
"github.com/NethermindEth/juno/p2p/starknet/spec"
"github.com/NethermindEth/juno/utils"
)

// Core Transaction receipt does not contain all the information required to create p2p spec Receipt, therefore,
// we have to pass the transaction as well.
func AdaptReceipt(r *core.TransactionReceipt, txn core.Transaction) *spec.Receipt {
if r == nil || txn == nil {
return nil
}
switch t := txn.(type) {
case *core.InvokeTransaction:
return &spec.Receipt{
Receipt: &spec.Receipt_Invoke_{
Invoke: &spec.Receipt_Invoke{
Common: receiptCommon(r),
},
},
}
case *core.L1HandlerTransaction:
return &spec.Receipt{
Receipt: &spec.Receipt_L1Handler_{
L1Handler: &spec.Receipt_L1Handler{
Common: receiptCommon(r),
MsgHash: &spec.Hash{Elements: t.MessageHash()},
},
},
}
case *core.DeclareTransaction:
return &spec.Receipt{
Receipt: &spec.Receipt_Declare_{
Declare: &spec.Receipt_Declare{
Common: receiptCommon(r),
},
},
}
case *core.DeployTransaction:
return &spec.Receipt{
Receipt: &spec.Receipt_DeprecatedDeploy{
DeprecatedDeploy: &spec.Receipt_Deploy{
Common: receiptCommon(r),
ContractAddress: AdaptFelt(t.ContractAddress),
},
},
}
case *core.DeployAccountTransaction:
return &spec.Receipt{
Receipt: &spec.Receipt_DeployAccount_{
DeployAccount: &spec.Receipt_DeployAccount{
Common: receiptCommon(r),
ContractAddress: AdaptFelt(t.ContractAddress),
},
},
}
default:
return nil
}
}

func receiptCommon(r *core.TransactionReceipt) *spec.Receipt_Common {
return &spec.Receipt_Common{
TransactionHash: AdaptHash(r.TransactionHash),
ActualFee: AdaptFelt(r.Fee),
MessagesSent: utils.Map(r.L2ToL1Message, AdaptMessageToL1),
ExecutionResources: AdaptExecutionResources(r.ExecutionResources),
RevertReason: r.RevertReason,
}
}

func AdaptMessageToL1(mL1 *core.L2ToL1Message) *spec.MessageToL1 {
return &spec.MessageToL1{
FromAddress: AdaptFelt(mL1.From),
Payload: utils.Map(mL1.Payload, AdaptFelt),
ToAddress: &spec.EthereumAddress{Elements: mL1.To.Bytes()},
}
}

func AdaptExecutionResources(er *core.ExecutionResources) *spec.Receipt_ExecutionResources {
return &spec.Receipt_ExecutionResources{
Builtins: &spec.Receipt_ExecutionResources_BuiltinCounter{
Bitwise: uint32(er.BuiltinInstanceCounter.Bitwise),
Ecdsa: uint32(er.BuiltinInstanceCounter.Ecsda),
EcOp: uint32(er.BuiltinInstanceCounter.EcOp),
Pedersen: uint32(er.BuiltinInstanceCounter.Pedersen),
RangeCheck: uint32(er.BuiltinInstanceCounter.RangeCheck),
Poseidon: uint32(er.BuiltinInstanceCounter.Poseidon),
Keccak: uint32(er.BuiltinInstanceCounter.Keccak),
},
Steps: uint32(er.Steps),
MemoryHoles: uint32(er.MemoryHoles),
}
}
39 changes: 29 additions & 10 deletions p2p/starknet/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,18 +205,37 @@ func (h *Handler) onEventsRequest(req *spec.EventsRequest) (Stream[proto.Message
}

func (h *Handler) onReceiptsRequest(req *spec.ReceiptsRequest) (Stream[proto.Message], error) {
// todo: read from bcReader and adapt to p2p type
count := uint64(0)
blockchainIt, err := h.newIterator(req.Iteration)
if err != nil {
return nil, err
}

fin := h.newFin(&spec.ReceiptsResponse{Responses: &spec.ReceiptsResponse_Fin{}})

return func() (proto.Message, bool) {
if count > 3 {
return nil, false
if !blockchainIt.Valid() {
return fin()
}
count++
return &spec.ReceiptsResponse{
Id: &spec.BlockID{
Number: count - 1,
},
}, true

b, err := blockchainIt.Block()
if err != nil {
h.log.Errorw("Failed to fetch block", "block number", b.Number, "err", err)
return fin()
}
blockchainIt.Next()

receipts := make([]*spec.Receipt, len(b.Receipts))
for i := 0; i < len(b.Receipts); i++ {
receipts[i] = core2p2p.AdaptReceipt(b.Receipts[i], b.Transactions[i])
}

rs := &spec.Receipts{Items: receipts}
res := &spec.ReceiptsResponse{
Id: core2p2p.AdaptBlockID(b.Header),
Responses: &spec.ReceiptsResponse_Receipts{Receipts: rs},
}

return res, true
}, nil
}

Expand Down
178 changes: 173 additions & 5 deletions p2p/starknet/starknet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,183 @@ func TestClientHandler(t *testing.T) {
})

t.Run("get receipts", func(t *testing.T) {
res, cErr := client.RequestReceipts(testCtx, &spec.ReceiptsRequest{})
txH := randFelt(t)
// There are common receipt fields shared by all of different transactions.
commonReceipt := &core.TransactionReceipt{
TransactionHash: txH,
Fee: randFelt(t),
L2ToL1Message: []*core.L2ToL1Message{fillFelts(t, &core.L2ToL1Message{}), fillFelts(t, &core.L2ToL1Message{})},
ExecutionResources: &core.ExecutionResources{
BuiltinInstanceCounter: core.BuiltinInstanceCounter{
Pedersen: 1,
RangeCheck: 2,
Bitwise: 3,
Output: 4,
Ecsda: 5,
EcOp: 6,
Keccak: 7,
Poseidon: 8,
},
MemoryHoles: 9,
Steps: 10,
},
RevertReason: "some revert reason",
Events: []*core.Event{fillFelts(t, &core.Event{}), fillFelts(t, &core.Event{})},
L1ToL2Message: fillFelts(t, &core.L1ToL2Message{}),
}

specReceiptCommon := &spec.Receipt_Common{
TransactionHash: core2p2p.AdaptHash(commonReceipt.TransactionHash),
ActualFee: core2p2p.AdaptFelt(commonReceipt.Fee),
MessagesSent: utils.Map(commonReceipt.L2ToL1Message, core2p2p.AdaptMessageToL1),
ExecutionResources: core2p2p.AdaptExecutionResources(commonReceipt.ExecutionResources),
RevertReason: commonReceipt.RevertReason,
}

invokeTx := &core.InvokeTransaction{TransactionHash: txH}
expectedInvoke := &spec.Receipt{
Receipt: &spec.Receipt_Invoke_{
Invoke: &spec.Receipt_Invoke{
Common: specReceiptCommon,
},
},
}

declareTx := &core.DeclareTransaction{TransactionHash: txH}
expectedDeclare := &spec.Receipt{
Receipt: &spec.Receipt_Declare_{
Declare: &spec.Receipt_Declare{
Common: specReceiptCommon,
},
},
}

l1Txn := &core.L1HandlerTransaction{
TransactionHash: txH,
CallData: []*felt.Felt{new(felt.Felt).SetBytes([]byte("calldata 1")), new(felt.Felt).SetBytes([]byte("calldata 2"))},
ContractAddress: new(felt.Felt).SetBytes([]byte("contract address")),
EntryPointSelector: new(felt.Felt).SetBytes([]byte("entry point selector")),
Nonce: new(felt.Felt).SetBytes([]byte("nonce")),
}
expectedL1Handler := &spec.Receipt{
Receipt: &spec.Receipt_L1Handler_{
L1Handler: &spec.Receipt_L1Handler{
Common: specReceiptCommon,
MsgHash: &spec.Hash{Elements: l1Txn.MessageHash()},
},
},
}

deployAccTxn := &core.DeployAccountTransaction{
DeployTransaction: core.DeployTransaction{
TransactionHash: txH,
ContractAddress: new(felt.Felt).SetBytes([]byte("contract address")),
},
}
expectedDeployAccount := &spec.Receipt{
Receipt: &spec.Receipt_DeployAccount_{
DeployAccount: &spec.Receipt_DeployAccount{
Common: specReceiptCommon,
ContractAddress: core2p2p.AdaptFelt(deployAccTxn.ContractAddress),
},
},
}

deployTxn := &core.DeployTransaction{
TransactionHash: txH,
ContractAddress: new(felt.Felt).SetBytes([]byte("contract address")),
}
expectedDeploy := &spec.Receipt{
Receipt: &spec.Receipt_DeprecatedDeploy{
DeprecatedDeploy: &spec.Receipt_Deploy{
Common: specReceiptCommon,
ContractAddress: core2p2p.AdaptFelt(deployTxn.ContractAddress),
},
},
}

tests := []struct {
b *core.Block
expectedRs *spec.Receipts
}{
{
b: &core.Block{
Header: &core.Header{Number: 0, Hash: randFelt(t)},
Transactions: []core.Transaction{invokeTx},
Receipts: []*core.TransactionReceipt{commonReceipt},
},
expectedRs: &spec.Receipts{Items: []*spec.Receipt{expectedInvoke}},
},
{
b: &core.Block{
Header: &core.Header{Number: 1, Hash: randFelt(t)},
Transactions: []core.Transaction{declareTx},
Receipts: []*core.TransactionReceipt{commonReceipt},
},
expectedRs: &spec.Receipts{Items: []*spec.Receipt{expectedDeclare}},
},
{
b: &core.Block{
Header: &core.Header{Number: 2, Hash: randFelt(t)},
Transactions: []core.Transaction{l1Txn},
Receipts: []*core.TransactionReceipt{commonReceipt},
},
expectedRs: &spec.Receipts{Items: []*spec.Receipt{expectedL1Handler}},
},
{
b: &core.Block{
Header: &core.Header{Number: 3, Hash: randFelt(t)},
Transactions: []core.Transaction{deployAccTxn},
Receipts: []*core.TransactionReceipt{commonReceipt},
},
expectedRs: &spec.Receipts{Items: []*spec.Receipt{expectedDeployAccount}},
},
{
b: &core.Block{
Header: &core.Header{Number: 4, Hash: randFelt(t)},
Transactions: []core.Transaction{deployTxn},
Receipts: []*core.TransactionReceipt{commonReceipt},
},
expectedRs: &spec.Receipts{Items: []*spec.Receipt{expectedDeploy}},
},
{
// block with multiple txs receipts
b: &core.Block{
Header: &core.Header{Number: 5, Hash: randFelt(t)},
Transactions: []core.Transaction{invokeTx, declareTx},
Receipts: []*core.TransactionReceipt{commonReceipt, commonReceipt},
},
expectedRs: &spec.Receipts{Items: []*spec.Receipt{expectedInvoke, expectedDeclare}},
},
}

numOfBs := uint64(len(tests))
for _, test := range tests {
mockReader.EXPECT().BlockByNumber(test.b.Number).Return(test.b, nil)
}

res, cErr := client.RequestReceipts(testCtx, &spec.ReceiptsRequest{Iteration: &spec.Iteration{
Start: &spec.Iteration_BlockNumber{BlockNumber: tests[0].b.Number},
Direction: spec.Iteration_Forward,
Limit: numOfBs,
Step: 1,
}})
require.NoError(t, cErr)

count := uint64(0)
for receipt, valid := res(); valid; receipt, valid = res() {
assert.Equal(t, count, receipt.Id.Number)
var count uint64
for receipts, valid := res(); valid; receipts, valid = res() {
if count == numOfBs {
assert.NotNil(t, receipts.GetFin())
continue
}

assert.Equal(t, count, receipts.Id.Number)

expectedRs := tests[count].expectedRs
assert.True(t, proto.Equal(expectedRs, receipts.GetReceipts()))
count++
}
require.Equal(t, uint64(4), count)
require.Equal(t, numOfBs, count)
})

t.Run("get txns", func(t *testing.T) {
Expand Down

0 comments on commit 7aba064

Please sign in to comment.