diff --git a/event/convention.go b/event/convention.go index 450ebf734fa0783d08d4b6608e4b57ea14244eb5..33a66a501f7e686898ecbaf86654ce8eb601ee52 100644 --- a/event/convention.go +++ b/event/convention.go @@ -11,6 +11,7 @@ const ( MessageTypeKey = "MessageType" TxTypeKey = "TxType" TxHashKey = "TxHash" + StackDepthKey = "StackDepth" ) // Get a query that matches events with a specific eventID diff --git a/execution/evm/events/events.go b/execution/evm/events/events.go index af90a399e8fcd203456684026df64eca7c825ef5..a9308bb5b075cfd6e6fe59a8de7d663ea86392e5 100644 --- a/execution/evm/events/events.go +++ b/execution/evm/events/events.go @@ -33,11 +33,12 @@ func EventStringLogEvent(addr acm.Address) string { return fmt.Sprintf("Log/% // EventDataCall fires when we call a contract, and when a contract calls another contract type EventDataCall struct { - CallData *CallData - Origin acm.Address - TxID []byte - Return []byte - Exception string + CallData *CallData + Origin acm.Address + TxHash []byte + StackDepth int + Return []byte + Exception string } type CallData struct { @@ -58,9 +59,11 @@ type EventDataLog struct { // Publish/Subscribe -// Subscribe to account call event - if TxHash is provided listens for a specifc Tx otherwise captures all +// Subscribe to account call event - if TxHash is provided listens for a specifc Tx otherwise captures all, if +// stackDepth is greater than or equal to 0 captures calls at a specific stack depth (useful for capturing the return +// of the root call over recursive calls func SubscribeAccountCall(ctx context.Context, subscribable event.Subscribable, subscriber string, address acm.Address, - txHash []byte, ch chan<- *EventDataCall) error { + txHash []byte, stackDepth int, ch chan<- *EventDataCall) error { query := event.QueryForEventID(EventStringAccountCall(address)) @@ -68,6 +71,10 @@ func SubscribeAccountCall(ctx context.Context, subscribable event.Subscribable, query = query.AndEquals(event.TxHashKey, hex.EncodeUpperToString(txHash)) } + if stackDepth >= 0 { + query = query.AndEquals(event.StackDepthKey, stackDepth) + } + return event.SubscribeCallback(ctx, subscribable, subscriber, query, func(message interface{}) bool { eventDataCall, ok := message.(*EventDataCall) if ok { @@ -93,7 +100,11 @@ func SubscribeLogEvent(ctx context.Context, subscribable event.Subscribable, sub func PublishAccountCall(publisher event.Publisher, address acm.Address, eventDataCall *EventDataCall) error { return event.PublishWithEventID(publisher, EventStringAccountCall(address), eventDataCall, - map[string]interface{}{"address": address, event.TxHashKey: hex.EncodeUpperToString(eventDataCall.TxID)}) + map[string]interface{}{ + "address": address, + event.TxHashKey: hex.EncodeUpperToString(eventDataCall.TxHash), + event.StackDepthKey: eventDataCall.StackDepth, + }) } func PublishLogEvent(publisher event.Publisher, address acm.Address, eventDataLog *EventDataLog) error { diff --git a/execution/evm/vm.go b/execution/evm/vm.go index f94cf768ea702cde7fd208e7fb117cc96ab68d46..1837b47d58466a2ab45f49aa2adfe188a0088ae3 100644 --- a/execution/evm/vm.go +++ b/execution/evm/vm.go @@ -159,11 +159,18 @@ func (vm *VM) fireCallEvent(exception *string, output *[]byte, callerAddress, ca // fire the post call event (including exception if applicable) if vm.publisher != nil { events.PublishAccountCall(vm.publisher, calleeAddress, &events.EventDataCall{ - &events.CallData{Caller: callerAddress, Callee: calleeAddress, Data: input, Value: value, Gas: *gas}, - vm.origin, - vm.txHash, - *output, - *exception, + CallData: &events.CallData{ + Caller: callerAddress, + Callee: calleeAddress, + Data: input, + Value: value, + Gas: *gas, + }, + Origin: vm.origin, + TxHash: vm.txHash, + StackDepth: vm.stackDepth, + Return: *output, + Exception: *exception, }) } } diff --git a/execution/evm/vm_test.go b/execution/evm/vm_test.go index 82d79012b6127a72fe7f4ae112613041145c8bcf..27ce077083f3aea74d99c513e551f78e101799f5 100644 --- a/execution/evm/vm_test.go +++ b/execution/evm/vm_test.go @@ -442,7 +442,8 @@ func runVM(eventCh chan<- *evm_events.EventDataCall, ourVm *VM, caller, callee a emitter := event.NewEmitter(logging.NewNoopLogger()) fmt.Printf("subscribe to %s\n", subscribeAddr) - err := evm_events.SubscribeAccountCall(context.Background(), emitter, "test", subscribeAddr, nil, eventCh) + err := evm_events.SubscribeAccountCall(context.Background(), emitter, "test", subscribeAddr, + nil, -1, eventCh) if err != nil { return nil, err } diff --git a/execution/transactor.go b/execution/transactor.go index ddbe8fe78385bfd878c90bc4cc0457e929744fbf..662407ecb1b9835420dd0a1dc13287e02bb2b451 100644 --- a/execution/transactor.go +++ b/execution/transactor.go @@ -276,7 +276,7 @@ func (trans *Transactor) TransactAndHold(privKey []byte, address *acm.Address, d } err = evm_events.SubscribeAccountCall(context.Background(), trans.eventEmitter, subID, receipt.ContractAddress, - receipt.TxHash, wc) + receipt.TxHash, 0, wc) if err != nil { return nil, err } diff --git a/rpc/tm/integration/websocket_helpers.go b/rpc/tm/integration/websocket_helpers.go index ec1c7ff2cfc22e50aa1b6c4840697fe0e72a316c..cbaa5749343add811a116d0628e5d0a92e800988 100644 --- a/rpc/tm/integration/websocket_helpers.go +++ b/rpc/tm/integration/websocket_helpers.go @@ -302,9 +302,9 @@ func unmarshalValidateCall(origin acm.Address, returnCode []byte, txid *[]byte) if !bytes.Equal(ret, returnCode) { return true, fmt.Errorf("call did not return correctly. Got %x, expected %x", ret, returnCode) } - if !bytes.Equal(data.TxID, *txid) { + if !bytes.Equal(data.TxHash, *txid) { return true, fmt.Errorf("TxIDs do not match up! Got %x, expected %x", - data.TxID, *txid) + data.TxHash, *txid) } return true, nil } diff --git a/rpc/v0/client.go b/rpc/v0/client.go index 791f89310977068cd3b957a9fd86ad707f915175..8f635e17df60aff8899f4c7574fdb20a9cff6c43 100644 --- a/rpc/v0/client.go +++ b/rpc/v0/client.go @@ -7,7 +7,9 @@ import ( "net/http" "time" + "github.com/hyperledger/burrow/execution/evm/events" "github.com/hyperledger/burrow/rpc" + "github.com/hyperledger/burrow/txs" ) type V0Client struct { @@ -33,6 +35,24 @@ func NewV0Client(url string) *V0Client { } } +func (vc *V0Client) Transact(param TransactParam) (*txs.Receipt, error) { + receipt := new(txs.Receipt) + err := vc.Call(TRANSACT, param, receipt) + if err != nil { + return nil, err + } + return receipt, nil +} + +func (vc *V0Client) TransactAndHold(param TransactParam) (*events.EventDataCall, error) { + eventDataCall := new(events.EventDataCall) + err := vc.Call(TRANSACT_AND_HOLD, param, eventDataCall) + if err != nil { + return nil, err + } + return eventDataCall, nil +} + func (vc *V0Client) Call(method string, param interface{}, result interface{}) error { // Marhsal into JSONRPC request object bs, err := vc.codec.EncodeBytes(param) diff --git a/rpc/v0/integration/v0_test.go b/rpc/v0/integration/v0_test.go index 433b2fae0178a1883b8d752b8a2f33d3dd738483..91ef37156c51f348fa8c30ed2e512a7ed36b74d0 100644 --- a/rpc/v0/integration/v0_test.go +++ b/rpc/v0/integration/v0_test.go @@ -21,25 +21,36 @@ import ( "testing" "github.com/hyperledger/burrow/rpc/v0" - "github.com/hyperledger/burrow/txs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestTransact(t *testing.T) { cli := v0.NewV0Client("http://localhost:1337/rpc") - receipt := new(txs.Receipt) address := privateAccounts[1].Address() - param := v0.TransactParam{ + receipt, err := cli.Transact(v0.TransactParam{ PrivKey: privateAccounts[0].PrivateKey().RawBytes(), Address: address.Bytes(), Data: []byte{}, Fee: 2, GasLimit: 10000, - } - err := cli.Call(v0.TRANSACT, param, receipt) + }) require.NoError(t, err) assert.False(t, receipt.CreatesContract) assert.Equal(t, address, receipt.ContractAddress) } + +func TestTransactAndHold(t *testing.T) { + cli := v0.NewV0Client("http://localhost:1337/rpc") + + call, err := cli.TransactAndHold(v0.TransactParam{ + PrivKey: privateAccounts[0].PrivateKey().RawBytes(), + Address: nil, + Data: []byte{}, + Fee: 2, + GasLimit: 10000, + }) + require.NoError(t, err) + assert.Equal(t, 0, call.StackDepth) +}