nef: add Source field

Follow neo-project/neo#2605.
This commit is contained in:
Roman Khimov 2021-09-24 00:19:37 +03:00
parent 0a52c32df3
commit 42a9d3d7b8
20 changed files with 54 additions and 10 deletions

View file

@ -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/" const dir = "./testdata/"
for _, name := range []string{"invalid1", "invalid2", "invalid3"} { for _, name := range []string{"invalid1", "invalid2", "invalid3", "invalid4"} {
outF := path.Join(tmpDir, name+".nef") outF := path.Join(tmpDir, name+".nef")
manifestF := path.Join(tmpDir, name+".manifest.json") manifestF := path.Join(tmpDir, name+".manifest.json")
e.RunWithError(t, "neo-go", "contract", "compile", e.RunWithError(t, "neo-go", "contract", "compile",

View file

@ -427,6 +427,7 @@ func initSmartContract(ctx *cli.Context) error {
m := ProjectConfig{ m := ProjectConfig{
Name: contractName, Name: contractName,
SourceURL: "http://example.com/",
SupportedStandards: []string{}, SupportedStandards: []string{},
SafeMethods: []string{}, SafeMethods: []string{},
Events: []manifest.Event{ Events: []manifest.Event{
@ -489,6 +490,7 @@ func contractCompile(ctx *cli.Context) error {
return err return err
} }
o.Name = conf.Name o.Name = conf.Name
o.SourceURL = conf.SourceURL
o.ContractEvents = conf.Events o.ContractEvents = conf.Events
o.ContractSupportedStandards = conf.SupportedStandards o.ContractSupportedStandards = conf.SupportedStandards
o.Permissions = make([]manifest.Permission, len(conf.Permissions)) o.Permissions = make([]manifest.Permission, len(conf.Permissions))
@ -733,6 +735,7 @@ func testInvokeScript(ctx *cli.Context) error {
// ProjectConfig contains project metadata. // ProjectConfig contains project metadata.
type ProjectConfig struct { type ProjectConfig struct {
Name string Name string
SourceURL string
SafeMethods []string SafeMethods []string
SupportedStandards []string SupportedStandards []string
Events []manifest.Event Events []manifest.Event

View file

@ -59,6 +59,7 @@ func RuntimeNotify(args []interface{}) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, require.Equal(t,
`name: testContract `name: testContract
sourceurl: http://example.com/
safemethods: [] safemethods: []
supportedstandards: [] supportedstandards: []
events: events:

5
cli/testdata/invalid4/invalid.go vendored Normal file
View file

@ -0,0 +1,5 @@
package invalid4
func Verify() bool {
return true
}

2
cli/testdata/invalid4/invalid.yml vendored Normal file
View file

@ -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

View file

@ -1,4 +1,5 @@
name: "Engine example" name: "Engine example"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: [] supportedstandards: []
events: events:
- name: Tx - name: Tx

View file

@ -1,4 +1,5 @@
name: "Event types example" name: "Event types example"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: [] supportedstandards: []
events: events:
- name: SomeBytes - name: SomeBytes

View file

@ -1,4 +1,5 @@
name: "Iterator example" name: "Iterator example"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: [] supportedstandards: []
events: events:
- name: found storage key-value pair - name: found storage key-value pair

View file

@ -1,4 +1,5 @@
name: "NameService" name: "NameService"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"] supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf",
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecord", "tokens", "properties", "roots", "getPrice", "isAvailable", "getRecord",

View file

@ -1,4 +1,5 @@
name: "HASHY NFT" name: "HASHY NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"] supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties"]
events: events:

View file

@ -1,4 +1,5 @@
name: "Oracle example" name: "Oracle example"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: [] supportedstandards: []
events: events:
permissions: permissions:

View file

@ -1,4 +1,5 @@
name: "Runtime example" name: "Runtime example"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: [] supportedstandards: []
events: events:
- name: Event - name: Event

View file

@ -1,3 +1,4 @@
name: "Storage example" name: "Storage example"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: [] supportedstandards: []
events: [] events: []

View file

@ -1,4 +1,5 @@
name: "Timer example" name: "Timer example"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: [] supportedstandards: []
events: [] events: []
permissions: permissions:

View file

@ -1,4 +1,5 @@
name: "My awesome token" name: "My awesome token"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-17"] supportedstandards: ["NEP-17"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"]
events: events:

View file

@ -1,4 +1,5 @@
name: "Awesome NEO Token" name: "Awesome NEO Token"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-17"] supportedstandards: ["NEP-17"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"]
events: events:

View file

@ -52,6 +52,9 @@ type Options struct {
// Name is contract's name to be written to manifest. // Name is contract's name to be written to manifest.
Name string Name string
// SourceURL is contract's source URL to be written to manifest.
SourceURL string
// Runtime notifications. // Runtime notifications.
ContractEvents []manifest.Event ContractEvents []manifest.Event
@ -199,6 +202,13 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("error while trying to create .nef file: %w", err) 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() bytes, err := f.Bytes()
if err != nil { if err != nil {
return nil, fmt.Errorf("error while serializing .nef file: %w", err) return nil, fmt.Errorf("error while serializing .nef file: %w", err)

View file

@ -1,4 +1,5 @@
name: "Rubl" name: "Rubl"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-17"] supportedstandards: ["NEP-17"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"] safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"]
events: events:

View file

@ -18,8 +18,9 @@ import (
// +------------+-----------+------------------------------------------------------------+ // +------------+-----------+------------------------------------------------------------+
// | Magic | 4 bytes | Magic header | // | Magic | 4 bytes | Magic header |
// | Compiler | 64 bytes | Compiler used and it's version | // | 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 | // | Tokens | Var array | List of method tokens |
// | Reserved | 2-bytes | Reserved for extensions. Must be 0. | // | Reserved | 2-bytes | Reserved for extensions. Must be 0. |
// | Script | Var bytes | Var bytes for the payload | // | Script | Var bytes | Var bytes for the payload |
@ -32,6 +33,8 @@ const (
Magic uint32 = 0x3346454E Magic uint32 = 0x3346454E
// MaxScriptLength is the maximum allowed contract script length. // MaxScriptLength is the maximum allowed contract script length.
MaxScriptLength = 512 * 1024 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 is the length of `Compiler` File header field in bytes.
compilerFieldSize = 64 compilerFieldSize = 64
) )
@ -39,6 +42,7 @@ const (
// File represents compiled contract file structure according to the NEF3 standard. // File represents compiled contract file structure according to the NEF3 standard.
type File struct { type File struct {
Header Header
Source string `json:"source"`
Tokens []MethodToken `json:"tokens"` Tokens []MethodToken `json:"tokens"`
Script []byte `json:"script"` Script []byte `json:"script"`
Checksum uint32 `json:"checksum"` Checksum uint32 `json:"checksum"`
@ -106,7 +110,12 @@ func (n *File) CalculateChecksum() uint32 {
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (n *File) EncodeBinary(w *io.BinWriter) { func (n *File) EncodeBinary(w *io.BinWriter) {
n.Header.EncodeBinary(w) 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.WriteArray(n.Tokens)
w.WriteU16LE(0) w.WriteU16LE(0)
w.WriteVarBytes(n.Script) w.WriteVarBytes(n.Script)
@ -118,13 +127,14 @@ var errInvalidReserved = errors.New("reserved bytes must be 0")
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (n *File) DecodeBinary(r *io.BinReader) { func (n *File) DecodeBinary(r *io.BinReader) {
n.Header.DecodeBinary(r) n.Header.DecodeBinary(r)
reserved := r.ReadU16LE() n.Source = r.ReadString(MaxSourceURLLength)
if r.Err == nil && reserved != 0 { reservedB := r.ReadB()
if r.Err == nil && reservedB != 0 {
r.Err = errInvalidReserved r.Err = errInvalidReserved
return return
} }
r.ReadArray(&n.Tokens) r.ReadArray(&n.Tokens)
reserved = r.ReadU16LE() reserved := r.ReadU16LE()
if r.Err == nil && reserved != 0 { if r.Err == nil && reserved != 0 {
r.Err = errInvalidReserved r.Err = errInvalidReserved
return return

View file

@ -149,6 +149,7 @@ func TestMarshalUnmarshalJSON(t *testing.T) {
require.JSONEq(t, `{ require.JSONEq(t, `{
"magic":`+strconv.FormatUint(uint64(Magic), 10)+`, "magic":`+strconv.FormatUint(uint64(Magic), 10)+`,
"compiler": "test.compiler-test.ver", "compiler": "test.compiler-test.ver",
"source": "",
"tokens": [ "tokens": [
{ {
"hash": "0x`+expected.Tokens[0].Hash.StringLE()+`", "hash": "0x`+expected.Tokens[0].Hash.StringLE()+`",