forked from TrueCloudLab/neoneo-go
rpc: provide cosigners accounts to CreateTxFromScript
We need to define network fee for each of cosigners, and the only way to do it is to access the cosigner's script.
This commit is contained in:
parent
b1b9a8cf66
commit
6c0faa4ea3
11 changed files with 193 additions and 56 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,10 +109,14 @@ 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)
|
||||||
} else if err = acc.SignTx(tx); err != nil {
|
} else if err = acc.SignTx(tx); err != nil {
|
||||||
|
@ -171,9 +176,12 @@ 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,9 +147,12 @@ 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,
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,13 +160,11 @@ func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, reci
|
||||||
// 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,7 +522,7 @@ 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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue