-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contracts: provide Go package with binary contracts, fix #384
Signed-off-by: Roman Khimov <[email protected]>
- Loading branch information
1 parent
5eca5fa
commit dcdfa66
Showing
3 changed files
with
217 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |