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

[Malleability] Event #7068

Open
wants to merge 6 commits into
base: yurii/malleability-check-test
Choose a base branch
from
Open
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
8 changes: 0 additions & 8 deletions engine/access/rpc/backend/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1993,14 +1993,6 @@ func (suite *Suite) setupConnectionFactory() connection.ConnectionFactory {
return connFactory
}

func getEvents(n int) []flow.Event {
events := make([]flow.Event, n)
for i := range events {
events[i] = flow.Event{Type: flow.EventAccountCreated}
}
return events
}

func generateEncodedEvents(t *testing.T, n int) ([]flow.Event, []flow.Event) {
ccfEvents := generator.GetEventsWithEncoding(n, entities.EventEncodingVersion_CCF_V0)
jsonEvents := make([]flow.Event, n)
Expand Down
52 changes: 5 additions & 47 deletions model/flow/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"time"

"github.com/onflow/flow-go/model/encoding/json"
"github.com/onflow/flow-go/model/fingerprint"
"github.com/onflow/flow-go/storage/merkle"
)
Expand Down Expand Up @@ -39,44 +38,13 @@ func (e Event) String() string {

// ID returns a canonical identifier that is guaranteed to be unique.
func (e Event) ID() Identifier {
return MakeID(wrapEventID(e))
return MakeID(e)
}

func (e Event) Checksum() Identifier {
return MakeID(e)
}

// Encode returns the canonical encoding of this event, containing only the fields necessary to uniquely identify it.
func (e Event) Encode() []byte {
w := wrapEventID(e)
return json.NewMarshaler().MustMarshal(w)
}

func (e Event) Fingerprint() []byte {
return fingerprint.Fingerprint(wrapEvent(e))
}

// Defines only the fields needed to uniquely identify an event.
type eventIDWrapper struct {
TxID []byte
Index uint32
}

type eventWrapper struct {
TxID []byte
Index uint32
Type string
TransactionIndex uint32
Payload []byte
}

func wrapEventID(e Event) eventIDWrapper {
return eventIDWrapper{
TxID: e.TransactionID[:],
Index: e.EventIndex,
}
}

// byteSize returns the number of bytes needed to store the wrapped version of the event.
// returned int is an approximate measure, ignoring the number of bytes needed as headers.
func (e Event) byteSize() int {
Expand All @@ -87,16 +55,6 @@ func (e Event) byteSize() int {
len(e.Payload) // Payload
}

func wrapEvent(e Event) eventWrapper {
return eventWrapper{
TxID: e.TransactionID[:],
Index: e.EventIndex,
Type: string(e.Type),
TransactionIndex: e.TransactionIndex,
Payload: e.Payload[:],
}
}

// BlockEvents contains events emitted in a single block.
type BlockEvents struct {
BlockID Identifier
Expand All @@ -107,7 +65,7 @@ type BlockEvents struct {

type EventsList []Event

// byteSize returns an approximate number of bytes needed to store the wrapped version of the event.
// ByteSize returns an approximate number of bytes needed to store the wrapped version of the event.
func (el EventsList) ByteSize() int {
size := 0
for _, event := range el {
Expand All @@ -125,10 +83,10 @@ func EventsMerkleRootHash(el EventsList) (Identifier, error) {
}

for _, event := range el {
// event fingerprint is the rlp encoding of the wrapperevent
// event fingerprint is the rlp encoding of the event
// eventID is the standard sha3 hash of the event fingerprint
fingerPrint := event.Fingerprint()
// computing enityID from the fingerprint
fingerPrint := fingerprint.Fingerprint(event)
// computing entityID from the fingerprint (this is equivalent to `event.ID()`)
eventID := MakeIDFromFingerPrint(fingerPrint)
_, err = tree.Put(eventID[:], fingerPrint)
if err != nil {
Expand Down
48 changes: 11 additions & 37 deletions model/flow/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,25 @@ import (
"github.com/onflow/flow-go/utils/unittest"
)

type eventWrapper struct {
TxID []byte
Index uint32
Type string
TransactionIndex uint32
Payload []byte
}

func wrapEvent(e flow.Event) eventWrapper {
return eventWrapper{
TxID: e.TransactionID[:],
Index: e.EventIndex,
Type: string(e.Type),
TransactionIndex: e.TransactionIndex,
Payload: e.Payload,
}
}

// TestEventFingerprint verifies that the Fingerprint function produces
// a consistent RLP-encoded representation of an Event. It ensures that
// decoding the fingerprint results in a correctly ordered structure.
func TestEventFingerprint(t *testing.T) {
evt := unittest.EventFixture(flow.EventAccountCreated, 13, 12, unittest.IdentifierFixture(), 32)

data := fingerprint.Fingerprint(evt)
var decoded eventWrapper
var decoded flow.Event
rlp.NewMarshaler().MustUnmarshal(data, &decoded)
assert.Equal(t, wrapEvent(evt), decoded)
assert.Equal(t, evt, decoded)
}

func TestEventID(t *testing.T) {

// EventID was historically calculated from just TxID and eventIndex which are enough to uniquely identify it in a system
// This test ensures we don't break this promise while introducing proper fingerprinting (which accounts for all the fields)

// TestEventMalleability checks that Event is not malleable: any change in its data
// should result in a different ID.
func TestEventMalleability(t *testing.T) {
txID := unittest.IdentifierFixture()
evtA := unittest.EventFixture(flow.EventAccountUpdated, 21, 37, txID, 2)
evtB := unittest.EventFixture(flow.EventAccountCreated, 0, 37, txID, 22)

evtC := unittest.EventFixture(evtA.Type, evtA.TransactionIndex, evtA.EventIndex+1, txID, 2)
evtC.Payload = evtA.Payload

a := evtA.ID()
b := evtB.ID()
c := evtC.ID()
event := unittest.EventFixture(flow.EventAccountUpdated, 21, 37, txID, 2)

assert.Equal(t, a, b)
assert.NotEqual(t, a, c)
unittest.RequireEntityNonMalleable(t, &event)
}

func TestEventsList(t *testing.T) {
Expand Down Expand Up @@ -113,7 +87,7 @@ func TestEventsMerkleRootHash(t *testing.T) {
TransactionID: [flow.IdentifierLen]byte{1, 2, 3},
}

expectedRootHashHex := "355446d7b2b9653403abe28ccc405f46c059d2059cb7863f4964c401ee1aa83b"
expectedRootHashHex := "c53a6592de573a24547b616172abd9131651d6b7d829e5694a25fa183db7ae01"

ABHash, err := flow.EventsMerkleRootHash([]flow.Event{eventA, eventB})
assert.NoError(t, err)
Expand Down
3 changes: 2 additions & 1 deletion module/chunks/chunkVerifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/onflow/flow-go/ledger"
"github.com/onflow/flow-go/ledger/partial"
chmodels "github.com/onflow/flow-go/model/chunks"
"github.com/onflow/flow-go/model/fingerprint"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/model/verification"
"github.com/onflow/flow-go/module/executiondatasync/execution_data"
Expand Down Expand Up @@ -263,7 +264,7 @@ func (fcv *ChunkVerifier) verifyTransactionsInContext(
for i, event := range events {
fcv.logger.Warn().Int("list_index", i).
Str("event_id", event.ID().String()).
Hex("event_fingerptint", event.Fingerprint()).
Hex("event_fingerprint", fingerprint.Fingerprint(event)).
Str("event_type", string(event.Type)).
Str("event_tx_id", event.TransactionID.String()).
Uint32("event_tx_index", event.TransactionIndex).
Expand Down
2 changes: 1 addition & 1 deletion utils/unittest/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package unittest

import (
"fmt"
"github.com/onflow/crypto"
"math/rand"
"reflect"
"testing"

"github.com/onflow/crypto"
"github.com/stretchr/testify/require"

"github.com/onflow/flow-go/model/flow"
Expand Down