From 4b7206b54fd3df7a9a254f22dec5c37ce72625c4 Mon Sep 17 00:00:00 2001 From: David Hill Date: Fri, 23 Oct 2015 11:47:27 -0400 Subject: [PATCH] btcjson: Add optional locktime to createrawtransaction rpcserver: If the locktime is given, the transaction inputs will be set to a non-max value, activating the locktime. The locktime for the new transaction will be set to the given value. This mimics Bitcoin Core commit 212bcca92089f406d9313dbe6d0e1d25143d61ff --- btcjson/chainsvrcmds.go | 14 +++++++++----- btcjson/chainsvrcmds_test.go | 23 ++++++++++++++++++++++- docs/json_rpc_api.md | 4 ++-- rpcserver.go | 17 +++++++++++++++++ rpcserverhelp.go | 1 + 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/btcjson/chainsvrcmds.go b/btcjson/chainsvrcmds.go index 4023b51d..90441e6e 100644 --- a/btcjson/chainsvrcmds.go +++ b/btcjson/chainsvrcmds.go @@ -53,18 +53,22 @@ type TransactionInput struct { // CreateRawTransactionCmd defines the createrawtransaction JSON-RPC command. type CreateRawTransactionCmd struct { - Inputs []TransactionInput - Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In BTC + Inputs []TransactionInput + Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In BTC + LockTime *int64 } // NewCreateRawTransactionCmd returns a new instance which can be used to issue // a createrawtransaction JSON-RPC command. // // Amounts are in BTC. -func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64) *CreateRawTransactionCmd { +func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64, + lockTime *int64) *CreateRawTransactionCmd { + return &CreateRawTransactionCmd{ - Inputs: inputs, - Amounts: amounts, + Inputs: inputs, + Amounts: amounts, + LockTime: lockTime, } } diff --git a/btcjson/chainsvrcmds_test.go b/btcjson/chainsvrcmds_test.go index 84e719c0..527bc0ba 100644 --- a/btcjson/chainsvrcmds_test.go +++ b/btcjson/chainsvrcmds_test.go @@ -51,7 +51,7 @@ func TestChainSvrCmds(t *testing.T) { {Txid: "123", Vout: 1}, } amounts := map[string]float64{"456": .0123} - return btcjson.NewCreateRawTransactionCmd(txInputs, amounts) + return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, nil) }, marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123}],"id":1}`, unmarshalled: &btcjson.CreateRawTransactionCmd{ @@ -59,6 +59,27 @@ func TestChainSvrCmds(t *testing.T) { Amounts: map[string]float64{"456": .0123}, }, }, + { + name: "createrawtransaction optional", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createrawtransaction", `[{"txid":"123","vout":1}]`, + `{"456":0.0123}`, 12312333333) + }, + staticCmd: func() interface{} { + txInputs := []btcjson.TransactionInput{ + {Txid: "123", Vout: 1}, + } + amounts := map[string]float64{"456": .0123} + return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, btcjson.Int64(12312333333)) + }, + marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123},12312333333],"id":1}`, + unmarshalled: &btcjson.CreateRawTransactionCmd{ + Inputs: []btcjson.TransactionInput{{Txid: "123", Vout: 1}}, + Amounts: map[string]float64{"456": .0123}, + LockTime: btcjson.Int64(12312333333), + }, + }, + { name: "decoderawtransaction", newCmd: func() (interface{}, error) { diff --git a/docs/json_rpc_api.md b/docs/json_rpc_api.md index 7779cf06..28928f04 100644 --- a/docs/json_rpc_api.md +++ b/docs/json_rpc_api.md @@ -199,10 +199,10 @@ the method name for further details such as parameter and return information. | | | |---|---| |Method|createrawtransaction| -|Parameters|1. transaction inputs (JSON array, required) - json array of json objects
`[`
  `{`
    `"txid": "hash", (string, required) the hash of the input transaction`
    `"vout": n (numeric, required) the specific output of the input transaction to redeem`
  `}, ...`
`]`
2. addresses and amounts (JSON object, required) - json object with addresses as keys and amounts as values
`{`
  `"address": n.nnn (numeric, required) the address to send to as the key and the amount in BTC as the value`
  `, ...`
`}`| +|Parameters|1. transaction inputs (JSON array, required) - json array of json objects
`[`
  `{`
    `"txid": "hash", (string, required) the hash of the input transaction`
    `"vout": n (numeric, required) the specific output of the input transaction to redeem`
  `}, ...`
`]`
2. addresses and amounts (JSON object, required) - json object with addresses as keys and amounts as values
`{`
  `"address": n.nnn (numeric, required) the address to send to as the key and the amount in BTC as the value`
  `, ...`
`}`
3. locktime (int64, optional, default=0) - specifies the transaction locktime. If non-zero, the inputs will also have their locktimes activated. | |Description|Returns a new transaction spending the provided inputs and sending to the provided addresses.
The transaction inputs are not signed in the created transaction.
The `signrawtransaction` RPC command provided by wallet must be used to sign the resulting transaction.| |Returns|`"transaction" (string) hex-encoded bytes of the serialized transaction`| -|Example Parameters|1. transaction inputs `[{"txid":"e6da89de7a6b8508ce8f371a3d0535b04b5e108cb1a6e9284602d3bfd357c018","vout":1}]`
2. addresses and amounts `{"13cgrTP7wgbZYWrY9BZ22BV6p82QXQT3nY": 0.49213337}`| +|Example Parameters|1. transaction inputs `[{"txid":"e6da89de7a6b8508ce8f371a3d0535b04b5e108cb1a6e9284602d3bfd357c018","vout":1}]`
2. addresses and amounts `{"13cgrTP7wgbZYWrY9BZ22BV6p82QXQT3nY": 0.49213337}`
3. locktime `0`| |Example Return|`010000000118c057d3bfd3024628e9a6b18c105e4bb035053d1a378fce08856b7ade89dae6010000`
`0000ffffffff0199efee02000000001976a9141cb013db35ecccc156fdfd81d03a11c51998f99388`
`ac00000000`
**Newlines added for display purposes. The actual return does not contain newlines.**| [Return to Overview](#MethodOverview)
diff --git a/rpcserver.go b/rpcserver.go index 3af8a5b4..e4365ad0 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -511,6 +511,15 @@ func messageToHex(msg wire.Message) (string, error) { func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.CreateRawTransactionCmd) + // Validate the locktime, if given. + if c.LockTime != nil && + (*c.LockTime < 0 || *c.LockTime > int64(wire.MaxTxInSequenceNum)) { + return nil, &btcjson.RPCError{ + Code: btcjson.ErrRPCInvalidParameter, + Message: "Locktime out of range", + } + } + // Add all transaction inputs to a new transaction after performing // some validity checks. mtx := wire.NewMsgTx() @@ -522,6 +531,9 @@ func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan prevOut := wire.NewOutPoint(txHash, uint32(input.Vout)) txIn := wire.NewTxIn(prevOut, []byte{}) + if c.LockTime != nil && *c.LockTime != 0 { + txIn.Sequence = wire.MaxTxInSequenceNum - 1 + } mtx.AddTxIn(txIn) } @@ -584,6 +596,11 @@ func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan mtx.AddTxOut(txOut) } + // Set the Locktime, if given. + if c.LockTime != nil { + mtx.LockTime = uint32(*c.LockTime) + } + // Return the serialized and hex-encoded transaction. Note that this // is intentionally not directly returning because the first return // value is a string and it would result in returning an empty string to diff --git a/rpcserverhelp.go b/rpcserverhelp.go index 915777a4..d9b0b305 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -52,6 +52,7 @@ var helpDescsEnUS = map[string]string{ "createrawtransaction-amounts--key": "address", "createrawtransaction-amounts--value": "n.nnn", "createrawtransaction-amounts--desc": "The destination address as the key and the amount in BTC as the value", + "createrawtransaction-locktime": "Locktime value; a non-zero value will also locktime-activate the inputs", "createrawtransaction--result0": "Hex-encoded bytes of the serialized transaction", // ScriptSig help.