-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7faf4d5
Showing
10 changed files
with
1,050 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
name: Build | ||
|
||
on: [workflow_dispatch] | ||
|
||
jobs: | ||
build_cli: | ||
name: Build CLI | ||
runs-on: ${{matrix.os}} | ||
strategy: | ||
matrix: | ||
os: [ubuntu-20.04, windows-2022, macos-12] | ||
arch: [amd64] | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Set up Go | ||
uses: actions/setup-go@v3 | ||
with: | ||
go-version: 1.19 | ||
cache: true | ||
|
||
- name: Update Go modules | ||
run: go mod download -json | ||
|
||
- name: Build CLI | ||
run: go build -trimpath -o ./bin/cpm$(go env GOEXE) | ||
env: | ||
GOARCH: ${{ matrix.arch }} | ||
|
||
- name: Upload artifact | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: cpm-${{ matrix.os }}-${{ matrix.arch }} | ||
path: ./bin/cpm* | ||
if-no-files-found: error |
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,32 @@ | ||
# Installation | ||
Download the tool from the releases page and place it somewhere on your path | ||
|
||
# Usage | ||
|
||
```shell | ||
cpm -h | ||
``` | ||
|
||
`cpm.json` is your project configuration file. Have a look or read more about it [here](docs/config.md). | ||
|
||
## Example commands | ||
|
||
### Download all contracts listed in `cpm.json` | ||
Note that only `neo-express` is supported as destination chain. An [issue](https://github.com/nspcc-dev/neo-go/issues/2406) for `neo-go` to add support (go vote!). | ||
|
||
```shell | ||
cpm --log-level DEBUG run | ||
``` | ||
|
||
### Download a single contract or contract manifest | ||
```shell | ||
cpm download contract -c 0x4380f2c1de98bb267d3ea821897ec571a04fe3e0 -n mainnet | ||
cpm download manifest -c 0x4380f2c1de98bb267d3ea821897ec571a04fe3e0 -N https://mainnet1.neo.coz.io:443 | ||
``` | ||
|
||
### Build SDK from local manifest | ||
```shell | ||
cpm generate -m samplecontract.manifest.json -l python | ||
cpm generate -m samplecontract.manifest.json -l go | ||
``` | ||
Note: the name from the manifest is used as output name |
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,118 @@ | ||
package main | ||
|
||
import ( | ||
_ "embed" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/nspcc-dev/neo-go/pkg/util" | ||
log "github.com/sirupsen/logrus" | ||
"io/ioutil" | ||
"os" | ||
"strings" | ||
) | ||
|
||
//go:embed sampleconfig.json | ||
var defaultConfig []byte | ||
var cfg CPMConfig | ||
|
||
type ContractConfig struct { | ||
Label string `json:"label"` | ||
ScriptHash util.Uint160 `json:"script-hash"` | ||
SourceNetwork *string `json:"source-network,omitempty"` | ||
ContractGenerateSdk *bool `json:"contract-generate-sdk,omitempty"` | ||
} | ||
|
||
type CPMConfig struct { | ||
Defaults struct { | ||
ContractSourceNetwork string `json:"contract-source-network"` | ||
ContractDestination string `json:"contract-destination"` | ||
ContractGenerateSdk bool `json:"contract-generate-sdk"` | ||
SdkLanguage string `json:"sdk-language"` | ||
} `json:"defaults"` | ||
Contracts []ContractConfig `json:"contracts"` | ||
Tools struct { | ||
NeoExpress struct { | ||
CanGenerateSDK bool `json:"canGenerateSDK"` | ||
CanDownloadContract bool `json:"canDownloadContract"` | ||
ExecutablePath *string `json:"executable-path"` | ||
ConfigPath string `json:"config-path"` | ||
} `json:"neo-express"` | ||
} `json:"tools"` | ||
Networks []struct { | ||
Label string `json:"label"` | ||
Hosts []string `json:"hosts"` | ||
} `json:"networks"` | ||
} | ||
|
||
func LoadConfig() { | ||
f, err := os.Open(DEFAULT_CONFIG_FILE) | ||
if err != nil { | ||
if os.IsNotExist(err) { | ||
log.Fatalf("Config file %s not found. Run `cpm init` to create a default config", DEFAULT_CONFIG_FILE) | ||
} else { | ||
log.Fatal(err) | ||
} | ||
} | ||
defer f.Close() | ||
|
||
jsonData, _ := ioutil.ReadAll(f) | ||
if err := json.Unmarshal(jsonData, &cfg); err != nil { | ||
log.Fatal(fmt.Errorf("failed to parse config file: %w", err)) | ||
} | ||
|
||
// ensure all contract configs can be worked with directly | ||
for i, c := range cfg.Contracts { | ||
if c.SourceNetwork == nil { | ||
cfg.Contracts[i].SourceNetwork = &cfg.Defaults.ContractSourceNetwork | ||
} | ||
if c.ContractGenerateSdk == nil { | ||
cfg.Contracts[i].ContractGenerateSdk = &cfg.Defaults.ContractGenerateSdk | ||
} | ||
} | ||
} | ||
|
||
func CreateDefaultConfig() { | ||
if _, err := os.Stat(DEFAULT_CONFIG_FILE); os.IsNotExist(err) { | ||
err = ioutil.WriteFile(DEFAULT_CONFIG_FILE, defaultConfig, 0644) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
log.Infof("Written %s\n", DEFAULT_CONFIG_FILE) | ||
} else { | ||
log.Fatalf("%s already exists", DEFAULT_CONFIG_FILE) | ||
} | ||
} | ||
|
||
func (c *CPMConfig) getHosts(networkLabel string) []string { | ||
for _, network := range c.Networks { | ||
if network.Label == networkLabel { | ||
return network.Hosts | ||
} | ||
} | ||
log.Fatalf("Could not find hosts for label: %s", networkLabel) | ||
return nil | ||
} | ||
|
||
type EnumValue struct { | ||
Enum []string | ||
Default string | ||
selected string | ||
} | ||
|
||
func (e *EnumValue) Set(value string) error { | ||
for _, enum := range e.Enum { | ||
if enum == value { | ||
e.selected = value | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf("allowed values are %s", strings.Join(e.Enum, ", ")) | ||
} | ||
|
||
func (e EnumValue) String() string { | ||
if e.selected == "" { | ||
return e.Default | ||
} | ||
return e.selected | ||
} |
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,49 @@ | ||
`cpm.json` is your project configuration file. It holds all information about which contracts it should download, | ||
from which network and whether it should generate an SDK that can quickly be consumed in the smart contract you're developing. | ||
|
||
It has 4 major sections which will be described in detail later on | ||
* `defaults` - this section holds settings that apply to all contracts unless explicitly overridden in the `contracts` section. | ||
* `contracts` - this section describes which contracts to download with what options. | ||
* `tools` - this section describes the available tools and if they can be used for contract downloading and/or generating SDKs. | ||
* `networks` - this section holds a list of networks with corresponding RPC server addresses to the networks used for source information downloading. | ||
|
||
# defaults | ||
* `contract-source-network` - describes which network is the source for downloading contracts from. Valid values are [networks.label](#Networks)s. | ||
* `contract-destination` - describe where the downloaded contract should be persisted. Valid values are [contract-destination](#contract-destination) keys. | ||
* `contract-generate-sdk` - set to `true` to generate SDKs based on the contract manifest that can be consumed in your smart contract. | ||
* `sdk-language` - the target language to generate the SDK in. Valid values: `python`. | ||
|
||
# contracts | ||
* `label` - a user defined label to identify the target contract in the config. Must be a string. Not used elsewhere. | ||
* `script-hash` - the script hash identifying the contract in `0x<hash>` format. i.e. `0x36d0bf624b90a9dad39d85dcafc83f14dab0272f`. | ||
* `source-network` - (Optional) overrides the `contract-source-network` setting in `defaults` to set the source for downloading the contract from. Valid values are [networks.label](#Networks)s. | ||
* `contract-generate-sdk` - (Optional) overrides the `contract-generate-sdk` setting in `defaults` to generate an SDK. Must be a bool value. | ||
|
||
# tools | ||
Currently `neo-express` is the only tool that supports downloading contracts. An [issue](https://github.com/nspcc-dev/neo-go/issues/2406) exists for `neo-go` to add download support. | ||
For SDK generation `python` is the only supported tool, but does not require a configuration section as it is part of the `cpm` package itself. Go-lang SDK generation exists but is still to be integrated. | ||
|
||
Each tool must specify the following 2 keys | ||
* `canGenerateSDK` - indicates if the tool can be used for generating SDKs. Must be a bool value. | ||
* `canDownloadContract` - indicates if the tool can be used for downloading contracts. Must be a bool value. | ||
|
||
Other keys are tool specific | ||
* `neo-express` | ||
* `express-path` - where to find the `neoxp` executable. Set to `null` if installed globally. Otherwise, specify the full path including the program name. | ||
* `config-path` - where to find the `*.neo-express` configuration file of the target network. Must include the file name. i.e. `default.neo-express` if the file is in the root directory. | ||
|
||
Example | ||
|
||
```json | ||
"neo-express": { | ||
"canGenerateSDK": false, | ||
"canDownloadContract": true, | ||
"executable-path": null, | ||
"config-path": "default.neo-express" | ||
} | ||
``` | ||
|
||
|
||
# networks | ||
* label - a user defined name for your network. Must be a string. | ||
* hosts - a list of RPC addresses that all point to the same network. They will be queried in order until one of them gives a successful response. |
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,74 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"github.com/nspcc-dev/neo-go/pkg/util" | ||
log "github.com/sirupsen/logrus" | ||
"os/exec" | ||
"runtime" | ||
"strings" | ||
) | ||
|
||
type Downloader interface { | ||
downloadContract(scriptHash util.Uint160, host string) (string, error) | ||
} | ||
|
||
type NeoExpressDownloader struct { | ||
expressConfigPath *string | ||
} | ||
|
||
func NewNeoExpressDownloader(configPath string) Downloader { | ||
executablePath := cfg.Tools.NeoExpress.ExecutablePath | ||
if executablePath == nil { | ||
var cmd *exec.Cmd | ||
if runtime.GOOS == "darwin" { | ||
cmd = exec.Command("bash", "-c", "neoxp -h") | ||
} else { | ||
cmd = exec.Command("neoxp", "-h") | ||
} | ||
err := cmd.Run() | ||
if err != nil { | ||
log.Fatal("Could not find 'neoxp' executable in $PATH. Please install neoxp globally using " + | ||
"'dotnet tool install Neo.Express -g'" + | ||
" or specify the 'executable-path' in cpm.json in the neo-express tools section") | ||
} | ||
} else { | ||
// Verify path works by calling help (which has a 0 exit code) | ||
cmd := exec.Command(*executablePath, "-h") | ||
err := cmd.Run() | ||
if err != nil { | ||
log.Fatal(fmt.Errorf("could not find 'neoxp' executable in the configured executable-path: %w", err)) | ||
} | ||
} | ||
return &NeoExpressDownloader{ | ||
expressConfigPath: &configPath, | ||
} | ||
} | ||
|
||
func (ned *NeoExpressDownloader) downloadContract(scriptHash util.Uint160, host string) (string, error) { | ||
// the name and arguments supplied to exec.Command differ slightly depending on the OS and whether neoxp is | ||
// installed globally. the following are the base arguments that hold for all scenarios | ||
args := []string{"contract", "download", "-i", cfg.Tools.NeoExpress.ConfigPath, "--force", "0x" + scriptHash.StringLE(), host} | ||
|
||
// global default | ||
executable := "neoxp" | ||
|
||
if cfg.Tools.NeoExpress.ExecutablePath != nil { | ||
executable = *cfg.Tools.NeoExpress.ExecutablePath | ||
} else if runtime.GOOS == "darwin" { | ||
executable = "bash" | ||
tmp := append([]string{"neoxp"}, args...) | ||
args = []string{"-c", strings.Join(tmp, " ")} | ||
} | ||
|
||
cmd := exec.Command(executable, args...) | ||
var errOut bytes.Buffer | ||
cmd.Stderr = &errOut | ||
out, err := cmd.Output() | ||
if err != nil { | ||
return "[NEOXP]" + errOut.String(), err | ||
} else { | ||
return "[NEOXP]" + string(out), 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 @@ | ||
package generators |
Oops, something went wrong.