forked from TrueCloudLab/frostfs-node
[#722] neofs-adm: Allow to initialize local dump
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
c55950bd70
commit
efb6545bfe
14 changed files with 500 additions and 134 deletions
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
@ -57,11 +56,19 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
ns, err := getNativeHashes(c)
|
ns, err := getNativeHashes(c)
|
||||||
if err != nil || ns[nativenames.Gas].Equals(util.Uint160{}) {
|
if err != nil {
|
||||||
return errors.New("can't fetch hash of the GAS contract")
|
return fmt.Errorf("can't fetch the list of native contracts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gasHash := ns[nativenames.Gas]
|
gasHash, ok := ns[nativenames.Gas]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("can't find the %s native contract hash", nativenames.Gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
desigHash, ok := ns[nativenames.Designation]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("can't find the %s native contract hash", nativenames.Designation)
|
||||||
|
}
|
||||||
|
|
||||||
if !notaryEnabled || dumpStorage || dumpAlphabet || dumpProxy {
|
if !notaryEnabled || dumpStorage || dumpAlphabet || dumpProxy {
|
||||||
nnsCs, err = c.GetContractStateByID(1)
|
nnsCs, err = c.GetContractStateByID(1)
|
||||||
|
@ -75,7 +82,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
irList, err := fetchIRNodes(c, nmHash)
|
irList, err := fetchIRNodes(c, nmHash, desigHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -86,7 +93,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
printBalances(cmd, "Inner ring nodes balances:", irList)
|
printBalances(cmd, "Inner ring nodes balances:", irList)
|
||||||
|
|
||||||
if dumpStorage {
|
if dumpStorage {
|
||||||
res, err := c.InvokeFunction(nmHash, "netmap", []smartcontract.Parameter{}, nil)
|
res, err := invokeFunction(c, nmHash, "netmap", []interface{}{}, nil)
|
||||||
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
||||||
return errors.New("can't fetch the list of storage nodes")
|
return errors.New("can't fetch the list of storage nodes")
|
||||||
}
|
}
|
||||||
|
@ -170,7 +177,7 @@ func dumpBalances(cmd *cobra.Command, _ []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchIRNodes(c Client, nmHash util.Uint160) ([]accBalancePair, error) {
|
func fetchIRNodes(c Client, nmHash, desigHash util.Uint160) ([]accBalancePair, error) {
|
||||||
var irList []accBalancePair
|
var irList []accBalancePair
|
||||||
|
|
||||||
if notaryEnabled {
|
if notaryEnabled {
|
||||||
|
@ -179,7 +186,7 @@ func fetchIRNodes(c Client, nmHash util.Uint160) ([]accBalancePair, error) {
|
||||||
return nil, fmt.Errorf("can't get block height: %w", err)
|
return nil, fmt.Errorf("can't get block height: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
arr, err := c.GetDesignatedByRole(noderoles.NeoFSAlphabet, height)
|
arr, err := getDesignatedByRole(c, desigHash, noderoles.NeoFSAlphabet, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
||||||
}
|
}
|
||||||
|
@ -189,7 +196,7 @@ func fetchIRNodes(c Client, nmHash util.Uint160) ([]accBalancePair, error) {
|
||||||
irList[i].scriptHash = arr[i].GetScriptHash()
|
irList[i].scriptHash = arr[i].GetScriptHash()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res, err := c.InvokeFunction(nmHash, "innerRingList", []smartcontract.Parameter{}, nil)
|
res, err := invokeFunction(c, nmHash, "innerRingList", []interface{}{}, nil)
|
||||||
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
||||||
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -49,8 +48,7 @@ func dumpContainers(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.InvokeFunction(ch, "list",
|
res, err := invokeFunction(c, ch, "list", []interface{}{""}, nil)
|
||||||
[]smartcontract.Parameter{{Type: smartcontract.StringType, Value: ""}}, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
@ -160,8 +158,7 @@ func dumpNetworkConfig(cmd *cobra.Command, _ []string) error {
|
||||||
return fmt.Errorf("can't get netmap contract hash: %w", err)
|
return fmt.Errorf("can't get netmap contract hash: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.InvokeFunction(nmHash, "listConfig",
|
res, err := invokeFunction(c, nmHash, "listConfig", nil, nil)
|
||||||
[]smartcontract.Parameter{}, []transaction.Signer{{}})
|
|
||||||
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
||||||
return errors.New("can't fetch list of network config keys from the netmap contract")
|
return errors.New("can't fetch list of network config keys from the netmap contract")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -29,7 +28,7 @@ func forceNewEpochCmd(cmd *cobra.Command, args []string) error {
|
||||||
return fmt.Errorf("can't get netmap contract hash: %w", err)
|
return fmt.Errorf("can't get netmap contract hash: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := wCtx.Client.InvokeFunction(nmHash, "epoch", []smartcontract.Parameter{}, nil)
|
res, err := invokeFunction(wCtx.Client, nmHash, "epoch", nil, nil)
|
||||||
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
if err != nil || res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
||||||
return errors.New("can't fetch current epoch from the netmap contract")
|
return errors.New("can't fetch current epoch from the netmap contract")
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ func initializeSideChainCmd(cmd *cobra.Command, args []string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("initialization error: %w", err)
|
return fmt.Errorf("initialization error: %w", err)
|
||||||
}
|
}
|
||||||
|
defer initCtx.close()
|
||||||
|
|
||||||
// 1. Transfer funds to committee accounts.
|
// 1. Transfer funds to committee accounts.
|
||||||
cmd.Println("Stage 1: transfer GAS to alphabet nodes.")
|
cmd.Println("Stage 1: transfer GAS to alphabet nodes.")
|
||||||
|
@ -98,6 +99,16 @@ func initializeSideChainCmd(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *initializeContext) close() {
|
||||||
|
if local, ok := c.Client.(*localClient); ok {
|
||||||
|
err := local.dump()
|
||||||
|
if err != nil {
|
||||||
|
c.Command.PrintErrf("Can't write dump: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContext, error) {
|
func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContext, error) {
|
||||||
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
||||||
wallets, err := openAlphabetWallets(walletDir)
|
wallets, err := openAlphabetWallets(walletDir)
|
||||||
|
@ -110,7 +121,18 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := getN3Client(v)
|
var c Client
|
||||||
|
if v.GetString(localDumpFlag) != "" {
|
||||||
|
if cmd.Name() != "init" {
|
||||||
|
return nil, errors.New("dump creation is only supported for `init` command")
|
||||||
|
}
|
||||||
|
if v.GetString(endpointFlag) != "" {
|
||||||
|
return nil, fmt.Errorf("`%s` and `%s` flags are mutually exclusive", endpointFlag, localDumpFlag)
|
||||||
|
}
|
||||||
|
c, err = newLocalClient(v, wallets)
|
||||||
|
} else {
|
||||||
|
c, err = getN3Client(v)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -287,6 +309,12 @@ func (c *clientContext) awaitTx(cmd *cobra.Command) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if local, ok := c.Client.(*localClient); ok {
|
||||||
|
if err := local.putTransactions(); err != nil {
|
||||||
|
return fmt.Errorf("can't persist transactions: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Println("Waiting for transactions to persist...")
|
cmd.Println("Waiting for transactions to persist...")
|
||||||
|
|
||||||
tick := time.NewTicker(c.PollInterval)
|
tick := time.NewTicker(c.PollInterval)
|
||||||
|
@ -304,8 +332,8 @@ loop:
|
||||||
res, err := c.Client.GetApplicationLog(c.Hashes[i], &at)
|
res, err := c.Client.GetApplicationLog(c.Hashes[i], &at)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vm.HaltState {
|
if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vm.HaltState {
|
||||||
retErr = fmt.Errorf("tx persisted in %s state: %s",
|
retErr = fmt.Errorf("tx %d persisted in %s state: %s",
|
||||||
res.Executions[0].VMState, res.Executions[0].FaultException)
|
i, res.Executions[0].VMState, res.Executions[0].FaultException)
|
||||||
}
|
}
|
||||||
continue loop
|
continue loop
|
||||||
}
|
}
|
||||||
|
@ -315,8 +343,8 @@ loop:
|
||||||
res, err := c.Client.GetApplicationLog(c.Hashes[i], &at)
|
res, err := c.Client.GetApplicationLog(c.Hashes[i], &at)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vm.HaltState {
|
if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vm.HaltState {
|
||||||
retErr = fmt.Errorf("tx persisted in %s state: %s",
|
retErr = fmt.Errorf("tx %d persisted in %s state: %s",
|
||||||
res.Executions[0].VMState, res.Executions[0].FaultException)
|
i, res.Executions[0].VMState, res.Executions[0].FaultException)
|
||||||
}
|
}
|
||||||
continue loop
|
continue loop
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ func (c *initializeContext) deployNNS(method string) error {
|
||||||
mgmtHash = nnsCs.Hash
|
mgmtHash = nnsCs.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.Client.InvokeFunction(mgmtHash, method, params, []transaction.Signer{signer})
|
res, err := invokeFunction(c.Client, mgmtHash, method, params, []transaction.Signer{signer})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't deploy NNS contract: %w", err)
|
return fmt.Errorf("can't deploy NNS contract: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ func (c *initializeContext) updateContracts() error {
|
||||||
totalGasCost := int64(0)
|
totalGasCost := int64(0)
|
||||||
w := io2.NewBufBinWriter()
|
w := io2.NewBufBinWriter()
|
||||||
|
|
||||||
var keysParam []smartcontract.Parameter
|
var keysParam []interface{}
|
||||||
|
|
||||||
// Update script size for a single-node committee is close to the maximum allowed size of 65535.
|
// Update script size for a single-node committee is close to the maximum allowed size of 65535.
|
||||||
// Because of this we want to reuse alphabet contract NEF and manifest for different updates.
|
// Because of this we want to reuse alphabet contract NEF and manifest for different updates.
|
||||||
|
@ -184,10 +184,7 @@ func (c *initializeContext) updateContracts() error {
|
||||||
return fmt.Errorf("can't resolve hash for contract update: %w", err)
|
return fmt.Errorf("can't resolve hash for contract update: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
keysParam = append(keysParam, smartcontract.Parameter{
|
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
||||||
Type: smartcontract.PublicKeyType,
|
|
||||||
Value: acc.PrivateKey().PublicKey().Bytes(),
|
|
||||||
})
|
|
||||||
|
|
||||||
params := c.getAlphabetDeployItems(i, len(c.Wallets))
|
params := c.getAlphabetDeployItems(i, len(c.Wallets))
|
||||||
emit.Array(w.BinWriter, params...)
|
emit.Array(w.BinWriter, params...)
|
||||||
|
@ -243,7 +240,7 @@ func (c *initializeContext) updateContracts() error {
|
||||||
Scopes: transaction.Global,
|
Scopes: transaction.Global,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.Client.InvokeFunction(invokeHash, method, params, []transaction.Signer{signer})
|
res, err := invokeFunction(c.Client, invokeHash, method, params, []transaction.Signer{signer})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
|
return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
|
||||||
}
|
}
|
||||||
|
@ -287,7 +284,7 @@ func (c *initializeContext) deployContracts() error {
|
||||||
mgmtHash := c.nativeHash(nativenames.Management)
|
mgmtHash := c.nativeHash(nativenames.Management)
|
||||||
alphaCs := c.getContract(alphabetContract)
|
alphaCs := c.getContract(alphabetContract)
|
||||||
|
|
||||||
var keysParam []smartcontract.Parameter
|
var keysParam []interface{}
|
||||||
|
|
||||||
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
||||||
for i, acc := range c.Accounts {
|
for i, acc := range c.Accounts {
|
||||||
|
@ -298,15 +295,12 @@ func (c *initializeContext) deployContracts() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
invokeHash := mgmtHash
|
invokeHash := mgmtHash
|
||||||
keysParam = append(keysParam, smartcontract.Parameter{
|
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
||||||
Type: smartcontract.PublicKeyType,
|
|
||||||
Value: acc.PrivateKey().PublicKey().Bytes(),
|
|
||||||
})
|
|
||||||
|
|
||||||
params := getContractDeployParameters(alphaCs.RawNEF, alphaCs.RawManifest,
|
params := getContractDeployParameters(alphaCs.RawNEF, alphaCs.RawManifest,
|
||||||
c.getAlphabetDeployParameters(i, len(c.Wallets)))
|
c.getAlphabetDeployItems(i, len(c.Wallets)))
|
||||||
|
|
||||||
res, err := c.Client.InvokeFunction(invokeHash, deployMethodName, params, []transaction.Signer{{
|
res, err := invokeFunction(c.Client, invokeHash, deployMethodName, params, []transaction.Signer{{
|
||||||
Account: acc.Contract.ScriptHash(),
|
Account: acc.Contract.ScriptHash(),
|
||||||
Scopes: transaction.CalledByEntry,
|
Scopes: transaction.CalledByEntry,
|
||||||
}})
|
}})
|
||||||
|
@ -344,7 +338,7 @@ func (c *initializeContext) deployContracts() error {
|
||||||
Scopes: transaction.Global,
|
Scopes: transaction.Global,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.Client.InvokeFunction(invokeHash, deployMethodName, params, []transaction.Signer{signer})
|
res, err := invokeFunction(c.Client, invokeHash, deployMethodName, params, []transaction.Signer{signer})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
|
return fmt.Errorf("can't deploy %s contract: %w", ctrName, err)
|
||||||
}
|
}
|
||||||
|
@ -497,43 +491,29 @@ func readContractsFromArchive(file io.Reader, names []string) (map[string]*contr
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContractDeployParameters(rawNef, rawManif []byte, deployData []smartcontract.Parameter) []smartcontract.Parameter {
|
func getContractDeployParameters(rawNef, rawManif []byte, deployData []interface{}) []interface{} {
|
||||||
return []smartcontract.Parameter{
|
return []interface{}{rawNef, rawManif, deployData}
|
||||||
{
|
|
||||||
Type: smartcontract.ByteArrayType,
|
|
||||||
Value: rawNef,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: smartcontract.ByteArrayType,
|
|
||||||
Value: rawManif,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: smartcontract.ArrayType,
|
|
||||||
Value: deployData,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) getContractDeployData(ctrName string, keysParam []smartcontract.Parameter) []smartcontract.Parameter {
|
func (c *initializeContext) getContractDeployData(ctrName string, keysParam []interface{}) []interface{} {
|
||||||
items := make([]smartcontract.Parameter, 1, 6)
|
items := make([]interface{}, 1, 6)
|
||||||
items[0] = newContractParameter(smartcontract.BoolType, false) // notaryDisabled is false
|
items[0] = false // notaryDisabled is false
|
||||||
|
|
||||||
switch ctrName {
|
switch ctrName {
|
||||||
case neofsContract:
|
case neofsContract:
|
||||||
items = append(items,
|
items = append(items,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[processingContract].Hash),
|
c.Contracts[processingContract].Hash,
|
||||||
newContractParameter(smartcontract.ArrayType, keysParam),
|
keysParam,
|
||||||
newContractParameter(smartcontract.ArrayType, smartcontract.Parameter{}))
|
smartcontract.Parameter{})
|
||||||
case processingContract:
|
case processingContract:
|
||||||
items = append(items, newContractParameter(smartcontract.Hash160Type, c.Contracts[neofsContract].Hash))
|
items = append(items, c.Contracts[neofsContract].Hash)
|
||||||
return items[1:] // no notary info
|
return items[1:] // no notary info
|
||||||
case auditContract:
|
case auditContract:
|
||||||
items = append(items,
|
items = append(items, c.Contracts[netmapContract].Hash)
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash))
|
|
||||||
case balanceContract:
|
case balanceContract:
|
||||||
items = append(items,
|
items = append(items,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash),
|
c.Contracts[netmapContract].Hash,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[containerContract].Hash))
|
c.Contracts[containerContract].Hash)
|
||||||
case containerContract:
|
case containerContract:
|
||||||
// In case if NNS is updated multiple times, we can't calculate
|
// In case if NNS is updated multiple times, we can't calculate
|
||||||
// it's actual hash based on local data, thus query chain.
|
// it's actual hash based on local data, thus query chain.
|
||||||
|
@ -542,43 +522,33 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []sm
|
||||||
panic("NNS is not yet deployed")
|
panic("NNS is not yet deployed")
|
||||||
}
|
}
|
||||||
items = append(items,
|
items = append(items,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash),
|
c.Contracts[netmapContract].Hash,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[balanceContract].Hash),
|
c.Contracts[balanceContract].Hash,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[neofsIDContract].Hash),
|
c.Contracts[neofsIDContract].Hash,
|
||||||
newContractParameter(smartcontract.Hash160Type, nnsCs.Hash),
|
nnsCs.Hash,
|
||||||
newContractParameter(smartcontract.StringType, "container"))
|
"container")
|
||||||
case neofsIDContract:
|
case neofsIDContract:
|
||||||
items = append(items,
|
items = append(items,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[netmapContract].Hash),
|
c.Contracts[netmapContract].Hash,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[containerContract].Hash))
|
c.Contracts[containerContract].Hash)
|
||||||
case netmapContract:
|
case netmapContract:
|
||||||
configParam := []smartcontract.Parameter{
|
configParam := []interface{}{
|
||||||
{Type: smartcontract.StringType, Value: netmapEpochKey},
|
netmapEpochKey, viper.GetInt64(epochDurationInitFlag),
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(epochDurationInitFlag)},
|
netmapMaxObjectSizeKey, viper.GetInt64(maxObjectSizeInitFlag),
|
||||||
{Type: smartcontract.StringType, Value: netmapMaxObjectSizeKey},
|
netmapAuditFeeKey, viper.GetInt64(auditFeeInitFlag),
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(maxObjectSizeInitFlag)},
|
netmapContainerFeeKey, viper.GetInt64(containerFeeInitFlag),
|
||||||
{Type: smartcontract.StringType, Value: netmapAuditFeeKey},
|
netmapContainerAliasFeeKey, viper.GetInt64(containerAliasFeeInitFlag),
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(auditFeeInitFlag)},
|
netmapEigenTrustIterationsKey, int64(defaultEigenTrustIterations),
|
||||||
{Type: smartcontract.StringType, Value: netmapContainerFeeKey},
|
netmapEigenTrustAlphaKey, defaultEigenTrustAlpha,
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(containerFeeInitFlag)},
|
netmapBasicIncomeRateKey, viper.GetInt64(incomeRateInitFlag),
|
||||||
{Type: smartcontract.StringType, Value: netmapContainerAliasFeeKey},
|
netmapInnerRingCandidateFeeKey, viper.GetInt64(candidateFeeInitFlag),
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(containerAliasFeeInitFlag)},
|
netmapWithdrawFeeKey, viper.GetInt64(withdrawFeeInitFlag),
|
||||||
{Type: smartcontract.StringType, Value: netmapEigenTrustIterationsKey},
|
|
||||||
{Type: smartcontract.IntegerType, Value: int64(defaultEigenTrustIterations)},
|
|
||||||
{Type: smartcontract.StringType, Value: netmapEigenTrustAlphaKey},
|
|
||||||
{Type: smartcontract.StringType, Value: defaultEigenTrustAlpha},
|
|
||||||
{Type: smartcontract.StringType, Value: netmapBasicIncomeRateKey},
|
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(incomeRateInitFlag)},
|
|
||||||
{Type: smartcontract.StringType, Value: netmapInnerRingCandidateFeeKey},
|
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(candidateFeeInitFlag)},
|
|
||||||
{Type: smartcontract.StringType, Value: netmapWithdrawFeeKey},
|
|
||||||
{Type: smartcontract.IntegerType, Value: viper.GetInt64(withdrawFeeInitFlag)},
|
|
||||||
}
|
}
|
||||||
items = append(items,
|
items = append(items,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[balanceContract].Hash),
|
c.Contracts[balanceContract].Hash,
|
||||||
newContractParameter(smartcontract.Hash160Type, c.Contracts[containerContract].Hash),
|
c.Contracts[containerContract].Hash,
|
||||||
newContractParameter(smartcontract.ArrayType, keysParam),
|
keysParam,
|
||||||
newContractParameter(smartcontract.ArrayType, configParam))
|
configParam)
|
||||||
case proxyContract:
|
case proxyContract:
|
||||||
items = nil
|
items = nil
|
||||||
case reputationContract:
|
case reputationContract:
|
||||||
|
@ -589,18 +559,6 @@ func (c *initializeContext) getContractDeployData(ctrName string, keysParam []sm
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) getAlphabetDeployParameters(i, n int) []smartcontract.Parameter {
|
|
||||||
items := c.getAlphabetDeployItems(i, n)
|
|
||||||
return []smartcontract.Parameter{
|
|
||||||
newContractParameter(smartcontract.BoolType, items[0]),
|
|
||||||
newContractParameter(smartcontract.Hash160Type, items[1]),
|
|
||||||
newContractParameter(smartcontract.Hash160Type, items[2]),
|
|
||||||
newContractParameter(smartcontract.StringType, items[3]),
|
|
||||||
newContractParameter(smartcontract.IntegerType, items[4]),
|
|
||||||
newContractParameter(smartcontract.IntegerType, items[5]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *initializeContext) getAlphabetDeployItems(i, n int) []interface{} {
|
func (c *initializeContext) getAlphabetDeployItems(i, n int) []interface{} {
|
||||||
items := make([]interface{}, 6)
|
items := make([]interface{}, 6)
|
||||||
items[0] = false
|
items[0] = false
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
@ -165,8 +164,8 @@ func (c *initializeContext) nnsRegisterDomain(nnsHash, expectedHash util.Uint160
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160) (bool, error) {
|
func (c *initializeContext) nnsRootRegistered(nnsHash util.Uint160) (bool, error) {
|
||||||
params := []smartcontract.Parameter{{Type: smartcontract.StringType, Value: "name.neofs"}}
|
params := []interface{}{"name.neofs"}
|
||||||
res, err := c.Client.InvokeFunction(nnsHash, "isAvailable", params, nil)
|
res, err := invokeFunction(c.Client, nnsHash, "isAvailable", params, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -185,16 +184,7 @@ func nnsResolveHash(c Client, nnsHash util.Uint160, domain string) (util.Uint160
|
||||||
}
|
}
|
||||||
|
|
||||||
func nnsResolve(c Client, nnsHash util.Uint160, domain string) (stackitem.Item, error) {
|
func nnsResolve(c Client, nnsHash util.Uint160, domain string) (stackitem.Item, error) {
|
||||||
result, err := c.InvokeFunction(nnsHash, "resolve", []smartcontract.Parameter{
|
result, err := invokeFunction(c, nnsHash, "resolve", []interface{}{domain, int64(nns.TXT)}, nil)
|
||||||
{
|
|
||||||
Type: smartcontract.StringType,
|
|
||||||
Value: domain,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: smartcontract.IntegerType,
|
|
||||||
Value: int64(nns.TXT),
|
|
||||||
},
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("`resolve`: %w", err)
|
return nil, fmt.Errorf("`resolve`: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -244,11 +234,24 @@ func parseNNSResolveResult(res stackitem.Item) (util.Uint160, error) {
|
||||||
return util.Uint160DecodeStringLE(string(bs))
|
return util.Uint160DecodeStringLE(string(bs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errNNSIsAvailableInvalid = errors.New("`isAvailable`: invalid response")
|
||||||
|
|
||||||
func nnsIsAvailable(c Client, nnsHash util.Uint160, name string) (bool, error) {
|
func nnsIsAvailable(c Client, nnsHash util.Uint160, name string) (bool, error) {
|
||||||
switch ct := c.(type) {
|
switch ct := c.(type) {
|
||||||
case *client.Client:
|
case *client.Client:
|
||||||
return ct.NNSIsAvailable(nnsHash, name)
|
return ct.NNSIsAvailable(nnsHash, name)
|
||||||
default:
|
default:
|
||||||
panic("unimplemented")
|
res, err := invokeFunction(c, nnsHash, "isAvailable", []interface{}{name}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if len(res.Stack) == 0 {
|
||||||
|
return false, errNNSIsAvailableInvalid
|
||||||
|
}
|
||||||
|
b, err := res.Stack[0].TryBool()
|
||||||
|
if err != nil {
|
||||||
|
return b, errNNSIsAvailableInvalid
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package morph
|
package morph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
|
@ -8,7 +9,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
@ -23,7 +23,7 @@ const initialAlphabetNEOAmount = native.NEOTotalSupply
|
||||||
func (c *initializeContext) registerCandidates() error {
|
func (c *initializeContext) registerCandidates() error {
|
||||||
neoHash := c.nativeHash(nativenames.Neo)
|
neoHash := c.nativeHash(nativenames.Neo)
|
||||||
|
|
||||||
res, err := c.Client.InvokeFunction(neoHash, "getCandidates", []smartcontract.Parameter{}, nil)
|
res, err := invokeFunction(c.Client, neoHash, "getCandidates", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func (c *initializeContext) registerCandidates() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
regPrice, err := getCandidateRegisterPrice(c.Client)
|
regPrice, err := c.getCandidateRegisterPrice()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't fetch registration price: %w", err)
|
return fmt.Errorf("can't fetch registration price: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -84,11 +84,25 @@ func (c *initializeContext) transferNEOFinished(neoHash util.Uint160) (bool, err
|
||||||
return bal < native.NEOTotalSupply, err
|
return bal < native.NEOTotalSupply, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCandidateRegisterPrice(c Client) (int64, error) {
|
var errGetPriceInvalid = errors.New("`getRegisterPrice`: invalid response")
|
||||||
switch ct := c.(type) {
|
|
||||||
|
func (c *initializeContext) getCandidateRegisterPrice() (int64, error) {
|
||||||
|
switch ct := c.Client.(type) {
|
||||||
case *client.Client:
|
case *client.Client:
|
||||||
return ct.GetCandidateRegisterPrice()
|
return ct.GetCandidateRegisterPrice()
|
||||||
default:
|
default:
|
||||||
panic("unimplemented")
|
neoHash := c.nativeHash(nativenames.Neo)
|
||||||
|
res, err := invokeFunction(c.Client, neoHash, "getRegisterPrice", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if len(res.Stack) == 0 {
|
||||||
|
return 0, errGetPriceInvalid
|
||||||
|
}
|
||||||
|
bi, err := res.Stack[0].TryInteger()
|
||||||
|
if err != nil || !bi.IsInt64() {
|
||||||
|
return 0, errGetPriceInvalid
|
||||||
|
}
|
||||||
|
return bi.Int64(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ func (c *initializeContext) setRolesFinished() (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pubs, err := c.Client.GetDesignatedByRole(noderoles.NeoFSAlphabet, height)
|
h := c.nativeHash(nativenames.Designation)
|
||||||
|
pubs, err := getDesignatedByRole(c.Client, h, noderoles.NeoFSAlphabet, height)
|
||||||
return len(pubs) == len(c.Wallets), err
|
return len(pubs) == len(c.Wallets), err
|
||||||
}
|
}
|
||||||
|
|
357
cmd/neofs-adm/internal/modules/morph/local_client.go
Normal file
357
cmd/neofs-adm/internal/modules/morph/local_client.go
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
package morph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type localClient struct {
|
||||||
|
bc *core.Blockchain
|
||||||
|
transactions []*transaction.Transaction
|
||||||
|
dumpPath string
|
||||||
|
accounts []*wallet.Account
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLocalClient(v *viper.Viper, wallets []*wallet.Wallet) (*localClient, error) {
|
||||||
|
cfg, err := config.LoadFile(v.GetString(protoConfigPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bc, err := core.NewBlockchain(storage.NewMemoryStore(), cfg.ProtocolConfiguration, zap.NewNop())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { go bc.Run() }()
|
||||||
|
|
||||||
|
m := smartcontract.GetDefaultHonestNodeCount(cfg.ProtocolConfiguration.ValidatorsCount)
|
||||||
|
accounts := make([]*wallet.Account, len(wallets))
|
||||||
|
for i := range accounts {
|
||||||
|
accounts[i], err = getWalletAccount(wallets[i], consensusAccountName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
indexMap := make(map[string]int)
|
||||||
|
for i, pub := range cfg.ProtocolConfiguration.StandbyCommittee {
|
||||||
|
indexMap[pub] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(accounts, func(i, j int) bool {
|
||||||
|
pi := accounts[i].PrivateKey().PublicKey().Bytes()
|
||||||
|
pj := accounts[j].PrivateKey().PublicKey().Bytes()
|
||||||
|
return indexMap[string(pi)] < indexMap[string(pj)]
|
||||||
|
})
|
||||||
|
sort.Slice(accounts[:cfg.ProtocolConfiguration.ValidatorsCount], func(i, j int) bool {
|
||||||
|
pi := accounts[i].PrivateKey().PublicKey().Bytes()
|
||||||
|
pj := accounts[j].PrivateKey().PublicKey().Bytes()
|
||||||
|
return bytes.Compare(pi, pj) == -1
|
||||||
|
})
|
||||||
|
|
||||||
|
return &localClient{
|
||||||
|
bc: bc,
|
||||||
|
dumpPath: v.GetString(localDumpFlag),
|
||||||
|
accounts: accounts[:m],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) GetBlockCount() (uint32, error) {
|
||||||
|
return l.bc.BlockHeight(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) GetContractStateByID(id int32) (*state.Contract, error) {
|
||||||
|
h, err := l.bc.GetContractScriptHash(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return l.GetContractStateByHash(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) GetContractStateByHash(h util.Uint160) (*state.Contract, error) {
|
||||||
|
if cs := l.bc.GetContractState(h); cs != nil {
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
return nil, storage.ErrKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) GetNativeContracts() ([]state.NativeContract, error) {
|
||||||
|
return l.bc.GetNatives(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) GetNetwork() (netmode.Magic, error) {
|
||||||
|
return l.bc.GetConfig().Magic, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) GetApplicationLog(h util.Uint256, t *trigger.Type) (*result.ApplicationLog, error) {
|
||||||
|
aer, err := l.bc.GetAppExecResults(h, *t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := result.NewApplicationLog(h, aer, *t)
|
||||||
|
return &a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee int64, netFee int64, cosigners []client.SignerAccount) (*transaction.Transaction, error) {
|
||||||
|
signers, accounts, err := getSigners(acc, cosigners)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to construct tx signers: %w", err)
|
||||||
|
}
|
||||||
|
if sysFee < 0 {
|
||||||
|
res, err := l.InvokeScript(script, signers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't add system fee to transaction: %w", err)
|
||||||
|
}
|
||||||
|
if res.State != "HALT" {
|
||||||
|
return nil, fmt.Errorf("can't add system fee to transaction: bad vm state: %s due to an error: %s", res.State, res.FaultException)
|
||||||
|
}
|
||||||
|
sysFee = res.GasConsumed
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := transaction.New(script, sysFee)
|
||||||
|
tx.Signers = signers
|
||||||
|
tx.ValidUntilBlock = l.bc.BlockHeight() + 2
|
||||||
|
|
||||||
|
err = l.addNetworkFee(tx, netFee, accounts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to add network fee: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addNetworkFee adds network fee for each witness script and optional extra
|
||||||
|
// network fee to transaction. `accs` is an array signer's accounts.
|
||||||
|
// Copied from neo-go with minor corrections (no need to support contract signers):
|
||||||
|
// https://github.com/nspcc-dev/neo-go/blob/6ff11baa1b9e4c71ef0d1de43b92a8c541ca732c/pkg/rpc/client/rpc.go#L960
|
||||||
|
func (l *localClient) addNetworkFee(tx *transaction.Transaction, extraFee int64, accs ...*wallet.Account) error {
|
||||||
|
if len(tx.Signers) != len(accs) {
|
||||||
|
return errors.New("number of signers must match number of scripts")
|
||||||
|
}
|
||||||
|
|
||||||
|
size := io.GetVarSize(tx)
|
||||||
|
ef := l.bc.GetBaseExecFee()
|
||||||
|
for i := range tx.Signers {
|
||||||
|
netFee, sizeDelta := fee.Calculate(ef, accs[i].Contract.Script)
|
||||||
|
tx.NetworkFee += netFee
|
||||||
|
size += sizeDelta
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.NetworkFee += int64(size)*l.bc.FeePerByte() + extraFee
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSigners returns an array of transaction signers and corresponding accounts from
|
||||||
|
// given sender and cosigners. If cosigners list already contains sender, the sender
|
||||||
|
// will be placed at the start of the list.
|
||||||
|
// Copied from neo-go with minor corrections:
|
||||||
|
// https://github.com/nspcc-dev/neo-go/blob/6ff11baa1b9e4c71ef0d1de43b92a8c541ca732c/pkg/rpc/client/rpc.go#L735
|
||||||
|
func getSigners(sender *wallet.Account, cosigners []client.SignerAccount) ([]transaction.Signer, []*wallet.Account, error) {
|
||||||
|
var (
|
||||||
|
signers []transaction.Signer
|
||||||
|
accounts []*wallet.Account
|
||||||
|
)
|
||||||
|
|
||||||
|
from := sender.Contract.ScriptHash()
|
||||||
|
s := transaction.Signer{
|
||||||
|
Account: from,
|
||||||
|
Scopes: transaction.None,
|
||||||
|
}
|
||||||
|
for _, c := range cosigners {
|
||||||
|
if c.Signer.Account == from {
|
||||||
|
s = c.Signer
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
signers = append(signers, c.Signer)
|
||||||
|
accounts = append(accounts, c.Account)
|
||||||
|
}
|
||||||
|
signers = append([]transaction.Signer{s}, signers...)
|
||||||
|
accounts = append([]*wallet.Account{sender}, accounts...)
|
||||||
|
return signers, accounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) NEP17BalanceOf(h util.Uint160, acc util.Uint160) (int64, error) {
|
||||||
|
res, err := invokeFunction(l, h, "balanceOf", []interface{}{acc}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
||||||
|
return 0, fmt.Errorf("`balance`: invalid response (empty: %t): %s",
|
||||||
|
len(res.Stack) == 0, res.FaultException)
|
||||||
|
}
|
||||||
|
bi, err := res.Stack[0].TryInteger()
|
||||||
|
if err != nil || !bi.IsInt64() {
|
||||||
|
return 0, fmt.Errorf("`balance`: invalid response")
|
||||||
|
}
|
||||||
|
return bi.Int64(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
|
||||||
|
lastBlock, err := l.bc.GetBlock(l.bc.CurrentBlockHash())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := transaction.New(script, 0)
|
||||||
|
tx.Signers = signers
|
||||||
|
tx.ValidUntilBlock = l.bc.BlockHeight() + 2
|
||||||
|
|
||||||
|
ic := l.bc.GetTestVM(trigger.Application, tx, &block.Block{
|
||||||
|
Header: block.Header{
|
||||||
|
Index: lastBlock.Index + 1,
|
||||||
|
Timestamp: lastBlock.Timestamp + 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
ic.VM.GasLimit = 100_0000_0000
|
||||||
|
ic.VM.LoadScriptWithFlags(script, callflag.All)
|
||||||
|
|
||||||
|
var errStr string
|
||||||
|
if err := ic.VM.Run(); err != nil {
|
||||||
|
errStr = err.Error()
|
||||||
|
}
|
||||||
|
return &result.Invoke{
|
||||||
|
State: ic.VM.State().String(),
|
||||||
|
GasConsumed: ic.VM.GasConsumed(),
|
||||||
|
Script: script,
|
||||||
|
Stack: ic.VM.Estack().ToArray(),
|
||||||
|
FaultException: errStr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) SendRawTransaction(tx *transaction.Transaction) (util.Uint256, error) {
|
||||||
|
l.transactions = append(l.transactions, tx)
|
||||||
|
return tx.Hash(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) putTransactions() error {
|
||||||
|
// 1. Prepare new block.
|
||||||
|
lastBlock, err := l.bc.GetBlock(l.bc.CurrentBlockHash())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer func() { l.transactions = l.transactions[:0] }()
|
||||||
|
|
||||||
|
b := &block.Block{
|
||||||
|
Header: block.Header{
|
||||||
|
NextConsensus: l.accounts[0].Contract.ScriptHash(),
|
||||||
|
Script: transaction.Witness{
|
||||||
|
VerificationScript: l.accounts[0].Contract.Script,
|
||||||
|
},
|
||||||
|
Timestamp: lastBlock.Timestamp + 1,
|
||||||
|
},
|
||||||
|
Transactions: l.transactions,
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.bc.GetConfig().StateRootInHeader {
|
||||||
|
b.StateRootEnabled = true
|
||||||
|
b.PrevStateRoot = l.bc.GetStateModule().CurrentLocalStateRoot()
|
||||||
|
}
|
||||||
|
b.PrevHash = lastBlock.Hash()
|
||||||
|
b.Index = lastBlock.Index + 1
|
||||||
|
b.RebuildMerkleRoot()
|
||||||
|
|
||||||
|
// 2. Sign prepared block.
|
||||||
|
var invocationScript []byte
|
||||||
|
|
||||||
|
magic := l.bc.GetConfig().Magic
|
||||||
|
for _, acc := range l.accounts {
|
||||||
|
sign := acc.PrivateKey().SignHashable(uint32(magic), b)
|
||||||
|
invocationScript = append(invocationScript, byte(opcode.PUSHDATA1), 64)
|
||||||
|
invocationScript = append(invocationScript, sign...)
|
||||||
|
}
|
||||||
|
b.Script.InvocationScript = invocationScript
|
||||||
|
|
||||||
|
// 3. Persist block.
|
||||||
|
return l.bc.AddBlock(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func invokeFunction(c Client, h util.Uint160, method string, parameters []interface{}, signers []transaction.Signer) (*result.Invoke, error) {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.Array(w.BinWriter, parameters...)
|
||||||
|
emit.AppCallNoArgs(w.BinWriter, h, method, callflag.All)
|
||||||
|
if w.Err != nil {
|
||||||
|
panic(fmt.Sprintf("BUG: invalid parameters for '%s': %v", method, w.Err))
|
||||||
|
}
|
||||||
|
return c.InvokeScript(w.Bytes(), signers)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errGetDesignatedByRoleResponse = errors.New("`getDesignatedByRole`: invalid response")
|
||||||
|
|
||||||
|
func getDesignatedByRole(c Client, h util.Uint160, role noderoles.Role, u uint32) (keys.PublicKeys, error) {
|
||||||
|
res, err := invokeFunction(c, h, "getDesignatedByRole", []interface{}{int64(role), int64(u)}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.State != vm.HaltState.String() || len(res.Stack) == 0 {
|
||||||
|
return nil, errGetDesignatedByRoleResponse
|
||||||
|
}
|
||||||
|
arr, ok := res.Stack[0].Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return nil, errGetDesignatedByRoleResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
pubs := make(keys.PublicKeys, len(arr))
|
||||||
|
for i := range arr {
|
||||||
|
bs, err := arr[i].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errGetDesignatedByRoleResponse
|
||||||
|
}
|
||||||
|
pubs[i], err = keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errGetDesignatedByRoleResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pubs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *localClient) dump() (err error) {
|
||||||
|
defer l.bc.Close()
|
||||||
|
|
||||||
|
f, err := os.Create(l.dumpPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
closeErr := f.Close()
|
||||||
|
if err == nil && closeErr != nil {
|
||||||
|
err = closeErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
w := io.NewBinWriterFromIO(f)
|
||||||
|
err = chaindump.Dump(l.bc, w, 0, l.bc.BlockHeight())
|
||||||
|
return
|
||||||
|
}
|
|
@ -7,13 +7,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
@ -25,7 +22,6 @@ import (
|
||||||
// and sending signed transactions to chain.
|
// and sending signed transactions to chain.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
GetBlockCount() (uint32, error)
|
GetBlockCount() (uint32, error)
|
||||||
GetDesignatedByRole(noderoles.Role, uint32) (keys.PublicKeys, error)
|
|
||||||
GetContractStateByID(int32) (*state.Contract, error)
|
GetContractStateByID(int32) (*state.Contract, error)
|
||||||
GetContractStateByHash(util.Uint160) (*state.Contract, error)
|
GetContractStateByHash(util.Uint160) (*state.Contract, error)
|
||||||
GetNativeContracts() ([]state.NativeContract, error)
|
GetNativeContracts() ([]state.NativeContract, error)
|
||||||
|
@ -33,7 +29,6 @@ type Client interface {
|
||||||
GetApplicationLog(util.Uint256, *trigger.Type) (*result.ApplicationLog, error)
|
GetApplicationLog(util.Uint256, *trigger.Type) (*result.ApplicationLog, error)
|
||||||
CreateTxFromScript([]byte, *wallet.Account, int64, int64, []client.SignerAccount) (*transaction.Transaction, error)
|
CreateTxFromScript([]byte, *wallet.Account, int64, int64, []client.SignerAccount) (*transaction.Transaction, error)
|
||||||
NEP17BalanceOf(util.Uint160, util.Uint160) (int64, error)
|
NEP17BalanceOf(util.Uint160, util.Uint160) (int64, error)
|
||||||
InvokeFunction(util.Uint160, string, []smartcontract.Parameter, []transaction.Signer) (*result.Invoke, error)
|
|
||||||
InvokeScript([]byte, []transaction.Signer) (*result.Invoke, error)
|
InvokeScript([]byte, []transaction.Signer) (*result.Invoke, error)
|
||||||
SendRawTransaction(*transaction.Transaction) (util.Uint256, error)
|
SendRawTransaction(*transaction.Transaction) (util.Uint256, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ const (
|
||||||
refillGasAmountFlag = "gas"
|
refillGasAmountFlag = "gas"
|
||||||
walletAccountFlag = "account"
|
walletAccountFlag = "account"
|
||||||
notaryDepositTillFlag = "till"
|
notaryDepositTillFlag = "till"
|
||||||
|
localDumpFlag = "local-dump"
|
||||||
|
protoConfigPath = "protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -68,6 +70,8 @@ var (
|
||||||
_ = viper.BindPFlag(containerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
_ = viper.BindPFlag(containerFeeInitFlag, cmd.Flags().Lookup(containerFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(containerAliasFeeInitFlag, cmd.Flags().Lookup(containerAliasFeeCLIFlag))
|
_ = viper.BindPFlag(containerAliasFeeInitFlag, cmd.Flags().Lookup(containerAliasFeeCLIFlag))
|
||||||
_ = viper.BindPFlag(withdrawFeeInitFlag, cmd.Flags().Lookup(withdrawFeeCLIFlag))
|
_ = viper.BindPFlag(withdrawFeeInitFlag, cmd.Flags().Lookup(withdrawFeeCLIFlag))
|
||||||
|
_ = viper.BindPFlag(protoConfigPath, cmd.Flags().Lookup(protoConfigPath))
|
||||||
|
_ = viper.BindPFlag(localDumpFlag, cmd.Flags().Lookup(localDumpFlag))
|
||||||
},
|
},
|
||||||
RunE: initializeSideChainCmd,
|
RunE: initializeSideChainCmd,
|
||||||
}
|
}
|
||||||
|
@ -197,6 +201,8 @@ func init() {
|
||||||
// Defaults are taken from neo-preodolenie.
|
// Defaults are taken from neo-preodolenie.
|
||||||
initCmd.Flags().Uint64(containerFeeCLIFlag, 1000, "container registration fee")
|
initCmd.Flags().Uint64(containerFeeCLIFlag, 1000, "container registration fee")
|
||||||
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "container alias fee")
|
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "container alias fee")
|
||||||
|
initCmd.Flags().String(protoConfigPath, "", "path to the consensus node configuration")
|
||||||
|
initCmd.Flags().String(localDumpFlag, "", "path to the blocks dump file")
|
||||||
|
|
||||||
RootCmd.AddCommand(generateStorageCmd)
|
RootCmd.AddCommand(generateStorageCmd)
|
||||||
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir")
|
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "path to alphabet wallets dir")
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -49,6 +49,7 @@ require (
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/holiman/uint256 v1.2.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/ipfs/go-cid v0.0.7 // indirect
|
github.com/ipfs/go-cid v0.0.7 // indirect
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -263,6 +263,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
|
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
|
||||||
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
@ -628,6 +629,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
|
Loading…
Reference in a new issue