wallet: add ScriptHash() to Account

It allows to simplify a lot of code and avoid getting a PrivateKey in some
cases.
This commit is contained in:
Roman Khimov 2022-09-01 19:04:47 +03:00
parent fd8da6fdb9
commit e569edc841
16 changed files with 38 additions and 74 deletions

View file

@ -199,10 +199,7 @@ func ParseParams(args []string, calledFromMain bool) (int, []smartcontract.Param
// accounts from the provided wallet. // accounts from the provided wallet.
func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers []transaction.Signer, accScope transaction.WitnessScope) ([]actor.SignerAccount, error) { func GetSignersAccounts(senderAcc *wallet.Account, wall *wallet.Wallet, signers []transaction.Signer, accScope transaction.WitnessScope) ([]actor.SignerAccount, error) {
signersAccounts := make([]actor.SignerAccount, 0, len(signers)+1) signersAccounts := make([]actor.SignerAccount, 0, len(signers)+1)
sender, err := address.StringToUint160(senderAcc.Address) sender := senderAcc.ScriptHash()
if err != nil {
return nil, err
}
signersAccounts = append(signersAccounts, actor.SignerAccount{ signersAccounts = append(signersAccounts, actor.SignerAccount{
Signer: transaction.Signer{ Signer: transaction.Signer{
Account: sender, Account: sender,

View file

@ -162,8 +162,7 @@ func TestNEP17Transfer(t *testing.T) {
e.checkNextLine(t, `^Total fee:\s*(\d|\.)+`) e.checkNextLine(t, `^Total fee:\s*(\d|\.)+`)
e.checkTxPersisted(t) e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address) sh := w.Accounts[0].ScriptHash()
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(1), b) require.Equal(t, big.NewInt(1), b)
@ -172,8 +171,6 @@ func TestNEP17Transfer(t *testing.T) {
e.Run(t, append(args, "--force")...) e.Run(t, append(args, "--force")...)
e.checkTxPersisted(t) e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(2), b) require.Equal(t, big.NewInt(2), b)
}) })
@ -198,8 +195,6 @@ func TestNEP17Transfer(t *testing.T) {
e.Run(t, args...) e.Run(t, args...)
e.checkTxPersisted(t) e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(3), b) require.Equal(t, big.NewInt(3), b)

View file

@ -7,7 +7,6 @@ import (
"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/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context" "github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
) )
@ -21,11 +20,7 @@ func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Acc
priv := acc.PrivateKey() priv := acc.PrivateKey()
pub := priv.PublicKey() pub := priv.PublicKey()
sign := priv.SignHashable(uint32(net), tx) sign := priv.SignHashable(uint32(net), tx)
h, err := address.StringToUint160(acc.Address) if err := scCtx.AddSignature(acc.ScriptHash(), acc.Contract, pub, sign); err != nil {
if err != nil {
return fmt.Errorf("invalid address: %s", acc.Address)
}
if err := scCtx.AddSignature(h, acc.Contract, pub, sign); err != nil {
return fmt.Errorf("can't add signature: %w", err) return fmt.Errorf("can't add signature: %w", err)
} }
} }

View file

@ -286,17 +286,12 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
} }
} }
for k, acc := range accounts { for k, acc := range accounts {
addrHash, err := address.StringToUint160(acc.Address)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid account address: %w", err), 1)
}
if k != 0 { if k != 0 {
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
} }
fmt.Fprintf(ctx.App.Writer, "Account %s\n", acc.Address) fmt.Fprintf(ctx.App.Writer, "Account %s\n", acc.Address)
err = accHandler(ctx, c, addrHash, name, token, tokenID) err = accHandler(ctx, c, acc.ScriptHash(), name, token, tokenID)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }

View file

@ -166,7 +166,7 @@ func TestNEO_Vote(t *testing.T) {
txes = append(txes, voteTx) txes = append(txes, voteTx)
} }
} }
txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().PublicKey().GetScriptHash())) txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().ScriptHash()))
neoValidatorsInvoker.AddNewBlock(t, txes...) neoValidatorsInvoker.AddNewBlock(t, txes...)
for _, tx := range txes { for _, tx := range txes {
e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values

View file

@ -7,7 +7,6 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"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/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -276,8 +275,5 @@ func TestSender(t *testing.T) {
client, acc := testRPCAndAccount(t) client, acc := testRPCAndAccount(t)
a, err := NewSimple(client, acc) a, err := NewSimple(client, acc)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, acc.ScriptHash(), a.Sender())
addr, err := address.StringToUint160(acc.Address)
require.NoError(t, err)
require.Equal(t, addr, a.Sender())
} }

View file

@ -6,7 +6,6 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"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/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "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"
@ -88,13 +87,9 @@ func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint1
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create NEP-11 transfer script: %w", err) return nil, fmt.Errorf("failed to create NEP-11 transfer script: %w", err)
} }
from, err := address.StringToUint160(acc.Address)
if err != nil {
return nil, fmt.Errorf("bad account address: %w", err)
}
return c.CreateTxFromScript(script, acc, -1, gas, append([]SignerAccount{{ return c.CreateTxFromScript(script, acc, -1, gas, append([]SignerAccount{{
Signer: transaction.Signer{ Signer: transaction.Signer{
Account: from, Account: acc.ScriptHash(),
Scopes: transaction.CalledByEntry, Scopes: transaction.CalledByEntry,
}, },
Account: acc, Account: acc,
@ -148,11 +143,7 @@ func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID []byte) (util.Ui
// versions. // versions.
func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160, func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160,
tokenHash util.Uint160, amount int64, tokenID []byte, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) { tokenHash util.Uint160, amount int64, tokenID []byte, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
from, err := address.StringToUint160(acc.Address) tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, acc.ScriptHash(), to, amount, tokenID, data)
if err != nil {
return util.Uint256{}, fmt.Errorf("bad account address: %w", err)
}
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, from, to, amount, tokenID, data)
if err != nil { if err != nil {
return util.Uint256{}, err return util.Uint256{}, err
} }

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"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/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -96,10 +95,7 @@ func (c *Client) CreateNEP17TransferTx(acc *wallet.Account, to util.Uint160,
// be removed in future versions. // be removed in future versions.
func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64,
recipients []TransferTarget, cosigners []SignerAccount) (*transaction.Transaction, error) { recipients []TransferTarget, cosigners []SignerAccount) (*transaction.Transaction, error) {
from, err := address.StringToUint160(acc.Address) from := acc.ScriptHash()
if err != nil {
return nil, fmt.Errorf("bad account address: %w", err)
}
b := smartcontract.NewBuilder() b := smartcontract.NewBuilder()
for i := range recipients { for i := range recipients {
b.InvokeWithAssert(recipients[i].Token, "transfer", b.InvokeWithAssert(recipients[i].Token, "transfer",

View file

@ -827,10 +827,7 @@ func getSigners(sender *wallet.Account, cosigners []SignerAccount) ([]transactio
signers []transaction.Signer signers []transaction.Signer
accounts []*wallet.Account accounts []*wallet.Account
) )
from, err := address.StringToUint160(sender.Address) from := sender.ScriptHash()
if err != nil {
return nil, nil, fmt.Errorf("bad sender account address: %v", err)
}
s := transaction.Signer{ s := transaction.Signer{
Account: from, Account: from,
Scopes: transaction.None, Scopes: transaction.None,
@ -875,10 +872,7 @@ func (c *Client) SignAndPushP2PNotaryRequest(mainTx *transaction.Transaction, fa
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get native Notary hash: %w", err) return nil, fmt.Errorf("failed to get native Notary hash: %w", err)
} }
from, err := address.StringToUint160(acc.Address) from := acc.ScriptHash()
if err != nil {
return nil, fmt.Errorf("bad account address: %v", err)
}
signers := []transaction.Signer{{Account: notaryHash}, {Account: from}} signers := []transaction.Signer{{Account: notaryHash}, {Account: from}}
if fallbackSysFee < 0 { if fallbackSysFee < 0 {
result, err := c.InvokeScript(fallbackScript, signers) result, err := c.InvokeScript(fallbackScript, signers)

View file

@ -175,7 +175,7 @@ func TestNotary(t *testing.T) {
Scopes: transaction.None, Scopes: transaction.None,
}, },
{ {
Account: requester.PublicKey().GetScriptHash(), Account: requester.ScriptHash(),
Scopes: transaction.None, Scopes: transaction.None,
}, },
} }
@ -721,9 +721,9 @@ func TestNotary(t *testing.T) {
requester1, _ := wallet.NewAccount() requester1, _ := wallet.NewAccount()
requester2, _ := wallet.NewAccount() requester2, _ := wallet.NewAccount()
amount := int64(100_0000_0000) amount := int64(100_0000_0000)
gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester1.PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)}) gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester1.ScriptHash(), int64(bc.BlockHeight() + 50)})
e.CheckGASBalance(t, notaryHash, big.NewInt(amount)) e.CheckGASBalance(t, notaryHash, big.NewInt(amount))
gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester2.PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)}) gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester2.ScriptHash(), int64(bc.BlockHeight() + 50)})
e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount)) e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount))
// create request for 2 standard signatures => main tx should be completed after the second request is added to the pool // create request for 2 standard signatures => main tx should be completed after the second request is added to the pool

