-
Notifications
You must be signed in to change notification settings - Fork 80
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
*: add invocations to applicationlog #3569
base: master
Are you sure you want to change the base?
Changes from 1 commit
d8a0d13
04c71f5
dfd5c9b
cd7caf5
e7063a2
ae3b481
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -356,6 +356,52 @@ to various blockchain events (with simple event filtering) and receive them on | |
the client as JSON-RPC notifications. More details on that are written in the | ||
[notifications specification](notifications.md). | ||
|
||
#### Applicationlog invocations | ||
|
||
The `SaveInvocations` node configuration setting stores smart contract invocation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/node/RPC server There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/stores/makes the node to store or something like this. |
||
details into the application logs under the `invocations` key. This feature is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/into the application logs under the The applog DB key is a pure internal thing, and I'd say it's not related to external user. |
||
specifically useful to capture information in the absence of `System.Runtime.Notify` | ||
calls for the given smart contract method. Other use-cases are described in | ||
[this issue](https://github.com/neo-project/neo/issues/3386). | ||
|
||
Example: | ||
```json | ||
"invocations": [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's attach the full response for |
||
{ | ||
"hash": "0xd2a4cff31913016155e38e474a2c06d08be276cf", | ||
"method": "transfer", | ||
"arguments": { | ||
"type": "Array", | ||
"value": [ | ||
{ | ||
"type": "ByteString", | ||
"value": "krOcd6pg8ptXwXPO2Rfxf9Mhpus=" | ||
}, | ||
{ | ||
"type": "ByteString", | ||
"value": "AZelPVEEY0csq+FRLl/HJ9cW+Qs=" | ||
}, | ||
{ | ||
"type": "Integer", | ||
"value": "1000000000000" | ||
}, | ||
{ | ||
"type": "Any" | ||
} | ||
] | ||
}, | ||
"argumentscount": 4, | ||
"truncated": false | ||
} | ||
] | ||
``` | ||
|
||
For security reasons the `arguments` field data may result in `null`. In such case the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, describe the exact size constraint that is used to filter out large notifications. Otherwise users don't have any other way to learn about this constraint other than code-level check. |
||
`Truncated` field will be set to `true`. | ||
|
||
Note that invocation records for faulted transactions are kept and are present in the | ||
applicationlog. This behaviour differs from notifications which are omitted for faulted transactions. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add a couple of words about ordering and flat structure of |
||
|
||
## Reference | ||
|
||
* [JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,14 +79,8 @@ func Call(ic *interop.Context) error { | |
if argBytes, err = ic.DAO.GetItemCtx().Serialize(stackitem.NewArray(args), false); err != nil { | ||
truncated = true | ||
} | ||
|
||
ic.InvocationCalls = append(ic.InvocationCalls, state.ContractInvocation{ | ||
Hash: u, | ||
Method: method, | ||
ArgumentsBytes: argBytes, | ||
ArgumentsCount: uint32(arrCount), | ||
Truncated: truncated, | ||
}) | ||
ci := state.NewContractInvocation(u, method, argBytes, uint32(arrCount), truncated) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
ic.InvocationCalls = append(ic.InvocationCalls, *ci) | ||
} | ||
return callInternal(ic, cs, method, fs, hasReturn, args, true) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package state | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/nspcc-dev/neo-go/pkg/io" | ||
"github.com/nspcc-dev/neo-go/pkg/util" | ||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" | ||
) | ||
|
||
// NewContractInvocation return a new ContractInvocation. | ||
func NewContractInvocation(hash util.Uint160, method string, argBytes []byte, argCnt uint32, truncated bool) *ContractInvocation { | ||
return &ContractInvocation{ | ||
Hash: hash, | ||
Method: method, | ||
argumentsBytes: argBytes, | ||
ArgumentsCount: argCnt, | ||
Truncated: truncated, | ||
} | ||
} | ||
|
||
// ContractInvocation contains method call information. | ||
// The Arguments field will be nil if serialization of the arguments exceeds the predefined limit | ||
// of [stackitem.MaxSerialized] (for security reasons). In that case Truncated will be set to true. | ||
type ContractInvocation struct { | ||
Hash util.Uint160 `json:"contract"` | ||
Method string `json:"method"` | ||
// Arguments are the arguments as passed to the `args` parameter of System.Contract.Call | ||
// for use in the RPC Server and RPC Client. | ||
Arguments *stackitem.Array `json:"arguments"` | ||
// argumentsBytes is the serialized arguments used at the interop level. | ||
argumentsBytes []byte | ||
ArgumentsCount uint32 `json:"argumentscount"` | ||
Truncated bool `json:"truncated"` | ||
} | ||
|
||
// DecodeBinary implements the Serializable interface. | ||
func (ci *ContractInvocation) DecodeBinary(r *io.BinReader) { | ||
ci.Hash.DecodeBinary(r) | ||
ci.Method = r.ReadString() | ||
ci.argumentsBytes = r.ReadVarBytes() | ||
ci.ArgumentsCount = r.ReadU32LE() | ||
ci.Truncated = r.ReadBool() | ||
} | ||
|
||
// EncodeBinary implements the Serializable interface. | ||
func (ci *ContractInvocation) EncodeBinary(w *io.BinWriter) { | ||
ci.EncodeBinaryWithContext(w, stackitem.NewSerializationContext()) | ||
} | ||
|
||
// EncodeBinaryWithContext is the same as EncodeBinary, but allows to efficiently reuse | ||
// stack item serialization context. | ||
func (ci *ContractInvocation) EncodeBinaryWithContext(w *io.BinWriter, sc *stackitem.SerializationContext) { | ||
ci.Hash.EncodeBinary(w) | ||
w.WriteString(ci.Method) | ||
w.WriteVarBytes(ci.argumentsBytes) | ||
w.WriteU32LE(ci.ArgumentsCount) | ||
w.WriteBool(ci.Truncated) | ||
} | ||
|
||
// MarshalJSON implements the json.Marshaler interface. | ||
func (ci ContractInvocation) MarshalJSON() ([]byte, error) { | ||
si, err := stackitem.Deserialize(ci.argumentsBytes) | ||
if err != nil { | ||
return nil, err | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
item, err := stackitem.ToJSONWithTypes(si.(*stackitem.Array)) | ||
if err != nil { | ||
item = []byte(fmt.Sprintf(`"error: %v"`, err)) | ||
} | ||
return json.Marshal(contractInvocationAux{ | ||
Hash: ci.Hash, | ||
Method: ci.Method, | ||
Arguments: item, | ||
ArgumentsCount: ci.ArgumentsCount, | ||
Truncated: ci.Truncated, | ||
}) | ||
} | ||
|
||
// 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.Arguments) | ||
if err != nil { | ||
return err | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to handle the case of invalid type ( |
||
} | ||
if t := params.Type(); t != stackitem.ArrayT { | ||
return fmt.Errorf("failed to convert invocation state of type %s to array", t.String()) | ||
} | ||
ci.Arguments = params.(*stackitem.Array) | ||
ci.Method = aux.Method | ||
ci.Hash = aux.Hash | ||
ci.ArgumentsCount = aux.ArgumentsCount | ||
ci.Truncated = aux.Truncated | ||
return nil | ||
} | ||
|
||
// contractInvocationAux is an auxiliary struct for ContractInvocation JSON marshalling. | ||
type contractInvocationAux struct { | ||
Hash util.Uint160 `json:"hash"` | ||
Method string `json:"method"` | ||
Arguments json.RawMessage `json:"arguments"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
ArgumentsCount uint32 `json:"argumentscount"` | ||
Truncated bool `json:"truncated"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace with "
getapplicationlog
call" to follow the style of other RPC call extension headers?