diff --git a/.gitignore b/.gitignore index cb8e5a620..6fe160927 100644 --- a/.gitignore +++ b/.gitignore @@ -31,8 +31,6 @@ TAGS # leveldb chains/ chain/ -blockchain/ -blockchains/ # patch *.orig diff --git a/README.md b/README.md index aa1aa2629..29e761596 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,9 @@ mode, after it ends the node can be started normally. Please refer to [neo-go smart contract development workshop](https://github.com/nspcc-dev/neo-go-sc-wrkshp) that shows some simple contracts that can be compiled/deployed/run using neo-go compiler, SDK -and private network. +and private network. For details on how Go code is translated to Neo VM +bytecode and what you can and can not do in smart contract please refer to the +[compiler documentation](docs/compiler.md). # Developer notes Nodes have such features as [Prometheus](https://prometheus.io/docs/guides/go-application) and diff --git a/docs/compiler.md b/docs/compiler.md index a7e59652c..593e5cfa2 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -2,66 +2,39 @@ The neo-go compiler compiles Go programs to bytecode that the NEO virtual machine can understand. -## Currently supported +## Language compatibility -### Go internals -- type checking -- multiple assignments -- global variables -- types int, string, byte and booleans -- struct types + method receives -- functions -- composite literals `[]int, []string, []byte` -- basic if statements -- binary expressions -- return statements -- for loops -- imports +The compiler is mostly compatible with regular Go language specification, but +there are some important deviations that you need to be aware of that make it +a dialect of Go rather than a complete port of the language: + * `make()` ane `new()` are not supported, most of the time you can substitute + them with composite literals + * there is no real distinction between different integer types, all of them + work as big.Int in Go with a limit of 256 bit in width, so you can use + `int` for just about anything. This is the way integers work in Neo VM and + adding proper Go types emulation is considered to be too costly. + * goroutines, channels and garbage collection are not supported and will + never be because emulating that aspects of Go runtime on top of Neo VM is + close to impossible + * even though `panic()` is supported, `recover()` is not, `panic` shuts the + VM down + * lambdas are not supported + * global variables can't be changed in functions (#638) + * it's not possible to rename imported interop packages, they won't work this + way (#397, #913) + * nested selectors are not yet supported (#957) + * using value variable in range-based loops is not yet supported (#958) -### Go builtins -- len -- append - -### VM API (interop layer) +## VM API (interop layer) Compiler translates interop function calls into NEO VM syscalls or (for custom -functions) into NEO VM instructions. [Refer to GoDoc](https://godoc.org/github.com/nspcc-dev/neo-go/pkg/interop) for full API documentation. - -#### Standard NEO Smart Contract API -- account -- asset -- attribute -- block -- blockchain -- contract -- engine -- header -- input -- iterator -- output -- runtime -- storage -- transaction - -#### Custom VM utility helper functions -- crypto: - - `SHA1` - - `SHA256` - - `Hash256` - - `Hash160` -- enumerator -- util: - - `Equals` (to emit `EQUALS` opcode, not needed usually) - - `FromAddress(address string) []byte` - -## Not supported -Due to the limitations of the NEO virtual machine, features listed below will not be supported. -- channels -- goroutines -- returning multiple values from functions +functions) into NEO VM instructions. [Refer to +pkg.go.dev](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/interop) +for full API documentation. In general it provides the same level of +functionality as Neo .net Framework library. ## Quick start -### Compile a smart contract +### Compiling ``` ./bin/neo-go contract compile -i mycontract.go @@ -73,7 +46,7 @@ By default the filename will be the name of your .go file with the .avm extensio ./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.avm ``` -### Debugging your smart contract +### Debugging You can dump the opcodes generated by the compiler with the following command: ``` @@ -112,33 +85,89 @@ INDEX OPCODE DESC 25 0x66 RET ``` -### Test invoke a compiled contract -You can simulate a test invocation of your compiled contract by the VM, to know the total gas cost for example, with the following command: +#### Neo Smart Contract Debugger support + +It's possible to debug contracts written in Go using standard [Neo Smart +Contract Debugger](https://github.com/neo-project/neo-debugger/) which is a +part of [Neo Blockchain +Toolkit](https://github.com/neo-project/neo-blockchain-toolkit/). To do that +you need to generate debug information using `--debug` option, like this: ``` -./bin/neo-go contract testinvoke -i mycompiledcontract.avm +$ ./bin/neo-go contract compile -i contract.go -o contract.avm --debug contract.debug.json ``` -Will output something like: -``` -{ - "state": "HALT, BREAK", - "gas_consumed": "0.006", - "Stack": [ - { - "type": "Integer", - "value": "9" - } - ] -} +This file can then be used by debugger and set up to work just like for any +other supported language. + +### Deploying + +Deploying a contract to blockchain with neo-go requires a configuration file +with contract's metadata in YAML format, like the following: ``` +project: + author: Jack Smith + email: jack@example.com + version: 1.0 + name: 'Smart contract' + description: 'Even smarter than Jack himself' + hasstorage: true + hasdynamicinvocation: false + ispayable: false + returntype: ByteArray + parameters: ['String', 'Array'] +``` -At the moment this is implemented via RPC call to the remote server. +It's passed to the `deploy` command via `-c` option: + +``` +$ ./bin/neo-go contract deploy -i contract.avm -c contract.yml -e http://localhost:20331 -w wallet.json -g 0.001 +``` + +Deployment works via an RPC server, an address of which is passed via `-e` +option and should be signed using a wallet from `-w` option. More details can +be found in `deploy` command help. + +#### Neo Express support + +It's possible to deploy contracts written in Go using [Neo +Express](https://github.com/neo-project/neo-express) which is a part of [Neo +Blockchain +Toolkit](https://github.com/neo-project/neo-blockchain-toolkit/). To do that +you need to generate a different metadata file using YAML written for +deployment with neo-go. It's done in the same step with compilation via +`--config` input parameter and `--abi` output parameter, combined with debug +support the command line will look like this: + +``` +$ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.avm --debug contract.debug.json --abi contract.abi.json +``` + +This file can then be used by toolkit to deploy contract the same way +contracts in other languagues are deployed. + + +### Invoking +You can import your contract into the standalone VM and run it there (see [VM +documentation](vm.md) for more info), but that only works for simple contracts +that don't use blockchain a lot. For more real contracts you need to deploy +them first and then do test invocations and regular invocations with `contract +testinvokefunction` and `contract invokefunction` commands (or their variants, +see `contract` command help for more details. They all work via RPC, so it's a +mandatory parameter. + +Example call (contract `f84d6a337fbc3d3a201d41da99e86b479e7a2554` with method +`balanceOf` and method's parameter `AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y` using +given RPC server and wallet and paying 0.00001 GAS for this transaction): + +``` +$ ./bin/neo-go contract invokefunction -e http://localhost:20331 -w my_wallet.json -g 0.00001 f84d6a337fbc3d3a201d41da99e86b479e7a2554 balanceOf AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y +``` ## Smart contract examples -Some examples are provided in the [examples directory](examples). +Some examples are provided in the [examples directory](../examples). ### Check if the invoker of the contract is the owning address diff --git a/docs/notifications.md b/docs/notifications.md new file mode 100644 index 000000000..0561a3282 --- /dev/null +++ b/docs/notifications.md @@ -0,0 +1,339 @@ +# Notification subsystem + +Original motivation, requirements and general solution strategy are described +in the issue #895. + +This extension allows a websocket client to subscribe to various events and +receive them as JSON-RPC notifications from the server. + +## Events +Currently supported events: + * new block added + Contents: block. + Filters: none. + * new transaction in the block + Contents: transaction. + Filters: type. + * notification generated during execution + Contents: container hash, contract script hash, stack item. + Filters: contract script hash. + * transaction executed + Contents: application execution result. + Filters: VM state. + +## Ordering and persistence guarantees + * new block is only announced after its processing is complete and the chain + is updated to the new height + * no disk-level persistence guarantees are given + * new in-block transaction is announced after block processing, but before + announcing the block itself + * transaction notifications are only announced for successful transactions + * all announcements are being done in the same order they happen on the chain + At first transaction execution is announced, then followed by notifications + generated during this execution, then followed by transaction announcement. + Transaction announcements are ordered the same way they're in the block. + * unsubscription may not cancel pending, but not yet sent events + +## Subscription management + +Errors are not described down below, but they can be returned as standard +JSON-RPC errors (most often caused by invalid parameters). + +### `subscribe` method + +Parameters: event stream name, stream-specific filter rules hash (can be +omitted if empty). + +Recognized stream names: + * `block_added` + No filter parameters defined. + * `transaction_added` + Filter: `type` as a string containing standard transaction types + (MinerTransaction, InvocationTransaction, etc) + * `notification_from_execution` + Filter: `contract` field containing string with hex-encoded Uint160 (LE + representation). + * `transaction_executed` + Filter: `state` field containing `HALT` or `FAULT` string for successful + and failed executions respectively. + +Response: returns subscription ID (string) as a result. This ID can be used to +cancel this subscription and has no meaning other than that. + +Example request (subscribe to notifications from contract +0x6293a440ed80a427038e175a507d3def1e04fb67 generated when executing +transactions): + +``` +{ + "jsonrpc": "2.0", + "method": "subscribe", + "params": ["notification_from_execution", {"contract": "6293a440ed80a427038e175a507d3def1e04fb67"}], + "id": 1 +} + +``` + +Example response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": "55aaff00" +} +``` + +### `unsubscribe` method + +Parameters: subscription ID as a string. + +Response: boolean true. + +Example request (unsubscribe from "55aaff00"): + +``` +{ + "jsonrpc": "2.0", + "method": "unsubscribe", + "params": ["55aaff00"], + "id": 1 +} +``` + +Example response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +``` + +## Events + +Events are sent as JSON-RPC notifications from the server with `method` field +being used for notification names. Notification names are identical to stream +names described for `subscribe` method with one important addition for +`event_missed` which can be sent for any subscription to signify that some +events were not delivered (usually when client isn't able to keep up with +event flow). + +Verbose responses for various structures like blocks and transactions are used +to simplify working with notifications on client side. Returned structures +mostly follow the one used by standard Neo RPC calls, but may have some minor +differences. + +### `block_added` notification +As a first parameter (`params` section) contains block converted to JSON +structure which is similar to verbose `getblock` response but with the +following differences: + * it doesn't have `size` field (you can calculate it client-side) + * it doesn't have `nextblockhash` field (it's supposed to be the latest one + anyway) + * it doesn't have `confirmations` field (see previous) + * transactions contained don't have `net_fee` and `sys_fee` fields + +No other parameters are sent. + +Example: +``` +{ + "jsonrpc" : "2.0", + "method" : "block_added", + "params" : [ + { + "previousblockhash" : "0x33f3e0e24542b2ec3b6420e6881c31f6460a39a4e733d88f7557cbcc3b5ed560", + "nextconsensus" : "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", + "index" : 205, + "nonce" : "0000000000000457", + "version" : 0, + "tx" : [ + { + "version" : 0, + "attributes" : [], + "txid" : "0xf9adfde059810f37b3d0686d67f6b29034e0c669537df7e59b40c14a0508b9ed", + "size" : 10, + "vin" : [], + "type" : "MinerTransaction", + "scripts" : [], + "vout" : [] + }, + { + "version" : 1, + "txid" : "0x93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7", + "attributes" : [], + "size" : 60, + "script" : "097465737476616c756507746573746b657952c103507574676f20ccfbd5f01d5b9633387428b8bab95a9e78c2", + "vin" : [], + "scripts" : [], + "type" : "InvocationTransaction", + "vout" : [] + } + ], + "time" : 1586154525, + "hash" : "0x48fba8aebf88278818a3dc0caecb230873d1d4ce1ea8bf473634317f94a609e5", + "script" : { + "invocation" : "4047a444a51218ac856f1cbc629f251c7c88187910534d6ba87847c86a9a73ed4951d203fd0a87f3e65657a7259269473896841f65c0a0c8efc79d270d917f4ff640435ee2f073c94a02f0276dfe4465037475e44e1c34c0decb87ec9c2f43edf688059fc4366a41c673d72ba772b4782c39e79f01cb981247353216d52d2df1651140527eb0dfd80a800fdd7ac8fbe68fc9366db2d71655d8ba235525a97a69a7181b1e069b82091be711c25e504a17c3c55eee6e76e6af13cb488fbe35d5c5d025c34041f39a02ebe9bb08be0e4aaa890f447dc9453209bbfb4705d8f2d869c2b55ee2d41dbec2ee476a059d77fb7c26400284328d05aece5f3168b48f1db1c6f7be0b", + "verification" : "532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae" + }, + "merkleroot" : "0x9d922c5cfd4c8cd1da7a6b2265061998dc438bd0dea7145192e2858155e6c57a" + } + ] +} +``` + +### `transaction_added` notification + +In the first parameter (`params` section) contains transaction converted to +JSON which is similar to verbose `getrawtransaction` response, but with the +following differences: + * `net_fee` and `sys_fee` fields are always missing + * block's metadata is missing (`blockhash`, `confirmations`, `blocktime`) + +No other parameters are sent. + +Example: +``` +{ + "params" : [ + { + "vin" : [], + "scripts" : [], + "attributes" : [], + "txid" : "0x93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7", + "size" : 60, + "vout" : [], + "type" : "InvocationTransaction", + "version" : 1, + "script" : "097465737476616c756507746573746b657952c103507574676f20ccfbd5f01d5b9633387428b8bab95a9e78c2" + } + ], + "method" : "transaction_added", + "jsonrpc" : "2.0" +} +``` + +### `notification_from_execution` notification + +Contains three parameters: container hash (hex-encoded LE Uint256 in a +string), contract script hash (hex-encoded LE Uint160 in a string) and stack +item (encoded the same way as `state` field contents for notifications from +`getapplicationlog` response). + +Example: + +``` +{ + "method" : "notification_from_execution", + "jsonrpc" : "2.0", + "params" : [ + { + "state" : { + "value" : [ + { + "value" : "636f6e74726163742063616c6c", + "type" : "ByteArray" + }, + { + "value" : "507574", + "type" : "ByteArray" + }, + { + "value" : [ + { + "type" : "ByteArray", + "value" : "746573746b6579" + }, + { + "value" : "7465737476616c7565", + "type" : "ByteArray" + } + ], + "type" : "Array" + } + ], + "type" : "Array" + }, + "contract" : "0xc2789e5ab9bab828743833965b1df0d5fbcc206f" + } + ] +} +``` + +### `transaction_executed` notification + +Contains the same result as from `getapplicationlog` method in the first +parameter and no other parameters. One difference from `getapplicationlog` is +that it always contains zero in the `contract` field. + +Example: +``` +{ + "method" : "transaction_executed", + "params" : [ + { + "executions" : [ + { + "vmstate" : "HALT", + "contract" : "0x0000000000000000000000000000000000000000", + "notifications" : [ + { + "state" : { + "type" : "Array", + "value" : [ + { + "type" : "ByteArray", + "value" : "636f6e74726163742063616c6c" + }, + { + "type" : "ByteArray", + "value" : "507574" + }, + { + "value" : [ + { + "value" : "746573746b6579", + "type" : "ByteArray" + }, + { + "type" : "ByteArray", + "value" : "7465737476616c7565" + } + ], + "type" : "Array" + } + ] + }, + "contract" : "0xc2789e5ab9bab828743833965b1df0d5fbcc206f" + } + ], + "gas_consumed" : "1.048", + "stack" : [ + { + "type" : "Integer", + "value" : "1" + } + ], + "trigger" : "Application" + } + ], + "txid" : "0x93670859cc8a42f6ea994869c944879678d33d7501d388f5a446a8c7de147df7" + } + ], + "jsonrpc" : "2.0" +} +``` + +### `event_missed` notification + +Never has any parameters. Example: + +``` +{ + "jsonrpc": "2.0", + "method": "event_missed", + "params": [] +} +``` diff --git a/docs/rpc.md b/docs/rpc.md index 9cae46a16..ba025c07d 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -65,6 +65,18 @@ which would yield the response: | `submitblock` | | `validateaddress` | +#### Implementation notices + +##### `invokefunction` and `invoke` + +neo-go's implementation of `invokefunction` and `invoke` does not return `tx` +field in the answer because that requires signing the transaction with some +key in the server which doesn't fit the model of our node-client interactions. +Lacking this signature the transaction is almost useless, so there is no point +in returning it. + +Both methods also don't currently support arrays in function parameters. + ### Unsupported methods Methods listed down below are not going to be supported for various reasons @@ -86,17 +98,24 @@ and we're not accepting issues related to them. | `sendmany` | Not applicable to neo-go, see `claimgas` comment | | `sendtoaddress` | Not applicable to neo-go, see `claimgas` comment | -#### Implementation notices +### Extensions -##### `invokefunction` and `invoke` +Some additional extensions are implemented as a part of this RPC server. -neo-go's implementation of `invokefunction` and `invoke` does not return `tx` -field in the answer because that requires signing the transaction with some -key in the server which doesn't fit the model of our node-client interactions. -Lacking this signature the transaction is almost useless, so there is no point -in returning it. +#### Websocket server -Both methods also don't currently support arrays in function parameters. +This server accepts websocket connections on `ws://$BASE_URL/ws` address. You +can use it to perform regular RPC calls over websockets (it's supposed to be a +little faster than going regular HTTP route) and you can also use it for +additional functionality provided only via websockets (like notifications). + +#### Notification subsystem + +Notification subsystem consists of two additional RPC methods (`subscribe` and +`unsubscribe` working only over websocket connection) that allow to subscribe +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). ## Reference diff --git a/docs/runtime.md b/docs/runtime.md deleted file mode 100644 index e98b3af20..000000000 --- a/docs/runtime.md +++ /dev/null @@ -1,523 +0,0 @@ -# Runtime -A brief overview of NEO smart contract API's that can be used in the neo-go framework. - -# Overview -1. [Account]() -2. [Asset]() -3. [Attribute]() -4. [Block]() -5. [Blockchain]() -6. [Contract]() -7. [Crypto]() -8. [Engine]() -9. [Enumerator]() -10. [Iterator]() -11. [Header]() -12. [Input]() -13. [Output]() -14. [Runtime]() -15. [Storage]() -16. [Transaction]() -17. [Util]() - -## Account -#### GetScriptHash -``` -GetScriptHash(a Account) []byte -``` -Returns the script hash of the given account. - -#### GetVotes -``` -GetVotes(a Account) [][]byte -``` -Returns the the votes (a slice of public keys) of the given account. - -#### GetBalance -``` -GetBalance(a Account, assetID []byte) int -``` -Returns the balance of the given asset id for the given account. - -## Asset -#### GetAssetID -``` -GetAssetID(a Asset) []byte -``` -Returns the id of the given asset. - -#### GetAmount -``` -GetAmount(a Asset) int -``` -Returns the amount of the given asset id. - -#### GetAvailable -``` -GetAvailable(a Asset) int -``` -Returns the available amount of the given asset. - -#### GetPrecision -``` -GetPrecision(a Asset) byte -``` -Returns the precision of the given Asset. - -#### GetOwner -``` -GetOwner(a Asset) []byte -``` -Returns the owner of the given asset. - -#### GetAdmin -``` -GetAdmin(a Asset) []byte -``` -Returns the admin of the given asset. - -#### GetIssuer -``` -GetIssuer(a Asset) []byte -``` -Returns the issuer of the given asset. - -#### Create -``` -Create(type byte, name string, amount int, precision byte, owner, admin, issuer []byte) -``` -Creates a new asset on the blockchain. - -#### Renew -``` -Renew(asset Asset, years int) -``` -Renews the given asset as long as the given years. - -## Attribute -#### GetUsage -``` -GetUsage(attr Attribute) []byte -``` -Returns the usage of the given attribute. - -#### GetData -``` -GetData(attr Attribute) []byte -``` -Returns the data of the given attribute. - -## Block -#### GetTransactionCount -``` -GetTransactionCount(b Block) int -``` -Returns the number of transactions that are recorded in the given block. - -#### GetTransactions -``` -GetTransactions(b Block) []transaction.Transaction -``` -Returns a slice of the transactions that are recorded in the given block. - -#### GetTransaction -``` -GetTransaction(b Block, hash []byte) transaction.Transaction -``` -Returns the transaction by the given hash that is recorded in the given block. - -## Blockchain -#### GetHeight -``` -GetHeight() int -``` -Returns the current height of the blockchain. - -#### GetHeader -``` -GetHeader(heightOrHash []interface{}) header.Header -``` -Return the header by the given hash or index. - -#### GetBlock -``` -GetBlock(heightOrHash interface{}) block.Block -``` -Returns the block by the given hash or index. - -#### GetTransaction -``` -GetTransaction(hash []byte) transaction.Transaction -``` -Returns a transaction by the given hash. - -#### GetContract -``` -GetContract(scriptHash []byte) contract.Contract -``` -Returns the contract found by the given script hash. - -#### GetAccount -``` -GetAccount(scriptHash []byte) account.Account -``` -Returns the account found by the given script hash. - -#### GetValiditors -``` -GetValidators() [][]byte -``` -Returns a list of validators public keys. - -#### GetAsset -``` -GetAsset(assetID []byte) asset.Asset -``` -Returns the asset found by the given asset id. - -## Contract -#### GetScript -``` -GetScript(c Contract) []byte -``` -Return the script of the given contract. - -#### IsPayable -``` -IsPayable(c Contract) bool -``` -Returns whether the given contract is payable. - -#### GetStorageContext -``` -GetStorageContext(c Contract) -``` -Returns the storage context of the given contract. - -#### Create -``` -Create( - script []byte, - params []interface{}, - returnType byte, - properties interface{}, - name, - version, - author, - email, - description string) -``` -Creates a new contract on the blockchain. - -#### Migrate -``` -Migrate( - script []byte, - params []interface{}, - returnType byte, - properties interface{}, - name, - version, - author, - email, - description string) -``` -Migrates a contract on the blockchain. - -#### Destroy -``` -Destroy(c Contract) -``` -Deletes the given contract from the blockchain. - -## Crypto -#### SHA1 -``` -SHA1(data []byte) []byte -``` -Computes the sha1 hash of the given bytes - -#### SHA256 -``` -SHA256(data []byte) []byte -``` -Computes the sha256 hash of the given bytes - -#### Hash256 -``` -Hash256(data []byte) []byte -``` -Computes the sha256^2 of the given data. - -#### Hash160 -``` -Hash160(data []byte) []byte) []byte -``` -Computes the ripemd160 over the sha256 hash of the given data. - -## Engine -#### GetScriptContainer -``` -GetScriptContainer() transaction.Transaction -``` -Returns the transaction that is in the context of the VM execution. - -#### GetExecutingScriptHash -``` -GetExecutingScriptHash() []byte -``` -Returns the script hash of the contract that is currently being executed. - -#### GetCallingScriptHash -``` -GetCallingScriptHash() []byte -``` -Returns the script hash of the contract that has started the execution of the current script. - -#### GetEntryScriptHash -``` -GetEntryScriptHash() []byte -``` -Returns the script hash of the contract that started the execution from the start. - -## Enumerator -#### Create -``` -Create(items []inteface{}) Enumerator -``` -Create a enumerator from the given items. - -#### Next -``` -Next(e Enumerator) interface{} -``` -Returns the next item from the given enumerator. - -#### Value -``` -Value(e Enumerator) interface{} -``` -Returns the enumerator value. - -## Iterator -#### Create -``` -Create(items []inteface{}) Iterator -``` -Creates an iterator from the given items. - -#### Key -``` -Key(it Iterator) interface{} -``` -Return the key from the given iterator. - -#### Keys -``` -Keys(it Iterator) []interface{} -``` -Returns the iterator's keys - -#### Values -``` -Values(it Iterator) []interface{} -``` -Returns the iterator's values - -## Header -#### GetIndex -``` -GetIndex(h Header) int -``` -Returns the height of the given header. - -#### GetHash -``` -GetHash(h Header) []byte -``` -Returns the hash of the given header. - -#### GetPrevHash -``` -GetPrevhash(h Header) []byte -``` -Returns the previous hash of the given header. - -#### GetTimestamp -``` -GetTimestamp(h Header) int -``` -Returns the timestamp of the given header. - -#### GetVersion -``` -GetVersion(h Header) int -``` -Returns the version of the given header. - -#### GetMerkleroot -``` -GetMerkleRoot(h Header) []byte -``` -Returns the merkle root of the given header. - -#### GetConsensusData -``` -GetConsensusData(h Header) int -``` -Returns the consensus data of the given header. - -#### GetNextConsensus -``` -GetNextConsensus(h Header) []byte -``` -Returns the next consensus of the given header. - -## Input -#### GetHash -``` -GetHash(in Input) []byte -``` -Returns the hash field of the given input. - -#### GetIndex -``` -GetIndex(in Input) int -``` -Returns the index field of the given input. - -## Output -#### GetAssetID -``` -GetAssetId(out Output) []byte -``` -Returns the asset id field of the given output. - -#### GetValue -``` -GetValue(out Output) int -``` -Returns the value field of the given output. - -#### GetScriptHash -``` -GetScriptHash(out Output) []byte -``` -Returns the script hash field of the given output. - -## Runtime -#### CheckWitness -``` -CheckWitness(hash []byte) bool -``` -Verifies if the given hash is the hash of the contract owner. - -#### Log -``` -Log(message string) -``` -Logs the given message. - -#### Notify -``` -Notify(args ...interface{}) int -``` -Notify any number of arguments to the VM. - -#### GetTime -``` -GetTime() int -``` -Returns the current time based on the highest block in the chain. - -#### GetTrigger -``` -GetTrigger() byte -``` -Returns the trigger type of the execution. - -#### Serialize -``` -Serialize(item interface{}) []byte -``` -Serialize the given stack item to a slice of bytes. - -#### Deserialize -``` -Deserialize(data []byte) interface{} -``` -Deserializes the given data to a stack item. - -## Storage -#### GetContext -``` -GetContext() Context -``` -Returns the current storage context. - -#### Put -``` -Put(ctx Context, key, value []interface{}) -``` -Stores the given value at the given key. - -#### Get -``` -Get(ctx Context, key interface{}) interface{} -``` -Returns the value found at the given key. - -#### Delete -``` -Delete(ctx Context, key interface{}) -``` -Delete's the given key from storage. - -#### Find -``` -Find(ctx Context, key interface{}) iterator.Iterator -``` -Find returns an iterator key-values that match the given key. - -## Transaction -#### GetHash -``` -GetHash(t Transacfion) []byte -``` -Returns the hash for the given transaction. - -#### GetType -``` -GetType(t Transacfion) byte -``` -Returns the type of the given transaction. - -#### GetAttributes -``` -GetAttributes(t Transacfion) []attribute.Attribute -``` -Returns the attributes of the given transaction. - -#### GetReferences -``` -GetReferences(t Transacfion) interface{} -``` -Returns the references of the given transaction. - -#### GetUnspentCoins -``` -GetUnspentCoins(t Transacfion) interface{} -``` -Returns the unspent coins of the given transaction. - -#### GetOutputs -``` -GetOutputs(t Transacfion) []output.Output -``` -Returns the outputs of the given transaction - -#### GetInputs -``` -GetInputs(t Transacfion) []input.Input -``` -Returns the inputs of the given transaction diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 34134c6d5..bf4430c74 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -16,7 +16,7 @@ var ( "SHA1", "Hash256", "Hash160", "VerifySignature", "AppCall", "FromAddress", "Equals", - "panic", + "panic", "DynAppCall", } ) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index a88f56cf6..c2c312c6b 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1085,14 +1085,23 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { emit.Opcode(c.prog.BinWriter, opcode.HASH160) case "VerifySignature": emit.Opcode(c.prog.BinWriter, opcode.VERIFY) - case "AppCall": - numArgs := len(expr.Args) - 1 + case "AppCall", "DynAppCall": + numArgs := len(expr.Args) + if name == "AppCall" { + numArgs-- + } c.emitReverse(numArgs) emit.Opcode(c.prog.BinWriter, opcode.APPCALL) - buf := c.getByteArray(expr.Args[0]) - if len(buf) != 20 { - c.prog.Err = errors.New("invalid script hash") + var buf []byte + if name == "AppCall" { + buf = c.getByteArray(expr.Args[0]) + if len(buf) != 20 { + c.prog.Err = errors.New("invalid script hash") + } + } else { + // Zeroes for DynAppCall. + buf = make([]byte, 20) } c.prog.WriteBytes(buf) diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 39435c243..e335f5882 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/stretchr/testify/require" ) @@ -52,6 +53,19 @@ func TestFromAddress(t *testing.T) { } func TestAppCall(t *testing.T) { + const srcDynApp = ` + package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/engine" + func Main(h []byte) []byte { + x := []byte{1, 2} + y := []byte{3, 4} + result := engine.DynAppCall(h, x, y) + return result.([]byte) + } +` + + var hasDynamicInvoke bool + srcInner := ` package foo func Main(a []byte, b []byte) []byte { @@ -62,14 +76,31 @@ func TestAppCall(t *testing.T) { inner, err := compiler.Compile(strings.NewReader(srcInner)) require.NoError(t, err) + dynapp, err := compiler.Compile(strings.NewReader(srcDynApp)) + require.NoError(t, err) + ih := hash.Hash160(inner) + dh := hash.Hash160(dynapp) getScript := func(u util.Uint160) ([]byte, bool) { if u.Equals(ih) { return inner, true } + if u.Equals(dh) { + return dynapp, hasDynamicInvoke + } return nil, false } + dynEntryScript := ` + package foo + import "github.com/nspcc-dev/neo-go/pkg/interop/engine" + func Main(h []byte) interface{} { + return engine.AppCall(` + fmt.Sprintf("%#v", dh.BytesBE()) + `, h) + } +` + dynentry, err := compiler.Compile(strings.NewReader(dynEntryScript)) + require.NoError(t, err) + t.Run("valid script", func(t *testing.T) { src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE())) v := vmAndCompile(t, src) @@ -118,6 +149,38 @@ func TestAppCall(t *testing.T) { assertResult(t, v, []byte{1, 2, 3, 4}) }) + + t.Run("dynamic", func(t *testing.T) { + t.Run("valid script", func(t *testing.T) { + hasDynamicInvoke = true + v := vm.New() + v.Load(dynentry) + v.SetScriptGetter(getScript) + v.Estack().PushVal(ih.BytesBE()) + + require.NoError(t, v.Run()) + + assertResult(t, v, []byte{1, 2, 3, 4}) + }) + t.Run("invalid script", func(t *testing.T) { + hasDynamicInvoke = true + v := vm.New() + v.Load(dynentry) + v.SetScriptGetter(getScript) + v.Estack().PushVal([]byte{1}) + + require.Error(t, v.Run()) + }) + t.Run("no dynamic invoke", func(t *testing.T) { + hasDynamicInvoke = false + v := vm.New() + v.Load(dynentry) + v.SetScriptGetter(getScript) + v.Estack().PushVal(ih.BytesBE()) + + require.Error(t, v.Run()) + }) + }) } func getAppCallScript(h string) string { diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 01234955b..26307270d 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -1,12 +1,30 @@ package compiler var syscalls = map[string]map[string]string{ + "account": { + "GetBalance": "Neo.Account.GetBalance", + "GetScriptHash": "Neo.Account.GetScriptHash", + "GetVotes": "Neo.Account.GetVotes", + "IsStandard": "Neo.Account.IsStandard", + }, + "attribute": { + "GetUsage": "Neo.Attribute.GetUsage", + "GetData": "Neo.Attribute.GetData", + }, + "enumerator": { + "Concat": "Neo.Enumerator.Concat", + "Create": "Neo.Enumerator.Create", + "Next": "Neo.Enumerator.Next", + "Value": "Neo.Enumerator.Value", + }, "storage": { - "GetContext": "Neo.Storage.GetContext", - "Put": "Neo.Storage.Put", - "Get": "Neo.Storage.Get", - "Delete": "Neo.Storage.Delete", - "Find": "Neo.Storage.Find", + "ConvertContextToReadOnly": "Neo.StorageContext.AsReadOnly", + "Delete": "Neo.Storage.Delete", + "Find": "Neo.Storage.Find", + "Get": "Neo.Storage.Get", + "GetContext": "Neo.Storage.GetContext", + "GetReadOnlyContext": "Neo.Storage.GetReadOnlyContext", + "Put": "Neo.Storage.Put", }, "runtime": { "GetTrigger": "Neo.Runtime.GetTrigger", @@ -18,14 +36,15 @@ var syscalls = map[string]map[string]string{ "Deserialize": "Neo.Runtime.Deserialize", }, "blockchain": { - "GetHeight": "Neo.Blockchain.GetHeight", - "GetHeader": "Neo.Blockchain.GetHeader", - "GetBlock": "Neo.Blockchain.GetBlock", - "GetTransaction": "Neo.Blockchain.GetTransaction", - "GetContract": "Neo.Blockchain.GetContract", - "GetAccount": "Neo.Blockchain.GetAccount", - "GetValidators": "Neo.Blockchain.GetValidators", - "GetAsset": "Neo.Blockchain.GetAsset", + "GetAccount": "Neo.Blockchain.GetAccount", + "GetAsset": "Neo.Blockchain.GetAsset", + "GetBlock": "Neo.Blockchain.GetBlock", + "GetContract": "Neo.Blockchain.GetContract", + "GetHeader": "Neo.Blockchain.GetHeader", + "GetHeight": "Neo.Blockchain.GetHeight", + "GetTransaction": "Neo.Blockchain.GetTransaction", + "GetTransactionHeight": "Neo.Blockchain.GetTransactionHeight", + "GetValidators": "Neo.Blockchain.GetValidators", }, "header": { "GetIndex": "Neo.Header.GetIndex", @@ -43,20 +62,25 @@ var syscalls = map[string]map[string]string{ "GetTransaction": "Neo.Block.GetTransaction", }, "transaction": { - "GetHash": "Neo.Transaction.GetHash", - "GetType": "Neo.Transaction.GetType", - "GetAttributes": "Neo.Transaction.GetAttributes", - "GetInputs": "Neo.Transaction.GetInputs", - "GetOutputs": "Neo.Transaction.GetOutputs", - "GetReferences": "Neo.Transaction.GetReferences", - "GetUnspentCoins": "Neo.Transaction.GetUnspentCoins", - "GetScript": "Neo.Transaction.GetScript", + "GetAttributes": "Neo.Transaction.GetAttributes", + "GetHash": "Neo.Transaction.GetHash", + "GetInputs": "Neo.Transaction.GetInputs", + "GetOutputs": "Neo.Transaction.GetOutputs", + "GetReferences": "Neo.Transaction.GetReferences", + "GetScript": "Neo.InvocationTransaction.GetScript", + "GetType": "Neo.Transaction.GetType", + "GetWitnesses": "Neo.Transaction.GetWitnesses", }, "asset": { + "Create": "Neo.Asset.Create", + "GetAdmin": "Neo.Asset.GetAdmin", + "GetAmount": "Neo.Asset.GetAmount", "GetAssetID": "Neo.Asset.GetAssetID", "GetAssetType": "Neo.Asset.GetAssetType", - "GetAmount": "Neo.Asset.GetAmount", - "Create": "Neo.Asset.Create", + "GetAvailable": "Neo.Asset.GetAvailable", + "GetIssuer": "Neo.Asset.GetIssuer", + "GetOwner": "Neo.Asset.GetOwner", + "GetPrecision": "Neo.Asset.GetPrecision", "Renew": "Neo.Asset.Renew", }, "contract": { @@ -83,6 +107,7 @@ var syscalls = map[string]map[string]string{ "GetExecutingScriptHash": "System.ExecutionEngine.GetExecutingScriptHash", }, "iterator": { + "Concat": "Neo.Iterator.Concat", "Create": "Neo.Iterator.Create", "Key": "Neo.Iterator.Key", "Keys": "Neo.Iterator.Keys", @@ -90,4 +115,7 @@ var syscalls = map[string]map[string]string{ "Value": "Neo.Iterator.Value", "Values": "Neo.Iterator.Values", }, + "witness": { + "GetVerificationScript": "Neo.Witness.GetVerificationScript", + }, } diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 277e06620..052166555 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -236,7 +236,14 @@ func (ic *interopContext) witnessGetVerificationScript(v *vm.VM) error { // bcGetValidators returns validators. func (ic *interopContext) bcGetValidators(v *vm.VM) error { - validators := ic.dao.GetValidators() + valStates := ic.dao.GetValidators() + if len(valStates) > vm.MaxArraySize { + return errors.New("too many validators") + } + validators := make([]vm.StackItem, 0, len(valStates)) + for _, val := range valStates { + validators = append(validators, vm.NewByteArrayItem(val.PublicKey.Bytes())) + } v.Estack().PushVal(validators) return nil } diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index f07da9905..9c4da985f 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -575,8 +575,12 @@ func (ic *interopContext) contractGetStorageContext(v *vm.VM) error { if !ok { return fmt.Errorf("%T is not a contract state", cs) } - contractState, err := ic.dao.GetContractState(cs.ScriptHash()) - if contractState == nil || err != nil { + _, err := ic.dao.GetContractState(cs.ScriptHash()) + if err != nil { + return fmt.Errorf("non-existent contract") + } + _, err = ic.lowerDao.GetContractState(cs.ScriptHash()) + if err == nil { return fmt.Errorf("contract was not created in this transaction") } stc := &StorageContext{ diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 445be4d76..25040bce6 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -27,6 +27,7 @@ type interopContext struct { block *block.Block tx *transaction.Transaction dao *dao.Cached + lowerDao dao.DAO notifications []state.NotificationEvent log *zap.Logger } @@ -34,7 +35,7 @@ type interopContext struct { func newInteropContext(trigger trigger.Type, bc Blockchainer, d dao.DAO, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *interopContext { dao := dao.NewCached(d) nes := make([]state.NotificationEvent, 0) - return &interopContext{bc, trigger, block, tx, dao, nes, log} + return &interopContext{bc, trigger, block, tx, dao, d, nes, log} } // SpawnVM returns a VM with script getter and interop functions set diff --git a/pkg/interop/account/account.go b/pkg/interop/account/account.go index c2e4cb19c..f0595eec0 100644 --- a/pkg/interop/account/account.go +++ b/pkg/interop/account/account.go @@ -1,23 +1,43 @@ +/* +Package account provides getter functions for Account interop structure. +To use these functions you need to get an Account first via blockchain.GetAccount +call. +*/ package account -// Package account provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Account stubs a NEO account type. +// Account represents NEO account type that is used in interop functions, it's +// an opaque data structure that you can get data from only using functions from +// this package. It's similar in function to the Account class in the Neo .net +// framework. type Account struct{} -// GetScriptHash returns the script hash of the given account. +// GetScriptHash returns the script hash of the given Account (20 bytes in BE +// representation). It uses `Neo.Account.GetBalance` syscall internally. func GetScriptHash(a Account) []byte { return nil } -// GetVotes returns the votes of the given account which should be a slice of -// public key raw bytes. +// GetVotes returns current votes of the given account represented as a slice of +// public keys. Keys are serialized into byte slices in their compressed form (33 +// bytes long each). This function uses `Neo.Account.GetVotes` syscall +// internally. func GetVotes(a Account) [][]byte { return nil } -// GetBalance returns the balance of for the given account and asset id. +// GetBalance returns current balance of the given asset (by its ID, 256 bit +// hash in BE form) for the given account. Only native UTXO assets can be +// queiried via this function, for NEP-5 ones use respective contract calls. +// The value returned is represented as an integer with original value multiplied +// by 10⁸ so you can work with fractional parts of the balance too. This function +// uses `Neo.Account.GetBalance` syscall internally. func GetBalance(a Account, assetID []byte) int { return 0 } + +// IsStandard checks whether given account uses standard (CHECKSIG or +// CHECKMULTISIG) contract. It only works for deployed contracts and uses +// `Neo.Account.IsStandard` syscall internally. +func IsStandard(a Account) bool { + return false +} diff --git a/pkg/interop/asset/asset.go b/pkg/interop/asset/asset.go index 3c84ea367..01547f64c 100644 --- a/pkg/interop/asset/asset.go +++ b/pkg/interop/asset/asset.go @@ -1,53 +1,97 @@ +/* +Package asset provides functions to work with regular UTXO assets (like NEO or GAS). +Mostly these are getters for Asset structure, but you can also create new assets +and renew them (although it's recommended to use NEP-5 standard for new tokens). +*/ package asset -// Package asset provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Asset stubs a NEO asset type. +// Asset represents NEO asset type that is used in interop functions, it's +// an opaque data structure that you can get data from only using functions from +// this package. It's similar in function to the Asset class in the Neo .net +// framework. To be able to use it you either need to get an existing Asset via +// blockchain.GetAsset function or create a new one via Create. type Asset struct{} -// GetAssetID returns the id of the given asset. +// GetAssetID returns ID (256-bit ID of Register transaction for this asset in BE +// representation) of the given asset. It uses `Neo.Asset.GetAssetId` syscall +// internally. func GetAssetID(a Asset) []byte { return nil } -// GetAssetType returns the type of the given asset. +// GetAssetType returns type of the given asset as a byte value. The value +// returned can be interpreted as a bit field with the following meaning: +// CreditFlag = 0x40 +// DutyFlag = 0x80 +// SystemShare = 0x00 +// SystemCoin = 0x01 +// Currency = 0x08 +// Share = DutyFlag | 0x10 +// Invoice = DutyFlag | 0x18 +// Token = CreditFlag | 0x20 +// It uses `Neo.Asset.GetAssetType` syscall internally. func GetAssetType(a Asset) byte { return 0x00 } -// GetAmount returns the amount of the given asset. +// GetAmount returns the total amount of the given asset as an integer +// multiplied by 10⁸. This value is the maximum possible circulating quantity of +// Asset. The function uses `Neo.Asset.GetAmount` syscall internally. func GetAmount(a Asset) int { return 0 } -// GetAvailable returns the available of the given asset. +// GetAvailable returns the amount of Asset currently available on the +// blockchain. It uses the same encoding as the result of GetAmount and its +// value can never exceed the value returned by GetAmount. This function uses +// `Neo.Asset.GetAvailable` syscall internally. func GetAvailable(a Asset) int { return 0 } -// GetPrecision returns the precision of the given asset. +// GetPrecision returns precision of the given Asset. It uses +// `Neo.Asset.GetPrecision` syscall internally. func GetPrecision(a Asset) byte { return 0x00 } -// GetOwner returns the owner of the given asset. +// GetOwner returns the owner of the given Asset. It's represented as a +// serialized (in compressed form) public key (33 bytes long). This function +// uses `Neo.Asset.GetOwner` syscall internally. func GetOwner(a Asset) []byte { return nil } -// GetAdmin returns the admin of the given asset. +// GetAdmin returns the admin of the given Asset represented as a 160 bit hash +// in BE form (contract script hash). Admin can modify attributes of this Asset. +// This function uses `Neo.Asset.GetAdmin` syscall internally. func GetAdmin(a Asset) []byte { return nil } -// GetIssuer returns the issuer of the given asset. +// GetIssuer returns the issuer of the given Asset represented as a 160 bit hash +// in BE form (contract script hash). Issuer can issue new tokens for this Asset. +// This function uses `Neo.Asset.GetIssuer` syscall internally. func GetIssuer(a Asset) []byte { return nil } -// Create registers a new asset on the blockchain. -func Create(assetType byte, name string, amount int, precision byte, owner, admin, issuer []byte) {} +// Create registers a new asset on the blockchain (similar to old Register +// transaction). `assetType` parameter has the same set of possible values as +// GetAssetType result, `amount` must be multiplied by 10⁸, `precision` limits +// the smallest possible amount of new Asset to 10⁻ⁿ (where n is precision which +// can't exceed 8), `owner` is a public key of the owner in compressed serialized +// form (33 bytes), `admin` and `issuer` should be represented as 20-byte slices +// storing 160-bit hash in BE form. Created Asset is set to expire in one year, +// so you need to renew it in time. If successful, this function returns a new +// Asset. It uses `Neo.Asset.Create` syscall internally. +func Create(assetType byte, name string, amount int, precision byte, owner, admin, issuer []byte) Asset { + return Asset{} +} -// Renew renews the existence of an asset by the given years. -func Renew(asset Asset, years int) {} +// Renew renews (make available for use) existing asset by the specified number +// of years. It returns the last block number when this asset will be active. +// It uses `Neo.Asset.Renew` syscall internally. +func Renew(asset Asset, years int) int { + return 0 +} diff --git a/pkg/interop/attribute/attribute.go b/pkg/interop/attribute/attribute.go index 6147b5be2..b4c0175c4 100644 --- a/pkg/interop/attribute/attribute.go +++ b/pkg/interop/attribute/attribute.go @@ -1,17 +1,65 @@ +/* +Package attribute provides getters for transaction attributes. +*/ package attribute -// Package attribute provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Attribute stubs a NEO transaction attribute type. +// Attribute represents transaction attribute in Neo, it's an opaque data +// structure that you can get data from only using functions from this package. +// It's similar in function to the TransactionAttribute class in the Neo .net +// framework. To use it you need to get is first using transaction.GetAttributes. type Attribute struct{} -// GetUsage returns the usage of the given attribute. +// GetUsage returns the Usage field of the given attribute. It is an enumeration +// with the following possible values: +// ContractHash = 0x00 +// ECDH02 = 0x02 +// ECDH03 = 0x03 +// Script = 0x20 +// Vote = 0x30 +// CertURL = 0x80 +// DescriptionURL = 0x81 +// Description = 0x90 +// +// Hash1 = 0xa1 +// Hash2 = 0xa2 +// Hash3 = 0xa3 +// Hash4 = 0xa4 +// Hash5 = 0xa5 +// Hash6 = 0xa6 +// Hash7 = 0xa7 +// Hash8 = 0xa8 +// Hash9 = 0xa9 +// Hash10 = 0xaa +// Hash11 = 0xab +// Hash12 = 0xac +// Hash13 = 0xad +// Hash14 = 0xae +// Hash15 = 0xaf +// +// Remark = 0xf0 +// Remark1 = 0xf1 +// Remark2 = 0xf2 +// Remark3 = 0xf3 +// Remark4 = 0xf4 +// Remark5 = 0xf5 +// Remark6 = 0xf6 +// Remark7 = 0xf7 +// Remark8 = 0xf8 +// Remark9 = 0xf9 +// Remark10 = 0xfa +// Remark11 = 0xfb +// Remark12 = 0xfc +// Remark13 = 0xfd +// Remark14 = 0xfe +// Remark15 = 0xff +// This function uses `Neo.Attribute.GetUsage` syscall internally. func GetUsage(attr Attribute) byte { return 0x00 } -// GetData returns the data of the given attribute. +// GetData returns the data of the given attribute, exact interpretation of this +// data depends on attribute's Usage type. It uses `Neo.Attribute.GetData` +// syscall internally. func GetData(attr Attribute) []byte { return nil } diff --git a/pkg/interop/block/block.go b/pkg/interop/block/block.go index e7171614a..98cd3a50d 100644 --- a/pkg/interop/block/block.go +++ b/pkg/interop/block/block.go @@ -1,25 +1,30 @@ +/* +Package block provides getters for Neo Block structure. +*/ package block import "github.com/nspcc-dev/neo-go/pkg/interop/transaction" -// Package block provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Block stubs a NEO block type. +// Block represents a NEO block, it's an opaque data structure that you can get +// data from only using functions from this package. It's similar in function to +// the Block class in the Neo .net framework. To use it you need to get it via +// blockchain.GetBlock function call. type Block struct{} -// GetTransactionCount returns the number of recorded transactions in the given block. +// GetTransactionCount returns the number of recorded transactions in the given +// block. It uses `Neo.Block.GetTransactionCount` syscall internally. func GetTransactionCount(b Block) int { return 0 } // GetTransactions returns a slice of transactions recorded in the given block. +// It uses `Neo.Block.GetTransactions` syscall internally. func GetTransactions(b Block) []transaction.Transaction { return []transaction.Transaction{} } -// GetTransaction returns a transaction from the given a block hash of the -// transaction. -func GetTransaction(b Block, hash []byte) transaction.Transaction { +// GetTransaction returns transaction from the given block by its index. It +// uses `Neo.Block.GetTransaction` syscall internally. +func GetTransaction(b Block, index int) transaction.Transaction { return transaction.Transaction{} } diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index 18ae7216b..c38e4dd7d 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -1,3 +1,6 @@ +/* +Package blockchain provides functions to access various blockchain data. +*/ package blockchain import ( @@ -9,45 +12,72 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/transaction" ) -// Package blockchain provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// GetHeight returns the height of te block recorded in the current execution scope. +// GetHeight returns current block height (index of the last accepted block). +// Note that when transaction is being run as a part of new block this block is +// considered as not yet accepted (persisted) and thus you'll get an index of +// the previous (already accepted) block. This function uses +// `Neo.Blockchain.GetHeight` syscall. func GetHeight() int { return 0 } -// GetHeader returns the header found by the given hash or index. +// GetHeader returns header found by the given hash (256 bit hash in BE format +// represented as a slice of 32 bytes) or index (integer). Refer to the `header` +// package for possible uses of returned structure. This function uses +// `Neo.Blockchain.GetHeader` syscall. func GetHeader(heightOrHash interface{}) header.Header { return header.Header{} } -// GetBlock returns the block found by the given hash or index. +// GetBlock returns block found by the given hash or index (with the same +// encoding as for GetHeader). Refer to the `block` package for possible uses +// of returned structure. This function uses `Neo.Blockchain.GetBlock` syscall. func GetBlock(heightOrHash interface{}) block.Block { return block.Block{} } -// GetTransaction returns the transaction found by the given hash. +// GetTransaction returns transaction found by the given (256 bit in BE format +// represented as a slice of 32 bytes). Refer to the `transaction` package for +// possible uses of returned structure. This function uses +// `Neo.Blockchain.GetTransaction` syscall. func GetTransaction(hash []byte) transaction.Transaction { return transaction.Transaction{} } -// GetContract returns the contract found by the given script hash. +// GetTransactionHeight returns transaction's height (index of the block that +// includes it) by the given ID (256 bit in BE format represented as a slice of +// 32 bytes). This function uses `Neo.Blockchain.GetTransactionHeight` syscall. +func GetTransactionHeight(hash []byte) int { + return 0 +} + +// GetContract returns contract found by the given script hash (160 bit in BE +// format represented as a slice of 20 bytes). Refer to the `contract` package +// for details on how to use the returned structure. This function uses +// `Neo.Blockchain.GetContract` syscall. func GetContract(scriptHash []byte) contract.Contract { return contract.Contract{} } -// GetAccount returns the account found by the given script hash. +// GetAccount returns account found by the given script hash (160 bit in BE +// format represented as a slice of 20 bytes). Refer to the `account` package +// for details on how to use the returned structure. This function uses +// `Neo.Blockchain.GetAccount` syscall. func GetAccount(scriptHash []byte) account.Account { return account.Account{} } -// GetValidators returns a slice of validator addresses. +// GetValidators returns a slice of current validators public keys represented +// as a compressed serialized byte slice (33 bytes long). This function uses +// `Neo.Blockchain.GetValidators` syscall. func GetValidators() [][]byte { return nil } -// GetAsset returns the asset found by the given asset id. +// GetAsset returns asset found by the given asset ID (256 bit in BE format +// represented as a slice of 32 bytes). Refer to the `asset` package for +// possible uses of returned structure. This function uses +// `Neo.Blockchain.GetAsset` syscall. func GetAsset(assetID []byte) asset.Asset { return asset.Asset{} } diff --git a/pkg/interop/contract/contract.go b/pkg/interop/contract/contract.go index f1d6af376..d220b85e2 100644 --- a/pkg/interop/contract/contract.go +++ b/pkg/interop/contract/contract.go @@ -1,55 +1,85 @@ +/* +Package contract provides functions to work with contracts. +*/ package contract import "github.com/nspcc-dev/neo-go/pkg/interop/storage" -// Package contract provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Contract stubs a NEO contract type. +// Contract represents a Neo contract and is used in interop functions. It's +// an opaque data structure that you can manipulate with using functions from +// this package. It's similar in function to the Contract class in the Neo .net +// framework. type Contract struct{} -// GetScript returns the script of the given contract. +// GetScript returns the script of the given contract. It uses +// `Neo.Contract.GetScript` syscall. func GetScript(c Contract) []byte { return nil } -// IsPayable returns whether the given contract is payable. +// IsPayable returns whether the given contract is payable (able to accept +// asset transfers to its address). It uses `Neo.Contract.IsPayable` syscall. func IsPayable(c Contract) bool { return false } -// GetStorageContext returns the storage context for the given contract. +// GetStorageContext returns storage context for the given contract. It only +// works for contracts created in this transaction (so you can't take a storage +// context for arbitrary contract). Refer to the `storage` package on how to +// use this context. This function uses `Neo.Contract.GetStorageContext` syscall. func GetStorageContext(c Contract) storage.Context { return storage.Context{} } -// Create creates a new contract. -// @FIXME What is the type of the returnType here? +// Create creates a new contract using a set of input parameters: +// script contract's bytecode (limited in length by 1M) +// params contract's input parameter types, one byte per parameter, see +// ParamType in the `smartcontract` package for value +// definitions. Maximum number of parameters: 252. +// returnType return value type, also a ParamType constant +// properties bit field with contract's permissions (storage, dynamic +// invoke, payable), see PropertyState in the `smartcontract` +// package +// name human-readable contract name (no longer than 252 bytes) +// version human-readable contract version (no longer than 252 bytes) +// author contract's author (no longer than 252 bytes) +// email contract's author/support e-mail (no longer than 252 bytes) +// description human-readable contract description (no longer than 64K bytes) +// It returns this new created Contract when successful (and fails transaction +// if not). It uses `Neo.Contract.Create` syscall. func Create( script []byte, - params []interface{}, + params []byte, returnType byte, - properties interface{}, + properties byte, name, version, author, email, - description string) { + description string) Contract { + return Contract{} } -// Migrate migrates a new contract. -// @FIXME What is the type of the returnType here? +// Migrate migrates calling contract (that is the one that calls Migrate) to +// the new contract. Its parameters have exactly the same semantics as for +// Create. The old contract will be deleted by this call, if it has any storage +// associated it will be migrated to the new contract. New contract is returned. +// This function uses `Neo.Contract.Migrate` syscall. func Migrate( script []byte, - params []interface{}, + params []byte, returnType byte, - properties interface{}, + properties byte, name, version, author, email, - description string) { + description string) Contract { + return Contract{} } -// Destroy deletes a contract that is registered on the blockchain. -func Destroy(c Contract) {} +// Destroy deletes calling contract (the one that calls Destroy) from the +// blockchain, so it's only possible to do that from the contract itself and +// not by any outside code. When contract is deleted all associated storage +// items are deleted too. This function uses `Neo.Contract.Destroy` syscall. +func Destroy() {} diff --git a/pkg/interop/crypto/crypto.go b/pkg/interop/crypto/crypto.go index abfe586e8..5dbee46ed 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -1,29 +1,32 @@ +/* +Package crypto provides an interface to VM cryptographic instructions. +*/ package crypto -// Package crypto provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// SHA1 computes the sha1 hash of b. +// SHA1 computes SHA1 hash of b. It uses `SHA1` VM instruction. func SHA1(b []byte) []byte { return nil } -// SHA256 computes the sha256 hash of b. +// SHA256 computes SHA256 hash of b. It uses `SHA256` VM instruction. func SHA256(b []byte) []byte { return nil } -// Hash160 computes the sha256 + ripemd160 of b. +// Hash160 computes SHA256 + RIPEMD-160 of b, which is commonly used for +// script hashing and address generation. It uses `HASH160` VM instruction. func Hash160(b []byte) []byte { return nil } -// Hash256 computes the sha256^2 hash of b. +// Hash256 computes double SHA256 hash of b (SHA256(SHA256(b))) which is used +// as ID for transactions, blocks and assets. It uses `HASH256` VM instruction. func Hash256(b []byte) []byte { return nil } -// VerifySignature checks that sig is msg's signature with pub. +// VerifySignature checks that sig is correct msg's signature for a given pub +// (serialized public key). It uses `VERIFY` VM instruction. func VerifySignature(msg []byte, sig []byte, pub []byte) bool { return false } diff --git a/pkg/interop/doc.go b/pkg/interop/doc.go new file mode 100644 index 000000000..56ddc3081 --- /dev/null +++ b/pkg/interop/doc.go @@ -0,0 +1,14 @@ +/* +Package interop contains smart contract API functions. +Its subpackages can be imported into smart contracts written in Go to provide +various functionality. Upon compilation, functions from these packages will +be substituted with appropriate NeoVM system calls implemented by Neo. Usually +these system calls have additional price in NeoVM, so they're explicitly written +in the documentation of respective functions. + +Note that unless written otherwise structures defined in this packages can't be +correctly created by new() or composite literals, they should be received from +some interop functions (and then used as parameters for some other interop +functions). +*/ +package interop diff --git a/pkg/interop/engine/engine.go b/pkg/interop/engine/engine.go index 06a54e3fc..92ec895f0 100644 --- a/pkg/interop/engine/engine.go +++ b/pkg/interop/engine/engine.go @@ -1,34 +1,66 @@ +/* +Package engine provides access to VM execution metadata and allows to make contract calls. +It's roughly similar in function to ExecutionEngine class in the Neo .net +framework. +*/ package engine import "github.com/nspcc-dev/neo-go/pkg/interop/transaction" -// Package engine provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// GetScriptContainer returns the transaction that is in the execution context. +// GetScriptContainer returns the transaction that initially triggered current +// execution context. It never changes in a single execution, no matter how deep +// this execution goes. See `transaction` package for details on how to use the +// returned value. This function uses `System.ExecutionEngine.GetScriptContainer` +// syscall. func GetScriptContainer() transaction.Transaction { return transaction.Transaction{} } -// GetExecutingScriptHash returns the script hash of the contract that is -// currently being executed. +// GetExecutingScriptHash returns script hash (160 bit in BE form represented +// as 20-byte slice) of the contract that is currently being executed. Any +// AppCall can change the value returned by this function if it calls a +// different contract. This function uses +// `System.ExecutionEngine.GetExecutingScriptHash` syscall. func GetExecutingScriptHash() []byte { return nil } -// GetCallingScriptHash returns the script hash of the contract that started -// the execution of the current script. +// GetCallingScriptHash returns script hash (160 bit in BE form represented +// as 20-byte slice) of the contract that started the execution of the currently +// running context (caller of current contract or function), so it's one level +// above the GetExecutingScriptHash in the call stack. It uses +// `System.ExecutionEngine.GetCallingScriptHash` syscall. func GetCallingScriptHash() []byte { return nil } -// GetEntryScriptHash returns the script hash of the contract that started the -// execution from the start. +// GetEntryScriptHash returns script hash (160 bit in BE form represented +// as 20-byte slice) of the contract that initially started current execution +// (this is a script that is contained in a transaction returned by +// GetScriptContainer) execution from the start. This function uses +// `System.ExecutionEngine.GetEntryScriptHash` syscall. func GetEntryScriptHash() []byte { return nil } -// AppCall executes script with specified hash using provided arguments. +// AppCall executes previously deployed blockchain contract with specified hash +// (160 bit in BE form represented as 20-byte slice) using provided arguments. +// It returns whatever this contract returns. Even though this function accepts +// a slice for scriptHash you can only use it for contracts known at +// compile time, because there is a significant difference between static and +// dynamic calls in Neo (contracts should have a special property declared +// and paid for to be able to use dynamic calls). This function uses `APPCALL` +// opcode. func AppCall(scriptHash []byte, args ...interface{}) interface{} { return nil } + +// DynAppCall executes previously deployed blockchain contract with specified +// hash (160 bit in BE form represented as 20-byte slice) using provided +// arguments. It returns whatever this contract returns. It differs from AppCall +// in that you can use it for truly dynamic scriptHash values, but at the same +// time using it requires HasDynamicInvoke property set for a contract doing +// this call. This function uses `APPCALL` opcode. +func DynAppCall(scriptHash []byte, args ...interface{}) interface{} { + return nil +} diff --git a/pkg/interop/enumerator/enumerator.go b/pkg/interop/enumerator/enumerator.go index 0d5531757..a47bff76f 100644 --- a/pkg/interop/enumerator/enumerator.go +++ b/pkg/interop/enumerator/enumerator.go @@ -1,29 +1,41 @@ +/* +Package enumerator provides functions to work with enumerators. +*/ package enumerator -// Package enumerator provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// TODO: Check enumerator use cases and add them to the examples folder. - -// Enumerator stubs a NEO enumerator type. +// Enumerator represents NEO enumerator type, it's an opaque data structure +// that can be used with functions from this package. It's similar to more +// widely used Iterator (see `iterator` package), but ranging over arrays +// or structures that have values with no explicit keys. type Enumerator struct{} -// Create creates a new enumerator from the given items. +// Create creates a new enumerator from the given items (slice or structure). +// New enumerator points at index -1 of its items, so the user of it has to +// advance it first with Next. This function uses `Neo.Enumerator.Create` +// syscall. func Create(items []interface{}) Enumerator { return Enumerator{} } -// Next returns the next item in the iteration. -func Next(e Enumerator) interface{} { - return nil +// Next moves position of the given enumerator by one and returns a bool that +// tells whether there is a new value present in this new position. If it is, +// you can use Value to get it, if not then there are no more values in this +// enumerator. This function uses `Neo.Enumerator.Next` syscall. +func Next(e Enumerator) bool { + return true } -// Value returns the enumerator value. +// Value returns current enumerator's item value, it's only valid to call it +// after Next returning true. This function uses `Neo.Enumerator.Value` syscall. func Value(e Enumerator) interface{} { return nil } -// Concat concatenates the 2 given enumerators. +// Concat concatenates two given enumerators returning one that will range on +// a first and then continue with b. Enumerator positions are not reset for a +// and b, so if any of them was already advanced by Next the resulting +// Enumerator will point at this new position and never go back to previous +// values. This function uses `Neo.Enumerator.Concat` syscall. func Concat(a, b Enumerator) Enumerator { return Enumerator{} } diff --git a/pkg/interop/header/header.go b/pkg/interop/header/header.go index 44fc2d9e6..37abe9676 100644 --- a/pkg/interop/header/header.go +++ b/pkg/interop/header/header.go @@ -1,47 +1,61 @@ +/* +Package header contains functions working with block headers. +*/ package header -// Package header provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Header stubs a NEO block header type. +// Header represents Neo block header type, it's an opaque data structure that +// can be used by functions from this package. You can create it with +// blockchain.GetHeader. In its function it's similar to the Header class +// of the Neo .net framework. type Header struct{} -// GetIndex returns the index of the given header. +// GetIndex returns the index (height) of the given header. It uses +// `Neo.Header.GetIndex` syscall. func GetIndex(h Header) int { return 0 } -// GetHash returns the hash of the given header. +// GetHash returns the hash (256-bit BE value packed into 32 byte slice) of the +// given header (which also is a hash of the block). It uses `Neo.Header.GetHash` +// syscall. func GetHash(h Header) []byte { return nil } -// GetPrevHash returns the previous hash of the given header. +// GetPrevHash returns the hash (256-bit BE value packed into 32 byte slice) of +// the previous block stored in the given header. It uses `Neo.Header.GetPrevHash` +// syscall. func GetPrevHash(h Header) []byte { return nil } -// GetTimestamp returns the timestamp of the given header. +// GetTimestamp returns the timestamp of the given header. It uses +// `Neo.Header.GetTimestamp` syscall. func GetTimestamp(h Header) int { return 0 } -// GetVersion returns the version of the given header. +// GetVersion returns the version of the given header. It uses +// `Neo.Header.GetVersion` syscall. func GetVersion(h Header) int { return 0 } -// GetMerkleRoot returns the merkle root of the given header. +// GetMerkleRoot returns the Merkle root (256-bit BE value packed into 32 byte +// slice) of the given header. It uses `Neo.Header.GetMerkleRoot` syscall. func GetMerkleRoot(h Header) []byte { return nil } -// GetConsensusData returns the consensus data of the given header. +// GetConsensusData returns the consensus data (nonce) of the given header. +// It uses `Neo.Header.GetConsensusData` syscall. func GetConsensusData(h Header) int { return 0 } -// GetNextConsensus returns the next consensus of the given header. +// GetNextConsensus returns the next consensus field (verification script hash, +// 160-bit BE value packed into 20 byte slice) of the given header. It uses +// `Neo.Header.GetNextConsensus` syscall. func GetNextConsensus(h Header) []byte { return nil } diff --git a/pkg/interop/input/input.go b/pkg/interop/input/input.go index 047637226..e577f2815 100644 --- a/pkg/interop/input/input.go +++ b/pkg/interop/input/input.go @@ -1,17 +1,22 @@ +/* +Package input provides functions dealing with transaction inputs. +*/ package input -// Package input provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Input stubs the input of a NEO transaction. +// Input is an opaque data structure that can only be created by +// transaction.GetInputs and it represents transaction's input. It's similar +// to Neo .net framework's TransactionInput. type Input struct{} -// GetHash returns the hash of the given input. +// GetHash returns the hash stored in the given input (which also is a +// transaction ID represented as 32 byte slice containing 256 bit BE value). +// It uses `Neo.Input.GetHash` syscall. func GetHash(in Input) []byte { return nil } -// GetIndex returns the index of the given input. +// GetIndex returns the index stored in the given input (which is a +// transaction's output number). It uses `Neo.Input.GetIndex` syscall. func GetIndex(in Input) int { return 0 } diff --git a/pkg/interop/iterator/iterator.go b/pkg/interop/iterator/iterator.go index 84e85310e..5ca5947cc 100644 --- a/pkg/interop/iterator/iterator.go +++ b/pkg/interop/iterator/iterator.go @@ -1,39 +1,63 @@ +/* +Package iterator provides functions to work with Neo iterators. +*/ package iterator -// Package iterator provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. +import "github.com/nspcc-dev/neo-go/pkg/interop/enumerator" -// Iterator stubs a NEO iterator object type. +// Iterator represents a Neo iterator, it's an opaque data structure that can +// be properly created by Create or storage.Find. Unlike enumerators, iterators +// range over key-value pairs, so it's convenient to use them for maps. This +// structure is similar in function to Neo .net framework's Iterator. type Iterator struct{} -// Create creates an iterator from the given items. +// Create creates an iterator from the given items (array, struct or map). A new +// iterator is set to point at element -1, so to access its first element you +// need to call Next first. This function uses `Neo.Iterator.Create` syscall. func Create(items []interface{}) Iterator { return Iterator{} } -// Key returns the iterator key. -// TODO: Better description for this. +// Concat concatenates two given iterators returning one that will range on +// a first and then continue with b. Iterator positions are not reset for a +// and b, so if any of them was already advanced by Next the resulting +// Iterator will point at this new position and never go back to previous +// key-value pairs. This function uses `Neo.Iterator.Concat` syscall. +func Concat(a, b Iterator) Iterator { + return Iterator{} +} + +// Key returns iterator's key at current position. It's only valid to call after +// successful Next call. This function uses `Neo.Iterator.Key` syscall. func Key(it Iterator) interface{} { return nil } -// Keys returns the iterator keys. -func Keys(it Iterator) []interface{} { - return nil +// Keys returns Enumerator ranging over keys or the given Iterator. Note that +// this Enumerator is actually directly tied to the underlying Iterator, so that +// advancing it with Next will actually advance the Iterator too. This function +// uses `Neo.Iterator.Keys` syscall. +func Keys(it Iterator) enumerator.Enumerator { + return enumerator.Enumerator{} } -// Next advances the iterator, return true if it is was successful -// and false otherwise. +// Next advances the iterator returning true if it is was successful (and you +// can use Key or Value) and false otherwise (and there are no more elements in +// this Iterator). This function uses `Neo.Iterator.Next` syscall. func Next(it Iterator) bool { return true } -// Value returns the current iterator value. +// Value returns iterator's current value. It's only valid to call after +// successful Next call. This function uses `Neo.Iterator.Value` syscall. func Value(it Iterator) interface{} { return nil } -// Values returns the iterator values. -func Values(it Iterator) []interface{} { - return nil +// Values returns Enumerator ranging over values or the given Iterator. Note that +// this Enumerator is actually directly tied to the underlying Iterator, so that +// advancing it with Next will actually advance the Iterator too. This function +// uses `Neo.Iterator.Values` syscall. +func Values(it Iterator) enumerator.Enumerator { + return enumerator.Enumerator{} } diff --git a/pkg/interop/output/output.go b/pkg/interop/output/output.go index 6f40ac544..01ad0c828 100644 --- a/pkg/interop/output/output.go +++ b/pkg/interop/output/output.go @@ -1,22 +1,28 @@ +/* +Package output provides functions dealing with transaction outputs. +*/ package output -// Package output provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Output stubs the output of a NEO transaction. +// Output is an opaque data structure that can only be created by +// transaction.GetOutputs and it represents transaction's output. It's similar +// to Neo .net framework's TransactionOutput. type Output struct{} -// GetAssetID returns the asset id of the given output. +// GetAssetID returns the asset ID (256 bit BE value in a 32 byte slice) of the +// given output. It uses `Neo.Output.GetAssetId` syscall. func GetAssetID(out Output) []byte { return nil } -// GetValue returns the value of the given output. +// GetValue returns the value (asset quantity) of the given output. It uses +// `Neo.Output.GetValue` syscall. func GetValue(out Output) int { return 0 } -// GetScriptHash returns the script hash of the given output. +// GetScriptHash returns the script hash (receiver's address represented as +// 20 byte slice containing 160 bit BE value) of the given output. It uses +// `Neo.Output.GetScriptHash` syscall. func GetScriptHash(out Output) []byte { return nil } diff --git a/pkg/interop/runtime/runtime.go b/pkg/interop/runtime/runtime.go index fd07d7bd6..9b7e837d3 100644 --- a/pkg/interop/runtime/runtime.go +++ b/pkg/interop/runtime/runtime.go @@ -1,48 +1,70 @@ +/* +Package runtime provides various service functions related to execution environment. +It has similar function to Runtime class in .net framwork for Neo. +*/ package runtime -// Package runtime provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// CheckWitness verifies if the given hash is the invoker of the contract. -func CheckWitness(hash []byte) bool { +// CheckWitness verifies if the given script hash (160-bit BE value in a 20 byte +// slice) or key (compressed serialized 33-byte form) is one of the signers of +// this invocation. It uses `Neo.Runtime.CheckWitness` syscall. +func CheckWitness(hashOrKey []byte) bool { return true } -// Log instucts the VM to log the given message. +// Log instructs VM to log the given message. It's mostly used for debugging +// purposes as these messages are not saved anywhere normally and usually are +// only visible in the VM logs. This function uses `Neo.Runtime.Log` syscall. func Log(message string) {} -// Notify an event to the VM. -func Notify(arg ...interface{}) int { - return 0 -} +// Notify sends a notification (collecting all arguments in an array) to the +// executing environment. Unlike Log it can accept any data and resulting +// notification is saved in application log. It's intended to be used as a +// part of contract's API to external systems, these events can be monitored +// from outside and act upon accordingly. This function uses +// `Neo.Runtime.Notify` syscall. +func Notify(arg ...interface{}) {} -// GetTime returns the timestamp of the most recent block. +// GetTime returns the timestamp of the most recent block. Note that when running +// script in test mode this would be the last accepted (persisted) block in the +// chain, but when running as a part of the new block the time returned is the +// time of this (currently being processed) block. This function uses +// `Neo.Runtime.GetTime` syscall. func GetTime() int { return 0 } -// GetTrigger returns the smart contract invoke trigger which can be either -// verification or application. +// GetTrigger returns the smart contract invocation trigger which can be either +// verification or application. It can be used to differentiate running contract +// as a part of verification process from running it as a regular application. +// Some interop functions (especially ones that change the state in any way) are +// not available when running with verification trigger. This function uses +// `Neo.Runtime.GetTrigger` syscall. func GetTrigger() byte { return 0x00 } -// Application returns the Application trigger type +// Application returns the Application trigger type value to compare with +// GetTrigger return value. func Application() byte { return 0x10 } -// Verification returns the Verification trigger type +// Verification returns the Verification trigger type value to compare with +// GetTrigger return value. func Verification() byte { return 0x00 } -// Serialize serializes and item into a bytearray. +// Serialize serializes any given item into a byte slice. It works for all +// regular VM types (not ones from interop package) and allows to save them in +// storage or pass into Notify and then Deserialize them on the next run or in +// the external event receiver. It uses `Neo.Runtime.Serialize` syscall. func Serialize(item interface{}) []byte { return nil } -// Deserialize an item from a bytearray. +// Deserialize unpacks previously serialized value from a byte slice, it's the +// opposite of Serialize. It uses `Neo.Runtime.Deserialize` syscall. func Deserialize(b []byte) interface{} { return nil } diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index be0f10bc0..a047eebb1 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -1,24 +1,54 @@ +/* +Package storage provides functions to access and modify contract's storage. +Neo storage's model follows simple key-value DB pattern, this storage is a part +of blockchain state, so you can use it between various invocations of the same +contract. +*/ package storage import "github.com/nspcc-dev/neo-go/pkg/interop/iterator" -// Package storage provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Context represents the storage context. +// Context represents storage context that is mandatory for Put/Get/Delete +// operations. It's an opaque type that can only be created properly by +// GetContext, GetReadOnlyContext or ConvertContextToReadOnly. It's similar +// to Neo .net framework's StorageContext class. type Context struct{} -// GetContext returns the storage context. +// ConvertContextToReadOnly returns new context from the given one, but with +// writing capability turned off, so that you could only invoke Get and Find +// using this new Context. If Context is already read-only this function is a +// no-op. It uses `Neo.StorageContext.AsReadOnly` syscall. +func ConvertContextToReadOnly(ctx Context) Context { return Context{} } + +// GetContext returns current contract's (that invokes this function) storage +// context. It uses `Neo.Storage.GetContext` syscall. func GetContext() Context { return Context{} } -// Put value at given key. +// GetReadOnlyContext returns current contract's (that invokes this function) +// storage context in read-only mode, you can use this context for Get and Find +// functions, but using it for Put and Delete will fail. It uses +// `Neo.Storage.GetReadOnlyContext` syscall. +func GetReadOnlyContext() Context { return Context{} } + +// Put saves given value with given key in the storage using given Context. +// Even though it accepts interface{} for both, you can only pass simple types +// there like string, []byte, int or bool (not structures or slices of more +// complex types). To put more complex types there serialize them first using +// runtime.Serialize. This function uses `Neo.Storage.Put` syscall. func Put(ctx Context, key interface{}, value interface{}) {} -// Get value matching given key. +// Get retrieves value stored for the given key using given Context. See Put +// documentation on possible key and value types. This function uses +// `Neo.Storage.Get` syscall. func Get(ctx Context, key interface{}) interface{} { return 0 } -// Delete key value pair from storage. +// Delete removes key-value pair from storage by the given key using given +// Context. See Put documentation on possible key types. This function uses +// `Neo.Storage.Delete` syscall. func Delete(ctx Context, key interface{}) {} -// Find returns an iterator.Iterator over the keys that matched the given key. +// Find returns an iterator.Iterator over key-value pairs in the given Context +// that match the given key (contain it as a prefix). See Put documentation on +// possible key types and iterator package documentation on how to use the +// returned value. This function uses `Neo.Storage.Find` syscall. func Find(ctx Context, key interface{}) iterator.Iterator { return iterator.Iterator{} } diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go index 1d863f5c3..00d06c25e 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -1,50 +1,82 @@ +/* +Package transaction provides functions to work with transactions. +*/ package transaction import ( "github.com/nspcc-dev/neo-go/pkg/interop/attribute" "github.com/nspcc-dev/neo-go/pkg/interop/input" "github.com/nspcc-dev/neo-go/pkg/interop/output" + "github.com/nspcc-dev/neo-go/pkg/interop/witness" ) -// Package transaction provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Transaction stubs a NEO transaction type. +// Transaction represents a NEO transaction, it's an opaque data structure +// that can be used with functions from this package. It's similar to +// Transaction class in Neo .net framework. type Transaction struct{} -// GetHash returns the hash of the given transaction. +// GetHash returns the hash (256 bit BE value in a 32 byte slice) of the given +// transaction (which also is its ID). Is uses `Neo.Transaction.GetHash` syscall. func GetHash(t Transaction) []byte { return nil } -// GetType returns the type of the given transaction. +// GetType returns the type of the given transaction. Possible values: +// MinerTransaction = 0x00 +// IssueTransaction = 0x01 +// ClaimTransaction = 0x02 +// EnrollmentTransaction = 0x20 +// RegisterTransaction = 0x40 +// ContractTransaction = 0x80 +// StateType = 0x90 +// AgencyTransaction = 0xb0 +// PublishTransaction = 0xd0 +// InvocationTransaction = 0xd1 +// It uses `Neo.Transaction.GetType` syscall. func GetType(t Transaction) byte { return 0x00 } -// GetAttributes returns a slice of attributes for the given transaction. +// GetAttributes returns a slice of attributes for agiven transaction. Refer to +// attribute package on how to use them. This function uses +// `Neo.Transaction.GetAttributes` syscall. func GetAttributes(t Transaction) []attribute.Attribute { return []attribute.Attribute{} } -// GetReferences returns a slice of references for the given transaction. -// FIXME: What is the correct return type for this? +// GetReferences returns a slice of references for a given Transaction. Elements +// of this slice can be casted to any of input.Input or output.Output, depending +// on which information you're interested in (as reference technically contains +// both input and corresponding output), refer to input and output package on +// how to use them. This function uses `Neo.Transaction.GetReferences` syscall. func GetReferences(t Transaction) []interface{} { return []interface{}{} } -// GetUnspentCoins returns the unspent coins for the given transaction. -// FIXME: What is the correct return type for this? -func GetUnspentCoins(t Transaction) interface{} { - return 0 -} - -// GetInputs returns the inputs of the given transaction. +// GetInputs returns a slice of inputs of a given Transaction. Refer to input +// package on how to use them. This function uses `Neo.Transaction.GetInputs` +// syscall. func GetInputs(t Transaction) []input.Input { return []input.Input{} } -// GetOutputs returns the outputs of the given transaction. +// GetOutputs returns a slice of outputs of a given Transaction. Refer to output +// package on how to use them. This function uses `Neo.Transaction.GetOutputs` +// syscall. func GetOutputs(t Transaction) []output.Output { return []output.Output{} } + +// GetScript returns the script stored in a given Invocation transaction. +// Calling it for any other Transaction type would lead to failure. It uses +// `Neo.InvocationTransaction.GetScript` syscall. +func GetScript(t Transaction) []byte { + return nil +} + +// GetWitnesses returns a slice of witnesses of a given Transaction. Refer to +// witness package on how to use them. This function uses +// `Neo.Transaction.GetWitnesses` syscall. +func GetWitnesses(t Transaction) []witness.Witness { + return []witness.Witness{} +} diff --git a/pkg/interop/util/util.go b/pkg/interop/util/util.go index 1f05d4941..1aba144cb 100644 --- a/pkg/interop/util/util.go +++ b/pkg/interop/util/util.go @@ -1,12 +1,19 @@ +/* +Package util contains some special useful functions that are provided by compiler and VM. +*/ package util -// FromAddress is an utility function that converts an NEO address to its hash. +// FromAddress is an utility function that converts a Neo address to its hash +// (160 bit BE value in a 20 byte slice). It can only be used for strings known +// at compilation time, because the convertion is actually being done by the +// compiler. func FromAddress(address string) []byte { return nil } -// Equals compares a with b and will return true whether a and b -// are equal. +// Equals compares a with b and will return true when a and b are equal. It's +// implemented as an EQUAL VM opcode, so the rules of comparison are those +// of EQUAL. func Equals(a, b interface{}) bool { return false } diff --git a/pkg/interop/witness/witness.go b/pkg/interop/witness/witness.go new file mode 100644 index 000000000..5bf742f83 --- /dev/null +++ b/pkg/interop/witness/witness.go @@ -0,0 +1,14 @@ +/* +Package witness provides functions dealing with transaction's witnesses. +*/ +package witness + +// Witness is an opaque data structure that can only be created by +// transaction.GetWitnesses and representing transaction's witness. +type Witness struct{} + +// GetVerificationScript returns verification script stored in the given +// witness. It uses `Neo.Witness.GetVerificationScript` syscall. +func GetVerificationScript(w Witness) []byte { + return nil +}