From fac2a0d7e355311dfb9c81caaefe29e3f51d1bcc Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 27 Jan 2021 21:39:14 +0300 Subject: [PATCH 1/5] cli: provide -m flag for manifest when deploying a contract It's a bit easier to use. Previously we couldn't have it because it was used for '--mainnet', but we no longer specify the network, so it can be used for '--manifest'. --- cli/smartcontract/smart_contract.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index a8d87a1d7..b01f5a31c 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -38,7 +38,7 @@ import ( var ( errNoInput = errors.New("no input file was found, specify an input file with the '--in or -i' flag") errNoConfFile = errors.New("no config file was found, specify a config file with the '--config' or '-c' flag") - errNoManifestFile = errors.New("no manifest file was found, specify a manifest file with the '--manifest' flag") + errNoManifestFile = errors.New("no manifest file was found, specify manifest file with '--manifest' or '-m' flag") errNoMethod = errors.New("no method specified for function invocation command") errNoWallet = errors.New("no wallet parameter found, specify it with the '--wallet or -w' flag") errNoScriptHash = errors.New("no smart contract hash was provided, specify one as the first argument") @@ -107,7 +107,7 @@ func NewCommands() []cli.Command { Usage: "Input file for the smart contract (*.nef)", }, cli.StringFlag{ - Name: "manifest", + Name: "manifest, m", Usage: "Manifest input file (*.manifest.json)", }, walletFlag, From 48e3a411334dccd56db21771378250c103a4601d Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 27 Jan 2021 21:45:44 +0300 Subject: [PATCH 2/5] cli: remove outdated `contract deploy` comment It's relevant for Neo 2.0, but not 3.0. --- cli/smartcontract/smart_contract.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index b01f5a31c..2145df0da 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -170,10 +170,7 @@ func NewCommands() []cli.Command { Name: "deploy", Usage: "deploy a smart contract (.nef with description)", Description: `Deploys given contract into the chain. The gas parameter is for additional - gas to be added as a network fee to prioritize the transaction. It may also - be required to add that to satisfy chain's policy regarding transaction size - and the minimum size fee (so if transaction send fails, try adding 0.001 GAS - to it). + gas to be added as a network fee to prioritize the transaction. `, Action: contractDeploy, Flags: deployFlags, From 79e11c93a1f99170417b5a1a3f6a63084cb87cdc Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 27 Jan 2021 22:32:29 +0300 Subject: [PATCH 3/5] cli: allow to specify cosigners with Neo addresses Make it a bit more usable. --- cli/contract_test.go | 3 +++ cli/smartcontract/smart_contract.go | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cli/contract_test.go b/cli/contract_test.go index 556dd5cc7..be5d7d24b 100644 --- a/cli/contract_test.go +++ b/cli/contract_test.go @@ -187,6 +187,9 @@ func TestComlileAndInvokeFunction(t *testing.T) { e.Run(t, "neo-go", "contract", "testinvokescript", "--rpc-endpoint", "http://"+e.RPC.Addr, "--in", nefName, "--", util.Uint160{1, 2, 3}.StringLE()) + e.Run(t, "neo-go", "contract", "testinvokescript", + "--rpc-endpoint", "http://"+e.RPC.Addr, + "--in", nefName, "--", address.Uint160ToString(util.Uint160{1, 2, 3})) e.In.WriteString("one\r") e.Run(t, "neo-go", "contract", "deploy", diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 2145df0da..790fb9ec1 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -266,8 +266,8 @@ func NewCommands() []cli.Command { Signers represent a set of Uint160 hashes with witness scopes and are used to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated as a sender. To specify signers use signer[:scope] syntax where - * 'signer' is hex-encoded 160 bit (20 byte) LE value of signer's address, - which could have '0x' prefix. + * 'signer' is a signer's address (as Neo address or hex-encoded 160 bit (20 byte) + LE value with or without '0x' prefix). * 'scope' is a comma-separated set of cosigner's scopes, which could be: - 'None' - default witness scope which may be used for the sender to only pay fee for the transaction. @@ -286,9 +286,9 @@ func NewCommands() []cli.Command { neo-go RPC server only. C# implementation does not support scopes capability. Examples: - * '0000000009070e030d0f0e020d0c06050e030c02' + * 'NNQk4QXsxvsrr3GSozoWBUxEmfag7B6hz5' + * 'NVquyZHoPirw6zAEPvY1ZezxM493zMWQqs:Global' * '0x0000000009070e030d0f0e020d0c06050e030c02' - * '0x0000000009070e030d0f0e020d0c06050e030c02:Global' * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,CustomGroups' `, Action: testInvokeFunction, @@ -888,7 +888,11 @@ func parseCosigner(c string) (transaction.Signer, error) { if len(s) == 2*util.Uint160Size+2 && s[0:2] == "0x" { s = s[2:] } - res.Account, err = util.Uint160DecodeStringLE(s) + if len(s) == util.Uint160Size*2 { + res.Account, err = util.Uint160DecodeStringLE(s) + } else { + res.Account, err = address.StringToUint160(s) + } if err != nil { return res, err } From a217d62f8a02d86bd80df2d1cfa1f3d9cd449f7c Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 27 Jan 2021 23:12:43 +0300 Subject: [PATCH 4/5] docs: make compiler documentation more up to date Add some notes on GOROOT setup and output files, properly describe YAML/manifest interactions and remove severely outdated examples (we have better ones in `examples` anyway). --- docs/compiler.md | 122 +++++++++++------------------------------------ 1 file changed, 28 insertions(+), 94 deletions(-) diff --git a/docs/compiler.md b/docs/compiler.md index f61305ea9..236a77746 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -42,6 +42,15 @@ It should return no value and accept single bool argument which will be true on ## Quick start +### Go setup + +The compiler uses Go parser internally and depends on regular Go compiler +presence, so make sure you have it installed and set up. On some distributions +this requires you to set proper `GOROOT` environment variable, like +``` +export GOROOT=/usr/lib64/go/1.14 +``` + ### Compiling ``` @@ -55,7 +64,8 @@ By default the filename will be the name of your .go file with the .nef extensio ``` If you contract is split across multiple files, you must provide a path -to the directory where package files are contained instead of a single Go file: +to the directory where package files are contained instead of a single Go file +(`out.nef` will be used as the default output file in this case): ``` ./bin/neo-go contract compile -i ./path/to/contract ``` @@ -133,7 +143,7 @@ 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 compile -i contract.go -o contract.nef --debug contract.debug.json +$ ./bin/neo-go contract compile -i contract.go -c contract.yml -m contract.manifest.json -o contract.nef --debug contract.debug.json ``` This file can then be used by debugger and set up to work just like for any @@ -141,10 +151,18 @@ 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: - +Deploying a contract to blockchain with neo-go requires both NEF and JSON +manifest generated by the compiler from configuration file provided in YAML +format. To create contract manifest pass YAML file with `-c` parameter and +specify manifest output file with `-m`: ``` +./bin/neo-go contract compile -i mycontract.go -c myconfig.yml -m mycontract.manifest.json +``` + +Example YAML file contents: +``` +name: Contract +safemethods: [] supportedstandards: [] events: - name: info @@ -153,10 +171,10 @@ events: type: ByteString ``` -It's passed to the `deploy` command via `-c` option: +Then the manifest can be passed to the `deploy` command via `-m` option: ``` -$ ./bin/neo-go contract deploy -i contract.nef -c contract.yml -r http://localhost:20331 -w wallet.json -g 0.001 +$ ./bin/neo-go contract deploy -i contract.nef -m contract.manifest.json -r http://localhost:20331 -w wallet.json ``` Deployment works via an RPC server, an address of which is passed via `-r` @@ -201,93 +219,9 @@ $ ./bin/neo-go contract invokefunction -r http://localhost:20331 -w my_wallet.js ## Smart contract examples -Some examples are provided in the [examples directory](../examples). - -### Check if the invoker of the contract is the owning address - -```Golang -package mycontract - -import ( - "github.com/nspcc-dev/neo-go/pkg/interop/runtime" - "github.com/nspcc-dev/neo-go/pkg/interop/util" -) - -var owner = util.FromAddress("AJX1jGfj3qPBbpAKjY527nPbnrnvSx9nCg") - -func Main() bool { - isOwner := runtime.CheckWitness(owner) - - if isOwner { - runtime.Log("invoker is the owner") - return true - } - - return false -} -``` - -### Simple token - -```Golang -package mytoken - -import ( - "github.com/nspcc-dev/neo-go/pkg/interop/runtime" - "github.com/nspcc-dev/neo-go/pkg/interop/storage" -) - -var owner = util.FromAddress("AJX1jGfj3qPBbpAKjY527nPbnrnvSx9nCg") - -type Token struct { - Name string - Symbol string - TotalSupply int - Owner []byte -} - -func (t Token) AddToCirculation(amount int) bool { - ctx := storage.Context() - var inCirc int - val := storage.Get(ctx, "in_circ") - if val != nil { - inCirc = val.(int) - } - inCirc += amount - storage.Put(ctx, "in_circ", inCirc) - return true -} - -func newToken() Token { - return Token{ - Name: "your awesome NEO token", - Symbol: "YANT", - TotalSupply: 1000, - Owner: owner, - } -} - -func Main(operation string, args []interface{}) bool { - token := newToken() - trigger := runtime.GetTrigger() - - if trigger == runtime.Verification { - isOwner := runtime.CheckWitness(token.Owner) - if isOwner { - return true - } - return false - } - - if trigger == runtime.Application { - if operation == "mintTokens" { - token.AddToCirculation(100) - } - } - - return true -} -``` +Some examples are provided in the [examples directory](../examples). For more +sophisticated real-world contracts written in Go check out [NeoFS +contracts](https://github.com/nspcc-dev/neofs-contract/). ## How to report compiler bugs 1. Make a proper testcase (example testcases can be found in the tests folder) From f2b12756ee784c23d6961c1308634a95514b0cb0 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 28 Jan 2021 12:14:50 +0300 Subject: [PATCH 5/5] docs: use contract instead of mycontract throughout the compiler doc --- docs/compiler.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/compiler.md b/docs/compiler.md index 236a77746..1eb2adaa8 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -54,13 +54,13 @@ export GOROOT=/usr/lib64/go/1.14 ### Compiling ``` -./bin/neo-go contract compile -i mycontract.go +./bin/neo-go contract compile -i contract.go ``` By default the filename will be the name of your .go file with the .nef extension, the file will be located in the same directory where your Go contract is. If you want another location for your compiled contract: ``` -./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.nef +./bin/neo-go contract compile -i contract.go --out /Users/foo/bar/contract.nef ``` If you contract is split across multiple files, you must provide a path @@ -74,7 +74,7 @@ to the directory where package files are contained instead of a single Go file You can dump the opcodes generated by the compiler with the following command: ``` -./bin/neo-go contract inspect -i mycontract.go -c +./bin/neo-go contract inspect -i contract.go -c ``` This will result in something like this: @@ -156,7 +156,7 @@ manifest generated by the compiler from configuration file provided in YAML format. To create contract manifest pass YAML file with `-c` parameter and specify manifest output file with `-m`: ``` -./bin/neo-go contract compile -i mycontract.go -c myconfig.yml -m mycontract.manifest.json +./bin/neo-go contract compile -i contract.go -c config.yml -m contract.manifest.json ``` Example YAML file contents: