Merge pull request #2672 from nspcc-dev/private-key-cleanup

Private key cleanup
This commit is contained in:
Roman Khimov 2022-09-02 16:20:39 +03:00 committed by GitHub
commit 3da8b98fc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 337 additions and 234 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

@ -802,7 +802,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
require.NoError(t, err)
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
err = acc.ConvertMultisig(2, keys.PublicKeys{acc.PrivateKey().PublicKey(), pk.PublicKey()})
err = acc.ConvertMultisig(2, keys.PublicKeys{acc.PublicKey(), pk.PublicKey()})
require.NoError(t, err)
t.Run("cosigner is multisig account", func(t *testing.T) {

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"
)
@ -18,14 +17,8 @@ import (
func InitAndSave(net netmode.Magic, tx *transaction.Transaction, acc *wallet.Account, filename string) error {
scCtx := context.NewParameterContext("Neo.Network.P2P.Payloads.Transaction", net, tx)
if acc != nil && acc.CanSign() {
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 {
sign := acc.SignHashable(net, tx)
if err := scCtx.AddSignature(acc.ScriptHash(), acc.Contract, acc.PublicKey(), sign); err != nil {
return fmt.Errorf("can't add signature: %w", err)
}
}

View file

@ -37,15 +37,16 @@ func manifestAddGroup(ctx *cli.Context) error {
h := state.CreateContractHash(sender, nf.Checksum, m.Name)
gAcc, _, err := getAccFromContext(ctx)
gAcc, w, err := getAccFromContext(ctx)
if err != nil {
return cli.NewExitError(fmt.Errorf("can't get account to sign group with: %w", err), 1)
}
defer w.Close()
var found bool
sig := gAcc.PrivateKey().Sign(h.BytesBE())
pub := gAcc.PrivateKey().PublicKey()
pub := gAcc.PublicKey()
for i := range m.Groups {
if m.Groups[i].PublicKey.Equal(pub) {
m.Groups[i].Signature = sig

View file

@ -650,6 +650,7 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer w.Close()
}
_, err = invokeWithArgs(ctx, acc, w, script, operation, params, cosigners)
@ -892,7 +893,7 @@ func getUnlockedAccount(wall *wallet.Wallet, addr util.Uint160, pass *string) (*
return nil, fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr))
}
if acc.PrivateKey() != nil {
if acc.CanSign() {
return acc, nil
}
@ -949,6 +950,7 @@ func contractDeploy(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1)
}
defer w.Close()
cosigners, sgnErr := cmdargs.GetSignersFromContext(ctx, signOffset)
if sgnErr != nil {

View file

@ -25,6 +25,7 @@ func signStoredTransaction(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
pc, err := paramcontext.Read(ctx.String("in"))
if err != nil {
@ -58,9 +59,8 @@ func signStoredTransaction(ctx *cli.Context) error {
}
if acc.CanSign() {
priv := acc.PrivateKey()
sign := priv.SignHashable(uint32(pc.Network), pc.Verifiable)
if err := pc.AddSignature(ch, acc.Contract, priv.PublicKey(), sign); err != nil {
sign := acc.SignHashable(pc.Network, pc.Verifiable)
if err := pc.AddSignature(ch, acc.Contract, acc.PublicKey(), sign); err != nil {
return cli.NewExitError(fmt.Errorf("can't add signature: %w", err), 1)
}
} else if rpcNode == "" {

View file

@ -222,6 +222,7 @@ func getNEPBalance(ctx *cli.Context, standard string, accHandler func(*cli.Conte
if err != nil {
return cli.NewExitError(fmt.Errorf("bad wallet: %w", err), 1)
}
defer wall.Close()
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
@ -286,17 +287,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)
}
@ -392,6 +388,7 @@ func importNEPToken(ctx *cli.Context, standard string) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
tokenHashFlag := ctx.Generic("token").(*flags.Address)
if !tokenHashFlag.IsSet {
@ -460,6 +457,7 @@ func printNEPInfo(ctx *cli.Context, standard string) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
if name := ctx.String("token"); name != "" {
token, err := getMatchingToken(ctx, wall, name, standard)
@ -495,6 +493,7 @@ func removeNEPToken(ctx *cli.Context, standard string) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
token, err := getMatchingToken(ctx, wall, ctx.String("token"), standard)
if err != nil {
@ -518,6 +517,7 @@ func multiTransferNEP17(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
fromFlag := ctx.Generic("from").(*flags.Address)
from, err := getDefaultAddress(fromFlag, wall)
@ -613,6 +613,7 @@ func transferNEP(ctx *cli.Context, standard string) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
fromFlag := ctx.Generic("from").(*flags.Address)
from, err := getDefaultAddress(fromFlag, wall)

View file

@ -76,13 +76,13 @@ func newValidatorCommands() []cli.Command {
func handleRegister(ctx *cli.Context) error {
return handleNeoAction(ctx, func(contract *neo.Contract, _ util.Uint160, acc *wallet.Account) (*transaction.Transaction, error) {
return contract.RegisterCandidateUnsigned(acc.PrivateKey().PublicKey())
return contract.RegisterCandidateUnsigned(acc.PublicKey())
})
}
func handleUnregister(ctx *cli.Context) error {
return handleNeoAction(ctx, func(contract *neo.Contract, _ util.Uint160, acc *wallet.Account) (*transaction.Transaction, error) {
return contract.UnregisterCandidateUnsigned(acc.PrivateKey().PublicKey())
return contract.UnregisterCandidateUnsigned(acc.PublicKey())
})
}
@ -94,6 +94,7 @@ func handleNeoAction(ctx *cli.Context, mkTx func(*neo.Contract, util.Uint160, *w
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
addrFlag := ctx.Generic("address").(*flags.Address)
if !addrFlag.IsSet {

View file

@ -173,6 +173,11 @@ func NewCommands() []cli.Command {
Name: "dump",
Usage: "check and dump an existing NEO wallet",
UsageText: "neo-go wallet dump -w wallet [--wallet-config path] [-d]",
Description: `Prints the given wallet (via -w option or via wallet configuration file) in JSON
format to the standard output. If -d is given, private keys are unencrypted and
displayed in clear text on the console! Be very careful with this option and
don't use it unless you know what you're doing.
`,
Action: dumpWallet,
Flags: []cli.Flag{
walletPathFlag,
@ -198,6 +203,12 @@ func NewCommands() []cli.Command {
Name: "export",
Usage: "export keys for address",
UsageText: "export -w wallet [--wallet-config path] [--decrypt] [<address>]",
Description: `Prints the key for the given account to the standard output. It uses NEP-2
encrypted format by default (the way NEP-6 wallets store it) or WIF format if
-d option is given. In the latter case the key can be displayed in clear text
on the console, so be extremely careful with this option and don't use unless
you really need it and know what you're doing.
`,
Action: exportKeys,
Flags: []cli.Flag{
walletPathFlag,
@ -336,6 +347,7 @@ func claimGas(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
addrFlag := ctx.Generic("address").(*flags.Address)
if !addrFlag.IsSet {
@ -377,6 +389,7 @@ func changePassword(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
if len(wall.Accounts) == 0 {
return cli.NewExitError("wallet has no accounts", 1)
}
@ -472,6 +485,7 @@ func addAccount(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
if err := createAccount(wall, pass); err != nil {
return cli.NewExitError(err, 1)
@ -485,6 +499,7 @@ func exportKeys(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
var addr string
@ -546,6 +561,7 @@ func importMultisig(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
m := ctx.Int("min")
if ctx.NArg() < m {
@ -589,6 +605,7 @@ func importDeployed(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
rawHash := ctx.Generic("contract").(*flags.Address)
if !rawHash.IsSet {
@ -645,6 +662,7 @@ func importWallet(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
acc, err := newAccountFromWIF(ctx.App.Writer, ctx.String("wif"), wall.Scrypt)
if err != nil {
@ -677,6 +695,7 @@ func removeAccount(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
addr := ctx.Generic("address").(*flags.Address)
if !addr.IsSet {
@ -723,6 +742,7 @@ func dumpWallet(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
if ctx.Bool("decrypt") {
if pass == nil {
password, err := input.ReadPassword(EnterPasswordPrompt)
@ -751,6 +771,7 @@ func dumpKeys(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
accounts := wall.Accounts
addrFlag := ctx.Generic("address").(*flags.Address)
@ -801,6 +822,7 @@ func stripKeys(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
if !ctx.Bool("force") {
fmt.Fprintln(ctx.App.Writer, "All private keys for all accounts will be removed from the wallet. This action is irreversible.")
if ok := askForConsent(ctx.App.Writer); !ok {
@ -850,6 +872,7 @@ func createWallet(ctx *cli.Context) error {
if err := createAccount(wall, pass); err != nil {
return cli.NewExitError(err, 1)
}
defer wall.Close()
}
fmtPrintWallet(ctx.App.Writer, wall)

4
go.mod
View file

@ -11,7 +11,7 @@ require (
github.com/holiman/uint256 v1.2.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/mr-tron/base58 v1.2.0
github.com/nspcc-dev/dbft v0.0.0-20220629112714-fd49ca59d354
github.com/nspcc-dev/dbft v0.0.0-20220902113116-58a5e763e647
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659
@ -42,7 +42,7 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/nspcc-dev/hrw v1.0.9 // indirect
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1 // indirect
github.com/nspcc-dev/neofs-crypto v0.3.0 // indirect
github.com/nspcc-dev/neofs-crypto v0.4.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect

7
go.sum
View file

@ -251,8 +251,8 @@ github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y=
github.com/nspcc-dev/dbft v0.0.0-20220629112714-fd49ca59d354 h1:/NYQJ9dmeOajNuS0b8x8SMt0b4mEYwUWOpMXuLQ1qwM=
github.com/nspcc-dev/dbft v0.0.0-20220629112714-fd49ca59d354/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y=
github.com/nspcc-dev/dbft v0.0.0-20220902113116-58a5e763e647 h1:handGBjqVzRx7HD6152zsP8ZRxw083zCMbN0IlUaPQk=
github.com/nspcc-dev/dbft v0.0.0-20220902113116-58a5e763e647/go.mod h1:g9xisXmX9NP9MjioaTe862n9SlZTrP+6PVUWLBYOr98=
github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg=
github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
@ -267,8 +267,9 @@ github.com/nspcc-dev/neofs-api-go/v2 v2.11.1 h1:SVqc523pZsSaS9vnPS1mm3VV6b6xY0gv
github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs=
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4=
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659 h1:rpMCoRa7expLc9gMiOP724gz6YSykZzmMALR/CmiwnU=
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40=

View file

@ -144,8 +144,8 @@ func Init(t *testing.T, rootpath string, e *neotest.Executor) {
require.NoError(t, err)
require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt))
designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(noderoles.P2PNotary), []interface{}{ntr.Accounts[0].PrivateKey().PublicKey().Bytes()})
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes()))
int64(noderoles.P2PNotary), []interface{}{ntr.Accounts[0].PublicKey().Bytes()})
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PublicKey().Bytes()))
// Block #10: push verification contract with arguments into the chain.
verifyPath = filepath.Join(testDataPrefix, "verify_args", "verification_with_args_contract.go")

View file

@ -74,7 +74,7 @@ func generateManagementHelperContracts(t *testing.T, saveState bool) {
stdHash := e.NativeHash(t, nativenames.StdLib)
neoHash := e.NativeHash(t, nativenames.Neo)
singleChainValidatorAcc := e.Validator.(neotest.MultiSigner).Single(2).Account() // priv0
require.NoError(t, singleChainValidatorAcc.ConvertMultisig(1, keys.PublicKeys{singleChainValidatorAcc.PrivateKey().PublicKey()}))
require.NoError(t, singleChainValidatorAcc.ConvertMultisig(1, keys.PublicKeys{singleChainValidatorAcc.PublicKey()}))
singleChainValidatorHash := singleChainValidatorAcc.Contract.ScriptHash()
w := io.NewBufBinWriter()

View file

@ -276,6 +276,7 @@ func (s *service) Shutdown() {
s.log.Info("stopping consensus service")
close(s.quit)
<-s.finished
s.wallet.Close()
}
}
@ -377,17 +378,15 @@ func (s *service) getKeyPair(pubs []crypto.PublicKey) (int, crypto.PrivateKey, c
continue
}
key := acc.PrivateKey()
if acc.PrivateKey() == nil {
if !acc.CanSign() {
err := acc.Decrypt(s.Config.Wallet.Password, s.wallet.Scrypt)
if err != nil {
s.log.Fatal("can't unlock account", zap.String("address", address.Uint160ToString(sh)))
break
}
key = acc.PrivateKey()
}
return i, &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()}
return i, &privateKey{PrivateKey: acc.PrivateKey()}, &publicKey{PublicKey: acc.PublicKey()}
}
return -1, nil, nil

View file

@ -115,7 +115,7 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3
func TestService_NextConsensus(t *testing.T) {
newAcc, err := wallet.NewAccount()
require.NoError(t, err)
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(keys.PublicKeys{newAcc.PrivateKey().PublicKey()})
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(keys.PublicKeys{newAcc.PublicKey()})
require.NoError(t, err)
checkNextConsensus := func(t *testing.T, bc *core.Blockchain, height uint32, h util.Uint160) {

View file

@ -13,17 +13,6 @@ type privateKey struct {
*keys.PrivateKey
}
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (p privateKey) MarshalBinary() (data []byte, err error) {
return p.PrivateKey.Bytes(), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
func (p *privateKey) UnmarshalBinary(data []byte) (err error) {
p.PrivateKey, err = keys.NewPrivateKeyFromBytes(data)
return
}
// Sign implements the dbft's crypto.PrivateKey interface.
func (p *privateKey) Sign(data []byte) ([]byte, error) {
return p.PrivateKey.Sign(data), nil

View file

@ -12,19 +12,12 @@ func TestCrypt(t *testing.T) {
require.NoError(t, err)
priv := privateKey{key}
data, err := priv.MarshalBinary()
require.NoError(t, err)
key1, err := keys.NewPrivateKey()
require.NoError(t, err)
priv1 := privateKey{key1}
require.NotEqual(t, priv, priv1)
require.NoError(t, priv1.UnmarshalBinary(data))
require.Equal(t, priv, priv1)
pub := publicKey{key.PublicKey()}
data, err = pub.MarshalBinary()
data, err := pub.MarshalBinary()
require.NoError(t, err)
pub1 := publicKey{key1.PublicKey()}

View file

@ -1060,7 +1060,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
notaryServiceFeePerKey := bc.GetNotaryServiceFeePerKey()
oracleAcc := accs[2]
oraclePubs := keys.PublicKeys{oracleAcc.PrivateKey().PublicKey()}
oraclePubs := keys.PublicKeys{oracleAcc.PublicKey()}
require.NoError(t, oracleAcc.ConvertMultisig(1, oraclePubs))
neoHash := e.NativeHash(t, nativenames.Neo)
@ -1179,7 +1179,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
})
t.Run("CalculateNetworkFee, multisignature script", func(t *testing.T) {
multisigAcc := accs[4]
pKeys := keys.PublicKeys{multisigAcc.PrivateKey().PublicKey()}
pKeys := keys.PublicKeys{multisigAcc.PublicKey()}
require.NoError(t, multisigAcc.ConvertMultisig(1, pKeys))
multisigHash := hash.Hash160(multisigAcc.Contract.Script)
tx := newTestTx(t, multisigHash, testScript)
@ -1594,7 +1594,7 @@ func TestBlockchain_VerifyTx(t *testing.T) {
notary, err := wallet.NewAccount()
require.NoError(t, err)
designateSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(noderoles.P2PNotary), []interface{}{notary.PrivateKey().PublicKey().Bytes()})
int64(noderoles.P2PNotary), []interface{}{notary.PublicKey().Bytes()})
txSetNotary := transaction.New([]byte{byte(opcode.RET)}, 0)
txSetNotary.Signers = []transaction.Signer{
{

View file

@ -65,7 +65,7 @@ func TestNEO_CandidateEvents(t *testing.T) {
singleSigner := c.Signers[0].(neotest.MultiSigner).Single(0)
cc := c.WithSigners(c.Signers[0], singleSigner)
e := c.Executor
pkb := singleSigner.Account().PrivateKey().PublicKey().Bytes()
pkb := singleSigner.Account().PublicKey().Bytes()
// Register 1 -> event
tx := cc.Invoke(t, true, "registerCandidate", pkb)
@ -160,13 +160,13 @@ func TestNEO_Vote(t *testing.T) {
transferTx = neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), referenceAccounts[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil)
txes = append(txes, transferTx)
if i > 0 {
registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, registerTx)
voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, voteTx)
}
}
txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().PrivateKey().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
@ -184,9 +184,9 @@ func TestNEO_Vote(t *testing.T) {
// Register and give some value to the last validator.
txes = txes[:0]
registerTx := neoValidatorsInvoker.WithSigners(candidates[0]).PrepareInvoke(t, "registerCandidate", candidates[0].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
registerTx := neoValidatorsInvoker.WithSigners(candidates[0]).PrepareInvoke(t, "registerCandidate", candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, registerTx)
voteTx := neoValidatorsInvoker.WithSigners(voters[0]).PrepareInvoke(t, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[0].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
voteTx := neoValidatorsInvoker.WithSigners(voters[0]).PrepareInvoke(t, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, voteTx)
neoValidatorsInvoker.AddNewBlock(t, txes...)
for _, tx := range txes {
@ -198,7 +198,7 @@ func TestNEO_Vote(t *testing.T) {
require.NoError(t, err)
sortedCandidates := make(keys.PublicKeys, validatorsCount)
for i := range candidates[:validatorsCount] {
sortedCandidates[i] = candidates[i].(neotest.SingleSigner).Account().PrivateKey().PublicKey()
sortedCandidates[i] = candidates[i].(neotest.SingleSigner).Account().PublicKey()
}
sort.Sort(sortedCandidates)
require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs))
@ -259,8 +259,8 @@ func TestNEO_Vote(t *testing.T) {
}
})
neoCommitteeInvoker.WithSigners(candidates[0]).Invoke(t, true, "unregisterCandidate", candidates[0].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
neoCommitteeInvoker.WithSigners(voters[0]).Invoke(t, false, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[0].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
neoCommitteeInvoker.WithSigners(candidates[0]).Invoke(t, true, "unregisterCandidate", candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes())
neoCommitteeInvoker.WithSigners(voters[0]).Invoke(t, false, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes())
advanceChain(t)
@ -564,9 +564,9 @@ func TestNEO_GetCandidates(t *testing.T) {
for i := 0; i < candidatesCount; i++ {
transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(candidatesCount+1-i)*1000000, nil)
txes = append(txes, transferTx)
registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, registerTx)
voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes())
voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes())
txes = append(txes, voteTx)
}
@ -576,7 +576,7 @@ func TestNEO_GetCandidates(t *testing.T) {
}
expected := make([]stackitem.Item, candidatesCount)
for i := range expected {
pub := candidates[i].(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes()
pub := candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()
v := stackitem.NewBigInteger(big.NewInt(int64(candidatesCount-i+1) * 1000000))
expected[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(pub),
@ -613,7 +613,7 @@ func TestNEO_GetCandidates(t *testing.T) {
checkGetAllCandidates(t, expected)
// Block candidate and check it won't be returned from getCandidates and getAllCandidates.
unlucky := candidates[len(candidates)-1].(neotest.SingleSigner).Account().PrivateKey().PublicKey()
unlucky := candidates[len(candidates)-1].(neotest.SingleSigner).Account().PublicKey()
policyInvoker.Invoke(t, true, "blockAccount", unlucky.GetScriptHash())
for i := range expected {
if bytes.Equal(expected[i].Value().([]stackitem.Item)[0].Value().([]byte), unlucky.Bytes()) {

View file

@ -71,8 +71,8 @@ func TestOracle_Request(t *testing.T) {
// Designate single Oracle node.
oracleNode := e.NewAccount(t)
designationCommitteeInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int(noderoles.Oracle), []interface{}{oracleNode.(neotest.SingleSigner).Account().PrivateKey().PublicKey().Bytes()})
err = oracleNode.(neotest.SingleSigner).Account().ConvertMultisig(1, []*keys.PublicKey{oracleNode.(neotest.SingleSigner).Account().PrivateKey().PublicKey()})
designationCommitteeInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", int(noderoles.Oracle), []interface{}{oracleNode.(neotest.SingleSigner).Account().PublicKey().Bytes()})
err = oracleNode.(neotest.SingleSigner).Account().ConvertMultisig(1, []*keys.PublicKey{oracleNode.(neotest.SingleSigner).Account().PublicKey()})
require.NoError(t, err)
oracleNodeMulti := neotest.NewMultiSigner(oracleNode.(neotest.SingleSigner).Account())
gasCommitteeInvoker.Invoke(t, true, "transfer", gasCommitteeInvoker.CommitteeHash, oracleNodeMulti.ScriptHash(), 100_0000_0000, nil)

View file

@ -7,6 +7,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/base58"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"golang.org/x/crypto/scrypt"
"golang.org/x/text/unicode/norm"
)
@ -52,10 +53,15 @@ func NEP2Encrypt(priv *PrivateKey, passphrase string, params ScryptParams) (s st
if err != nil {
return s, err
}
defer slice.Clean(derivedKey)
derivedKey1 := derivedKey[:32]
derivedKey2 := derivedKey[32:]
xr := xor(priv.Bytes(), derivedKey1)
privBytes := priv.Bytes()
defer slice.Clean(privBytes)
xr := xor(privBytes, derivedKey1)
defer slice.Clean(xr)
encrypted, err := aesEncrypt(xr, derivedKey2)
if err != nil {
@ -93,6 +99,7 @@ func NEP2Decrypt(key, passphrase string, params ScryptParams) (*PrivateKey, erro
if err != nil {
return nil, err
}
defer slice.Clean(derivedKey)
derivedKey1 := derivedKey[:32]
derivedKey2 := derivedKey[32:]
@ -102,8 +109,10 @@ func NEP2Decrypt(key, passphrase string, params ScryptParams) (*PrivateKey, erro
if err != nil {
return nil, err
}
defer slice.Clean(decrypted)
privBytes := xor(decrypted, derivedKey1)
defer slice.Clean(privBytes)
// Rebuild the private key.
privKey, err := NewPrivateKeyFromBytes(privBytes)

View file

@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/btcec"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/rfc6979"
)
@ -48,6 +49,7 @@ func NewPrivateKeyFromHex(str string) (*PrivateKey, error) {
if err != nil {
return nil, err
}
defer slice.Clean(b)
return NewPrivateKeyFromBytes(b)
}
@ -108,7 +110,9 @@ func NewPrivateKeyFromWIF(wif string) (*PrivateKey, error) {
// Good documentation about this process can be found here:
// https://en.bitcoin.it/wiki/Wallet_import_format
func (p *PrivateKey) WIF() string {
w, err := WIFEncode(p.Bytes(), WIFVersion, true)
pb := p.Bytes()
defer slice.Clean(pb)
w, err := WIFEncode(pb, WIFVersion, true)
// The only way WIFEncode() can fail is if we're to give it a key of
// wrong size, but we have a proper key here, aren't we?
if err != nil {
@ -117,6 +121,15 @@ func (p *PrivateKey) WIF() string {
return w
}
// Destroy wipes the contents of the private key from memory. Any operations
// with the key after call to Destroy have undefined behavior.
func (p *PrivateKey) Destroy() {
bits := p.D.Bits()
for i := range bits {
bits[i] = 0
}
}
// Address derives the public NEO address that is coupled with the private key, and
// returns it as a string.
func (p *PrivateKey) Address() string {

View file

@ -2,6 +2,7 @@ package keys
import (
"encoding/hex"
"math/big"
"strings"
"testing"
@ -27,6 +28,9 @@ func TestPrivateKey(t *testing.T) {
assert.Equal(t, testCase.Wif, wif)
pubKey := privKey.PublicKey()
assert.Equal(t, hex.EncodeToString(pubKey.Bytes()), testCase.PublicKey)
oldD := new(big.Int).Set(privKey.D)
privKey.Destroy()
assert.NotEqual(t, oldD, privKey.D)
}
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/nspcc-dev/neo-go/pkg/encoding/base58"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
)
const (
@ -53,40 +54,36 @@ func WIFDecode(wif string, version byte) (*WIF, error) {
if err != nil {
return nil, err
}
defer slice.Clean(b)
if version == 0x00 {
version = WIFVersion
}
w := &WIF{
Version: version,
S: wif,
}
switch len(b) {
case 33: // OK, uncompressed public key.
case 34: // OK, compressed public key.
// Check the compression flag.
if b[33] != 0x01 {
return nil, fmt.Errorf("invalid compression flag %d expecting %d", b[33], 0x01)
}
w.Compressed = true
default:
return nil, fmt.Errorf("invalid WIF length %d, expecting 33 or 34", len(b))
}
if b[0] != version {
return nil, fmt.Errorf("invalid WIF version got %d, expected %d", b[0], version)
}
// Derive the PrivateKey.
privKey, err := NewPrivateKeyFromBytes(b[1:33])
w.PrivateKey, err = NewPrivateKeyFromBytes(b[1:33])
if err != nil {
return nil, err
}
w := &WIF{
Version: version,
PrivateKey: privKey,
S: wif,
}
// This is an uncompressed WIF.
if len(b) == 33 {
w.Compressed = false
return w, nil
}
if len(b) != 34 {
return nil, fmt.Errorf("invalid WIF length: %d expecting 34", len(b))
}
// Check the compression flag.
if b[33] != 0x01 {
return nil, fmt.Errorf("invalid compression flag %d expecting %d", b[34], 0x01)
}
w.Compressed = true
return w, nil
}

View file

@ -4,6 +4,7 @@ import (
"encoding/hex"
"testing"
"github.com/nspcc-dev/neo-go/pkg/encoding/base58"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -65,3 +66,35 @@ func TestWIFEncodeDecode(t *testing.T) {
_, err := WIFEncode(wifInv, 0, true)
require.Error(t, err)
}
func TestBadWIFDecode(t *testing.T) {
_, err := WIFDecode("garbage", 0)
require.Error(t, err)
s := base58.CheckEncode([]byte{})
_, err = WIFDecode(s, 0)
require.Error(t, err)
uncompr := make([]byte, 33)
compr := make([]byte, 34)
s = base58.CheckEncode(compr)
_, err = WIFDecode(s, 0)
require.Error(t, err)
s = base58.CheckEncode(uncompr)
_, err = WIFDecode(s, 0)
require.Error(t, err)
compr[33] = 1
compr[0] = WIFVersion
uncompr[0] = WIFVersion
s = base58.CheckEncode(compr)
_, err = WIFDecode(s, 0)
require.NoError(t, err)
s = base58.CheckEncode(uncompr)
_, err = WIFDecode(s, 0)
require.NoError(t, err)
}

View file

@ -58,7 +58,7 @@ var (
func init() {
committeeAcc, _ = wallet.NewAccountFromWIF(singleValidatorWIF)
pubs := keys.PublicKeys{committeeAcc.PrivateKey().PublicKey()}
pubs := keys.PublicKeys{committeeAcc.PublicKey()}
err := committeeAcc.ConvertMultisig(1, pubs)
if err != nil {
panic(err)
@ -70,7 +70,7 @@ func init() {
pubs = make(keys.PublicKeys, len(accs))
for i := range committeeWIFs {
accs[i], _ = wallet.NewAccountFromWIF(committeeWIFs[i])
pubs[i] = accs[i].PrivateKey().PublicKey()
pubs[i] = accs[i].PublicKey()
}
// Config entry must contain validators first in a specific order.
@ -86,8 +86,8 @@ func init() {
sort.Sort(pubs[:4])
sort.Slice(accs[:4], func(i, j int) bool {
p1 := accs[i].PrivateKey().PublicKey()
p2 := accs[j].PrivateKey().PublicKey()
p1 := accs[i].PublicKey()
p2 := accs[j].PublicKey()
return p1.Cmp(p2) == -1
})
for i := range multiValidatorAcc {
@ -102,8 +102,8 @@ func init() {
sort.Sort(pubs)
sort.Slice(accs, func(i, j int) bool {
p1 := accs[i].PrivateKey().PublicKey()
p2 := accs[j].PrivateKey().PublicKey()
p1 := accs[i].PublicKey()
p2 := accs[j].PublicKey()
return p1.Cmp(p2) == -1
})
for i := range multiCommitteeAcc {
@ -141,7 +141,7 @@ func NewSingleWithCustomConfigAndStore(t testing.TB, f func(cfg *config.Protocol
Magic: netmode.UnitTestNet,
MaxTraceableBlocks: MaxTraceableBlocks,
SecondsPerBlock: SecondsPerBlock,
StandbyCommittee: []string{hex.EncodeToString(committeeAcc.PrivateKey().PublicKey().Bytes())},
StandbyCommittee: []string{hex.EncodeToString(committeeAcc.PublicKey().Bytes())},
ValidatorsCount: 1,
VerifyBlocks: true,
VerifyTransactions: true,

View file

@ -74,7 +74,7 @@ func (s *signer) ScriptHash() util.Uint160 {
// SignHashable implements Signer interface.
func (s *signer) SignHashable(magic uint32, item hash.Hashable) []byte {
return append([]byte{byte(opcode.PUSHDATA1), 64},
(*wallet.Account)(s).PrivateKey().SignHashable(magic, item)...)
(*wallet.Account)(s).SignHashable(netmode.Magic(magic), item)...)
}
// SignTx implements Signer interface.
@ -103,8 +103,8 @@ func NewMultiSigner(accs ...*wallet.Account) MultiSigner {
"but only %d accounts were provided", m, len(accs)))
}
sort.Slice(accs, func(i, j int) bool {
p1 := accs[i].PrivateKey().PublicKey()
p2 := accs[j].PrivateKey().PublicKey()
p1 := accs[i].PublicKey()
p2 := accs[j].PublicKey()
return p1.Cmp(p2) == -1
})
for _, acc := range accs {
@ -130,7 +130,7 @@ func (m multiSigner) Script() []byte {
func (m multiSigner) SignHashable(magic uint32, item hash.Hashable) []byte {
var script []byte
for i := 0; i < m.m; i++ {
sign := m.accounts[i].PrivateKey().SignHashable(magic, item)
sign := m.accounts[i].SignHashable(netmode.Magic(magic), item)
script = append(script, byte(opcode.PUSHDATA1), 64)
script = append(script, sign...)
}

View file

@ -28,7 +28,7 @@ func TestMultiSigner(t *testing.T) {
require.NoError(t, err)
accs[i] = a
pubs[i] = a.PrivateKey().PublicKey()
pubs[i] = a.PublicKey()
}
sort.Sort(pubs)
@ -40,8 +40,8 @@ func TestMultiSigner(t *testing.T) {
s := NewMultiSigner(accs...)
for i := range pubs {
for j := range accs {
if pub := accs[j].PrivateKey().PublicKey(); pub.Equal(pubs[i]) {
require.Equal(t, pub, s.Single(i).Account().PrivateKey().PublicKey())
if pub := accs[j].PublicKey(); pub.Equal(pubs[i]) {
require.Equal(t, pub, s.Single(i).Account().PublicKey())
}
}
}

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

@ -300,7 +300,7 @@ func (a *Actor) SendRequestExactly(mainTx *transaction.Transaction, fbTx *transa
FallbackTransaction: fbTx,
}
req.Witness = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, a.sender.PrivateKey().SignHashable(uint32(a.GetNetwork()), req)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, a.sender.SignHashable(a.GetNetwork(), req)...),
VerificationScript: a.sender.GetVerificationScript(),
}
actualHash, err := a.rpc.SubmitP2PNotaryRequest(req)

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)
@ -944,7 +938,7 @@ func (c *Client) SignAndPushP2PNotaryRequest(mainTx *transaction.Transaction, fa
FallbackTransaction: fallbackTx,
}
req.Witness = transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().SignHashable(uint32(m), req)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.SignHashable(m, req)...),
VerificationScript: acc.GetVerificationScript(),
}
actualHash, err := c.SubmitP2PNotaryRequest(req)

View file

@ -154,7 +154,7 @@ func TestNotary(t *testing.T) {
mp1.StopSubscriptions()
})
notaryNodes := []interface{}{acc1.PrivateKey().PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()}
notaryNodes := []interface{}{acc1.PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()}
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(noderoles.P2PNotary), notaryNodes)
@ -175,7 +175,7 @@ func TestNotary(t *testing.T) {
Scopes: transaction.None,
},
{
Account: requester.PrivateKey().PublicKey().GetScriptHash(),
Account: requester.ScriptHash(),
Scopes: transaction.None,
},
}
@ -222,12 +222,12 @@ func TestNotary(t *testing.T) {
var script []byte
switch requesters[i].typ {
case notary.Signature:
script = requesters[i].accounts[0].PrivateKey().PublicKey().GetVerificationScript()
script = requesters[i].accounts[0].PublicKey().GetVerificationScript()
nKeys++
case notary.MultiSignature:
pubs := make(keys.PublicKeys, len(requesters[i].accounts))
for j, r := range requesters[i].accounts {
pubs[j] = r.PrivateKey().PublicKey()
pubs[j] = r.PublicKey()
}
script, err = smartcontract.CreateMultiSigRedeemScript(requesters[i].m, pubs)
require.NoError(t, err)
@ -456,7 +456,7 @@ func TestNotary(t *testing.T) {
r, _ := checkCompleteStandardRequest(t, 1, false)
checkFallbackTxs(t, r, false)
// set account back for the next tests
ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PrivateKey().PublicKey()})
ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PublicKey()})
// OnNewRequest: signature request
for _, i := range []int{1, 2, 3, 10} {
@ -496,7 +496,7 @@ func TestNotary(t *testing.T) {
checkMainTx(t, requesters, r, 1, false)
checkFallbackTxs(t, r, false)
// set account back for the next tests
ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PrivateKey().PublicKey()})
ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PublicKey()})
// PostPersist: complete main transaction, signature request
setFinalizeWithError(true)
@ -634,7 +634,7 @@ func TestNotary(t *testing.T) {
checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false)
// set account back for the next tests
ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PrivateKey().PublicKey()})
ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PublicKey()})
// OnRequestRemoval: signature request, remove one fallback
// check OnNewRequest with finalization error
@ -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.PrivateKey().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.PrivateKey().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

@ -15,7 +15,7 @@ func (n *Notary) UpdateNotaryNodes(notaryNodes keys.PublicKeys) {
if n.currAccount != nil {
for _, node := range notaryNodes {
if node.Equal(n.currAccount.PrivateKey().PublicKey()) {
if node.Equal(n.currAccount.PublicKey()) {
return
}
}
@ -25,7 +25,7 @@ func (n *Notary) UpdateNotaryNodes(notaryNodes keys.PublicKeys) {
for _, node := range notaryNodes {
acc = n.wallet.GetAccount(node.GetScriptHash())
if acc != nil {
if acc.PrivateKey() != nil {
if acc.CanSign() {
break
}
err := acc.Decrypt(n.Config.MainCfg.UnlockWallet.Password, n.wallet.Scrypt)

View file

@ -44,11 +44,11 @@ func TestUpdateNotaryNodes(t *testing.T) {
// currAcc is nil before UpdateNotaryNodes call
require.Nil(t, ntr.currAccount)
// set account for the first time
ntr.UpdateNotaryNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()})
ntr.UpdateNotaryNodes(keys.PublicKeys{acc.PublicKey()})
require.Equal(t, acc, ntr.currAccount)
t.Run("account is already set", func(t *testing.T) {
ntr.UpdateNotaryNodes(keys.PublicKeys{acc.PrivateKey().PublicKey(), randomKey.PublicKey()})
ntr.UpdateNotaryNodes(keys.PublicKeys{acc.PublicKey(), randomKey.PublicKey()})
require.Equal(t, acc, ntr.currAccount)
})
@ -57,14 +57,14 @@ func TestUpdateNotaryNodes(t *testing.T) {
w, err := wallet.NewWalletFromFile("./testdata/notary1.json")
require.NoError(t, err)
require.NoError(t, w.Accounts[1].Decrypt("one", w.Scrypt))
ntr.UpdateNotaryNodes(keys.PublicKeys{w.Accounts[1].PrivateKey().PublicKey()})
ntr.UpdateNotaryNodes(keys.PublicKeys{w.Accounts[1].PublicKey()})
require.Equal(t, w.Accounts[1], ntr.currAccount)
})
t.Run("bad config password", func(t *testing.T) {
w, err := wallet.NewWalletFromFile("./testdata/notary1.json")
require.NoError(t, err)
require.NoError(t, w.Accounts[2].Decrypt("four", w.Scrypt))
ntr.UpdateNotaryNodes(keys.PublicKeys{w.Accounts[2].PrivateKey().PublicKey()})
ntr.UpdateNotaryNodes(keys.PublicKeys{w.Accounts[2].PublicKey()})
require.Nil(t, ntr.currAccount)
})
})

View file

@ -222,6 +222,7 @@ func (n *Notary) Shutdown() {
n.Config.Log.Info("stopping notary service")
close(n.stopCh)
<-n.done
n.wallet.Close()
}
// OnNewRequest is a callback method which is called after a new notary request is added to the notary request pool.
@ -391,7 +392,7 @@ func (n *Notary) PostPersist() {
// finalize adds missing Notary witnesses to the transaction (main or fallback) and pushes it to the network.
func (n *Notary) finalize(acc *wallet.Account, tx *transaction.Transaction, h util.Uint256) error {
notaryWitness := transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().SignHashable(uint32(n.Network), tx)...),
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc.SignHashable(n.Network, tx)...),
VerificationScript: []byte{},
}
for i, signer := range tx.Signers {

View file

@ -30,7 +30,7 @@ func (o *Oracle) UpdateOracleNodes(oracleNodes keys.PublicKeys) {
for i := range oracleNodes {
acc = o.wallet.GetAccount(oracleNodes[i].GetScriptHash())
if acc != nil {
if acc.PrivateKey() != nil {
if acc.CanSign() {
break
}
err := acc.Decrypt(o.MainCfg.UnlockWallet.Password, o.wallet.Scrypt)

View file

@ -191,6 +191,7 @@ func (o *Oracle) Shutdown() {
close(o.close)
o.ResponseHandler.Shutdown()
<-o.done
o.wallet.Close()
}
// Start runs the oracle service in a separate goroutine.

View file

@ -122,7 +122,7 @@ func TestCreateResponseTx(t *testing.T) {
}
cInvoker.Invoke(t, stackitem.Null{}, "requestURL", req.URL, *req.Filter, req.CallbackMethod, req.UserData, int64(req.GasForResponse))
bc.SetOracle(orc)
orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()})
orc.UpdateOracleNodes(keys.PublicKeys{acc.PublicKey()})
tx, err = orc.CreateResponseTx(int64(req.GasForResponse), 1, resp)
require.NoError(t, err)
assert.Equal(t, 166, tx.Size())
@ -150,7 +150,7 @@ func TestOracle(t *testing.T) {
acc1, orc1, m1, ch1 := getTestOracle(t, bc, "./testdata/oracle1.json", "one")
acc2, orc2, m2, ch2 := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
oracleNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
oracleNodes := keys.PublicKeys{acc1.PublicKey(), acc2.PrivateKey().PublicKey()}
// Must be set in native contract for tx verification.
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.Oracle), []interface{}{oracleNodes[0].Bytes(), oracleNodes[1].Bytes()})
@ -249,10 +249,10 @@ func TestOracle(t *testing.T) {
require.Empty(t, ch2)
t.Run("InvalidSignature", func(t *testing.T) {
orc1.AddResponse(acc2.PrivateKey().PublicKey(), m2[0].resp.ID, []byte{1, 2, 3})
orc1.AddResponse(acc2.PublicKey(), m2[0].resp.ID, []byte{1, 2, 3})
require.Empty(t, ch1)
})
orc1.AddResponse(acc2.PrivateKey().PublicKey(), m2[0].resp.ID, m2[0].txSig)
orc1.AddResponse(acc2.PublicKey(), m2[0].resp.ID, m2[0].txSig)
checkEmitTx(t, ch1)
t.Run("FirstOtherThenMe", func(t *testing.T) {
@ -264,7 +264,7 @@ func TestOracle(t *testing.T) {
Result: []byte{1, 2, 3, 4},
}
req := checkResp(t, reqID, resp)
orc2.AddResponse(acc1.PrivateKey().PublicKey(), reqID, m1[reqID].txSig)
orc2.AddResponse(acc1.PublicKey(), reqID, m1[reqID].txSig)
require.Empty(t, ch2)
reqs := map[uint64]*state.OracleRequest{reqID: req}
@ -357,7 +357,7 @@ func TestOracleFull(t *testing.T) {
})
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.Oracle), []interface{}{acc.PrivateKey().PublicKey().Bytes()})
int64(roles.Oracle), []interface{}{acc.PublicKey().Bytes()})
cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
e.DeployContract(t, &neotest.Contract{
@ -391,7 +391,7 @@ func TestNotYetRunningOracle(t *testing.T) {
t.Cleanup(bc.Close)
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.Oracle), []interface{}{acc.PrivateKey().PublicKey().Bytes()})
int64(roles.Oracle), []interface{}{acc.PublicKey().Bytes()})
var req state.OracleRequest
var reqs = make(map[uint64]*state.OracleRequest)

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.SignHashable(s.Network, ep)
buf := io.NewBufBinWriter()
emit.Bytes(buf.BinWriter, sig)
ep.Witness.InvocationScript = buf.Bytes()

View file

@ -65,13 +65,13 @@ func newMajorityMultisigWithGAS(t *testing.T, n int) (util.Uint160, keys.PublicK
accs[i] = acc
}
sort.Slice(accs, func(i, j int) bool {
pi := accs[i].PrivateKey().PublicKey()
pj := accs[j].PrivateKey().PublicKey()
pi := accs[i].PublicKey()
pj := accs[j].PublicKey()
return pi.Cmp(pj) == -1
})
pubs := make(keys.PublicKeys, n)
for i := range pubs {
pubs[i] = accs[i].PrivateKey().PublicKey()
pubs[i] = accs[i].PublicKey()
}
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(pubs)
require.NoError(t, err)
@ -126,7 +126,7 @@ func TestStateRoot(t *testing.T) {
t.Run("invalid signer", func(t *testing.T) {
accInv, err := wallet.NewAccount()
require.NoError(t, err)
pubs := keys.PublicKeys{accInv.PrivateKey().PublicKey()}
pubs := keys.PublicKeys{accInv.PublicKey()}
require.NoError(t, accInv.ConvertMultisig(1, pubs))
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), accInv.Contract.ScriptHash(), 1_0000_0000, nil)
r, err := bc.GetStateModule().GetStateRoot(1)

View file

@ -74,6 +74,7 @@ func (s *service) Shutdown() {
s.log.Info("stopping state validation service")
close(s.stopCh)
<-s.done
s.wallet.Close()
}
func (s *service) signAndSend(r *state.MPTRoot) error {
@ -86,12 +87,12 @@ func (s *service) signAndSend(r *state.MPTRoot) error {
return nil
}
sig := acc.PrivateKey().SignHashable(uint32(s.Network), r)
sig := acc.SignHashable(s.Network, r)
incRoot := s.getIncompleteRoot(r.Index, myIndex)
incRoot.Lock()
defer incRoot.Unlock()
incRoot.root = r
incRoot.addSignature(acc.PrivateKey().PublicKey(), sig)
incRoot.addSignature(acc.PublicKey(), sig)
incRoot.reverify(s.Network)
s.trySendRoot(incRoot, acc)
@ -110,13 +111,13 @@ 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(),
},
}
sig = acc.PrivateKey().SignHashable(uint32(s.Network), e)
sig = acc.SignHashable(s.Network, e)
buf := io.NewBufBinWriter()
emit.Bytes(buf.BinWriter, sig)
e.Witness.InvocationScript = buf.Bytes()

View file

@ -33,6 +33,7 @@ func ExampleBuilder() {
b.Reset() // Copy the old script above if you need it!
w, _ := wallet.NewWalletFromFile("somewhere")
defer w.Close()
// Assuming there is one Account inside
a, _ := actor.NewSimple(c, w.Accounts[0])
from := w.Accounts[0].Contract.ScriptHash() // Assuming Contract is present.

View file

@ -28,3 +28,10 @@ func Copy(b []byte) []byte {
copy(d, b)
return d
}
// Clean wipes the data in b by filling it with zeros.
func Clean(b []byte) {
for i := range b {
b[i] = 0
}
}

View file

@ -49,3 +49,11 @@ func TestCopyReverse(t *testing.T) {
}
}
}
func TestClean(t *testing.T) {
for _, tc := range testCases[1:] { // Empty one will be equal.
cp := Copy(tc.arr)
Clean(cp)
require.NotEqual(t, tc.arr, cp)
}
}

View file

@ -1,7 +1,6 @@
package wallet
import (
"bytes"
"errors"
"fmt"
@ -21,11 +20,8 @@ type Account struct {
// NEO private key.
privateKey *keys.PrivateKey
// NEO public key.
publicKey []byte
// Account import file.
wif string
// Script hash corresponding to the Address.
scriptHash util.Uint160
// NEO public address.
Address string `json:"address"`
@ -87,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")
@ -96,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
@ -136,6 +126,15 @@ func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
return nil
}
// SignHashable signs the given Hashable item and returns the signature. If this
// account can't sign (CanSign() returns false) nil is returned.
func (a *Account) SignHashable(net netmode.Magic, item hash.Hashable) []byte {
if !a.CanSign() {
return nil
}
return a.privateKey.SignHashable(uint32(net), item)
}
// CanSign returns true when account is not locked and has a decrypted private
// key inside, so it's ready to create real signatures.
func (a *Account) CanSign() bool {
@ -147,11 +146,13 @@ func (a *Account) GetVerificationScript() []byte {
if a.Contract != nil {
return a.Contract.Script
}
return a.PrivateKey().PublicKey().GetVerificationScript()
return a.privateKey.PublicKey().GetVerificationScript()
}
// Decrypt decrypts the EncryptedWIF with the given passphrase returning error
// if anything goes wrong.
// if anything goes wrong. After the decryption Account can be used to sign
// things unless it's locked. Don't decrypt the key unless you want to sign
// something and don't forget to call Close after use for maximum safety.
func (a *Account) Decrypt(passphrase string, scrypt keys.ScryptParams) error {
var err error
@ -163,9 +164,6 @@ func (a *Account) Decrypt(passphrase string, scrypt keys.ScryptParams) error {
return err
}
a.publicKey = a.privateKey.PublicKey().Bytes()
a.wif = a.privateKey.WIF()
return nil
}
@ -180,11 +178,45 @@ func (a *Account) Encrypt(passphrase string, scrypt keys.ScryptParams) error {
return nil
}
// PrivateKey returns private key corresponding to the account.
// PrivateKey returns private key corresponding to the account if it's unlocked.
// Please be very careful when using it, do not copy its contents and do not
// keep a pointer to it unless you absolutely need to. Most of the time you can
// use other methods (PublicKey, ScriptHash, SignHashable) depending on your
// needs and it'll be safer this way.
func (a *Account) PrivateKey() *keys.PrivateKey {
return a.privateKey
}
// PublicKey returns the public key associated with the private key corresponding to
// the account. It can return nil if account is locked (use CanSign to check).
func (a *Account) PublicKey() *keys.PublicKey {
if !a.CanSign() {
return nil
}
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.
func (a *Account) Close() {
if a.privateKey == nil {
return
}
a.privateKey.Destroy()
a.privateKey = nil
}
// NewAccountFromWIF creates a new Account from the given WIF.
func NewAccountFromWIF(wif string) (*Account, error) {
privKey, err := keys.NewPrivateKeyFromWIF(wif)
@ -209,9 +241,16 @@ func NewAccountFromEncryptedWIF(wif string, pass string, scrypt keys.ScryptParam
// ConvertMultisig sets a's contract to multisig contract with m sufficient signatures.
func (a *Account) ConvertMultisig(m int, pubs []*keys.PublicKey) error {
if a.Locked {
return errors.New("account is locked")
}
if a.privateKey == nil {
return errors.New("account key is not available (need to decrypt?)")
}
var found bool
accKey := a.privateKey.PublicKey()
for i := range pubs {
if bytes.Equal(a.publicKey, pubs[i].Bytes()) {
if accKey.Equal(pubs[i]) {
found = true
break
}
@ -226,7 +265,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),
@ -238,14 +278,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()
wif := p.WIF()
a := &Account{
publicKey: pubKey.Bytes(),
privateKey: p,
Address: pubAddr,
wif: wif,
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) {
@ -105,7 +107,7 @@ func TestContractSignTx(t *testing.T) {
require.Error(t, acc2.SignTx(0, tx))
pubs := keys.PublicKeys{acc.privateKey.PublicKey(), acc2.privateKey.PublicKey()}
pubs := keys.PublicKeys{acc.PublicKey(), acc2.PublicKey()}
multiS, err := smartcontract.CreateDefaultMultiSigRedeemScript(pubs)
require.NoError(t, err)
multiAcc := NewAccountFromPrivateKey(acc.privateKey)
@ -139,17 +141,22 @@ func TestContractSignTx(t *testing.T) {
acc2.Locked = true
require.False(t, acc2.CanSign())
require.Error(t, acc2.SignTx(0, tx)) // Locked account.
require.Nil(t, acc2.PublicKey()) // Locked account.
require.Nil(t, acc2.SignHashable(0, tx)) // Locked account.
acc2.Locked = false
acc2.privateKey = nil
acc2.Close()
require.False(t, acc2.CanSign())
require.Error(t, acc2.SignTx(0, tx)) // No private key.
acc2.Close() // No-op.
require.False(t, acc2.CanSign())
tx.Scripts = append(tx.Scripts, transaction.Witness{
VerificationScript: acc.Contract.Script,
})
require.NoError(t, acc.SignTx(0, tx)) // Add invocation script for existing witness.
require.Equal(t, 66, len(tx.Scripts[1].InvocationScript))
require.NotNil(t, acc.SignHashable(0, tx)) // Works via Hashable too.
require.NoError(t, multiAcc.SignTx(0, tx))
require.Equal(t, 3, len(tx.Scripts))
@ -179,6 +186,19 @@ func TestAccount_ConvertMultisig(t *testing.T) {
"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
}
t.Run("locked", func(t *testing.T) {
a.Locked = true
pubs := convertPubs(t, hexs)
require.Error(t, a.ConvertMultisig(1, pubs))
a.Locked = false
})
t.Run("no private key", func(t *testing.T) {
pk := a.privateKey
a.privateKey = nil
pubs := convertPubs(t, hexs)
require.Error(t, a.ConvertMultisig(0, pubs))
a.privateKey = pk
})
t.Run("invalid number of signatures", func(t *testing.T) {
pubs := convertPubs(t, hexs)
require.Error(t, a.ConvertMultisig(0, pubs))
@ -215,9 +235,9 @@ func convertPubs(t *testing.T, hexKeys []string) []*keys.PublicKey {
func compareFields(t *testing.T, tk keytestcases.Ktype, acc *Account) {
want, have := tk.Address, acc.Address
require.Equalf(t, want, have, "expected address %s got %s", want, have)
want, have = tk.Wif, acc.wif
want, have = tk.Wif, acc.privateKey.WIF()
require.Equalf(t, want, have, "expected wif %s got %s", want, have)
want, have = tk.PublicKey, hex.EncodeToString(acc.publicKey)
want, have = tk.PublicKey, hex.EncodeToString(acc.PublicKey().Bytes())
require.Equalf(t, want, have, "expected pub key %s got %s", want, have)
want, have = tk.PrivateKey, acc.privateKey.String()
require.Equalf(t, want, have, "expected priv key %s got %s", want, have)

View file

@ -168,9 +168,14 @@ func (w *Wallet) JSON() ([]byte, error) {
return json.MarshalIndent(w, " ", " ")
}
// Deprecated: Close is deprecated.
// Close performs nothing and is left for backwards compatibility.
func (w *Wallet) Close() {}
// Close closes all Wallet accounts making them incapable of signing anything
// (unless they're decrypted again). It's not doing anything to the underlying
// wallet file.
func (w *Wallet) Close() {
for _, acc := range w.Accounts {
acc.Close()
}
}
// GetAccount returns an account corresponding to the provided scripthash.
func (w *Wallet) GetAccount(h util.Uint160) *Account {

View file

@ -34,13 +34,16 @@ func TestNewWalletFromFile_Negative_NoFile(t *testing.T) {
require.Errorf(t, err, "open testWallet: no such file or directory")
}
func TestCreateAccount(t *testing.T) {
func TestCreateAccountAndClose(t *testing.T) {
wallet := checkWalletConstructor(t)
errAcc := wallet.CreateAccount("testName", "testPass")
require.NoError(t, errAcc)
accounts := wallet.Accounts
require.Len(t, accounts, 1)
require.True(t, wallet.Accounts[0].CanSign())
wallet.Close()
require.False(t, wallet.Accounts[0].CanSign())
}
func TestAddAccount(t *testing.T) {
@ -48,8 +51,6 @@ func TestAddAccount(t *testing.T) {
wallet.AddAccount(&Account{
privateKey: nil,
publicKey: nil,
wif: "",
Address: "real",
EncryptedWIF: "",
Label: "",
@ -78,8 +79,6 @@ func TestSave(t *testing.T) {
wallet.AddAccount(&Account{
privateKey: nil,
publicKey: nil,
wif: "",
Address: "",
EncryptedWIF: "",
Label: "",
@ -103,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)
})
}