forked from TrueCloudLab/neoneo-go
Merge pull request #1797 from nspcc-dev/rpc/fix-createtxfromscript
rpc, cli: allow to provide cosigners accounts to (*Client).CreateTxFromScript
This commit is contained in:
commit
0e7bb5cc77
11 changed files with 197 additions and 60 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
||||||
"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/crypto/keys"
|
||||||
"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/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
|
@ -22,6 +23,7 @@ import (
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -275,6 +277,78 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("real invoke and save tx", func(t *testing.T) {
|
||||||
|
txout := path.Join(tmpDir, "test_contract_tx.json")
|
||||||
|
|
||||||
|
nefName = path.Join(tmpDir, "verify.nef")
|
||||||
|
manifestName = path.Join(tmpDir, "verify.manifest.json")
|
||||||
|
e.Run(t, "neo-go", "contract", "compile",
|
||||||
|
"--in", "testdata/verify.go",
|
||||||
|
"--config", "testdata/verify.yml",
|
||||||
|
"--out", nefName, "--manifest", manifestName)
|
||||||
|
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.Run(t, "neo-go", "contract", "deploy",
|
||||||
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
|
"--wallet", validatorWallet, "--address", validatorAddr,
|
||||||
|
"--in", nefName, "--manifest", manifestName)
|
||||||
|
|
||||||
|
line, err := e.Out.ReadString('\n')
|
||||||
|
require.NoError(t, err)
|
||||||
|
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
|
||||||
|
hVerify, err := util.Uint160DecodeStringLE(line)
|
||||||
|
require.NoError(t, err)
|
||||||
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
|
cmd = []string{"neo-go", "contract", "invokefunction",
|
||||||
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||||
|
"--out", txout,
|
||||||
|
"--wallet", validatorWallet, "--address", validatorAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("without cosigner", func(t *testing.T) {
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.Run(t, append(cmd, hVerify.StringLE(), "verify")...)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with cosigner", func(t *testing.T) {
|
||||||
|
t.Run("cosigner is sender", func(t *testing.T) {
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", validatorAddr+":Global")...)
|
||||||
|
})
|
||||||
|
|
||||||
|
acc, err := wallet.NewAccount()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
acc.ConvertMultisig(2, keys.PublicKeys{acc.PrivateKey().PublicKey(), pk.PublicKey()})
|
||||||
|
|
||||||
|
t.Run("cosigner is multisig account", func(t *testing.T) {
|
||||||
|
t.Run("missing in the wallet", func(t *testing.T) {
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", acc.Address)...)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("good", func(t *testing.T) {
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", multisigAddr)...)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cosigner is deployed contract", func(t *testing.T) {
|
||||||
|
t.Run("missing in the wallet", func(t *testing.T) {
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.RunWithError(t, append(cmd, hVerify.StringLE(), "verify", "--", h.StringLE())...)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("good", func(t *testing.T) {
|
||||||
|
e.In.WriteString("one\r")
|
||||||
|
e.Run(t, append(cmd, hVerify.StringLE(), "verify", "--", hVerify.StringLE())...)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("test Storage.Find", func(t *testing.T) {
|
t.Run("test Storage.Find", func(t *testing.T) {
|
||||||
cmd := []string{"neo-go", "contract", "testinvokefunction",
|
cmd := []string{"neo-go", "contract", "testinvokefunction",
|
||||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
const (
|
const (
|
||||||
validatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY"
|
validatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY"
|
||||||
validatorAddr = "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK"
|
validatorAddr = "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK"
|
||||||
|
multisigAddr = "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY"
|
||||||
|
|
||||||
validatorWallet = "testdata/wallet1_solo.json"
|
validatorWallet = "testdata/wallet1_solo.json"
|
||||||
)
|
)
|
||||||
|
|
|
@ -83,6 +83,11 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.checkNextLine(t, "^\\s*$")
|
||||||
|
addr4, err := address.StringToUint160("NbxpLNCCSWZ9BkYpCYT8NfN1uoxq9Rfbrn") // deployed verify.go contract
|
||||||
|
require.NoError(t, err)
|
||||||
|
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr4))
|
||||||
e.checkEOF(t)
|
e.checkEOF(t)
|
||||||
})
|
})
|
||||||
t.Run("Bad token", func(t *testing.T) {
|
t.Run("Bad token", func(t *testing.T) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ 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/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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
@ -509,15 +510,17 @@ func invokeFunction(ctx *cli.Context) error {
|
||||||
|
|
||||||
func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
gas fixedn.Fixed8
|
gas fixedn.Fixed8
|
||||||
operation string
|
operation string
|
||||||
params = make([]smartcontract.Parameter, 0)
|
params = make([]smartcontract.Parameter, 0)
|
||||||
paramsStart = 1
|
paramsStart = 1
|
||||||
cosigners []transaction.Signer
|
cosigners []transaction.Signer
|
||||||
cosignersOffset = 0
|
cosignersAccounts []client.SignerAccount
|
||||||
resp *result.Invoke
|
cosignersOffset = 0
|
||||||
acc *wallet.Account
|
resp *result.Invoke
|
||||||
|
acc *wallet.Account
|
||||||
|
wall *wallet.Wallet
|
||||||
)
|
)
|
||||||
|
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
|
@ -554,10 +557,20 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
||||||
|
|
||||||
if signAndPush {
|
if signAndPush {
|
||||||
gas = flags.Fixed8FromContext(ctx, "gas")
|
gas = flags.Fixed8FromContext(ctx, "gas")
|
||||||
acc, err = getAccFromContext(ctx)
|
acc, wall, err = getAccFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for i := range cosigners {
|
||||||
|
cosignerAcc := wall.GetAccount(cosigners[i].Account)
|
||||||
|
if cosignerAcc == nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("can't calculate network fee: no account was found in the wallet for cosigner #%d", i), 1)
|
||||||
|
}
|
||||||
|
cosignersAccounts = append(cosignersAccounts, client.SignerAccount{
|
||||||
|
Signer: cosigners[i],
|
||||||
|
Account: cosignerAcc,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -579,7 +592,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
||||||
fmt.Fprintln(ctx.App.Writer, errText+". Sending transaction...")
|
fmt.Fprintln(ctx.App.Writer, errText+". Sending transaction...")
|
||||||
}
|
}
|
||||||
if out := ctx.String("out"); out != "" {
|
if out := ctx.String("out"); out != "" {
|
||||||
tx, err := c.CreateTxFromScript(resp.Script, acc, resp.GasConsumed, int64(gas), cosigners...)
|
tx, err := c.CreateTxFromScript(resp.Script, acc, resp.GasConsumed, int64(gas), cosignersAccounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
|
||||||
}
|
}
|
||||||
|
@ -593,7 +606,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
|
||||||
if len(resp.Script) == 0 {
|
if len(resp.Script) == 0 {
|
||||||
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
|
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
|
||||||
}
|
}
|
||||||
txHash, err := c.SignAndPushInvocationTx(resp.Script, acc, resp.GasConsumed, gas, cosigners)
|
txHash, err := c.SignAndPushInvocationTx(resp.Script, acc, resp.GasConsumed, gas, cosignersAccounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
|
||||||
}
|
}
|
||||||
|
@ -747,17 +760,17 @@ func inspect(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAccFromContext(ctx *cli.Context) (*wallet.Account, error) {
|
func getAccFromContext(ctx *cli.Context) (*wallet.Account, *wallet.Wallet, error) {
|
||||||
var addr util.Uint160
|
var addr util.Uint160
|
||||||
|
|
||||||
wPath := ctx.String("wallet")
|
wPath := ctx.String("wallet")
|
||||||
if len(wPath) == 0 {
|
if len(wPath) == 0 {
|
||||||
return nil, cli.NewExitError(errNoWallet, 1)
|
return nil, nil, cli.NewExitError(errNoWallet, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
wall, err := wallet.NewWalletFromFile(wPath)
|
wall, err := wallet.NewWalletFromFile(wPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cli.NewExitError(err, 1)
|
return nil, nil, cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
addrFlag := ctx.Generic("address").(*flags.Address)
|
addrFlag := ctx.Generic("address").(*flags.Address)
|
||||||
if addrFlag.IsSet {
|
if addrFlag.IsSet {
|
||||||
|
@ -767,20 +780,20 @@ func getAccFromContext(ctx *cli.Context) (*wallet.Account, error) {
|
||||||
}
|
}
|
||||||
acc := wall.GetAccount(addr)
|
acc := wall.GetAccount(addr)
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
return nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1)
|
return nil, nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawPass, err := input.ReadPassword(
|
rawPass, err := input.ReadPassword(
|
||||||
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
|
fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cli.NewExitError(err, 1)
|
return nil, nil, cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
pass := strings.TrimRight(string(rawPass), "\n")
|
pass := strings.TrimRight(string(rawPass), "\n")
|
||||||
err = acc.Decrypt(pass)
|
err = acc.Decrypt(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cli.NewExitError(err, 1)
|
return nil, nil, cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
return acc, nil
|
return acc, wall, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractDeploy deploys contract.
|
// contractDeploy deploys contract.
|
||||||
|
@ -795,7 +808,7 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
gas := flags.Fixed8FromContext(ctx, "gas")
|
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||||
|
|
||||||
acc, err := getAccFromContext(ctx)
|
acc, _, err := getAccFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
4
cli/testdata/verify.go
vendored
4
cli/testdata/verify.go
vendored
|
@ -1,8 +1,10 @@
|
||||||
package testdata
|
package testdata
|
||||||
|
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
|
||||||
func Verify() bool {
|
func Verify() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func OnNEP17Payment(from []byte, amount int, data interface{}) {
|
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
|
||||||
}
|
}
|
||||||
|
|
1
cli/testdata/verify.yml
vendored
Normal file
1
cli/testdata/verify.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
name: Test verify
|
12
cli/testdata/wallet1_solo.json
vendored
12
cli/testdata/wallet1_solo.json
vendored
|
@ -59,6 +59,18 @@
|
||||||
},
|
},
|
||||||
"lock": false,
|
"lock": false,
|
||||||
"isdefault": false
|
"isdefault": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address" : "NbxpLNCCSWZ9BkYpCYT8NfN1uoxq9Rfbrn",
|
||||||
|
"key" : "6PYXDze5Ak4HahYKygcNzk6n65ACjWdDCYLSuKgA5KG8vyMJSFboUNSiPD",
|
||||||
|
"label" : "",
|
||||||
|
"contract" : {
|
||||||
|
"script" : "VwEAEUBXAANA",
|
||||||
|
"deployed" : true,
|
||||||
|
"parameters" : []
|
||||||
|
},
|
||||||
|
"lock" : false,
|
||||||
|
"isdefault" : false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scrypt": {
|
"scrypt": {
|
||||||
|
|
|
@ -11,6 +11,7 @@ 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/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"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/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"
|
||||||
|
@ -108,9 +109,13 @@ func handleCandidate(ctx *cli.Context, method string, sysGas int64) error {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCall(w.BinWriter, neoContractHash, method, callflag.States, acc.PrivateKey().PublicKey().Bytes())
|
emit.AppCall(w.BinWriter, neoContractHash, method, callflag.States, acc.PrivateKey().PublicKey().Bytes())
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
tx, err := c.CreateTxFromScript(w.Bytes(), acc, sysGas, int64(gas), transaction.Signer{
|
tx, err := c.CreateTxFromScript(w.Bytes(), acc, sysGas, int64(gas), []client.SignerAccount{{
|
||||||
Account: acc.Contract.ScriptHash(),
|
Signer: transaction.Signer{
|
||||||
Scopes: transaction.CalledByEntry,
|
Account: acc.Contract.ScriptHash(),
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
Account: acc,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
|
@ -171,10 +176,13 @@ func handleVote(ctx *cli.Context) error {
|
||||||
emit.AppCall(w.BinWriter, neoContractHash, "vote", callflag.States, addr.BytesBE(), pubArg)
|
emit.AppCall(w.BinWriter, neoContractHash, "vote", callflag.States, addr.BytesBE(), pubArg)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{
|
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), []client.SignerAccount{{
|
||||||
Account: acc.Contract.ScriptHash(),
|
Signer: transaction.Signer{
|
||||||
Scopes: transaction.CalledByEntry,
|
Account: acc.Contract.ScriptHash(),
|
||||||
})
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
Account: acc,
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,13 @@ type TransferTarget struct {
|
||||||
Amount int64
|
Amount int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignerAccount represents combination of the transaction.Signer and the
|
||||||
|
// corresponding wallet.Account.
|
||||||
|
type SignerAccount struct {
|
||||||
|
Signer transaction.Signer
|
||||||
|
Account *wallet.Account
|
||||||
|
}
|
||||||
|
|
||||||
// NEP17Decimals invokes `decimals` NEP17 method on a specified contract.
|
// NEP17Decimals invokes `decimals` NEP17 method on a specified contract.
|
||||||
func (c *Client) NEP17Decimals(tokenHash util.Uint160) (int64, error) {
|
func (c *Client) NEP17Decimals(tokenHash util.Uint160) (int64, error) {
|
||||||
result, err := c.InvokeFunction(tokenHash, "decimals", []smartcontract.Parameter{}, nil)
|
result, err := c.InvokeFunction(tokenHash, "decimals", []smartcontract.Parameter{}, nil)
|
||||||
|
@ -140,23 +147,24 @@ func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, reci
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bad account address: %v", err)
|
return nil, fmt.Errorf("bad account address: %v", err)
|
||||||
}
|
}
|
||||||
return c.CreateTxFromScript(w.Bytes(), acc, -1, gas, transaction.Signer{
|
return c.CreateTxFromScript(w.Bytes(), acc, -1, gas, []SignerAccount{{
|
||||||
Account: accAddr,
|
Signer: transaction.Signer{
|
||||||
Scopes: transaction.CalledByEntry,
|
Account: accAddr,
|
||||||
})
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
Account: acc,
|
||||||
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee.
|
// CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee.
|
||||||
// If sysFee <= 0, it is determined via result of `invokescript` RPC. You should
|
// If sysFee <= 0, it is determined via result of `invokescript` RPC. You should
|
||||||
// initialize network magic with Init before calling CreateTxFromScript.
|
// initialize network magic with Init before calling CreateTxFromScript.
|
||||||
func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, netFee int64,
|
func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee, netFee int64,
|
||||||
cosigners ...transaction.Signer) (*transaction.Transaction, error) {
|
cosigners []SignerAccount) (*transaction.Transaction, error) {
|
||||||
from, err := address.StringToUint160(acc.Address)
|
signers, accounts, err := getSigners(acc, cosigners)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bad account address: %v", err)
|
return nil, fmt.Errorf("failed to construct tx signers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signers := getSigners(from, cosigners)
|
|
||||||
if sysFee < 0 {
|
if sysFee < 0 {
|
||||||
result, err := c.InvokeScript(script, signers)
|
result, err := c.InvokeScript(script, signers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -179,7 +187,7 @@ func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, sysFee,
|
||||||
return nil, fmt.Errorf("failed to add validUntilBlock to transaction: %w", err)
|
return nil, fmt.Errorf("failed to add validUntilBlock to transaction: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.AddNetworkFee(tx, netFee, acc)
|
err = c.AddNetworkFee(tx, netFee, accounts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to add network fee: %w", err)
|
return nil, fmt.Errorf("failed to add network fee: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -522,11 +522,11 @@ func (c *Client) SubmitRawOracleResponse(ps request.RawParams) error {
|
||||||
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
||||||
// transaction using given wif to sign it and spending the amount of gas
|
// transaction using given wif to sign it and spending the amount of gas
|
||||||
// specified. It returns a hash of the invocation transaction and an error.
|
// specified. It returns a hash of the invocation transaction and an error.
|
||||||
func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee int64, netfee fixedn.Fixed8, cosigners []transaction.Signer) (util.Uint256, error) {
|
func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sysfee int64, netfee fixedn.Fixed8, cosigners []SignerAccount) (util.Uint256, error) {
|
||||||
var txHash util.Uint256
|
var txHash util.Uint256
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
tx, err := c.CreateTxFromScript(script, acc, sysfee, int64(netfee), cosigners...)
|
tx, err := c.CreateTxFromScript(script, acc, sysfee, int64(netfee), cosigners)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return txHash, fmt.Errorf("failed to create tx: %w", err)
|
return txHash, fmt.Errorf("failed to create tx: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -544,25 +544,33 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys
|
||||||
return txHash, nil
|
return txHash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSigners returns an array of transaction signers from given sender and cosigners.
|
// getSigners returns an array of transaction signers and corresponding accounts from
|
||||||
// If cosigners list already contains sender, the sender will be placed at the start of
|
// given sender and cosigners. If cosigners list already contains sender, the sender
|
||||||
// the list.
|
// will be placed at the start of the list.
|
||||||
func getSigners(sender util.Uint160, cosigners []transaction.Signer) []transaction.Signer {
|
func getSigners(sender *wallet.Account, cosigners []SignerAccount) ([]transaction.Signer, []*wallet.Account, error) {
|
||||||
|
var (
|
||||||
|
signers []transaction.Signer
|
||||||
|
accounts []*wallet.Account
|
||||||
|
)
|
||||||
|
from, err := address.StringToUint160(sender.Address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("bad sender account address: %v", err)
|
||||||
|
}
|
||||||
s := transaction.Signer{
|
s := transaction.Signer{
|
||||||
Account: sender,
|
Account: from,
|
||||||
Scopes: transaction.None,
|
Scopes: transaction.None,
|
||||||
}
|
}
|
||||||
for i, c := range cosigners {
|
for _, c := range cosigners {
|
||||||
if c.Account == sender {
|
if c.Signer.Account == from {
|
||||||
if i == 0 {
|
s.Scopes = c.Signer.Scopes
|
||||||
return cosigners
|
continue
|
||||||
}
|
|
||||||
s.Scopes = c.Scopes
|
|
||||||
cosigners = append(cosigners[:i], cosigners[i+1:]...)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
signers = append(signers, c.Signer)
|
||||||
|
accounts = append(accounts, c.Account)
|
||||||
}
|
}
|
||||||
return append([]transaction.Signer{s}, cosigners...)
|
signers = append([]transaction.Signer{s}, signers...)
|
||||||
|
accounts = append([]*wallet.Account{sender}, accounts...)
|
||||||
|
return signers, accounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignAndPushP2PNotaryRequest creates and pushes P2PNotary request constructed from the main
|
// SignAndPushP2PNotaryRequest creates and pushes P2PNotary request constructed from the main
|
||||||
|
|
|
@ -203,10 +203,15 @@ func TestSignAndPushInvocationTx(t *testing.T) {
|
||||||
|
|
||||||
priv := testchain.PrivateKey(0)
|
priv := testchain.PrivateKey(0)
|
||||||
acc := wallet.NewAccountFromPrivateKey(priv)
|
acc := wallet.NewAccountFromPrivateKey(priv)
|
||||||
h, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc, 30, 0, []transaction.Signer{{
|
h, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc, 30, 0, []client.SignerAccount{
|
||||||
Account: priv.GetScriptHash(),
|
{
|
||||||
Scopes: transaction.CalledByEntry,
|
Signer: transaction.Signer{
|
||||||
}})
|
Account: priv.GetScriptHash(),
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
Account: acc,
|
||||||
|
},
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mp := chain.GetMemPool()
|
mp := chain.GetMemPool()
|
||||||
|
@ -347,7 +352,7 @@ func TestCreateTxFromScript(t *testing.T) {
|
||||||
priv := testchain.PrivateKey(0)
|
priv := testchain.PrivateKey(0)
|
||||||
acc := wallet.NewAccountFromPrivateKey(priv)
|
acc := wallet.NewAccountFromPrivateKey(priv)
|
||||||
t.Run("NoSystemFee", func(t *testing.T) {
|
t.Run("NoSystemFee", func(t *testing.T) {
|
||||||
tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, -1, 10)
|
tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, -1, 10, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, tx.ValidUntilBlock > chain.BlockHeight())
|
require.True(t, tx.ValidUntilBlock > chain.BlockHeight())
|
||||||
require.EqualValues(t, 30, tx.SystemFee) // PUSH1
|
require.EqualValues(t, 30, tx.SystemFee) // PUSH1
|
||||||
|
@ -355,7 +360,7 @@ func TestCreateTxFromScript(t *testing.T) {
|
||||||
require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account)
|
require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account)
|
||||||
})
|
})
|
||||||
t.Run("ProvideSystemFee", func(t *testing.T) {
|
t.Run("ProvideSystemFee", func(t *testing.T) {
|
||||||
tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, 123, 10)
|
tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, 123, 10, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, tx.ValidUntilBlock > chain.BlockHeight())
|
require.True(t, tx.ValidUntilBlock > chain.BlockHeight())
|
||||||
require.EqualValues(t, 123, tx.SystemFee)
|
require.EqualValues(t, 123, tx.SystemFee)
|
||||||
|
|
Loading…
Reference in a new issue