From 43774fe6bb034c89d3a36bc7a5f0fce93139c999 Mon Sep 17 00:00:00 2001 From: danda Date: Wed, 19 Aug 2015 17:46:00 -0700 Subject: [PATCH] adds optional prevOut section to vin for searchrawtransactions api. See https://github.com/btcsuite/btcd/issues/485 --- blockchain/txlookup.go | 9 ++- blockmanager.go | 2 +- btcjson/chainsvrcmds.go | 20 +++--- btcjson/chainsvrcmds_test.go | 62 ++++++++++++------ btcjson/chainsvrresults.go | 68 ++++++++++++++++++- btcjson/chainsvrresults_test.go | 25 +++++++ docs/json_rpc_api.md | 10 +-- mempool.go | 8 +-- rpcserver.go | 112 ++++++++++++++++++++++++++++++-- rpcserverhelp.go | 31 ++++++++- 10 files changed, 294 insertions(+), 53 deletions(-) diff --git a/blockchain/txlookup.go b/blockchain/txlookup.go index b7c8fe65..7810d343 100644 --- a/blockchain/txlookup.go +++ b/blockchain/txlookup.go @@ -300,7 +300,7 @@ func (b *BlockChain) fetchInputTransactions(node *blockNode, block *btcutil.Bloc // passed transaction from the point of view of the end of the main chain. It // also attempts to fetch the transaction itself so the returned TxStore can be // examined for duplicate transactions. -func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx) (TxStore, error) { +func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx, includeSpent bool) (TxStore, error) { // Create a set of needed transactions from the transactions referenced // by the inputs of the passed transaction. Also, add the passed // transaction itself as a way for the caller to detect duplicates. @@ -311,9 +311,8 @@ func (b *BlockChain) FetchTransactionStore(tx *btcutil.Tx) (TxStore, error) { } // Request the input transactions from the point of view of the end of - // the main chain without including fully spent trasactions in the - // results. Fully spent transactions are only needed for chain - // reorganization which does not apply here. - txStore := fetchTxStoreMain(b.db, txNeededSet, false) + // the main chain with or without without including fully spent transactions + // in the results. + txStore := fetchTxStoreMain(b.db, txNeededSet, includeSpent) return txStore, nil } diff --git a/blockmanager.go b/blockmanager.go index c907e6e1..f0d7390b 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -1119,7 +1119,7 @@ out: } case fetchTransactionStoreMsg: - txStore, err := b.blockChain.FetchTransactionStore(msg.tx) + txStore, err := b.blockChain.FetchTransactionStore(msg.tx, false) msg.reply <- fetchTransactionStoreResponse{ TxStore: txStore, err: err, diff --git a/btcjson/chainsvrcmds.go b/btcjson/chainsvrcmds.go index 11bdf27e..24aea338 100644 --- a/btcjson/chainsvrcmds.go +++ b/btcjson/chainsvrcmds.go @@ -545,10 +545,11 @@ func NewReconsiderBlockCmd(blockHash string) *ReconsiderBlockCmd { // SearchRawTransactionsCmd defines the searchrawtransactions JSON-RPC command. type SearchRawTransactionsCmd struct { - Address string - Verbose *int `jsonrpcdefault:"1"` - Skip *int `jsonrpcdefault:"0"` - Count *int `jsonrpcdefault:"100"` + Address string + Verbose *int `jsonrpcdefault:"1"` + Skip *int `jsonrpcdefault:"0"` + Count *int `jsonrpcdefault:"100"` + VinExtra *int `jsonrpcdefault:"0"` } // NewSearchRawTransactionsCmd returns a new instance which can be used to issue a @@ -556,12 +557,13 @@ type SearchRawTransactionsCmd struct { // // The parameters which are pointers indicate they are optional. Passing nil // for optional parameters will use the default value. -func NewSearchRawTransactionsCmd(address string, verbose, skip, count *int) *SearchRawTransactionsCmd { +func NewSearchRawTransactionsCmd(address string, verbose, skip, count *int, vinExtra *int) *SearchRawTransactionsCmd { return &SearchRawTransactionsCmd{ - Address: address, - Verbose: verbose, - Skip: skip, - Count: count, + Address: address, + Verbose: verbose, + Skip: skip, + Count: count, + VinExtra: vinExtra, } } diff --git a/btcjson/chainsvrcmds_test.go b/btcjson/chainsvrcmds_test.go index baaf7233..424cde1b 100644 --- a/btcjson/chainsvrcmds_test.go +++ b/btcjson/chainsvrcmds_test.go @@ -679,14 +679,15 @@ func TestChainSvrCmds(t *testing.T) { return btcjson.NewCmd("searchrawtransactions", "1Address") }, staticCmd: func() interface{} { - return btcjson.NewSearchRawTransactionsCmd("1Address", nil, nil, nil) + return btcjson.NewSearchRawTransactionsCmd("1Address", nil, nil, nil, nil) }, marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address"],"id":1}`, unmarshalled: &btcjson.SearchRawTransactionsCmd{ - Address: "1Address", - Verbose: btcjson.Int(1), - Skip: btcjson.Int(0), - Count: btcjson.Int(100), + Address: "1Address", + Verbose: btcjson.Int(1), + Skip: btcjson.Int(0), + Count: btcjson.Int(100), + VinExtra: btcjson.Int(0), }, }, { @@ -696,14 +697,15 @@ func TestChainSvrCmds(t *testing.T) { }, staticCmd: func() interface{} { return btcjson.NewSearchRawTransactionsCmd("1Address", - btcjson.Int(0), nil, nil) + btcjson.Int(0), nil, nil, nil) }, marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0],"id":1}`, unmarshalled: &btcjson.SearchRawTransactionsCmd{ - Address: "1Address", - Verbose: btcjson.Int(0), - Skip: btcjson.Int(0), - Count: btcjson.Int(100), + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(0), + Count: btcjson.Int(100), + VinExtra: btcjson.Int(0), }, }, { @@ -713,14 +715,15 @@ func TestChainSvrCmds(t *testing.T) { }, staticCmd: func() interface{} { return btcjson.NewSearchRawTransactionsCmd("1Address", - btcjson.Int(0), btcjson.Int(5), nil) + btcjson.Int(0), btcjson.Int(5), nil, nil) }, marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5],"id":1}`, unmarshalled: &btcjson.SearchRawTransactionsCmd{ - Address: "1Address", - Verbose: btcjson.Int(0), - Skip: btcjson.Int(5), - Count: btcjson.Int(100), + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(100), + VinExtra: btcjson.Int(0), }, }, { @@ -730,14 +733,33 @@ func TestChainSvrCmds(t *testing.T) { }, staticCmd: func() interface{} { return btcjson.NewSearchRawTransactionsCmd("1Address", - btcjson.Int(0), btcjson.Int(5), btcjson.Int(10)) + btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), nil) }, marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10],"id":1}`, unmarshalled: &btcjson.SearchRawTransactionsCmd{ - Address: "1Address", - Verbose: btcjson.Int(0), - Skip: btcjson.Int(5), - Count: btcjson.Int(10), + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(10), + VinExtra: btcjson.Int(0), + }, + }, + { + name: "searchrawtransactions", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10, 1) + }, + staticCmd: func() interface{} { + return btcjson.NewSearchRawTransactionsCmd("1Address", + btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), btcjson.Int(1)) + }, + marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10,1],"id":1}`, + unmarshalled: &btcjson.SearchRawTransactionsCmd{ + Address: "1Address", + Verbose: btcjson.Int(0), + Skip: btcjson.Int(5), + Count: btcjson.Int(10), + VinExtra: btcjson.Int(1), }, }, { diff --git a/btcjson/chainsvrresults.go b/btcjson/chainsvrresults.go index 518d78fe..086caeeb 100644 --- a/btcjson/chainsvrresults.go +++ b/btcjson/chainsvrresults.go @@ -272,6 +272,56 @@ func (v *Vin) MarshalJSON() ([]byte, error) { return json.Marshal(txStruct) } +// PrevOut represents previous output for an input Vin. +type PrevOut struct { + Addresses []string `json:"addresses,omitempty"` + Value float64 `json:"value"` +} + +// VinPrevOut is like Vin except it includes PrevOut. It is used by searchrawtransaction +type VinPrevOut struct { + Coinbase string `json:"coinbase"` + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptSig *ScriptSig `json:"scriptSig"` + PrevOut *PrevOut `json:"prevOut"` + Sequence uint32 `json:"sequence"` +} + +// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not. +func (v *VinPrevOut) IsCoinBase() bool { + return len(v.Coinbase) > 0 +} + +// MarshalJSON provides a custom Marshal method for VinPrevOut. +func (v *VinPrevOut) MarshalJSON() ([]byte, error) { + if v.IsCoinBase() { + coinbaseStruct := struct { + Coinbase string `json:"coinbase"` + Sequence uint32 `json:"sequence"` + }{ + Coinbase: v.Coinbase, + Sequence: v.Sequence, + } + return json.Marshal(coinbaseStruct) + } + + txStruct := struct { + Txid string `json:"txid"` + Vout uint32 `json:"vout"` + ScriptSig *ScriptSig `json:"scriptSig"` + PrevOut *PrevOut `json:"prevOut,omitempty"` + Sequence uint32 `json:"sequence"` + }{ + Txid: v.Txid, + Vout: v.Vout, + ScriptSig: v.ScriptSig, + PrevOut: v.PrevOut, + Sequence: v.Sequence, + } + return json.Marshal(txStruct) +} + // Vout models parts of the tx data. It is defined seperately since both // getrawtransaction and decoderawtransaction use the same structure. type Vout struct { @@ -332,8 +382,7 @@ type NetworksResult struct { Proxy string `json:"proxy"` } -// TxRawResult models the data from the getrawtransaction and -// searchrawtransaction commands. +// TxRawResult models the data from the getrawtransaction command. type TxRawResult struct { Hex string `json:"hex"` Txid string `json:"txid"` @@ -347,6 +396,21 @@ type TxRawResult struct { Blocktime int64 `json:"blocktime,omitempty"` } +// SearchRawTransactionsResult models the data from the searchrawtransaction +// command. +type SearchRawTransactionsResult struct { + Hex string `json:"hex"` + Txid string `json:"txid"` + Version int32 `json:"version"` + LockTime uint32 `json:"locktime"` + Vin []VinPrevOut `json:"vin"` + Vout []Vout `json:"vout"` + BlockHash string `json:"blockhash,omitempty"` + Confirmations uint64 `json:"confirmations,omitempty"` + Time int64 `json:"time,omitempty"` + Blocktime int64 `json:"blocktime,omitempty"` +} + // TxRawDecodeResult models the data from the decoderawtransaction command. type TxRawDecodeResult struct { Txid string `json:"txid"` diff --git a/btcjson/chainsvrresults_test.go b/btcjson/chainsvrresults_test.go index e6e72cfd..1d568e26 100644 --- a/btcjson/chainsvrresults_test.go +++ b/btcjson/chainsvrresults_test.go @@ -43,6 +43,31 @@ func TestChainSvrCustomResults(t *testing.T) { }, expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"sequence":4294967295}`, }, + { + name: "custom vinprevout marshal with coinbase", + result: &btcjson.VinPrevOut{ + Coinbase: "021234", + Sequence: 4294967295, + }, + expected: `{"coinbase":"021234","sequence":4294967295}`, + }, + { + name: "custom vinprevout marshal without coinbase", + result: &btcjson.VinPrevOut{ + Txid: "123", + Vout: 1, + ScriptSig: &btcjson.ScriptSig{ + Asm: "0", + Hex: "00", + }, + PrevOut: &btcjson.PrevOut{ + Addresses: []string{"addr1"}, + Value: 0, + }, + Sequence: 4294967295, + }, + expected: `{"txid":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"prevOut":{"addresses":["addr1"],"value":0},"sequence":4294967295}`, + }, } t.Logf("Running %d tests", len(tests)) diff --git a/docs/json_rpc_api.md b/docs/json_rpc_api.md index bf95a845..8ff62c87 100644 --- a/docs/json_rpc_api.md +++ b/docs/json_rpc_api.md @@ -214,7 +214,7 @@ the method name for further details such as parameter and return information. |Method|decoderawtransaction| |Parameters|1. data (string, required) - serialized, hex-encoded transaction| |Description|Returns a JSON object representing the provided serialized, hex-encoded transaction.| -|Returns|`{ (json object)`
  `"txid": "hash", (string) the hash of the transaction`
  `"version": n, (numeric) the transaction version`
  `"locktime": n, (numeric) the transaction lock time`
  `"vin": [ (array of json objects) the transaction inputs as json objects`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "data", (string) the hex-dencoded bytes of the signature script`
      `"sequence": n, (numeric) the script sequence number`
    `}`
  For non-coinbase transactions:
    `{ (json object)`
      `"txid": "hash", (string) the hash of the origin transaction`
      `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`
      `"scriptSig": { (json object) the signature script used to redeem the origin transaction`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
      `}`
      `"sequence": n, (numeric) the script sequence number`
    `}, ...`
  `]`
  `"vout": [ (array of json objects) the transaction outputs as json objects`
    `{ (json object)`
      `"value": n, (numeric) the value in BTC`
      `"n": n, (numeric) the index of this transaction output`
      `"scriptPubKey": { (json object) the public key script used to pay coins`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
        `"reqSigs": n, (numeric) the number of required signatures`
        `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`
        `"addresses": [ (json array of string) the bitcoin addresses associated with this output`
          `"bitcoinaddress", (string) the bitcoin address`
          `...`
        `]`
      `}`
    `}, ...`
  `]`
`}`| +|Returns|`{ (json object)`
  `"txid": "hash", (string) the hash of the transaction`
  `"version": n, (numeric) the transaction version`
  `"locktime": n, (numeric) the transaction lock time`
  `"vin": [ (array of json objects) the transaction inputs as json objects`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "data", (string) the hex-encoded bytes of the signature script`
      `"sequence": n, (numeric) the script sequence number`
    `}`
  For non-coinbase transactions:
    `{ (json object)`
      `"txid": "hash", (string) the hash of the origin transaction`
      `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`
      `"scriptSig": { (json object) the signature script used to redeem the origin transaction`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
      `}`
      `"sequence": n, (numeric) the script sequence number`
    `}, ...`
  `]`
  `"vout": [ (array of json objects) the transaction outputs as json objects`
    `{ (json object)`
      `"value": n, (numeric) the value in BTC`
      `"n": n, (numeric) the index of this transaction output`
      `"scriptPubKey": { (json object) the public key script used to pay coins`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
        `"reqSigs": n, (numeric) the number of required signatures`
        `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`
        `"addresses": [ (json array of string) the bitcoin addresses associated with this output`
          `"bitcoinaddress", (string) the bitcoin address`
          `...`
        `]`
      `}`
    `}, ...`
  `]`
`}`| |Example Return|`{`
  `"txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",`
  `"version": 1,`
  `"locktime": 0,`
  `"vin": [`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6...",`
      `"sequence": 4294967295,`
    `}`
  For non-coinbase transactions:
    `{`
      `"txid": "60ac4b057247b3d0b9a8173de56b5e1be8c1d1da970511c626ef53706c66be04",`
      `"vout": 0,`
      `"scriptSig": {`
        `"asm": "3046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8f0...",`
        `"hex": "493046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8...",`
      `}`
      `"sequence": 4294967295,`
    `}`
  `]`
  `"vout": [`
    `{`
      `"value": 50,`
      `"n": 0,`
      `"scriptPubKey": {`
        `"asm": "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4ce...",`
        `"hex": "4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4...",`
        `"reqSigs": 1,`
        `"type": "pubkey"`
        `"addresses": [`
          `"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",`
        `]`
      `}`
    `}`
  `]`
`}`| [Return to Overview](#MethodOverview)
@@ -437,7 +437,7 @@ Example Return|`{`
  `"bytes": 310768,`
  `"size": |Parameters|1. transaction hash (string, required) - the hash of the transaction
2. verbose (int, optional, default=0) - specifies the transaction is returned as a JSON object instead of hex-encoded string| |Description|Returns information about a transaction given its hash.| |Returns (verbose=0)|`"data" (string) hex-encoded bytes of the serialized transaction`| -|Returns (verbose=1)|`{ (json object)`
  `"hex": "data", (string) hex-encoded transaction`
  `"txid": "hash", (string) the hash of the transaction`
  `"version": n, (numeric) the transaction version`
  `"locktime": n, (numeric) the transaction lock time`
  `"vin": [ (array of json objects) the transaction inputs as json objects`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "data", (string) the hex-dencoded bytes of the signature script`
      `"sequence": n, (numeric) the script sequence number`
    `}`
  For non-coinbase transactions:
    `{ (json object)`
      `"txid": "hash", (string) the hash of the origin transaction`
      `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`
      `"scriptSig": { (json object) the signature script used to redeem the origin transaction`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
      `}`
      `"sequence": n, (numeric) the script sequence number`
    `}, ...`
  `]`
  `"vout": [ (array of json objects) the transaction outputs as json objects`
    `{ (json object)`
      `"value": n, (numeric) the value in BTC`
      `"n": n, (numeric) the index of this transaction output`
      `"scriptPubKey": { (json object) the public key script used to pay coins`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
        `"reqSigs": n, (numeric) the number of required signatures`
        `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`
        `"addresses": [ (json array of string) the bitcoin addresses associated with this output`
          `"bitcoinaddress", (string) the bitcoin address`
          `...`
        `]`
      `}`
    `}, ...`
  `]`
`}`| +|Returns (verbose=1)|`{ (json object)`
  `"hex": "data", (string) hex-encoded transaction`
  `"txid": "hash", (string) the hash of the transaction`
  `"version": n, (numeric) the transaction version`
  `"locktime": n, (numeric) the transaction lock time`
  `"vin": [ (array of json objects) the transaction inputs as json objects`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "data", (string) the hex-encoded bytes of the signature script`
      `"sequence": n, (numeric) the script sequence number`
    `}`
  For non-coinbase transactions:
    `{ (json object)`
      `"txid": "hash", (string) the hash of the origin transaction`
      `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`
      `"scriptSig": { (json object) the signature script used to redeem the origin transaction`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
      `}`
      `"sequence": n, (numeric) the script sequence number`
    `}, ...`
  `]`
  `"vout": [ (array of json objects) the transaction outputs as json objects`
    `{ (json object)`
      `"value": n, (numeric) the value in BTC`
      `"n": n, (numeric) the index of this transaction output`
      `"scriptPubKey": { (json object) the public key script used to pay coins`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
        `"reqSigs": n, (numeric) the number of required signatures`
        `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`
        `"addresses": [ (json array of string) the bitcoin addresses associated with this output`
          `"bitcoinaddress", (string) the bitcoin address`
          `...`
        `]`
      `}`
    `}, ...`
  `]`
`}`| |Example Return (verbose=0)|`"010000000104be666c7053ef26c6110597dad1c1e81b5e6be53d17a8b9d0b34772054bac60000000`
`008c493046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8f`
`022100fbce8d84fcf2839127605818ac6c3e7a1531ebc69277c504599289fb1e9058df0141045a33`
`76eeb85e494330b03c1791619d53327441002832f4bd618fd9efa9e644d242d5e1145cb9c2f71965`
`656e276633d4ff1a6db5e7153a0a9042745178ebe0f5ffffffff0280841e00000000001976a91406`
`f1b6703d3f56427bfcfd372f952d50d04b64bd88ac4dd52700000000001976a9146b63f291c295ee`
`abd9aee6be193ab2d019e7ea7088ac00000000`
**Newlines added for display purposes. The actual return does not contain newlines.**| |Example Return (verbose=1)|`{`
  `"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000f...",`
  `"txid": "90743aad855880e517270550d2a881627d84db5265142fd1e7fb7add38b08be9",`
  `"version": 1,`
  `"locktime": 0,`
  `"vin": [`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "03708203062f503253482f04066d605108f800080100000ea2122f6f7a636f696e4065757374726174756d2f",`
      `"sequence": 0,`
    `}`
  For non-coinbase transactions:
    `{`
      `"txid": "60ac4b057247b3d0b9a8173de56b5e1be8c1d1da970511c626ef53706c66be04",`
      `"vout": 0,`
      `"scriptSig": {`
        `"asm": "3046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8f0...",`
        `"hex": "493046022100cb42f8df44eca83dd0a727988dcde9384953e830b1f8004d57485e2ede1b9c8...",`
      `}`
      `"sequence": 4294967295,`
    `}`
  `]`
  `"vout": [`
    `{`
      `"value": 25.1394,`
      `"n": 0,`
      `"scriptPubKey": {`
        `"asm": "OP_DUP OP_HASH160 ea132286328cfc819457b9dec386c4b5c84faa5c OP_EQUALVERIFY OP_CHECKSIG",`
        `"hex": "76a914ea132286328cfc819457b9dec386c4b5c84faa5c88ac",`
        `"reqSigs": 1,`
        `"type": "pubkeyhash"`
        `"addresses": [`
          `"1NLg3QJMsMQGM5KEUaEu5ADDmKQSLHwmyh",`
        `]`
      `}`
    `}`
  `]`
`}`| [Return to Overview](#MethodOverview)
@@ -632,12 +632,12 @@ The following is an overview of the RPC methods which are implemented by btcd, b | | | |---|---| |Method|searchrawtransactions| -|Parameters|1. address (string, required) - bitcoin address
2. verbose (int, optional, default=true) - specifies the transaction is returned as a JSON object instead of hex-encoded string
3. skip (int, optional, default=0) - the number of leading transactions to leave out of the final response
4. count (int, optional, default=100) - the maximum number of transactions to return| +|Parameters|1. address (string, required) - bitcoin address
2. verbose (int, optional, default=true) - specifies the transaction is returned as a JSON object instead of hex-encoded string
3. skip (int, optional, default=0) - the number of leading transactions to leave out of the final response
4. count (int, optional, default=100) - the maximum number of transactions to return
5. (vinextra int, optional, default=0) - Specify that extra data from previous output will be returned in vin| |Description|Returns raw data for transactions involving the passed address. Returned transactions are pulled from both the database, and transactions currently in the mempool. Transactions pulled from the mempool will have the `"confirmations"` field set to 0. Usage of this RPC requires the optional `--addrindex` flag to be activated, otherwise all responses will simply return with an error stating the address index has not yet been built up. Similarly, until the address index has caught up with the current best height, all requests will return an error response in order to avoid serving stale data.| |Returns (verbose=0)|`[ (json array of strings)`
   `"serializedtx", ... hex-encoded bytes of the serialized transaction`
`]` | -|Returns (verbose=1)|`[ (array of json objects)`
   `{ (json object)`
  `"hex": "data", (string) hex-encoded transaction`
  `"txid": "hash", (string) the hash of the transaction`
  `"version": n, (numeric) the transaction version`
  `"locktime": n, (numeric) the transaction lock time`
  `"vin": [ (array of json objects) the transaction inputs as json objects`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "data", (string) the hex-dencoded bytes of the signature script`
      `"sequence": n, (numeric) the script sequence number`
    `}`
  For non-coinbase transactions:
    `{ (json object)`
      `"txid": "hash", (string) the hash of the origin transaction`
      `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`
      `"scriptSig": { (json object) the signature script used to redeem the origin transaction`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
      `}`
      `"sequence": n, (numeric) the script sequence number`
    `}, ...`
  `]`
  `"vout": [ (array of json objects) the transaction outputs as json objects`
    `{ (json object)`
      `"value": n, (numeric) the value in BTC`
      `"n": n, (numeric) the index of this transaction output`
      `"scriptPubKey": { (json object) the public key script used to pay coins`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
        `"reqSigs": n, (numeric) the number of required signatures`
        `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`
        `"addresses": [ (json array of string) the bitcoin addresses associated with this output`
          `"address", (string) the bitcoin address`
          `...`
        `]`
      `}`
    `}, ...`
   `]`
   `"blockhash":"hash" Hash of the block the transaction is part of.`
   `"confirmations":n, Number of numeric confirmations of block.`
   `"time":t, Transaction time in seconds since the epoch.`
   `"blocktime":t, Block time in seconds since the epoch.`
`},...`
`]`| +|Returns (verbose=1)|`[ (array of json objects)`
   `{ (json object)`
  `"hex": "data", (string) hex-encoded transaction`
  `"txid": "hash", (string) the hash of the transaction`
  `"version": n, (numeric) the transaction version`
  `"locktime": n, (numeric) the transaction lock time`
  `"vin": [ (array of json objects) the transaction inputs as json objects`
  For coinbase transactions:
    `{ (json object)`
      `"coinbase": "data", (string) the hex-encoded bytes of the signature script`
      `"sequence": n, (numeric) the script sequence number`
    `}`
  For non-coinbase transactions:
    `{ (json object)`
      `"txid": "hash", (string) the hash of the origin transaction`
      `"vout": n, (numeric) the index of the output being redeemed from the origin transaction`
      `"scriptSig": { (json object) the signature script used to redeem the origin transaction`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
      `}`
      `"prevOut": { (json object) Data from the origin transaction output with index vout.`
        `"addresses": ["value",...], (array of string) previous output addresses`
        `"value": n.nnn, (numeric) previous output value`
      `}`
      `"sequence": n, (numeric) the script sequence number`
    `}, ...`
  `]`
  `"vout": [ (array of json objects) the transaction outputs as json objects`
    `{ (json object)`
      `"value": n, (numeric) the value in BTC`
      `"n": n, (numeric) the index of this transaction output`
      `"scriptPubKey": { (json object) the public key script used to pay coins`
        `"asm": "asm", (string) disassembly of the script`
        `"hex": "data", (string) hex-encoded bytes of the script`
        `"reqSigs": n, (numeric) the number of required signatures`
        `"type": "scripttype" (string) the type of the script (e.g. 'pubkeyhash')`
        `"addresses": [ (json array of string) the bitcoin addresses associated with this output`
          `"address", (string) the bitcoin address`
          `...`
        `]`
      `}`
    `}, ...`
   `]`
   `"blockhash":"hash" Hash of the block the transaction is part of.`
   `"confirmations":n, Number of numeric confirmations of block.`
   `"time":t, Transaction time in seconds since the epoch.`
   `"blocktime":t, Block time in seconds since the epoch.`
`},...`
`]`| [Return to Overview](#ExtMethodOverview)
- + *** diff --git a/mempool.go b/mempool.go index ddbd66aa..9ba9fb46 100644 --- a/mempool.go +++ b/mempool.go @@ -749,7 +749,7 @@ func (mp *txMemPool) addTransactionToAddrIndex(tx *btcutil.Tx) error { // // This function MUST be called with the mempool lock held (for reads). func (mp *txMemPool) fetchReferencedOutputScripts(tx *btcutil.Tx) ([][]byte, error) { - txStore, err := mp.fetchInputTransactions(tx) + txStore, err := mp.fetchInputTransactions(tx, false) if err != nil || len(txStore) == 0 { return nil, err } @@ -919,8 +919,8 @@ func (mp *txMemPool) checkPoolDoubleSpend(tx *btcutil.Tx) error { // fetch any missing inputs from the transaction pool. // // This function MUST be called with the mempool lock held (for reads). -func (mp *txMemPool) fetchInputTransactions(tx *btcutil.Tx) (blockchain.TxStore, error) { - txStore, err := mp.server.blockManager.blockChain.FetchTransactionStore(tx) +func (mp *txMemPool) fetchInputTransactions(tx *btcutil.Tx, includeSpent bool) (blockchain.TxStore, error) { + txStore, err := mp.server.blockManager.blockChain.FetchTransactionStore(tx, includeSpent) if err != nil { return nil, err } @@ -1069,7 +1069,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit boo // transaction. This function also attempts to fetch the transaction // itself to be used for detecting a duplicate transaction without // needing to do a separate lookup. - txStore, err := mp.fetchInputTransactions(tx) + txStore, err := mp.fetchInputTransactions(tx, false) if err != nil { if cerr, ok := err.(blockchain.RuleError); ok { return nil, chainRuleError(cerr) diff --git a/rpcserver.go b/rpcserver.go index d095a0b3..24e2b69d 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -637,6 +637,79 @@ func createVinList(mtx *wire.MsgTx) []btcjson.Vin { return vinList } +// createVinList returns a slice of JSON objects for the inputs of the passed +// transaction. +func createVinListPrevOut(s *rpcServer, mtx *wire.MsgTx, chainParams *chaincfg.Params, vinExtra int) []btcjson.VinPrevOut { + + // Coinbase transactions only have a single txin by definition. + vinList := make([]btcjson.VinPrevOut, len(mtx.TxIn)) + if blockchain.IsCoinBaseTx(mtx) { + txIn := mtx.TxIn[0] + vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript) + vinList[0].Sequence = txIn.Sequence + return vinList + } + + // Lookup all of the referenced transactions needed to populate the + // previous output information if requested. + var txStore blockchain.TxStore + if vinExtra != 0 { + tx := btcutil.NewTx(mtx) + txStoreNew, err := s.server.txMemPool.fetchInputTransactions(tx, true) + if err == nil { + txStore = txStoreNew + } + } + + for i, txIn := range mtx.TxIn { + // The disassembled string will contain [error] inline + // if the script doesn't fully parse, so ignore the + // error here. + disbuf, _ := txscript.DisasmString(txIn.SignatureScript) + + vinEntry := &vinList[i] + vinEntry.Txid = txIn.PreviousOutPoint.Hash.String() + vinEntry.Vout = txIn.PreviousOutPoint.Index + vinEntry.Sequence = txIn.Sequence + vinEntry.ScriptSig = &btcjson.ScriptSig{ + Asm: disbuf, + Hex: hex.EncodeToString(txIn.SignatureScript), + } + + // Only populate previous output information if requested and + // available. + if vinExtra == 0 || len(txStore) == 0 { + continue + } + txData := txStore[txIn.PreviousOutPoint.Hash] + if txData == nil { + continue + } + + originTxOut := txData.Tx.MsgTx().TxOut[txIn.PreviousOutPoint.Index] + + // Ignore the error here since an error means the script + // couldn't parse and there is no additional information about + // it anyways. + var strAddrs []string + _, addrs, _, _ := txscript.ExtractPkScriptAddrs( + originTxOut.PkScript, chainParams) + if addrs != nil { + strAddrs = make([]string, len(addrs)) + for j, addr := range addrs { + strAddrs[j] = addr.EncodeAddress() + } + } + + vinEntry.PrevOut = &btcjson.PrevOut{ + Addresses: strAddrs, + Value: btcutil.Amount(originTxOut.Value).ToBTC(), + } + } + + return vinList +} + // createVoutList returns a slice of JSON objects for the outputs of the passed // transaction. func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params) []btcjson.Vout { @@ -672,6 +745,37 @@ func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params) []btcjson.Vou return voutList } +// createSearchRawTransactionsResult converts the passed transaction and associated parameters +// to a raw transaction JSON object, possibly with vin.PrevOut section. +func createSearchRawTransactionsResult(s *rpcServer, chainParams *chaincfg.Params, mtx *wire.MsgTx, + txHash string, blkHeader *wire.BlockHeader, blkHash string, + blkHeight int32, chainHeight int32, vinExtra int) (*btcjson.SearchRawTransactionsResult, error) { + + mtxHex, err := messageToHex(mtx) + if err != nil { + return nil, err + } + + txReply := &btcjson.SearchRawTransactionsResult{ + Hex: mtxHex, + Txid: txHash, + Vout: createVoutList(mtx, chainParams), + Vin: createVinListPrevOut(s, mtx, chainParams, vinExtra), + Version: mtx.Version, + LockTime: mtx.LockTime, + } + + if blkHeader != nil { + // This is not a typo, they are identical in bitcoind as well. + txReply.Time = blkHeader.Timestamp.Unix() + txReply.Blocktime = blkHeader.Timestamp.Unix() + txReply.BlockHash = blkHash + txReply.Confirmations = uint64(1 + chainHeight - blkHeight) + } + + return txReply, nil +} + // createTxRawResult converts the passed transaction and associated parameters // to a raw transaction JSON object. func createTxRawResult(chainParams *chaincfg.Params, mtx *wire.MsgTx, @@ -2198,7 +2302,7 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{ // the tx's inputs. Use zeros if one or more of the // input transactions can't be found for some reason. var startingPriority, currentPriority float64 - inputTxs, err := mp.fetchInputTransactions(desc.Tx) + inputTxs, err := mp.fetchInputTransactions(desc.Tx, false) if err == nil { startingPriority = desc.StartingPriority(inputTxs) currentPriority = desc.CurrentPriority(inputTxs, @@ -2935,7 +3039,7 @@ func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan return nil, internalRPCError(err.Error(), context) } - rawTxns := make([]btcjson.TxRawResult, len(addressTxs), len(addressTxs)) + rawTxns := make([]btcjson.SearchRawTransactionsResult, len(addressTxs), len(addressTxs)) for i, txReply := range addressTxs { txHash := txReply.Sha.String() mtx := txReply.Tx @@ -2960,8 +3064,8 @@ func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan blkHeight = txReply.Height } - rawTxn, err := createTxRawResult(s.server.chainParams, mtx, - txHash, blkHeader, blkHashStr, blkHeight, maxIdx) + rawTxn, err := createSearchRawTransactionsResult(s, s.server.chainParams, mtx, + txHash, blkHeader, blkHashStr, blkHeight, maxIdx, *c.VinExtra) if err != nil { return nil, err } diff --git a/rpcserverhelp.go b/rpcserverhelp.go index 368c2b11..bf8c7519 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -58,6 +58,18 @@ var helpDescsEnUS = map[string]string{ "scriptsig-asm": "Disassembly of the script", "scriptsig-hex": "Hex-encoded bytes of the script", + // PrevOut help. + "prevout-addresses": "previous output addresses", + "prevout-value": "previous output value", + + // VinPrevOut help. + "vinprevout-coinbase": "The hex-encoded bytes of the signature script (coinbase txns only)", + "vinprevout-txid": "The hash of the origin transaction (non-coinbase txns only)", + "vinprevout-vout": "The index of the output being redeemed from the origin transaction (non-coinbase txns only)", + "vinprevout-scriptSig": "The signature script used to redeem the origin transaction as a JSON object (non-coinbase txns only)", + "vinprevout-prevOut": "Data from the origin transaction output with index vout.", + "vinprevout-sequence": "The script sequence number", + // Vin help. "vin-coinbase": "The hex-encoded bytes of the signature script (coinbase txns only)", "vin-txid": "The hash of the origin transaction (non-coinbase txns only)", @@ -155,6 +167,18 @@ var helpDescsEnUS = map[string]string{ "txrawresult-time": "Transaction time in seconds since 1 Jan 1970 GMT", "txrawresult-blocktime": "Block time in seconds since the 1 Jan 1970 GMT", + // SearchRawTransactionsResult help. + "searchrawtransactionsresult-hex": "Hex-encoded transaction", + "searchrawtransactionsresult-txid": "The hash of the transaction", + "searchrawtransactionsresult-version": "The transaction version", + "searchrawtransactionsresult-locktime": "The transaction lock time", + "searchrawtransactionsresult-vin": "The transaction inputs as JSON objects", + "searchrawtransactionsresult-vout": "The transaction outputs as JSON objects", + "searchrawtransactionsresult-blockhash": "Hash of the block the transaction is part of", + "searchrawtransactionsresult-confirmations": "Number of confirmations of the block", + "searchrawtransactionsresult-time": "Transaction time in seconds since 1 Jan 1970 GMT", + "searchrawtransactionsresult-blocktime": "Block time in seconds since the 1 Jan 1970 GMT", + // GetBlockVerboseResult help. "getblockverboseresult-hash": "The hash of the block (same as provided)", "getblockverboseresult-confirmations": "The number of confirmations", @@ -441,10 +465,11 @@ var helpDescsEnUS = map[string]string{ "Similarly, until the address index has caught up with the current best height, all requests will return an error response in order to avoid serving stale data.", "searchrawtransactions-address": "The Bitcoin address to search for", "searchrawtransactions-verbose": "Specifies the transaction is returned as a JSON object instead of hex-encoded string", - "searchrawtransactions-skip": "The number of leading transactions to leave out of the final response", - "searchrawtransactions-count": "The maximum number of transactions to return", "searchrawtransactions--condition0": "verbose=0", "searchrawtransactions--condition1": "verbose=1", + "searchrawtransactions-skip": "The number of leading transactions to leave out of the final response", + "searchrawtransactions-count": "The maximum number of transactions to return", + "searchrawtransactions-vinextra": "Specify that extra data from previous output will be returned in vin", "searchrawtransactions--result0": "Hex-encoded serialized transaction", // SendRawTransactionCmd help. @@ -581,7 +606,7 @@ var rpcResultTypes = map[string][]interface{}{ "node": nil, "help": []interface{}{(*string)(nil), (*string)(nil)}, "ping": nil, - "searchrawtransactions": []interface{}{(*string)(nil), (*[]btcjson.TxRawResult)(nil)}, + "searchrawtransactions": []interface{}{(*string)(nil), (*[]btcjson.SearchRawTransactionsResult)(nil)}, "sendrawtransaction": []interface{}{(*string)(nil)}, "setgenerate": nil, "stop": []interface{}{(*string)(nil)},