Skip to content

Commit

Permalink
SignRawTransactionWithKey
Browse files Browse the repository at this point in the history
  • Loading branch information
Dragan Milivojević committed Sep 10, 2022
1 parent 0f49e10 commit e12d5db
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 1 deletion.
23 changes: 23 additions & 0 deletions btcjson/walletsvrcmds.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit e12d5db

Please sign in to comment.