From 293d71e9c36389a2bfc47cdc40e802a4fcfce792 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 4 Jul 2014 17:42:45 -0700 Subject: [PATCH] Implement gettxout rpc. Use new error type defined in Roasbeef/btcjson@4dc9db809 --- rpcserver.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/rpcserver.go b/rpcserver.go index 1281a29b..18c08453 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -144,7 +144,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{ "getpeerinfo": handleGetPeerInfo, "getrawmempool": handleGetRawMempool, "getrawtransaction": handleGetRawTransaction, - "gettxout": handleUnimplemented, + "gettxout": handleGetTxOut, "getwork": handleGetWork, "help": handleHelp, "ping": handlePing, @@ -2434,6 +2434,97 @@ func reverseUint32Array(b []byte) { } } +// handleGetTxOut handles gettxout commands. +func handleGetTxOut(s *rpcServer, cmd btcjson.Cmd, closeChan <-chan struct{}) (interface{}, error) { + c := cmd.(*btcjson.GetTxOutCmd) + + // Convert the provided transaction hash hex to a ShaHash. + txSha, err := btcwire.NewShaHashFromStr(c.Txid) + if err != nil { + return nil, btcjson.Error{ + Code: btcjson.ErrInvalidParameter.Code, + Message: fmt.Sprintf("argument must be hexadecimal "+ + "string (not %q)", c.Txid), + } + } + + // If requested and the tx is available in the mempool try to fetch it from + // there, otherwise attempt to fetch from the block database. + var mtx *btcwire.MsgTx + var bestBlockSha string + var confirmations int64 + if c.IncludeMempool && s.server.txMemPool.HaveTransaction(txSha) { + tx, err := s.server.txMemPool.FetchTransaction(txSha) + if err != nil { + rpcsLog.Errorf("Error fetching tx: %v", err) + return nil, btcjson.ErrNoTxInfo + } + mtx = tx.MsgTx() + confirmations = 0 + bestBlockSha = "" + } else { + txList, err := s.server.db.FetchTxBySha(txSha) + if err != nil || len(txList) == 0 { + return nil, btcjson.ErrNoTxInfo + } + + lastTx := len(txList) - 1 + mtx = txList[lastTx].Tx + blksha := txList[lastTx].BlkSha + txHeight := txList[lastTx].Height + + _, bestHeight, err := s.server.db.NewestSha() + if err != nil { + rpcsLog.Errorf("Cannot get newest sha: %v", err) + return nil, btcjson.ErrBlockNotFound + } + + confirmations = 1 + bestHeight - txHeight + bestBlockSha = blksha.String() + } + + if c.Output < 0 || c.Output > len(mtx.TxOut)-1 { + return nil, btcjson.ErrInvalidTxVout + } + + if mtx.TxOut[c.Output] == nil { + rpcsLog.Errorf("Output index: %d, for txid: %s does not exist.", c.Output, c.Txid) + return nil, btcjson.ErrInternal + } + + // Disassemble script into single line printable format. + // The disassembled string will contain [error] inline if the script + // doesn't fully parse, so ignore the error here. + script := mtx.TxOut[c.Output].PkScript + disbuf, _ := btcscript.DisasmString(script) + + // Get further info about the script. + // Ignore the error here since an error means the script couldn't parse + // and there is no additional information about it anyways. + net := s.server.netParams + scriptClass, addrs, reqSigs, _ := btcscript.ExtractPkScriptAddrs(script, net) + addresses := make([]string, len(addrs)) + for i, addr := range addrs { + addresses[i] = addr.EncodeAddress() + } + + txOutReply := &btcjson.GetTxOutResult{ + BestBlock: bestBlockSha, + Confirmations: confirmations, + Value: btcutil.Amount(mtx.TxOut[c.Output].Value).ToUnit(btcutil.AmountBTC), + Version: mtx.Version, + ScriptPubKey: btcjson.ScriptPubKeyResult{ + Asm: disbuf, + Hex: hex.EncodeToString(script), + ReqSigs: int32(reqSigs), + Type: scriptClass.String(), + Addresses: addresses, + }, + Coinbase: btcchain.IsCoinBase(btcutil.NewTx(mtx)), + } + return txOutReply, nil +} + // handleGetWorkRequest is a helper for handleGetWork which deals with // generating and returning work to the caller. //