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 1998111ea..eae5ca8fe 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ Build the **neo-go** CLI: make build ``` +### Running + Quick start a NEO node on the private network. This requires the [neo-privatenet](https://hub.docker.com/r/cityofzion/neo-privatenet/) Docker image running on your machine. ``` @@ -92,7 +94,30 @@ Available network flags: - `--privnet, -p` - `--testnet, -t` -#Developer notes +#### Importing mainnet/testnet dump files + +If you want to jump-start your mainnet or testnet node with [chain archives +provided by NGD](https://sync.ngd.network/) follow these instructions (when +they'd be available for 3.0 networks): +``` +$ wget .../chain.acc.zip # chain dump file +$ unzip chain.acc.zip +$ ./bin/neo-go db restore -m -i chain.acc # for testnet use '-t' flag instead of '-m' +``` + +The process differs from the C# node in that block importing is a separate +mode, after it ends the node can be started normally. + +## Smart contract development + +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. 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 [Pprof](https://golang.org/pkg/net/http/pprof/) in order to have additional information about them for debugging. @@ -105,7 +130,7 @@ In `config/protocol.*.yml` there is ``` where you can switch on/off and define port. Prometheus is enabled and Pprof is disabled by default. -# Contributing +## Contributing Feel free to contribute to this project after reading the [contributing guidelines](CONTRIBUTING.md). diff --git a/docs/compiler.md b/docs/compiler.md index a7e59652c..bd45239ef 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -2,66 +2,36 @@ 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 (#939) + * it's not possible to rename imported interop packages, they won't work this + way (#397, #913) -### 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 +43,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: ``` @@ -83,62 +53,143 @@ You can dump the opcodes generated by the compiler with the following command: This will result in something like this: ``` -INDEX OPCODE DESC -0 0x54 PUSH4 -1 0xc5 NEWARRAY -2 0x6b TOALTSTACK -3 0x1 PUSHBYTES1 -3 0x2a * -5 0x6a DUPFROMALTSTACK -6 0x0 PUSH0 -7 0x52 PUSH2 -8 0x7a ROLL -9 0xc4 SETITEM -10 0x6a DUPFROMALTSTACK -11 0x0 PUSH0 -12 0xc3 PICKITEM -13 0x5a PUSH10 -14 0xa2 GTE -15 0x64 JMPIFNOT -16 0x7 7 -16 0x0 0 -18 0x51 PUSH1 -19 0x6c FROMALTSTACK -20 0x75 DROP -21 0x66 RET -22 0x0 PUSH0 -23 0x6c FROMALTSTACK -24 0x75 DROP -25 0x66 RET +INDEX OPCODE PARAMETER +0 INITSLOT 0500 ("\x05\x00") << +3 PUSH0 +4 REVERSEN +5 SYSCALL "\x9a\x1f\x19J" +10 NOP +11 STLOC0 +12 LDLOC0 +13 PUSH1 +14 REVERSEN +15 PUSH1 +16 PACK +17 SYSCALL "\x05\a\x92\x16" +22 NOP +23 PUSH0 +24 REVERSEN +25 SYSCALL "E\x99Z\\" +30 NOP +31 STLOC1 +32 LDLOC1 +33 PUSH1 +34 REVERSEN +35 PUSH1 +36 PACK +37 SYSCALL "\x05\a\x92\x16" +42 NOP +43 PUSH0 +44 REVERSEN +45 SYSCALL "\x87\xc3\xd2d" +50 NOP +51 STLOC2 +52 LDLOC2 +53 PUSH1 +54 REVERSEN +55 PUSH1 +56 PACK +57 SYSCALL "\x05\a\x92\x16" +62 NOP +63 PUSH0 +64 REVERSEN +65 SYSCALL "\x1dY\xe1\x19" +70 NOP +71 STLOC3 +72 LDLOC3 +73 PUSH1 +74 REVERSEN +75 PUSH1 +76 PACK +77 SYSCALL "\x05\a\x92\x16" +82 NOP +83 PUSH1 +84 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/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/syscall.go b/pkg/compiler/syscall.go index 633335b4b..c002d2bd7 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -1,15 +1,33 @@ 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", + }, "crypto": { "ECDsaVerify": "Neo.Crypto.ECDsaVerify", }, + "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", @@ -21,14 +39,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", @@ -46,20 +65,26 @@ var syscalls = map[string]map[string]string{ "GetTransaction": "Neo.Block.GetTransaction", }, "transaction": { - "GetHash": "Neo.Transaction.GetHash", - "GetType": "Neo.Transaction.GetType", "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", "GetUnspentCoins": "Neo.Transaction.GetUnspentCoins", - "GetScript": "Neo.Transaction.GetScript", + "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": { @@ -86,6 +111,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", @@ -93,4 +119,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/context.go b/pkg/core/interop/context.go index 59b2337b0..61e56ad59 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -27,6 +27,7 @@ type Context struct { Block *block.Block Tx *transaction.Transaction DAO *dao.Cached + LowerDAO dao.DAO Notifications []state.NotificationEvent Log *zap.Logger } @@ -42,6 +43,7 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n Block: block, Tx: tx, DAO: dao, + LowerDAO: d, Notifications: nes, Log: log, } diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 0507764dc..b5bfea395 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -169,10 +170,20 @@ func txGetUnspentCoins(ic *interop.Context, v *vm.VM) error { return errors.New("value is not a transaction") } ucs, err := ic.DAO.GetUnspentCoinState(tx.Hash()) - if err != nil { + if err == storage.ErrKeyNotFound { + v.Estack().PushVal([]vm.StackItem{}) + return nil + } else if err != nil { return errors.New("no unspent coin state found") } - v.Estack().PushVal(vm.NewInteropItem(ucs)) + + items := make([]vm.StackItem, 0, len(ucs.States)) + for i := range items { + if ucs.States[i].State&state.CoinSpent == 0 { + items = append(items, vm.NewInteropItem(&ucs.States[i].Output)) + } + } + v.Estack().PushVal(items) return nil } diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 4f6bfea4b..b6dc95aa6 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -537,8 +537,12 @@ func contractGetStorageContext(ic *interop.Context, 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/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 dfcab6a5d..fa2d394f6 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -1,14 +1,15 @@ +/* +Package crypto provides an interface to cryptographic syscalls. +*/ package crypto -// Package crypto provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// SHA256 computes the sha256 hash of b. +// SHA256 computes SHA256 hash of b. It uses `Neo.Crypto.SHA256` syscall. func SHA256(b []byte) []byte { return nil } -// ECDsaVerify checks that sig is msg's signature with pub. +// ECDsaVerify checks that sig is correct msg's signature for a given pub +// (serialized public key). It uses `Neo.Crypto.ECDsaVerify` syscall. func ECDsaVerify(msg []byte, pub []byte, sig []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..c2ebda9dd 100644 --- a/pkg/interop/engine/engine.go +++ b/pkg/interop/engine/engine.go @@ -1,34 +1,56 @@ +/* +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 +// `System.Contract.Call` syscall. func AppCall(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..ec407ee0a 100644 --- a/pkg/interop/iterator/iterator.go +++ b/pkg/interop/iterator/iterator.go @@ -1,39 +1,65 @@ +/* +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. Concatenated iterators also remain completely independent +// in results they return, so if both contain the same key you'll receive this +// key twice when iterating. 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..e354d7baf 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -1,50 +1,88 @@ +/* +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 +// GetUnspentCoins returns a slice of not yet spent ouputs of a given transaction. +// This function uses `Neo.Transaction.GetUnspentCoint` syscall. +func GetUnspentCoins(t Transaction) []output.Output { + return []output.Output{} } -// 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 +}