Skip to content
This repository has been archived by the owner on Dec 2, 2021. It is now read-only.

Commit

Permalink
[FAB-10789] EVM bytecode events are emitted
Browse files Browse the repository at this point in the history
 - the events are emitted as a single Fabric event for the transaction
 - When querying getTransactionReceipt, relevant event information will
 also be returned as a log

Change-Id: I4c7590e0e6ca4d7cbed90cea9ed282c324f5c70c
Signed-off-by: Swetha Repakula <srepaku@us.ibm.com>
Signed-off-by: Morgan Bauer <mbauer@us.ibm.com>
Signed-off-by: A V Lakshmy <cs16b101@smail.iitm.ac.in>
  • Loading branch information
avlakshmy authored and swetharepakula committed Dec 14, 2018
1 parent b5144f7 commit 6bb8493
Show file tree
Hide file tree
Showing 8 changed files with 594 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions event/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package event

import (
"context"
"encoding/json"
"fmt"

"github.com/hyperledger/burrow/execution/evm/events"
"github.com/hyperledger/fabric/core/chaincode/shim"
)

type EventManager struct {
stub shim.ChaincodeStubInterface
EventCache []events.EventDataLog
}

func NewEventManager(stub shim.ChaincodeStubInterface) *EventManager {
return &EventManager{
stub: stub,
EventCache: []events.EventDataLog{},
}
}

func (evmgr *EventManager) Flush(eventName string) error {
if len(evmgr.EventCache) == 0 {
return nil
}
payload, err := json.Marshal(evmgr.EventCache)
if err != nil {
return fmt.Errorf("Failed to marshal event messages: %s", err.Error())
}
return evmgr.stub.SetEvent(eventName, payload)
}

func (evmgr *EventManager) Publish(ctx context.Context, message interface{}, tags map[string]interface{}) error {
evID, ok := tags["EventID"].(string)
if !ok {
return fmt.Errorf("type mismatch: expected string but received %T", tags["EventID"])
}

msg, ok := message.(*events.EventDataLog)
if !ok {
return fmt.Errorf("type mismatch: expected *events.EventDataLog but received %T", message)
}

//Burrow EVM emits other events related to state (such as account call) as well, but we are only interested in log events
if evID[0:3] == "Log" {
evmgr.EventCache = append(evmgr.EventCache, *msg)
}
return nil
}
18 changes: 18 additions & 0 deletions event/event_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package event_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"testing"
)

func TestStatemanager(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Event Suite")
}
190 changes: 190 additions & 0 deletions event/event_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package event_test

