Skip to content

Commit

Permalink
Contract binary package (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-khimov committed Jun 17, 2024
2 parents ebde4b6 + b22d2d7 commit e882639
Show file tree
Hide file tree
Showing 47 changed files with 465 additions and 64 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,11 @@ jobs:
git diff --name-only;
exit 1;
fi
- name: Check that committed binary contracts match generated ones
run: |
if [[ $(git diff --name-only | grep '^contracts/*' -c) != 0 ]]; then
echo "Fresh version of binaries/manifests should be committed for the following contracts:";
git diff --name-only;
exit 1;
fi
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
*.avm
*.nef
*~
bindings_config.yml
config.json
/vendor/
.idea
/bin/
Expand Down
File renamed without changes.
7 changes: 4 additions & 3 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ This repository provides several different components that use different
licenses. Contracts, tests and commands use GPLv3 (see [LICENSE.GPLv3.md](LICENSE.GPLv3.md)),
while RPC bindings (95% of which are autogenerated code) and other
code intended to be used as a library to integrate with these contracts
is distributed under Apache 2.0 license (see [LICENSE.Apache2](LICENSE.Apache2)).
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
* deploy
* contracts (top-level Go package with binaries, contracts themselves are GPLv3)
27 changes: 13 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export GOBIN ?= $(shell pwd)/bin
export CGO_ENABLED=0
NEOGO ?= $(GOBIN)/cli
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
NEOGOORIGMOD = github.com/nspcc-dev/neo-go@v0.104.0
NEOGOORIGMOD = github.com/nspcc-dev/neo-go@v0.106.2
NEOGOMOD = $(shell go list -f '{{.Path}}' -m $(NEOGOORIGMOD))
NEOGOVER = $(shell go list -f '{{.Version}}' -m $(NEOGOORIGMOD) | tr -d v)

Expand All @@ -31,17 +31,17 @@ nns_sc = nns

all_sc = $(alphabet_sc) $(morph_sc) $(mainnet_sc) $(nns_sc)

%/contract.nef %/bindings_config.yml %/config.json: $(NEOGO) %/contract.go %/config.yml
$(NEOGO) contract compile -i $* -c $*/config.yml -m $*/config.json -o $*/contract.nef --bindings $*/bindings_config.yml
%/contract.nef %/bindings_config.yml %/manifest.json: $(NEOGO) %/contract.go %/config.yml
$(NEOGO) contract compile -i $* -c $*/config.yml -m $*/manifest.json -o $*/contract.nef --bindings $*/bindings_config.yml

rpc/%/rpcbinding.go: contracts/%/config.json contracts/%/bindings_config.yml
rpc/%/rpcbinding.go: contracts/%/manifest.json contracts/%/bindings_config.yml
mkdir -p rpc/$*
$(NEOGO) contract generate-rpcwrapper -o rpc/$*/rpcbinding.go -m contracts/$*/config.json --config contracts/$*/bindings_config.yml
$(NEOGO) contract generate-rpcwrapper -o rpc/$*/rpcbinding.go -m contracts/$*/manifest.json --config contracts/$*/bindings_config.yml

alphabet: $(foreach sc,$(alphabet_sc),contracts/$(sc)/contract.nef contracts/$(sc)/config.json rpc/$(sc)/rpcbinding.go)
morph: $(foreach sc,$(morph_sc),contracts/$(sc)/contract.nef contracts/$(sc)/config.json rpc/$(sc)/rpcbinding.go)
mainnet: $(foreach sc,$(mainnet_sc),contracts/$(sc)/contract.nef contracts/$(sc)/config.json rpc/$(sc)/rpcbinding.go)
nns: $(foreach sc,$(nns_sc),contracts/$(sc)/contract.nef contracts/$(sc)/config.json rpc/$(sc)/rpcbinding.go)
alphabet: $(foreach sc,$(alphabet_sc),contracts/$(sc)/contract.nef contracts/$(sc)/manifest.json rpc/$(sc)/rpcbinding.go)
morph: $(foreach sc,$(morph_sc),contracts/$(sc)/contract.nef contracts/$(sc)/manifest.json rpc/$(sc)/rpcbinding.go)
mainnet: $(foreach sc,$(mainnet_sc),contracts/$(sc)/contract.nef contracts/$(sc)/manifest.json rpc/$(sc)/rpcbinding.go)
nns: $(foreach sc,$(nns_sc),contracts/$(sc)/contract.nef contracts/$(sc)/manifest.json rpc/$(sc)/rpcbinding.go)

neo-go: $(NEOGO)

Expand All @@ -52,15 +52,14 @@ test:
@go test ./...

clean:
rm -rf ./bin $(foreach sc,$(all_sc),contracts/$(sc)/contract.nef contracts/$(sc)/config.json contracts/$(sc)/bindings_config.yml)
rm -rf ./bin $(foreach sc,$(all_sc),contracts/$(sc)/bindings_config.yml)

archive: neofs-contract-$(VERSION).tar.gz

neofs-contract-$(VERSION).tar.gz: $(foreach sc,$(all_sc),contracts/$(sc)/contract.nef contracts/$(sc)/config.json)
@tar --transform "s|^\(contracts\)/\([a-z]\+\)/\(contract.nef\)$$|\\1/\\2/\\2_\\3|" \
--transform "s|^contracts/|neofs-contract-$(VERSION)/|" \
neofs-contract-$(VERSION).tar.gz: $(foreach sc,$(all_sc),contracts/$(sc)/contract.nef contracts/$(sc)/manifest.json)
@tar --transform "s|^contracts/|neofs-contract-$(VERSION)/|" \
-czf $@ \
$(shell find contracts -name '*.nef' -o -name 'config.json')
$(shell find contracts -name 'contract.nef' -o -name 'manifest.json')

# Package for Debian
debpackage:
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ To compile smart contracts you need:
## Compilation

To build and compile smart contract, run `make all` command. Compiled contracts
`*_contract.nef` and manifest `config.json` files are placed in the
`contract.nef` and manifest `manifest.json` files are placed in the
corresponding directories. Generated RPC binding files `rpcbinding.go` are
placed in the corresponding `rpc` directories.

```
$ make all
/home/user/go/bin/cli contract compile -i alphabet -c alphabet/config.yml -m alphabet/config.json -o alphabet/alphabet_contract.nef --bindings alphabet/bindings_config.yml
/home/user/go/bin/cli contract compile -i contracts/alphabet -c contracts/alphabet/config.yml -m contracts/alphabet/manifest.json -o contracts/alphabet/contract.nef --bindings contracts/alphabet/bindings_config.yml
mkdir -p rpc/alphabet
/home/user/go/bin/cli contract generate-rpcwrapper -o rpc/alphabet/rpcbinding.go -m alphabet/config.json --config alphabet/bindings_config.yml
/home/user/go/bin/cli contract generate-rpcwrapper -o rpc/alphabet/rpcbinding.go -m contracts/alphabet/manifest.json --config contracts/alphabet/bindings_config.yml
...
```

Expand All @@ -64,8 +64,8 @@ Remove compiled files with `make clean` command.
## Building Debian package

To build Debian package containing compiled contracts, run `make debpackage`
command. Package will install compiled contracts `*_contract.nef` and manifest
`config.json` with corresponding directories to `/var/lib/neofs/contract` for
command. Package will install compiled contracts `contract.nef` and manifest
`manifest.json` with corresponding directories to `/var/lib/neofs/contract` for
further usage.
It will download and build neo-go, if needed.

Expand Down
Binary file added contracts/alphabet/contract.nef
Binary file not shown.
1 change: 1 addition & 0 deletions contracts/alphabet/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"NeoFS Alphabet","abi":{"methods":[{"name":"_initialize","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"_deploy","offset":35,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"emit","offset":2846,"parameters":[],"returntype":"Void","safe":false},{"name":"gas","offset":2667,"parameters":[],"returntype":"Integer","safe":true},{"name":"name","offset":3477,"parameters":[],"returntype":"String","safe":true},{"name":"neo","offset":2681,"parameters":[],"returntype":"Integer","safe":true},{"name":"onNEP17Payment","offset":1163,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"update","offset":2535,"parameters":[{"name":"script","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"verify","offset":3497,"parameters":[],"returntype":"Boolean","safe":true},{"name":"version","offset":3493,"parameters":[],"returntype":"Integer","safe":true},{"name":"vote","offset":3305,"parameters":[{"name":"epoch","type":"Integer"},{"name":"candidates","type":"Array"}],"returntype":"Void","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":["update","transfer","vote"]}],"supportedstandards":[],"trusts":[],"extra":null}
Binary file added contracts/audit/contract.nef
Binary file not shown.
1 change: 1 addition & 0 deletions contracts/audit/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"NeoFS Audit","abi":{"methods":[{"name":"_deploy","offset":0,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"get","offset":832,"parameters":[{"name":"id","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"list","offset":857,"parameters":[],"returntype":"Array","safe":true},{"name":"listByCID","offset":923,"parameters":[{"name":"epoch","type":"Integer"},{"name":"cid","type":"ByteArray"}],"returntype":"Array","safe":true},{"name":"listByEpoch","offset":891,"parameters":[{"name":"epoch","type":"Integer"}],"returntype":"Array","safe":true},{"name":"listByNode","offset":966,"parameters":[{"name":"epoch","type":"Integer"},{"name":"cid","type":"ByteArray"},{"name":"key","type":"PublicKey"}],"returntype":"Array","safe":true},{"name":"put","offset":685,"parameters":[{"name":"rawAuditResult","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"update","offset":556,"parameters":[{"name":"script","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"version","offset":1044,"parameters":[],"returntype":"Integer","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":["update"]}],"supportedstandards":[],"trusts":[],"extra":null}
Binary file added contracts/balance/contract.nef
Binary file not shown.
1 change: 1 addition & 0 deletions contracts/balance/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"NeoFS Balance","abi":{"methods":[{"name":"_initialize","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"_deploy","offset":93,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"balanceOf","offset":1432,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"burn","offset":1965,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"txDetails","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"decimals","offset":1410,"parameters":[],"returntype":"Integer","safe":true},{"name":"lock","offset":1578,"parameters":[{"name":"txDetails","type":"ByteArray"},{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"until","type":"Integer"}],"returntype":"Void","safe":false},{"name":"mint","offset":1844,"parameters":[{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"txDetails","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"newEpoch","offset":1715,"parameters":[{"name":"epochNum","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":1406,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":1414,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":1451,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"transferX","offset":1475,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"details","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"update","offset":1275,"parameters":[{"name":"script","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"version","offset":2116,"parameters":[],"returntype":"Integer","safe":true}],"events":[{"name":"Lock","parameters":[{"name":"txID","type":"ByteArray"},{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"until","type":"Integer"}]},{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"TransferX","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"details","type":"ByteArray"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":["update","subscribeForNewEpoch"]}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null}
Binary file added contracts/container/contract.nef
Binary file not shown.
1 change: 1 addition & 0 deletions contracts/container/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"NeoFS Container","abi":{"methods":[{"name":"_initialize","offset":0,"parameters":[],"returntype":"Void","safe":false},{"name":"_deploy","offset":83,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"alias","offset":3554,"parameters":[{"name":"cid","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"containersOf","offset":3694,"parameters":[{"name":"owner","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"count","offset":3649,"parameters":[],"returntype":"Integer","safe":true},{"name":"delete","offset":3145,"parameters":[{"name":"containerID","type":"ByteArray"},{"name":"signature","type":"Signature"},{"name":"token","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"eACL","offset":4106,"parameters":[{"name":"containerID","type":"ByteArray"}],"returntype":"Array","safe":true},{"name":"get","offset":3441,"parameters":[{"name":"containerID","type":"ByteArray"}],"returntype":"Array","safe":true},{"name":"getContainerSize","offset":4366,"parameters":[{"name":"id","type":"ByteArray"}],"returntype":"Array","safe":true},{"name":"iterateAllContainerSizes","offset":4739,"parameters":[{"name":"epoch","type":"Integer"}],"returntype":"InteropInterface","safe":true},{"name":"iterateContainerSizes","offset":4641,"parameters":[{"name":"epoch","type":"Integer"},{"name":"cid","type":"Hash256"}],"returntype":"InteropInterface","safe":true},{"name":"list","offset":3748,"parameters":[{"name":"owner","type":"ByteArray"}],"returntype":"Array","safe":true},{"name":"listContainerSizes","offset":4480,"parameters":[{"name":"epoch","type":"Integer"}],"returntype":"Array","safe":true},{"name":"newEpoch","offset":4791,"parameters":[{"name":"epochNum","type":"Integer"}],"returntype":"Void","safe":false},{"name":"onNEP11Payment","offset":1670,"parameters":[{"name":"a","type":"Hash160"},{"name":"b","type":"Integer"},{"name":"c","type":"ByteArray"},{"name":"d","type":"Any"}],"returntype":"Void","safe":false},{"name":"owner","offset":3503,"parameters":[{"name":"containerID","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"put","offset":2061,"parameters":[{"name":"container","type":"ByteArray"},{"name":"signature","type":"Signature"},{"name":"publicKey","type":"PublicKey"},{"name":"token","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"putContainerSize","offset":4164,"parameters":[{"name":"epoch","type":"Integer"},{"name":"cid","type":"ByteArray"},{"name":"usedSize","type":"Integer"},{"name":"pubKey","type":"PublicKey"}],"returntype":"Void","safe":false},{"name":"putNamed","offset":2077,"parameters":[{"name":"container","type":"ByteArray"},{"name":"signature","type":"Signature"},{"name":"publicKey","type":"PublicKey"},{"name":"token","type":"ByteArray"},{"name":"name","type":"String"},{"name":"zone","type":"String"}],"returntype":"Void","safe":false},{"name":"setEACL","offset":3844,"parameters":[{"name":"eACL","type":"ByteArray"},{"name":"signature","type":"Signature"},{"name":"publicKey","type":"PublicKey"},{"name":"token","type":"ByteArray"}],"returntype":"Void","safe":false},{"name":"startContainerEstimation","offset":4821,"parameters":[{"name":"epoch","type":"Integer"}],"returntype":"Void","safe":false},{"name":"stopContainerEstimation","offset":4902,"parameters":[{"name":"epoch","type":"Integer"}],"returntype":"Void","safe":false},{"name":"update","offset":1928,"parameters":[{"name":"script","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"version","offset":4982,"parameters":[],"returntype":"Integer","safe":true}],"events":[{"name":"PutSuccess","parameters":[{"name":"containerID","type":"Hash256"},{"name":"publicKey","type":"PublicKey"}]},{"name":"DeleteSuccess","parameters":[{"name":"containerID","type":"ByteArray"}]},{"name":"SetEACLSuccess","parameters":[{"name":"containerID","type":"ByteArray"},{"name":"publicKey","type":"PublicKey"}]},{"name":"StartEstimation","parameters":[{"name":"epoch","type":"Integer"}]},{"name":"StopEstimation","parameters":[{"name":"epoch","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":["update","addKey","transferX","register","registerTLD","addRecord","deleteRecords","subscribeForNewEpoch"]}],"supportedstandards":[],"trusts":[],"extra":null}
125 changes: 125 additions & 0 deletions contracts/contracts.go
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
}
Loading

0 comments on commit e882639

Please sign in to comment.