Skip to content

Commit

Permalink
contracts: provide Go package with binary contracts, fix #384
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Khimov <[email protected]>
  • Loading branch information
roman-khimov committed Jun 14, 2024
1 parent 5eca5fa commit dcdfa66
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 1 deletion.
3 changes: 2 additions & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ is distributed under Apache 2.0 license (see [LICENSE-APACHE](LICENSE-APACHE)).
Specifically, GPLv3 is used by code in these folders:
* cmd
* common
* contracts
* contracts/*/
* tests
* scripts

Apache 2.0 is used in:
* rpc
* deploy
* contracts (top-level Go package with binaries, contracts themselves are GPLv3)
126 changes: 126 additions & 0 deletions contracts/contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
Package contracts embeds compiled NeoFS contracts and provides access to them.
*/
package contracts

import (
"embed"
"encoding/json"
"errors"
"fmt"
"io/fs"
"path/filepath"

"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
)

const (
neofsDir = "neofs" // not deployed to FS chain.
processingDir = "processing" // not deployed to FS chain.

nnsDir = "nns"
alphabetDir = "alphabet"
auditDir = "audit"
balanceDir = "balance"
containerDir = "container"
neofsIDDir = "neofsid"
netmapDir = "netmap"
proxyDir = "proxy"
reputationDir = "reputation"

nefName = "contract.nef"
manifestName = "manifest.json"
)

// Contract groups information about Neo contract stored in the current package.
type Contract struct {
NEF nef.File
Manifest manifest.Manifest
}

var (
//go:embed */contract.nef */manifest.json
_fs embed.FS

errInvalidFilename = errors.New("invalid file name")
errDuplicatedContract = errors.New("duplicated contract")
errInvalidNEF = errors.New("invalid NEF")
errInvalidManifest = errors.New("invalid manifest")

fsContracts = []string{
nnsDir,
alphabetDir,
netmapDir,
auditDir,
balanceDir,
containerDir,
neofsIDDir,
proxyDir,
reputationDir,
}
mainContracts = []string{
neofsDir,
processingDir,
}
)

// GetFS returns current set of NeoFS chain contracts stored in the package.
// They're returned in the order they're supposed to be deployed starting
// from NNS.
func GetFS() ([]Contract, error) {
return read(_fs, fsContracts)
}

// GetMain returns current set of mainnet contracts stored in the package.
// They're returned in the order they're supposed to be deployed starting
// from NeoFS contract.
func GetMain() ([]Contract, error) {
return read(_fs, mainContracts)
}

// read same as Read by allows to override source fs.FS.
func read(_fs fs.FS, dirs []string) ([]Contract, error) {
var res = make([]Contract, 0, len(dirs))

for i := range dirs {
c, err := readContractFromDir(_fs, dirs[i])
if err != nil {
return nil, fmt.Errorf("read contract %s: %w", dirs[i], err)
}

res = append(res, c)
}

return res, nil
}

func readContractFromDir(_fs fs.FS, dir string) (Contract, error) {
var c Contract

fNEF, err := _fs.Open(filepath.Join(dir, nefName))
if err != nil {
return c, fmt.Errorf("open NEF: %w", err)
}
defer fNEF.Close()

fManifest, err := _fs.Open(filepath.Join(dir, manifestName))
if err != nil {
return c, fmt.Errorf("open manifest: %w", err)
}
defer fManifest.Close()

bReader := io.NewBinReaderFromIO(fNEF)
c.NEF.DecodeBinary(bReader)
if bReader.Err != nil {
return c, fmt.Errorf("%w: %w", errInvalidNEF, bReader.Err)
}

err = json.NewDecoder(fManifest).Decode(&c.Manifest)
if err != nil {
return c, fmt.Errorf("%w: %w", errInvalidManifest, err)
}

return c, nil
}
89 changes: 89 additions & 0 deletions contracts/contracts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package contracts

import (
"crypto/rand"
"encoding/json"
"path/filepath"
"testing"
"testing/fstest"

"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/stretchr/testify/require"
)

func TestGetFS(t *testing.T) {
c, err := GetFS()
require.NoError(t, err)
require.Equal(t, len(fsContracts), len(c))
}

func TestGetMain(t *testing.T) {
c, err := GetMain()
require.NoError(t, err)
require.Equal(t, len(mainContracts), len(c))
}

func TestGetMissingFiles(t *testing.T) {
_fs := fstest.MapFS{}

// Missing NEF
_, err := read(_fs, mainContracts)
require.Error(t, err)

// Missing manifest.
_fs[filepath.Join(neofsDir, nefName)] = &fstest.MapFile{}
_, err = read(_fs, mainContracts)
require.Error(t, err)
}

func TestReadInvalidFormat(t *testing.T) {
var (
_fs = fstest.MapFS{}
nefPath = filepath.Join(neofsDir, nefName)
manifestPath = filepath.Join(neofsDir, manifestName)
)

_, validNEF := anyValidNEF(t)
_, validManifest := anyValidManifest(t, "zero")

_fs[nefPath] = &fstest.MapFile{Data: validNEF}
_fs[manifestPath] = &fstest.MapFile{Data: validManifest}

_, err := read(_fs, []string{neofsDir})
require.NoError(t, err)

_fs[nefPath] = &fstest.MapFile{Data: []byte("not a NEF")}
_fs[manifestPath] = &fstest.MapFile{Data: validManifest}

_, err = read(_fs, []string{neofsDir})
require.ErrorIs(t, err, errInvalidNEF)

_fs[nefPath] = &fstest.MapFile{Data: validNEF}
_fs[manifestPath] = &fstest.MapFile{Data: []byte("not a manifest")}

_, err = read(_fs, []string{neofsDir})
require.ErrorIs(t, err, errInvalidManifest)
}

func anyValidNEF(tb testing.TB) (nef.File, []byte) {
script := make([]byte, 32)
rand.Read(script)

_nef, err := nef.NewFile(script)
require.NoError(tb, err)

bNEF, err := _nef.Bytes()
require.NoError(tb, err)

return *_nef, bNEF
}

func anyValidManifest(tb testing.TB, name string) (manifest.Manifest, []byte) {
_manifest := manifest.NewManifest(name)

jManifest, err := json.Marshal(_manifest)
require.NoError(tb, err)

return *_manifest, jManifest
}

0 comments on commit dcdfa66

Please sign in to comment.