Skip to content

Commit

Permalink
sidechain/deploy: Fix version reading of local contracts
Browse files Browse the repository at this point in the history
Previously, `readContractLocalVersion` didn't pass extra arguments to
deploy method. This was normal for the NNS contract, but some other
system contracts would fail without the args. The function accepts
optional data from now.

Another drawback: if contract already exists (deployed earlier),
`deploy` method throws `contract already exists` exception. To avoid
this, dummy (zero) signer is used as a sender which almost definitely
doesn't collide with the real one.

One more disadvantage: function didn't add committee signers to deploy
invocation. For some contracts that require committee witness for
deployment (e.g. Container) version reader could not execute. To cover
this, the function now accepts committee members and, for simplicity,
always attach them as deployent transaction's signers.

Signed-off-by: Leonard Lyubich <[email protected]>
  • Loading branch information
cthulhu-rider committed Jul 18, 2023
1 parent 1f7becb commit 73f9189
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 5 deletions.
31 changes: 27 additions & 4 deletions pkg/morph/deploy/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"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/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neorpc"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"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/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
Expand Down Expand Up @@ -259,8 +262,14 @@ func readContractOnChainVersion(b Blockchain, onChainAddress util.Uint160) (cont
}

// readContractLocalVersion returns version of the local smart contract
// represented by its compiled artifacts.
func readContractLocalVersion(rpc invoker.RPCInvoke, localNEF nef.File, localManifest manifest.Manifest) (contractVersion, error) {
// represented by its compiled artifacts. Deployment is tested using provided
// invoker on behalf of the committee.
func readContractLocalVersion(rpc invoker.RPCInvoke, committee keys.PublicKeys, localNEF nef.File, localManifest manifest.Manifest, deployArgs ...interface{}) (contractVersion, error) {
multiSigScript, err := smartcontract.CreateMultiSigRedeemScript(smartcontract.GetMajorityHonestNodeCount(len(committee)), committee)
if err != nil {
return contractVersion{}, fmt.Errorf("create committee multi-signature verification script: %w", err)
}

jManifest, err := json.Marshal(localManifest)
if err != nil {
return contractVersion{}, fmt.Errorf("encode manifest into JSON: %w", err)
Expand All @@ -271,15 +280,29 @@ func readContractLocalVersion(rpc invoker.RPCInvoke, localNEF nef.File, localMan
return contractVersion{}, fmt.Errorf("encode NEF into binary: %w", err)
}

var deployData interface{}
if len(deployArgs) > 0 {
deployData = deployArgs
}

script := io.NewBufBinWriter()
emit.Opcodes(script.BinWriter, opcode.NEWARRAY0)
emit.Int(script.BinWriter, int64(callflag.All))
emit.String(script.BinWriter, methodVersion)
emit.AppCall(script.BinWriter, management.Hash, "deploy", callflag.All, bNEF, jManifest)
emit.AppCall(script.BinWriter, management.Hash, "deploy", callflag.All, bNEF, jManifest, deployData)
emit.Opcodes(script.BinWriter, opcode.PUSH2, opcode.PICKITEM)
emit.Syscall(script.BinWriter, interopnames.SystemContractCall)

res, err := invoker.New(rpc, nil).Run(script.Bytes())
res, err := invoker.New(rpc, []transaction.Signer{
{
Account: util.Uint160{}, // zero hash to avoid 'contract already exists' case
Scopes: transaction.None,
},
{
Account: hash.Hash160(multiSigScript),
Scopes: transaction.Global,
},
}).Run(script.Bytes())
if err != nil {
return contractVersion{}, fmt.Errorf("run test script deploying contract and calling its '%s' method: %w", methodVersion, err)
}
Expand Down
11 changes: 10 additions & 1 deletion pkg/morph/deploy/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/nspcc-dev/neo-go/pkg/compiler"
"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/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
Expand Down Expand Up @@ -148,7 +149,15 @@ func TestReadContractLocalVersion(t *testing.T) {

ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{Name: "Helper"})

res, err := readContractLocalVersion(newTestRPCInvoker(t, e), *ctr.NEF, *ctr.Manifest)
var committeeSigner neotest.SingleSigner

if single, ok := acc.(neotest.SingleSigner); ok {
committeeSigner = single
} else {
committeeSigner = acc.(neotest.MultiSigner).Single(0)
}

res, err := readContractLocalVersion(newTestRPCInvoker(t, e), keys.PublicKeys{committeeSigner.Account().PublicKey()}, *ctr.NEF, *ctr.Manifest)
require.NoError(t, err)
require.EqualValues(t, version, res.toUint64())
}

0 comments on commit 73f9189

Please sign in to comment.