import (
"context"
"encoding/json"
"errors"
"fmt"

"github.com/hyperledger/burrow/account"
"github.com/hyperledger/burrow/execution/evm/events"
evm_event "github.com/hyperledger/fabric-chaincode-evm/event"
mocks "github.com/hyperledger/fabric-chaincode-evm/mocks/evmcc"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Event", func() {

var (
eventManager evm_event.EventManager
mockStub *mocks.MockStub
addr account.Address
)

BeforeEach(func() {
mockStub = &mocks.MockStub{}
eventManager = *evm_event.NewEventManager(mockStub)

var err error
addr, err = account.AddressFromBytes([]byte("0000000000000address"))
Expect(err).ToNot(HaveOccurred())
})

Describe("Publish", func() {
var (
ctx context.Context
message events.EventDataLog
tags map[string]interface{}
)

BeforeEach(func() {
ctx = context.Background()
message = events.EventDataLog{
Address: addr,
Height: 0,
}
tags = map[string]interface{}{"EventID": fmt.Sprintf("Log/%s", addr)}
})

Context("when an event is emitted by calling the publish function", func() {
Context("if it is a log event", func() {
It("appends the new message info into the eventCache", func() {
originalLength := len(eventManager.EventCache)
err := eventManager.Publish(ctx, &message, tags)
Expect(err).ToNot(HaveOccurred())
newLength := len(eventManager.EventCache)
Expect(newLength).To(Equal(originalLength + 1))
Expect(eventManager.EventCache[newLength-1]).To(Equal(message))
})
})

Context("if it is not a log event", func() {
It("does nothing", func() {
originalLength := len(eventManager.EventCache)
originalEventCache := eventManager.EventCache
var alt_tags map[string]interface{}
alt_tags = map[string]interface{}{"EventID": fmt.Sprintf("Acc/%s/Call", addr)}
err := eventManager.Publish(ctx, &message, alt_tags)
Expect(err).ToNot(HaveOccurred())
newLength := len(eventManager.EventCache)
newEventCache := eventManager.EventCache
Expect(newLength).To(Equal(originalLength))
Expect(newEventCache).To(Equal(originalEventCache))
})
})
})

Context("when there is a type mismatch in the event ID tag", func() {
It("an error occurs", func() {
var err_tags map[string]interface{}
err_tags = map[string]interface{}{"EventID": []byte(fmt.Sprintf("Log/%s", addr))}
err := eventManager.Publish(ctx, &message, err_tags)
Expect(err).To(HaveOccurred())
})
})

Context("when there is a type mismatch in the message type", func() {
It("an error occurs", func() {
err := eventManager.Publish(ctx, message, tags) //passing events.EventDataLog instead of *events.EventDataLog
Expect(err).To(HaveOccurred())

msg1 := make(chan events.EventDataLog) //passing chan events.EventDataLog instead of *events.EventDataLog
err = eventManager.Publish(ctx, msg1, tags)
Expect(err).To(HaveOccurred())
})
})
})

Describe("Flush", func() {
var (
ctx context.Context
message1 events.EventDataLog
message2 events.EventDataLog
tags map[string]interface{}
)

BeforeEach(func() {
ctx = context.Background()
message1 = events.EventDataLog{
Address: addr,
Height: 0,
}
message2 = events.EventDataLog{
Address: addr,
Height: 1,
}
tags = map[string]interface{}{"EventID": fmt.Sprintf("Log/%s", addr)}
})

Context("when a single event is emitted", func() {
It("sets a new event with a single messageInfo object payload", func() {
err := eventManager.Publish(ctx, &message1, tags)
Expect(err).ToNot(HaveOccurred())
err = eventManager.Flush("Chaincode event")
Expect(err).ToNot(HaveOccurred())

messagePayloads := []events.EventDataLog{message1}
expectedPayload, err := json.Marshal(messagePayloads)
Expect(err).ToNot(HaveOccurred())

Expect(mockStub.SetEventCallCount()).To(Equal(1))
setEventName, setEventPayload := mockStub.SetEventArgsForCall(0)
Expect(setEventName).To(Equal("Chaincode event"))
Expect(setEventPayload).To(Equal(expectedPayload))

var unmarshaledPayloads []events.EventDataLog
err = json.Unmarshal(setEventPayload, &unmarshaledPayloads)
Expect(err).ToNot(HaveOccurred())
Expect(unmarshaledPayloads).To(Equal(messagePayloads))
})
})

Context("when multiple events are emitted", func() {
It("sets a new event with a payload consisting of messageInfo objects marshaled together", func() {
err := eventManager.Publish(ctx, &message1, tags)
Expect(err).ToNot(HaveOccurred())
err = eventManager.Publish(ctx, &message2, tags)
Expect(err).ToNot(HaveOccurred())
err = eventManager.Flush("Chaincode event")
Expect(err).ToNot(HaveOccurred())

messagePayloads := []events.EventDataLog{message1, message2}
expectedPayload, err := json.Marshal(messagePayloads)
Expect(err).ToNot(HaveOccurred())

Expect(mockStub.SetEventCallCount()).To(Equal(1))
setEventName, setEventPayload := mockStub.SetEventArgsForCall(0)
Expect(setEventName).To(Equal("Chaincode event"))
Expect(setEventPayload).To(Equal(expectedPayload))

var unmarshaledPayloads []events.EventDataLog
err = json.Unmarshal(setEventPayload, &unmarshaledPayloads)
Expect(err).ToNot(HaveOccurred())
Expect(unmarshaledPayloads).To(Equal(messagePayloads))
})
})

Context("when the event name is invalid (nil string)", func() {
BeforeEach(func() {
mockStub.SetEventReturns(errors.New("error: nil event name"))
})

It("returns an error", func() {
err := eventManager.Publish(ctx, &message1, tags)
Expect(err).ToNot(HaveOccurred())
err1 := eventManager.Publish(ctx, &message2, tags)
Expect(err1).ToNot(HaveOccurred())
er := eventManager.Flush("")
Expect(er).To(HaveOccurred())
})
})
})
})
11 changes: 11 additions & 0 deletions evmcc/evmcc.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hyperledger/burrow/binary"
"github.com/hyperledger/burrow/execution/evm"
"github.com/hyperledger/burrow/logging"
evm_event "github.com/hyperledger/fabric-chaincode-evm/event"
"github.com/hyperledger/fabric-chaincode-evm/statemanager"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/core/chaincode/shim"
Expand Down Expand Up @@ -80,6 +81,9 @@ func (evmcc *EvmChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response
state := statemanager.NewStateManager(stub)
vm := evm.NewVM(state, newParams(), callerAddr, nil, evmLogger)

evmgr := evm_event.NewEventManager(stub)
vm.SetPublisher(evmgr)

if calleeAddr == account.ZeroAddress {
logger.Debugf("Deploy contract")

Expand Down Expand Up @@ -131,6 +135,13 @@ func (evmcc *EvmChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response
return shim.Error(fmt.Sprintf("failed to execute contract: %s", err.Error()))
}

// Passing the function hash of the method that has triggered the event
// The function hash is the first 8 bytes of the Input argument
er := evmgr.Flush(string(args[1][0:8]))
if er != nil {
return shim.Error(fmt.Sprintf("error in Flush: %s", er.Error()))
}

return shim.Success(output)
}
}
Expand Down
Loading

0 comments on commit 6bb8493

Please # to comment.