Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SignRawTransactionWithKey #1885

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions btcjson/walletsvrcmds.go
Expand Up @@ -760,6 +760,28 @@ func NewSignRawTransactionWithWalletCmd(hexEncodedTx string, inputs *[]RawTxWitn
}
}

// SignRawTransactionWithKeyCmd defines the signrawtransactionwithkey JSON-RPC command.
type SignRawTransactionWithKeyCmd struct {
RawTx string
PrivKeys *[]string
Inputs *[]RawTxWitnessInput
SigHashType *string `jsonrpcdefault:"\"ALL\""`
}

// NewSignRawTransactionWithKeyCmd returns a new instance which can be used to issue a
// signrawtransactionwithkey JSON-RPC command.
//
// The parameters which are pointers indicate they are optional. Passing nil
// for optional parameters will use the default value.
func NewSignRawTransactionWithKeyCmd(hexEncodedTx string, privKeys []string, inputs *[]RawTxWitnessInput, sigHashType *string) *SignRawTransactionWithKeyCmd {
return &SignRawTransactionWithKeyCmd{
RawTx: hexEncodedTx,
PrivKeys: &privKeys,
Inputs: inputs,
SigHashType: sigHashType,
}
}

// WalletLockCmd defines the walletlock JSON-RPC command.
type WalletLockCmd struct{}

Expand Down Expand Up @@ -1131,6 +1153,7 @@ func init() {
MustRegisterCmd("signmessage", (*SignMessageCmd)(nil), flags)
MustRegisterCmd("signrawtransaction", (*SignRawTransactionCmd)(nil), flags)
MustRegisterCmd("signrawtransactionwithwallet", (*SignRawTransactionWithWalletCmd)(nil), flags)
MustRegisterCmd("signrawtransactionwithkey", (*SignRawTransactionWithKeyCmd)(nil), flags)
MustRegisterCmd("unloadwallet", (*UnloadWalletCmd)(nil), flags)
MustRegisterCmd("walletlock", (*WalletLockCmd)(nil), flags)
MustRegisterCmd("walletpassphrase", (*WalletPassphraseCmd)(nil), flags)
Expand Down
106 changes: 106 additions & 0 deletions btcjson/walletsvrcmds_test.go
Expand Up @@ -1460,6 +1460,112 @@ func TestWalletSvrCmds(t *testing.T) {
SigHashType: btcjson.String("ALL"),
},
},
{
name: "signrawtransactionwithkey",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("signrawtransactionwithkey", "001122", `["abc"]`)
},
staticCmd: func() interface{} {
privKeys := []string{"abc"}
return btcjson.NewSignRawTransactionWithKeyCmd("001122", privKeys, nil, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"signrawtransactionwithkey","params":["001122",["abc"]],"id":1}`,
unmarshalled: &btcjson.SignRawTransactionWithKeyCmd{
RawTx: "001122",
PrivKeys: &[]string{"abc"},
Inputs: nil,
SigHashType: btcjson.String("ALL"),
},
},
{
name: "signrawtransactionwithkey optional1",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("signrawtransactionwithkey", "001122", `["abc"]`, `[{"txid":"123","vout":1,"scriptPubKey":"00","redeemScript":"01","witnessScript":"02","amount":1.5}]`)
},
staticCmd: func() interface{} {
privKeys := []string{"abc"}
txInputs := []btcjson.RawTxWitnessInput{
{
Txid: "123",
Vout: 1,
ScriptPubKey: "00",
RedeemScript: btcjson.String("01"),
WitnessScript: btcjson.String("02"),
Amount: btcjson.Float64(1.5),
},
}

return btcjson.NewSignRawTransactionWithKeyCmd("001122", privKeys, &txInputs, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"signrawtransactionwithkey","params":["001122",["abc"],[{"txid":"123","vout":1,"scriptPubKey":"00","redeemScript":"01","witnessScript":"02","amount":1.5}]],"id":1}`,
unmarshalled: &btcjson.SignRawTransactionWithKeyCmd{
RawTx: "001122",
PrivKeys: &[]string{"abc"},
Inputs: &[]btcjson.RawTxWitnessInput{
{
Txid: "123",
Vout: 1,
ScriptPubKey: "00",
RedeemScript: btcjson.String("01"),
WitnessScript: btcjson.String("02"),
Amount: btcjson.Float64(1.5),
},
},
SigHashType: btcjson.String("ALL"),
},
},
{
name: "signrawtransactionwithkey optional1 with blank fields in input",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("signrawtransactionwithkey", "001122", `["abc"]`, `[{"txid":"123","vout":1,"scriptPubKey":"00","redeemScript":"01"}]`)
},
staticCmd: func() interface{} {
privKeys := []string{"abc"}
txInputs := []btcjson.RawTxWitnessInput{
{
Txid: "123",
Vout: 1,
ScriptPubKey: "00",
RedeemScript: btcjson.String("01"),
},
}

return btcjson.NewSignRawTransactionWithKeyCmd("001122", privKeys, &txInputs, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"signrawtransactionwithkey","params":["001122",["abc"],[{"txid":"123","vout":1,"scriptPubKey":"00","redeemScript":"01"}]],"id":1}`,
unmarshalled: &btcjson.SignRawTransactionWithKeyCmd{
RawTx: "001122",
PrivKeys: &[]string{"abc"},
Inputs: &[]btcjson.RawTxWitnessInput{
{
Txid: "123",
Vout: 1,
ScriptPubKey: "00",
RedeemScript: btcjson.String("01"),
},
},
SigHashType: btcjson.String("ALL"),
},
},
{
name: "signrawtransactionwithkey optional2",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("signrawtransactionwithkey", "001122", `["abc"]`, `[]`, "ALL")
},
staticCmd: func() interface{} {
privKeys := []string{"abc"}
txInputs := []btcjson.RawTxWitnessInput{}

return btcjson.NewSignRawTransactionWithKeyCmd("001122", privKeys, &txInputs, btcjson.String("ALL"))
},
marshalled: `{"jsonrpc":"1.0","method":"signrawtransactionwithkey","params":["001122",["abc"],[],"ALL"],"id":1}`,
unmarshalled: &btcjson.SignRawTransactionWithKeyCmd{
RawTx: "001122",
PrivKeys: &[]string{"abc"},
Inputs: &[]btcjson.RawTxWitnessInput{},
SigHashType: btcjson.String("ALL"),
},
},
{
name: "walletlock",
newCmd: func() (interface{}, error) {
Expand Down
8 changes: 8 additions & 0 deletions btcjson/walletsvrresults.go
Expand Up @@ -322,6 +322,14 @@ type SignRawTransactionWithWalletResult struct {
Errors []SignRawTransactionError `json:"errors,omitempty"`
}

// SignRawTransactionWithKeyResult models the data from the
// signrawtransactionwithkey command.
type SignRawTransactionWithKeyResult struct {
Hex string `json:"hex"`
Complete bool `json:"complete"`
Errors []SignRawTransactionError `json:"errors,omitempty"`
}

// ValidateAddressWalletResult models the data returned by the wallet server
// validateaddress command.
type ValidateAddressWalletResult struct {
Expand Down
145 changes: 144 additions & 1 deletion rpcclient/rawtransactions.go
Expand Up @@ -10,9 +10,9 @@ import (
"encoding/json"

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcd/btcutil"
)

const (
Expand Down Expand Up @@ -725,6 +725,149 @@ func (c *Client) SignRawTransactionWithWallet3(tx *wire.MsgTx,
return c.SignRawTransactionWithWallet3Async(tx, inputs, hashType).Receive()
}

// FutureSignRawTransactionWithKeyResult is a future promise to deliver
// the result of the SignRawTransactionWithKeyAsync RPC invocation (or
// an applicable error).
type FutureSignRawTransactionWithKeyResult chan *Response

// Receive waits for the Response promised by the future and returns the
// signed transaction as well as whether or not all inputs are now signed.
func (r FutureSignRawTransactionWithKeyResult) Receive() (*wire.MsgTx, bool, error) {
res, err := ReceiveFuture(r)
if err != nil {
return nil, false, err
}

// Unmarshal as a signtransactionwithkey result.
var signRawTxWithKeyResult btcjson.SignRawTransactionWithKeyResult
err = json.Unmarshal(res, &signRawTxWithKeyResult)
if err != nil {
return nil, false, err
}

// Decode the serialized transaction hex to raw bytes.
serializedTx, err := hex.DecodeString(signRawTxWithKeyResult.Hex)
if err != nil {
return nil, false, err
}

// Deserialize the transaction and return it.
var msgTx wire.MsgTx
if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil {
return nil, false, err
}

return &msgTx, signRawTxWithKeyResult.Complete, nil
}

// SignRawTransactionWithKeyAsync returns an instance of a type that can be used
// to get the result of the RPC at some future time by invoking the Receive function
// on the returned instance.
//
// See SignRawTransactionWithKey for the blocking version and more details.
func (c *Client) SignRawTransactionWithKeyAsync(tx *wire.MsgTx, privKeysWIF []string) FutureSignRawTransactionWithKeyResult {
txHex := ""
if tx != nil {
// Serialize the transaction and convert to hex string.
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
if err := tx.Serialize(buf); err != nil {
return newFutureError(err)
}
txHex = hex.EncodeToString(buf.Bytes())
}

cmd := btcjson.NewSignRawTransactionWithKeyCmd(txHex, privKeysWIF, nil, nil)
return c.SendCmd(cmd)
}

// SignRawTransactionWithKey signs inputs for the passed transaction and returns
// the signed transaction as well as whether or not all inputs are now signed.
//
// This function assumes the RPC server already knows the input transactions for the
// passed transaction which needs to be signed and uses the default signature hash
// type. Use one of the SignRawTransactionWithKey# variants to specify that
// information, if needed.
func (c *Client) SignRawTransactionWithKey(tx *wire.MsgTx, privKeysWIF []string) (*wire.MsgTx, bool, error) {
return c.SignRawTransactionWithKeyAsync(tx, privKeysWIF).Receive()
}

// SignRawTransactionWithKey2Async returns an instance of a type that can be
// used to get the result of the RPC at some future time by invoking the Receive
// function on the returned instance.
//
// See SignRawTransactionWithKey2 for the blocking version and more details.
func (c *Client) SignRawTransactionWithKey2Async(tx *wire.MsgTx, privKeysWIF []string,
inputs []btcjson.RawTxWitnessInput) FutureSignRawTransactionWithKeyResult {

txHex := ""
if tx != nil {
// Serialize the transaction and convert to hex string.
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
if err := tx.Serialize(buf); err != nil {
return newFutureError(err)
}
txHex = hex.EncodeToString(buf.Bytes())
}

cmd := btcjson.NewSignRawTransactionWithKeyCmd(txHex, privKeysWIF, &inputs, nil)
return c.SendCmd(cmd)
}

// SignRawTransactionWithKey2 signs inputs for the passed transaction given the
// list of information about the input transactions needed to perform the signing
// process.
//
// The only input transactions that need to be specified are the ones the
// RPC server does not already know. Already known input transactions will be
// merged with the specified transactions.
//
// See SignRawTransactionWithKey if the RPC server already knows the input
// transactions.
func (c *Client) SignRawTransactionWithKey2(tx *wire.MsgTx, privKeysWIF []string,
inputs []btcjson.RawTxWitnessInput) (*wire.MsgTx, bool, error) {

return c.SignRawTransactionWithKey2Async(tx, privKeysWIF, inputs).Receive()
}

// SignRawTransactionWithKey3Async returns an instance of a type that can
// be used to get the result of the RPC at some future time by invoking the
// Receive function on the returned instance.
//
// See SignRawTransactionWithKey3 for the blocking version and more details.
func (c *Client) SignRawTransactionWithKey3Async(tx *wire.MsgTx, privKeysWIF []string,
inputs []btcjson.RawTxWitnessInput, hashType SigHashType) FutureSignRawTransactionWithKeyResult {

txHex := ""
if tx != nil {
// Serialize the transaction and convert to hex string.
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
if err := tx.Serialize(buf); err != nil {
return newFutureError(err)
}
txHex = hex.EncodeToString(buf.Bytes())
}

cmd := btcjson.NewSignRawTransactionWithKeyCmd(txHex, privKeysWIF, &inputs, btcjson.String(string(hashType)))
return c.SendCmd(cmd)
}

// SignRawTransactionWithKey3 signs inputs for the passed transaction using
// the specified signature hash type given the list of information about extra
// input transactions.
//
// The only input transactions that need to be specified are the ones the RPC server
// does not already know. This means the list of transaction inputs can be nil
// if the RPC server already knows them all.
//
// This function should only be used if a non-default signature hash type is
// desired. Otherwise, see SignRawTransactionWithKey if the RPC server already
// knows the input transactions, or SignRawTransactionWithKey2 if it does not.
func (c *Client) SignRawTransactionWithKey3(tx *wire.MsgTx, privKeysWIF []string,
inputs []btcjson.RawTxWitnessInput, hashType SigHashType) (*wire.MsgTx, bool, error) {

return c.SignRawTransactionWithKey3Async(tx, privKeysWIF, inputs, hashType).Receive()
}

// FutureSearchRawTransactionsResult is a future promise to deliver the result
// of the SearchRawTransactionsAsync RPC invocation (or an applicable error).
type FutureSearchRawTransactionsResult chan *Response
Expand Down