Skip to content

Commit

Permalink
Store invocations in application log
Browse files Browse the repository at this point in the history
  • Loading branch information
ixje committed Oct 31, 2024
1 parent e8b8c1a commit d8a0d13
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 9 deletions.
1 change: 1 addition & 0 deletions pkg/core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1717,6 +1717,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
Stack: v.Estack().ToArray(),
Events: systemInterop.Notifications,
FaultException: faultException,
Invocations: systemInterop.InvocationCalls,
},
}
appExecResults = append(appExecResults, aer)
Expand Down
19 changes: 17 additions & 2 deletions pkg/core/dao/dao_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,23 @@ func TestStoreAsTransaction(t *testing.T) {
Container: hash,
Execution: state.Execution{
Trigger: trigger.Application,
Events: []state.NotificationEvent{},
Stack: []stackitem.Item{},
Events: []state.NotificationEvent{
{
ScriptHash: util.Uint160{},
Name: "fakeTransferEvent",
Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewBool(false),
}),
},
},
Stack: []stackitem.Item{},
Invocations: []state.ContractInvocation{{
Hash: util.Uint160{},
Method: "fakeMethodCall",
Params: stackitem.NewArray([]stackitem.Item{
stackitem.NewBool(false),
}),
}},
},
}
err := dao.StoreAsTransaction(tx, 0, aer)
Expand Down
1 change: 1 addition & 0 deletions pkg/core/interop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type Context struct {
VM *vm.VM
Functions []Function
Invocations map[util.Uint160]int
InvocationCalls []state.ContractInvocation
cancelFuncs []context.CancelFunc
getContract func(*dao.Simple, util.Uint160) (*state.Contract, error)
baseExecFee int64
Expand Down
6 changes: 6 additions & 0 deletions pkg/core/interop/contract/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ func Call(ic *interop.Context) error {
return fmt.Errorf("method not found: %s/%d", method, len(args))
}
hasReturn := md.ReturnType != smartcontract.VoidType

ic.InvocationCalls = append(ic.InvocationCalls, state.ContractInvocation{
Hash: u,
Method: method,
Params: stackitem.NewArray(args),
})
return callInternal(ic, cs, method, fs, hasReturn, args, true)
}

Expand Down
96 changes: 89 additions & 7 deletions pkg/core/state/notification_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"

"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
Expand All @@ -20,6 +19,74 @@ type NotificationEvent struct {
Item *stackitem.Array `json:"state"`
}

type ContractInvocation struct {
Hash util.Uint160 `json:"contract_hash"`
Method string `json:"method"`
Params *stackitem.Array `json:"parameters"`
}

func (ci *ContractInvocation) DecodeBinary(r *io.BinReader) {
ci.Hash.DecodeBinary(r)
ci.Method = r.ReadString()
params := stackitem.DecodeBinary(r)
if r.Err != nil {
return
}
arr, ok := params.Value().([]stackitem.Item)
if !ok {
r.Err = errors.New("Array or Struct expected")
return
}
ci.Params = stackitem.NewArray(arr)
}

func (ci *ContractInvocation) EncodeBinaryWithContext(w *io.BinWriter, sc *stackitem.SerializationContext) {
ci.Hash.EncodeBinary(w)
w.WriteString(ci.Method)
b, err := sc.Serialize(ci.Params, false)
if err != nil {
w.Err = err
return
}
w.WriteBytes(b)
}

// MarshalJSON implements the json.Marshaler interface.
func (ci ContractInvocation) MarshalJSON() ([]byte, error) {
item, err := stackitem.ToJSONWithTypes(ci.Params)
if err != nil {
item = []byte(fmt.Sprintf(`"error: %v"`, err))
}
return json.Marshal(ContractInvocationAux{
Hash: ci.Hash,
Method: ci.Method,
Params: item,
})
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (ci *ContractInvocation) UnmarshalJSON(data []byte) error {
aux := new(ContractInvocationAux)
if err := json.Unmarshal(data, aux); err != nil {
return err
}
params, err := stackitem.FromJSONWithTypes(aux.Params)
if err != nil {
return err
}
if t := params.Type(); t != stackitem.ArrayT {
return fmt.Errorf("failed to convert invocation state of type %s to array", t.String())
}
ci.Params = params.(*stackitem.Array)
ci.Method = aux.Method
ci.Hash = aux.Hash
return nil
}

func (ci *ContractInvocation) EncodeBinary(w *io.BinWriter) {
ci.EncodeBinaryWithContext(w, stackitem.NewSerializationContext())
}

// AppExecResult represents the result of the script execution, gathering together
// all resulting notifications, state, stack and other metadata.
type AppExecResult struct {
Expand Down Expand Up @@ -95,6 +162,10 @@ func (aer *AppExecResult) EncodeBinaryWithContext(w *io.BinWriter, sc *stackitem
aer.Events[i].EncodeBinaryWithContext(w, sc)
}
w.WriteVarBytes([]byte(aer.FaultException))
w.WriteVarUint(uint64(len(aer.Invocations)))
for i := range aer.Invocations {
aer.Invocations[i].EncodeBinaryWithContext(w, sc)
}
}

// DecodeBinary implements the Serializable interface.
Expand All @@ -120,6 +191,7 @@ func (aer *AppExecResult) DecodeBinary(r *io.BinReader) {
aer.Stack = arr
r.ReadArray(&aer.Events)
aer.FaultException = r.ReadString()
r.ReadArray(&aer.Invocations)
}

// notificationEventAux is an auxiliary struct for NotificationEvent JSON marshalling.
Expand Down Expand Up @@ -209,16 +281,24 @@ type Execution struct {
Stack []stackitem.Item
Events []NotificationEvent
FaultException string
Invocations []ContractInvocation
}

type ContractInvocationAux struct {
Hash util.Uint160 `json:"contract_hash"`
Method string `json:"method"`
Params json.RawMessage `json:"parameters"`
}

// executionAux represents an auxiliary struct for Execution JSON marshalling.
type executionAux struct {
Trigger string `json:"trigger"`
VMState string `json:"vmstate"`
GasConsumed int64 `json:"gasconsumed,string"`
Stack json.RawMessage `json:"stack"`
Events []NotificationEvent `json:"notifications"`
FaultException *string `json:"exception"`
Trigger string `json:"trigger"`
VMState string `json:"vmstate"`
GasConsumed int64 `json:"gasconsumed,string"`
Stack json.RawMessage `json:"stack"`
Events []NotificationEvent `json:"notifications"`
FaultException *string `json:"exception"`
Invocations []ContractInvocation `json:"invocations"`
}

// MarshalJSON implements the json.Marshaler interface.
Expand Down Expand Up @@ -246,6 +326,7 @@ func (e Execution) MarshalJSON() ([]byte, error) {
Stack: st,
Events: e.Events,
FaultException: exception,
Invocations: e.Invocations,
})
}

Expand Down Expand Up @@ -287,6 +368,7 @@ func (e *Execution) UnmarshalJSON(data []byte) error {
if aux.FaultException != nil {
e.FaultException = *aux.FaultException
}
e.Invocations = aux.Invocations
return nil
}

Expand Down

0 comments on commit d8a0d13

Please sign in to comment.