-
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
Make them available via a simple package update. Signed-off-by: Roman Khimov <[email protected]>
- Loading branch information
1 parent
ac958c5
commit bf690d3
Showing
3 changed files
with
213 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,125 @@ | ||
/* | ||
Package contracts embeds compiled NeoFS contracts and provides access to them. | ||
*/ | ||
package contracts | ||
|
||
import ( | ||
"embed" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io/fs" | ||
|
||
"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 | ||
|
||
errInvalidNEF = errors.New("invalid NEF") | ||
errInvalidManifest = errors.New("invalid manifest") | ||
|
||
fsContracts = []string{ | ||
nnsDir, | ||
proxyDir, | ||
auditDir, | ||
netmapDir, | ||
balanceDir, | ||
reputationDir, | ||
neofsIDDir, | ||
containerDir, | ||
alphabetDir, | ||
} | ||
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 | ||
|
||
// Only embedded FS is supported now and it uses "/" even on Windows, | ||
// so filepath.Join() is not applicable. | ||
fNEF, err := _fs.Open(dir + "/" + nefName) | ||
if err != nil { | ||
return c, fmt.Errorf("open NEF: %w", err) | ||
} | ||
defer fNEF.Close() | ||
|
||
fManifest, err := _fs.Open(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,86 @@ | ||
package contracts | ||
|
||
import ( | ||
"encoding/json" | ||
"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[neofsDir+"/"+nefName] = &fstest.MapFile{} | ||
_, err = read(_fs, mainContracts) | ||
require.Error(t, err) | ||
} | ||
|
||
func TestReadInvalidFormat(t *testing.T) { | ||
var ( | ||
_fs = fstest.MapFS{} | ||
nefPath = neofsDir + "/" + nefName | ||
manifestPath = 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) | ||
|
||
_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 | ||
} |