From 42a9d3d7b8202e1fb0601be262436ae3ac2a1e37 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 24 Sep 2021 00:19:37 +0300 Subject: [PATCH] nef: add Source field Follow neo-project/neo#2605. --- cli/contract_test.go | 4 ++-- cli/smartcontract/smart_contract.go | 3 +++ cli/smartcontract/smart_contract_test.go | 1 + cli/testdata/invalid4/invalid.go | 5 +++++ cli/testdata/invalid4/invalid.yml | 2 ++ examples/engine/engine.yml | 3 ++- examples/events/events.yml | 3 ++- examples/iterator/iterator.yml | 1 + examples/nft-nd-nns/nns.yml | 1 + examples/nft-nd/nft.yml | 1 + examples/oracle/oracle.yml | 1 + examples/runtime/runtime.yml | 1 + examples/storage/storage.yml | 1 + examples/timer/timer.yml | 1 + examples/token-sale/token_sale.yml | 1 + examples/token/token.yml | 1 + pkg/compiler/compiler.go | 10 ++++++++++ pkg/rpc/server/testdata/test_contract.yml | 3 ++- pkg/smartcontract/nef/nef.go | 20 +++++++++++++++----- pkg/smartcontract/nef/nef_test.go | 1 + 20 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 cli/testdata/invalid4/invalid.go create mode 100644 cli/testdata/invalid4/invalid.yml diff --git a/cli/contract_test.go b/cli/contract_test.go index 77ecebb84..19e103896 100644 --- a/cli/contract_test.go +++ b/cli/contract_test.go @@ -601,9 +601,9 @@ func TestCompileExamples(t *testing.T) { }) } - t.Run("invalid events in manifest", func(t *testing.T) { + t.Run("invalid manifest", func(t *testing.T) { const dir = "./testdata/" - for _, name := range []string{"invalid1", "invalid2", "invalid3"} { + for _, name := range []string{"invalid1", "invalid2", "invalid3", "invalid4"} { outF := path.Join(tmpDir, name+".nef") manifestF := path.Join(tmpDir, name+".manifest.json") e.RunWithError(t, "neo-go", "contract", "compile", diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 808e6b9c6..6a076de84 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -427,6 +427,7 @@ func initSmartContract(ctx *cli.Context) error { m := ProjectConfig{ Name: contractName, + SourceURL: "http://example.com/", SupportedStandards: []string{}, SafeMethods: []string{}, Events: []manifest.Event{ @@ -489,6 +490,7 @@ func contractCompile(ctx *cli.Context) error { return err } o.Name = conf.Name + o.SourceURL = conf.SourceURL o.ContractEvents = conf.Events o.ContractSupportedStandards = conf.SupportedStandards o.Permissions = make([]manifest.Permission, len(conf.Permissions)) @@ -733,6 +735,7 @@ func testInvokeScript(ctx *cli.Context) error { // ProjectConfig contains project metadata. type ProjectConfig struct { Name string + SourceURL string SafeMethods []string SupportedStandards []string Events []manifest.Event diff --git a/cli/smartcontract/smart_contract_test.go b/cli/smartcontract/smart_contract_test.go index 7316dfcb5..02ac41e49 100644 --- a/cli/smartcontract/smart_contract_test.go +++ b/cli/smartcontract/smart_contract_test.go @@ -59,6 +59,7 @@ func RuntimeNotify(args []interface{}) { require.NoError(t, err) require.Equal(t, `name: testContract +sourceurl: http://example.com/ safemethods: [] supportedstandards: [] events: diff --git a/cli/testdata/invalid4/invalid.go b/cli/testdata/invalid4/invalid.go new file mode 100644 index 000000000..acb769bab --- /dev/null +++ b/cli/testdata/invalid4/invalid.go @@ -0,0 +1,5 @@ +package invalid4 + +func Verify() bool { + return true +} diff --git a/cli/testdata/invalid4/invalid.yml b/cli/testdata/invalid4/invalid.yml new file mode 100644 index 000000000..636d91441 --- /dev/null +++ b/cli/testdata/invalid4/invalid.yml @@ -0,0 +1,2 @@ +name: Test bad source url +sourceurl: http://example.com/with/some/huge/path/that/cant/be/ever/normally/reached/but/we/need/to/test/for/it/anyway/because/there/is/some/path/in/the/code/that/does/this/check/and/it/should/be/triggered/to/ensure/proper/coverage/in/this/particular/component/of/our/perfect/compiler diff --git a/examples/engine/engine.yml b/examples/engine/engine.yml index df94d5a01..baef3bcb2 100644 --- a/examples/engine/engine.yml +++ b/examples/engine/engine.yml @@ -1,4 +1,5 @@ name: "Engine example" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: [] events: - name: Tx @@ -16,4 +17,4 @@ events: - name: Entry parameters: - name: hash - type: Hash160 \ No newline at end of file + type: Hash160 diff --git a/examples/events/events.yml b/examples/events/events.yml index 51d25cdde..f16bc347e 100644 --- a/examples/events/events.yml +++ b/examples/events/events.yml @@ -1,4 +1,5 @@ name: "Event types example" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: [] events: - name: SomeBytes @@ -20,4 +21,4 @@ events: - name: SomeArray parameters: - name: a - type: Array \ No newline at end of file + type: Array diff --git a/examples/iterator/iterator.yml b/examples/iterator/iterator.yml index 3f881c477..1c6477104 100644 --- a/examples/iterator/iterator.yml +++ b/examples/iterator/iterator.yml @@ -1,4 +1,5 @@ name: "Iterator example" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: [] events: - name: found storage key-value pair diff --git a/examples/nft-nd-nns/nns.yml b/examples/nft-nd-nns/nns.yml index 289793c9d..1f24f3bc9 100644 --- a/examples/nft-nd-nns/nns.yml +++ b/examples/nft-nd-nns/nns.yml @@ -1,4 +1,5 @@ name: "NameService" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: ["NEP-11"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties", "roots", "getPrice", "isAvailable", "getRecord", diff --git a/examples/nft-nd/nft.yml b/examples/nft-nd/nft.yml index d56844578..ff6357150 100644 --- a/examples/nft-nd/nft.yml +++ b/examples/nft-nd/nft.yml @@ -1,4 +1,5 @@ name: "HASHY NFT" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: ["NEP-11"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties"] events: diff --git a/examples/oracle/oracle.yml b/examples/oracle/oracle.yml index 4554d6e67..053348d6c 100644 --- a/examples/oracle/oracle.yml +++ b/examples/oracle/oracle.yml @@ -1,4 +1,5 @@ name: "Oracle example" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: [] events: permissions: diff --git a/examples/runtime/runtime.yml b/examples/runtime/runtime.yml index c24377b55..b152cbc0a 100644 --- a/examples/runtime/runtime.yml +++ b/examples/runtime/runtime.yml @@ -1,4 +1,5 @@ name: "Runtime example" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: [] events: - name: Event diff --git a/examples/storage/storage.yml b/examples/storage/storage.yml index 9aa294359..5d1dcd7e7 100644 --- a/examples/storage/storage.yml +++ b/examples/storage/storage.yml @@ -1,3 +1,4 @@ name: "Storage example" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: [] events: [] diff --git a/examples/timer/timer.yml b/examples/timer/timer.yml index 13d2daad7..648607135 100644 --- a/examples/timer/timer.yml +++ b/examples/timer/timer.yml @@ -1,4 +1,5 @@ name: "Timer example" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: [] events: [] permissions: diff --git a/examples/token-sale/token_sale.yml b/examples/token-sale/token_sale.yml index 20ea78365..979bbc4a2 100644 --- a/examples/token-sale/token_sale.yml +++ b/examples/token-sale/token_sale.yml @@ -1,4 +1,5 @@ name: "My awesome token" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: ["NEP-17"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] events: diff --git a/examples/token/token.yml b/examples/token/token.yml index 8c7b3d005..a84342afc 100644 --- a/examples/token/token.yml +++ b/examples/token/token.yml @@ -1,4 +1,5 @@ name: "Awesome NEO Token" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: ["NEP-17"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] events: diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index c4e01f7be..8933605ac 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -52,6 +52,9 @@ type Options struct { // Name is contract's name to be written to manifest. Name string + // SourceURL is contract's source URL to be written to manifest. + SourceURL string + // Runtime notifications. ContractEvents []manifest.Event @@ -199,6 +202,13 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if err != nil { return nil, fmt.Errorf("error while trying to create .nef file: %w", err) } + if o.SourceURL != "" { + if len(o.SourceURL) > nef.MaxSourceURLLength { + return nil, errors.New("too long source URL") + } + f.Source = o.SourceURL + f.Checksum = f.CalculateChecksum() + } bytes, err := f.Bytes() if err != nil { return nil, fmt.Errorf("error while serializing .nef file: %w", err) diff --git a/pkg/rpc/server/testdata/test_contract.yml b/pkg/rpc/server/testdata/test_contract.yml index e0a5ce927..5e9b44d50 100644 --- a/pkg/rpc/server/testdata/test_contract.yml +++ b/pkg/rpc/server/testdata/test_contract.yml @@ -1,4 +1,5 @@ name: "Rubl" +sourceurl: https://github.com/nspcc-dev/neo-go/ supportedstandards: ["NEP-17"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] events: @@ -13,4 +14,4 @@ events: permissions: - hash: ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5 methods: ["transfer"] - - methods: ["onNEP17Payment"] \ No newline at end of file + - methods: ["onNEP17Payment"] diff --git a/pkg/smartcontract/nef/nef.go b/pkg/smartcontract/nef/nef.go index 71e470dc2..d8d30ec8d 100644 --- a/pkg/smartcontract/nef/nef.go +++ b/pkg/smartcontract/nef/nef.go @@ -18,8 +18,9 @@ import ( // +------------+-----------+------------------------------------------------------------+ // | Magic | 4 bytes | Magic header | // | Compiler | 64 bytes | Compiler used and it's version | +// | Source | Var bytes | Source file URL. | // +------------+-----------+------------------------------------------------------------+ -// | Reserved | 2-bytes | Reserved for extensions. Must be 0. | +// | Reserved | 1 byte | Reserved for extensions. Must be 0. | // | Tokens | Var array | List of method tokens | // | Reserved | 2-bytes | Reserved for extensions. Must be 0. | // | Script | Var bytes | Var bytes for the payload | @@ -32,6 +33,8 @@ const ( Magic uint32 = 0x3346454E // MaxScriptLength is the maximum allowed contract script length. MaxScriptLength = 512 * 1024 + // MaxSourceURLLength is the maximum allowed source URL length. + MaxSourceURLLength = 256 // compilerFieldSize is the length of `Compiler` File header field in bytes. compilerFieldSize = 64 ) @@ -39,6 +42,7 @@ const ( // File represents compiled contract file structure according to the NEF3 standard. type File struct { Header + Source string `json:"source"` Tokens []MethodToken `json:"tokens"` Script []byte `json:"script"` Checksum uint32 `json:"checksum"` @@ -106,7 +110,12 @@ func (n *File) CalculateChecksum() uint32 { // EncodeBinary implements io.Serializable interface. func (n *File) EncodeBinary(w *io.BinWriter) { n.Header.EncodeBinary(w) - w.WriteU16LE(0) + if len(n.Source) > MaxSourceURLLength { + w.Err = errors.New("source url too long") + return + } + w.WriteString(n.Source) + w.WriteB(0) w.WriteArray(n.Tokens) w.WriteU16LE(0) w.WriteVarBytes(n.Script) @@ -118,13 +127,14 @@ var errInvalidReserved = errors.New("reserved bytes must be 0") // DecodeBinary implements io.Serializable interface. func (n *File) DecodeBinary(r *io.BinReader) { n.Header.DecodeBinary(r) - reserved := r.ReadU16LE() - if r.Err == nil && reserved != 0 { + n.Source = r.ReadString(MaxSourceURLLength) + reservedB := r.ReadB() + if r.Err == nil && reservedB != 0 { r.Err = errInvalidReserved return } r.ReadArray(&n.Tokens) - reserved = r.ReadU16LE() + reserved := r.ReadU16LE() if r.Err == nil && reserved != 0 { r.Err = errInvalidReserved return diff --git a/pkg/smartcontract/nef/nef_test.go b/pkg/smartcontract/nef/nef_test.go index 9bd8873e1..f265d2ad7 100644 --- a/pkg/smartcontract/nef/nef_test.go +++ b/pkg/smartcontract/nef/nef_test.go @@ -149,6 +149,7 @@ func TestMarshalUnmarshalJSON(t *testing.T) { require.JSONEq(t, `{ "magic":`+strconv.FormatUint(uint64(Magic), 10)+`, "compiler": "test.compiler-test.ver", + "source": "", "tokens": [ { "hash": "0x`+expected.Tokens[0].Hash.StringLE()+`",