diff --git a/dcrjson/chainsvrcmds.go b/dcrjson/chainsvrcmds.go index e7918f08..dae03df9 100644 --- a/dcrjson/chainsvrcmds.go +++ b/dcrjson/chainsvrcmds.go @@ -58,6 +58,7 @@ type CreateRawTransactionCmd struct { Inputs []TransactionInput Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In DCR LockTime *int64 + Expiry *int64 } // NewCreateRawTransactionCmd returns a new instance which can be used to issue @@ -65,12 +66,13 @@ type CreateRawTransactionCmd struct { // // Amounts are in BTC. func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64, - lockTime *int64) *CreateRawTransactionCmd { + lockTime *int64, expiry *int64) *CreateRawTransactionCmd { return &CreateRawTransactionCmd{ Inputs: inputs, Amounts: amounts, LockTime: lockTime, + Expiry: expiry, } } diff --git a/dcrjson/chainsvrcmds_test.go b/dcrjson/chainsvrcmds_test.go index e74816a2..e404c2ce 100644 --- a/dcrjson/chainsvrcmds_test.go +++ b/dcrjson/chainsvrcmds_test.go @@ -52,7 +52,7 @@ func TestChainSvrCmds(t *testing.T) { {Txid: "123", Vout: 1}, } amounts := map[string]float64{"456": .0123} - return dcrjson.NewCreateRawTransactionCmd(txInputs, amounts, nil) + return dcrjson.NewCreateRawTransactionCmd(txInputs, amounts, nil, nil) }, marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1,"tree":0}],{"456":0.0123}],"id":1}`, unmarshalled: &dcrjson.CreateRawTransactionCmd{ @@ -64,20 +64,21 @@ func TestChainSvrCmds(t *testing.T) { name: "createrawtransaction optional", newCmd: func() (interface{}, error) { return dcrjson.NewCmd("createrawtransaction", `[{"txid":"123","vout":1,"tree":0}]`, - `{"456":0.0123}`, int64(12312333333)) + `{"456":0.0123}`, int64(12312333333), int64(12312333333)) }, staticCmd: func() interface{} { txInputs := []dcrjson.TransactionInput{ {Txid: "123", Vout: 1}, } amounts := map[string]float64{"456": .0123} - return dcrjson.NewCreateRawTransactionCmd(txInputs, amounts, dcrjson.Int64(12312333333)) + return dcrjson.NewCreateRawTransactionCmd(txInputs, amounts, dcrjson.Int64(12312333333), dcrjson.Int64(12312333333)) }, - marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1,"tree":0}],{"456":0.0123},12312333333],"id":1}`, + marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1,"tree":0}],{"456":0.0123},12312333333,12312333333],"id":1}`, unmarshalled: &dcrjson.CreateRawTransactionCmd{ Inputs: []dcrjson.TransactionInput{{Txid: "123", Vout: 1}}, Amounts: map[string]float64{"456": .0123}, LockTime: dcrjson.Int64(12312333333), + Expiry: dcrjson.Int64(12312333333), }, }, { diff --git a/rpcclient/rawtransactions.go b/rpcclient/rawtransactions.go index da76270a..f6a14cc0 100644 --- a/rpcclient/rawtransactions.go +++ b/rpcclient/rawtransactions.go @@ -240,22 +240,22 @@ func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) { // // See CreateRawTransaction for the blocking version and more details. func (c *Client) CreateRawTransactionAsync(inputs []dcrjson.TransactionInput, - amounts map[dcrutil.Address]dcrutil.Amount, lockTime *int64) FutureCreateRawTransactionResult { + amounts map[dcrutil.Address]dcrutil.Amount, lockTime *int64, expiry *int64) FutureCreateRawTransactionResult { convertedAmts := make(map[string]float64, len(amounts)) for addr, amount := range amounts { convertedAmts[addr.String()] = amount.ToCoin() } - cmd := dcrjson.NewCreateRawTransactionCmd(inputs, convertedAmts, lockTime) + cmd := dcrjson.NewCreateRawTransactionCmd(inputs, convertedAmts, lockTime, expiry) return c.sendCmd(cmd) } // CreateRawTransaction returns a new transaction spending the provided inputs // and sending to the provided addresses. func (c *Client) CreateRawTransaction(inputs []dcrjson.TransactionInput, - amounts map[dcrutil.Address]dcrutil.Amount, lockTime *int64) (*wire.MsgTx, error) { + amounts map[dcrutil.Address]dcrutil.Amount, lockTime *int64, expiry *int64) (*wire.MsgTx, error) { - return c.CreateRawTransactionAsync(inputs, amounts, lockTime).Receive() + return c.CreateRawTransactionAsync(inputs, amounts, lockTime, expiry).Receive() } // FutureCreateRawSStxResult is a future promise to deliver the result diff --git a/rpcserver.go b/rpcserver.go index b57c7915..4335032d 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -621,6 +621,11 @@ func messageToHex(msg wire.Message) (string, error) { func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*dcrjson.CreateRawTransactionCmd) + // Validate expiry, if given. + if c.Expiry != nil && *c.Expiry < 0 { + return nil, rpcInvalidError("Expiry out of range") + } + // Validate the locktime, if given. if c.LockTime != nil && (*c.LockTime < 0 || @@ -703,6 +708,11 @@ func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan mtx.LockTime = uint32(*c.LockTime) } + // Set the Expiry, if given. + if c.Expiry != nil { + mtx.Expiry = uint32(*c.Expiry) + } + // 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 5b460e24..2fb3d282 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -91,6 +91,7 @@ var helpDescsEnUS = map[string]string{ "createrawtransaction-amounts--value": "n.nnn", "createrawtransaction-amounts--desc": "The destination address as the key and the amount in DCR as the value", "createrawtransaction-locktime": "Locktime value; a non-zero value will also locktime-activate the inputs", + "createrawtransaction-expiry": "Expiry value; a non-zero value when the transaction expiry", "createrawtransaction--result0": "Hex-encoded bytes of the serialized transaction", // ScriptSig help.