wallet: implement (*Account).SignTx

It is used in both CLI and RPC.
This commit is contained in:
Evgenii Stratonikov 2020-02-28 19:01:07 +03:00
parent 6541bd4d42
commit 05a3625b7d
6 changed files with 44 additions and 50 deletions

View file

@ -16,7 +16,6 @@ import (
"github.com/CityOfZion/neo-go/pkg/rpc/client"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
"github.com/CityOfZion/neo-go/pkg/wallet"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
@ -232,7 +231,7 @@ func claimGas(ctx *cli.Context) error {
ScriptHash: scriptHash,
})
signTx(tx, acc)
_ = acc.SignTx(tx)
if err := c.SendRawTransaction(tx); err != nil {
return cli.NewExitError(err, 1)
}
@ -433,7 +432,7 @@ func transferAsset(ctx *cli.Context) error {
Position: 1,
})
signTx(tx, acc)
_ = acc.SignTx(tx)
if err := c.SendRawTransaction(tx); err != nil {
return cli.NewExitError(err, 1)
}
@ -495,23 +494,6 @@ func createWallet(ctx *cli.Context) error {
return nil
}
func signTx(tx *transaction.Transaction, acc *wallet.Account) {
priv := acc.PrivateKey()
sign := priv.Sign(tx.GetSignedPart())
invoc := append([]byte{byte(opcode.PUSHBYTES64)}, sign...)
tx.Scripts = []transaction.Witness{{
InvocationScript: invoc,
VerificationScript: getVerificationScript(acc),
}}
}
func getVerificationScript(acc *wallet.Account) []byte {
if acc.Contract != nil {
return acc.Contract.Script
}
return acc.PrivateKey().PublicKey().GetVerificationScript()
}
func readAccountInfo() (string, string, error) {
buf := bufio.NewReader(os.Stdin)
fmt.Print("Enter the name of the account > ")

View file

@ -11,7 +11,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/rpc/request"
"github.com/CityOfZion/neo-go/pkg/wallet"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
@ -49,10 +49,12 @@ func prepareData(t *testing.B) []*transaction.Transaction {
var data []*transaction.Transaction
wif := getWif(t)
acc, err := wallet.NewAccountFromWIF(wif.S)
require.NoError(t, err)
for n := 0; n < t.N; n++ {
tx := getTX(t, wif)
require.NoError(t, request.SignTx(tx, wif))
require.NoError(t, acc.SignTx(tx))
data = append(data, tx)
}
return data

View file

@ -20,6 +20,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm/emit"
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
"github.com/CityOfZion/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
@ -276,11 +277,9 @@ func _(t *testing.T) {
ScriptHash: priv1.GetScriptHash(),
})
tx5.Scripts = []transaction.Witness{{
InvocationScript: getInvocationScript(tx5.GetSignedPart(), priv),
VerificationScript: priv.PublicKey().GetVerificationScript(),
}}
acc, err := wallet.NewAccountFromWIF(priv.WIF())
require.NoError(t, err)
require.NoError(t, acc.SignTx(tx5))
b = bc.newBlock(newMinerTX(), tx5)
require.NoError(t, bc.AddBlock(b))

View file

@ -11,6 +11,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/rpc/response/result"
"github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/wallet"
"github.com/pkg/errors"
)
@ -179,7 +180,10 @@ func (c *Client) SignAndPushInvocationTx(script []byte, wif *keys.WIF, gas util.
}
}
if err = request.SignTx(tx, wif); err != nil {
acc, err := wallet.NewAccountFromWIF(wif.S)
if err != nil {
return txHash, err
} else if err = acc.SignTx(tx); err != nil {
return txHash, errors.Wrap(err, "failed to sign tx")
}
txHash = tx.Hash()

View file

@ -13,6 +13,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm/emit"
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
"github.com/CityOfZion/neo-go/pkg/wallet"
errs "github.com/pkg/errors"
)
@ -48,7 +49,9 @@ func CreateRawContractTransaction(params ContractTxParams) (*transaction.Transac
}
receiverOutput = transaction.NewOutput(assetID, amount, toAddressHash)
tx.AddOutput(receiverOutput)
if err = SignTx(tx, &wif); err != nil {
if acc, err := wallet.NewAccountFromWIF(wif.S); err != nil {
return nil, err
} else if err = acc.SignTx(tx); err != nil {
return nil, errs.Wrap(err, "failed to sign tx")
}
@ -77,27 +80,6 @@ func AddInputsAndUnspentsToTx(tx *transaction.Transaction, addr string, assetID
return nil
}
// SignTx signs given transaction in-place using given key.
func SignTx(tx *transaction.Transaction, wif *keys.WIF) error {
var witness transaction.Witness
var err error
if witness.InvocationScript, err = GetInvocationScript(tx, wif); err != nil {
return errs.Wrap(err, "failed to create invocation script")
}
witness.VerificationScript = wif.PrivateKey.PublicKey().GetVerificationScript()
tx.Scripts = append(tx.Scripts, witness)
tx.Hash()
return nil
}
// GetInvocationScript returns NEO VM script containing transaction signature.
func GetInvocationScript(tx *transaction.Transaction, wif *keys.WIF) ([]byte, error) {
signature := wif.PrivateKey.Sign(tx.GetSignedPart())
return append([]byte{byte(opcode.PUSHBYTES64)}, signature...), nil
}
// CreateDeploymentScript returns a script that deploys given smart contract
// with its metadata.
func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, error) {

View file

@ -7,11 +7,13 @@ import (
"errors"
"fmt"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
)
// Account represents a NEO account. It holds the private and public key
@ -122,6 +124,29 @@ func NewAccount() (*Account, error) {
return newAccountFromPrivateKey(priv), nil
}
// SignTx signs transaction t and updates it's Witnesses.
func (a *Account) SignTx(t *transaction.Transaction) error {
if a.privateKey == nil {
return errors.New("account is not unlocked")
}
data := t.GetSignedPart()
sign := a.privateKey.Sign(data)
t.Scripts = append(t.Scripts, transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHBYTES64)}, sign...),
VerificationScript: a.getVerificationScript(),
})
return nil
}
func (a *Account) getVerificationScript() []byte {
if a.Contract != nil {
return a.Contract.Script
}
return a.PrivateKey().PublicKey().GetVerificationScript()
}
// Decrypt decrypts the EncryptedWIF with the given passphrase returning error
// if anything goes wrong.
func (a *Account) Decrypt(passphrase string) error {