View file

@ -1033,11 +1033,6 @@ func TestSignAndPushP2PNotaryRequest(t *testing.T) {
}) })
require.NoError(t, c.Init()) require.NoError(t, c.Init())
t.Run("bad account address", func(t *testing.T) {
_, err := c.SignAndPushP2PNotaryRequest(nil, nil, 0, 0, 0, &wallet.Account{Address: "not-an-addr"}) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
require.NotNil(t, err)
})
t.Run("bad fallback script", func(t *testing.T) { t.Run("bad fallback script", func(t *testing.T) {
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.ASSERT)}, -1, 0, 0, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated _, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.ASSERT)}, -1, 0, 0, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
require.NotNil(t, err) require.NotNil(t, err)

View file

@ -90,7 +90,6 @@ func (s *service) trySendRoot(ir *incompleteRoot, acc *wallet.Account) {
} }
func (s *service) sendValidatedRoot(r *state.MPTRoot, acc *wallet.Account) { func (s *service) sendValidatedRoot(r *state.MPTRoot, acc *wallet.Account) {
priv := acc.PrivateKey()
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
m := NewMessage(RootT, r) m := NewMessage(RootT, r)
m.EncodeBinary(w.BinWriter) m.EncodeBinary(w.BinWriter)
@ -98,13 +97,13 @@ func (s *service) sendValidatedRoot(r *state.MPTRoot, acc *wallet.Account) {
Category: Category, Category: Category,
ValidBlockStart: r.Index, ValidBlockStart: r.Index,
ValidBlockEnd: r.Index + rootValidEndInc, ValidBlockEnd: r.Index + rootValidEndInc,
Sender: priv.GetScriptHash(), Sender: acc.ScriptHash(),
Data: w.Bytes(), Data: w.Bytes(),
Witness: transaction.Witness{ Witness: transaction.Witness{
VerificationScript: acc.GetVerificationScript(), VerificationScript: acc.GetVerificationScript(),
}, },
} }
sig := priv.SignHashable(uint32(s.Network), ep) sig := acc.PrivateKey().SignHashable(uint32(s.Network), ep)
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
emit.Bytes(buf.BinWriter, sig) emit.Bytes(buf.BinWriter, sig)
ep.Witness.InvocationScript = buf.Bytes() ep.Witness.InvocationScript = buf.Bytes()

View file

@ -110,7 +110,7 @@ func (s *service) signAndSend(r *state.MPTRoot) error {
Category: Category, Category: Category,
ValidBlockStart: r.Index, ValidBlockStart: r.Index,
ValidBlockEnd: r.Index + voteValidEndInc, ValidBlockEnd: r.Index + voteValidEndInc,
Sender: acc.PrivateKey().GetScriptHash(), Sender: acc.ScriptHash(),
Data: w.Bytes(), Data: w.Bytes(),
Witness: transaction.Witness{ Witness: transaction.Witness{
VerificationScript: acc.GetVerificationScript(), VerificationScript: acc.GetVerificationScript(),

View file

@ -20,6 +20,9 @@ type Account struct {
// NEO private key. // NEO private key.
privateKey *keys.PrivateKey privateKey *keys.PrivateKey
// Script hash corresponding to the Address.
scriptHash util.Uint160
// NEO public address. // NEO public address.
Address string `json:"address"` Address string `json:"address"`
@ -80,8 +83,6 @@ func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
var ( var (
haveAcc bool haveAcc bool
pos int pos int
accHash util.Uint160
err error
) )
if a.Locked { if a.Locked {
return errors.New("account is locked") return errors.New("account is locked")
@ -89,12 +90,8 @@ func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
if a.Contract == nil { if a.Contract == nil {
return errors.New("account has no contract") return errors.New("account has no contract")
} }
accHash, err = address.StringToUint160(a.Address)
if err != nil {
return err
}
for i := range t.Signers { for i := range t.Signers {
if t.Signers[i].Account.Equals(accHash) { if t.Signers[i].Account.Equals(a.ScriptHash()) {
haveAcc = true haveAcc = true
pos = i pos = i
break break
@ -184,6 +181,16 @@ func (a *Account) PublicKey() *keys.PublicKey {
return a.privateKey.PublicKey() return a.privateKey.PublicKey()
} }
// ScriptHash returns the script hash (account) that the Account.Address is
// derived from. It never returns an error, so if this Account has an invalid
// Address you'll just get a zero script hash.
func (a *Account) ScriptHash() util.Uint160 {
if a.scriptHash.Equals(util.Uint160{}) {
a.scriptHash, _ = address.StringToUint160(a.Address)
}
return a.scriptHash
}
// Close cleans up the private key used by Account and disassociates it from // Close cleans up the private key used by Account and disassociates it from
// Account. The Account can no longer sign anything after this call, but Decrypt // Account. The Account can no longer sign anything after this call, but Decrypt
// can make it usable again. // can make it usable again.
@ -243,7 +250,8 @@ func (a *Account) ConvertMultisig(m int, pubs []*keys.PublicKey) error {
return err return err
} }
a.Address = address.Uint160ToString(hash.Hash160(script)) a.scriptHash = hash.Hash160(script)
a.Address = address.Uint160ToString(a.scriptHash)
a.Contract = &Contract{ a.Contract = &Contract{
Script: script, Script: script,
Parameters: getContractParams(m), Parameters: getContractParams(m),
@ -255,11 +263,11 @@ func (a *Account) ConvertMultisig(m int, pubs []*keys.PublicKey) error {
// NewAccountFromPrivateKey creates a wallet from the given PrivateKey. // NewAccountFromPrivateKey creates a wallet from the given PrivateKey.
func NewAccountFromPrivateKey(p *keys.PrivateKey) *Account { func NewAccountFromPrivateKey(p *keys.PrivateKey) *Account {
pubKey := p.PublicKey() pubKey := p.PublicKey()
pubAddr := p.Address()
a := &Account{ a := &Account{
privateKey: p, privateKey: p,
Address: pubAddr, scriptHash: p.GetScriptHash(),
Address: p.Address(),
Contract: &Contract{ Contract: &Contract{
Script: pubKey.GetVerificationScript(), Script: pubKey.GetVerificationScript(),
Parameters: getContractParams(1), Parameters: getContractParams(1),

View file

@ -9,6 +9,7 @@ import (
"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/hash" "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -18,6 +19,7 @@ func TestNewAccount(t *testing.T) {
acc, err := NewAccount() acc, err := NewAccount()
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, acc) require.NotNil(t, acc)
require.Equal(t, acc.Address, address.Uint160ToString(acc.ScriptHash()))
} }
func TestDecryptAccount(t *testing.T) { func TestDecryptAccount(t *testing.T) {

View file

@ -102,6 +102,7 @@ func TestSave(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(w2.Accounts)) require.Equal(t, 2, len(w2.Accounts))
require.NoError(t, w2.Accounts[1].Decrypt("pass", w2.Scrypt)) require.NoError(t, w2.Accounts[1].Decrypt("pass", w2.Scrypt))
_ = w2.Accounts[1].ScriptHash() // openedWallet has it for acc 1.
require.Equal(t, openedWallet.Accounts, w2.Accounts) require.Equal(t, openedWallet.Accounts, w2.Accounts)
}) })
} }