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.
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)
sender, err := address.StringToUint160(senderAcc.Address)
if err != nil {
return nil, err
}
sender := senderAcc.ScriptHash()
signersAccounts = append(signersAccounts, actor.SignerAccount{
Signer: transaction.Signer{
Account: sender,

View file

@ -162,8 +162,7 @@ func TestNEP17Transfer(t *testing.T) {
e.checkNextLine(t, `^Total fee:\s*(\d|\.)+`)
e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
sh := w.Accounts[0].ScriptHash()
b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(1), b)
@ -172,8 +171,6 @@ func TestNEP17Transfer(t *testing.T) {
e.Run(t, append(args, "--force")...)
e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(2), b)
})
@ -198,8 +195,6 @@ func TestNEP17Transfer(t *testing.T) {
e.Run(t, args...)
e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh)
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/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/wallet"
)
@ -21,11 +20,7 @@ func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Acc
priv := acc.PrivateKey()
pub := priv.PublicKey()
sign := priv.SignHashable(uint32(net), tx)
h, err := address.StringToUint160(acc.Address)
if err != nil {
return fmt.Errorf("invalid address: %s", acc.Address)
}
if err := scCtx.AddSignature(h, acc.Contract, pub, sign); err != nil {
if err := scCtx.AddSignature(acc.ScriptHash(), acc.Contract, pub, sign); err != nil {
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 {
addrHash, err := address.StringToUint160(acc.Address)
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid account address: %w", err), 1)
}
if k != 0 {
fmt.Fprintln(ctx.App.Writer)
}
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 {
return cli.NewExitError(err, 1)
}

View file

@ -166,7 +166,7 @@ func TestNEO_Vote(t *testing.T) {
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...)
for _, tx := range txes {
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/nspcc-dev/neo-go/pkg/config/netmode"
"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/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -276,8 +275,5 @@ func TestSender(t *testing.T) {
client, acc := testRPCAndAccount(t)
a, err := NewSimple(client, acc)
require.NoError(t, err)
addr, err := address.StringToUint160(acc.Address)
require.NoError(t, err)
require.Equal(t, addr, a.Sender())
require.Equal(t, acc.ScriptHash(), a.Sender())
}

View file

@ -6,7 +6,6 @@ import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/config"
"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/rpcclient/unwrap"
"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 {
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{{
Signer: transaction.Signer{
Account: from,
Account: acc.ScriptHash(),
Scopes: transaction.CalledByEntry,
},
Account: acc,
@ -148,11 +143,7 @@ func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID []byte) (util.Ui
// versions.
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) {
from, err := address.StringToUint160(acc.Address)
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)
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, acc.ScriptHash(), to, amount, tokenID, data)
if err != nil {
return util.Uint256{}, err
}

View file

@ -4,7 +4,6 @@ import (
"fmt"
"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/manifest"
"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.
func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64,
recipients []TransferTarget, cosigners []SignerAccount) (*transaction.Transaction, error) {
from, err := address.StringToUint160(acc.Address)
if err != nil {
return nil, fmt.Errorf("bad account address: %w", err)
}
from := acc.ScriptHash()
b := smartcontract.NewBuilder()
for i := range recipients {
b.InvokeWithAssert(recipients[i].Token, "transfer",

View file

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

View file

@ -175,7 +175,7 @@ func TestNotary(t *testing.T) {
Scopes: transaction.None,
},
{
Account: requester.PublicKey().GetScriptHash(),
Account: requester.ScriptHash(),
Scopes: transaction.None,
},
}
@ -721,9 +721,9 @@ func TestNotary(t *testing.T) {
requester1, _ := wallet.NewAccount()
requester2, _ := wallet.NewAccount()
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))
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))
// 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())
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) {
_, err := c.SignAndPushP2PNotaryRequest(nil, []byte{byte(opcode.ASSERT)}, -1, 0, 0, acc) //nolint:staticcheck // SA1019: c.SignAndPushP2PNotaryRequest is deprecated
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) {
priv := acc.PrivateKey()
w := io.NewBufBinWriter()
m := NewMessage(RootT, r)
m.EncodeBinary(w.BinWriter)
@ -98,13 +97,13 @@ func (s *service) sendValidatedRoot(r *state.MPTRoot, acc *wallet.Account) {
Category: Category,
ValidBlockStart: r.Index,
ValidBlockEnd: r.Index + rootValidEndInc,
Sender: priv.GetScriptHash(),
Sender: acc.ScriptHash(),
Data: w.Bytes(),
Witness: transaction.Witness{
VerificationScript: acc.GetVerificationScript(),
},
}
sig := priv.SignHashable(uint32(s.Network), ep)
sig := acc.PrivateKey().SignHashable(uint32(s.Network), ep)
buf := io.NewBufBinWriter()
emit.Bytes(buf.BinWriter, sig)
ep.Witness.InvocationScript = buf.Bytes()

View file

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

View file

@ -20,6 +20,9 @@ type Account struct {
// NEO private key.
privateKey *keys.PrivateKey
// Script hash corresponding to the Address.
scriptHash util.Uint160
// NEO public address.
Address string `json:"address"`
@ -80,8 +83,6 @@ func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
var (
haveAcc bool
pos int
accHash util.Uint160
err error
)
if a.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 {
return errors.New("account has no contract")
}
accHash, err = address.StringToUint160(a.Address)
if err != nil {
return err
}
for i := range t.Signers {
if t.Signers[i].Account.Equals(accHash) {
if t.Signers[i].Account.Equals(a.ScriptHash()) {
haveAcc = true
pos = i
break
@ -184,6 +181,16 @@ func (a *Account) PublicKey() *keys.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
// Account. The Account can no longer sign anything after this call, but Decrypt
// can make it usable again.
@ -243,7 +250,8 @@ func (a *Account) ConvertMultisig(m int, pubs []*keys.PublicKey) error {
return err
}
a.Address = address.Uint160ToString(hash.Hash160(script))
a.scriptHash = hash.Hash160(script)
a.Address = address.Uint160ToString(a.scriptHash)
a.Contract = &Contract{
Script: script,
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.
func NewAccountFromPrivateKey(p *keys.PrivateKey) *Account {
pubKey := p.PublicKey()
pubAddr := p.Address()
a := &Account{
privateKey: p,
Address: pubAddr,
scriptHash: p.GetScriptHash(),
Address: p.Address(),
Contract: &Contract{
Script: pubKey.GetVerificationScript(),
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/crypto/hash"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -18,6 +19,7 @@ func TestNewAccount(t *testing.T) {
acc, err := NewAccount()
require.NoError(t, err)
require.NotNil(t, acc)
require.Equal(t, acc.Address, address.Uint160ToString(acc.ScriptHash()))
}
func TestDecryptAccount(t *testing.T) {

View file

@ -102,6 +102,7 @@ func TestSave(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 2, len(w2.Accounts))
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)
})
}