Impelment the gettransaction rpc command.

Closes #44.
This commit is contained in:
Owain G. Ainsworth 2014-02-03 18:29:25 +00:00
parent 9aa27517eb
commit b978c7e059
4 changed files with 188 additions and 10 deletions

View file

@ -198,7 +198,7 @@ func (a *Account) ListSinceBlock(since, curBlockHeight int32, minconf int) ([]ma
var txInfoList []map[string]interface{}
for _, tx := range a.TxStore {
// check block number.
if since != -1 && tx.Height() <= since {
if since != -1 && tx.GetBlockHeight() <= since {
continue
}
@ -629,7 +629,7 @@ func (a *Account) TotalReceived(confirms int) (float64, error) {
}
// Tally if the appropiate number of block confirmations have passed.
if confirmed(confirms, recvtx.Height(), bs.Height) {
if confirmed(confirms, recvtx.GetBlockHeight(), bs.Height) {
totalSatoshis += recvtx.Amount
}
}

View file

@ -472,6 +472,35 @@ func (am *AccountManager) ListSinceBlock(since, curBlockHeight int32, minconf in
return txInfoList, nil
}
// accountTx represents an account/transaction pair to be used by
// GetTransaction().
type accountTx struct {
Account string
Tx tx.Tx
}
// GetTransaction returns an array of accountTx to fully represent the effect of
// a transaction on locally known wallets. If we know nothing about a
// transaction an empty array will be returned.
func (am *AccountManager) GetTransaction(txid string) []accountTx {
accumulatedTxen := []accountTx{}
for _, a := range am.AllAccounts() {
for _, t := range a.TxStore {
if t.GetTxID().String() != txid {
continue
}
accumulatedTxen = append(accumulatedTxen,
accountTx{
Account: a.name,
Tx: t.Copy(),
})
}
}
return accumulatedTxen
}
// RescanActiveAddresses begins a rescan for all active addresses for
// each account.
//

View file

@ -41,6 +41,7 @@ var rpcHandlers = map[string]cmdHandler{
"getnewaddress": GetNewAddress,
"getrawchangeaddress": GetRawChangeAddress,
"getreceivedbyaccount": GetReceivedByAccount,
"gettransaction": GetTransaction,
"importprivkey": ImportPrivKey,
"keypoolrefill": KeypoolRefill,
"listaccounts": ListAccounts,
@ -60,7 +61,6 @@ var rpcHandlers = map[string]cmdHandler{
"dumpwallet": Unimplemented,
"getblocktemplate": Unimplemented,
"getreceivedbyaddress": Unimplemented,
"gettransaction": Unimplemented,
"gettxout": Unimplemented,
"gettxoutsetinfo": Unimplemented,
"getwork": Unimplemented,
@ -782,6 +782,88 @@ func GetReceivedByAccount(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
return amt, nil
}
func GetTransaction(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
// Type assert icmd to access parameters.
cmd, ok := icmd.(*btcjson.GetTransactionCmd)
if !ok {
return nil, &btcjson.ErrInternal
}
accumulatedTxen := AcctMgr.GetTransaction(cmd.Txid)
if len(accumulatedTxen) == 0 {
return nil, &btcjson.ErrNoTxInfo
}
details := []map[string]interface{}{}
totalAmount := int64(0)
for _, e := range accumulatedTxen {
switch t := e.Tx.(type) {
case *tx.SendTx:
var amount int64
for i := range t.Receivers {
if t.Receivers[i].Change {
continue
}
amount += t.Receivers[i].Amount
}
totalAmount -= amount
details = append(details, map[string]interface{}{
"account": e.Account,
"category": "send",
// negative since it is a send
"amount": -amount,
"fee": t.Fee,
})
case *tx.RecvTx:
totalAmount += t.Amount
details = append(details, map[string]interface{}{
"account": e.Account,
// TODO(oga) We don't mine for now so there
// won't be any special coinbase types. If the
// tx is a coinbase then we should handle it
// specially with the category depending on
// whether it is an orphan or in the blockchain.
"category": "receive",
"amount": t.Amount,
"address": hex.EncodeToString(t.ReceiverHash),
})
}
}
// Generic information should be the same, so just use the first one.
first := accumulatedTxen[0]
ret := map[string]interface{}{
// "amount"
// "confirmations
"amount": totalAmount,
"txid": first.Tx.GetTxID().String(),
// TODO(oga) technically we have different time and
// timereceived depending on if a transaction was send or
// receive. We ideally should provide the correct numbers for
// both. Right now they will always be the same
"time": first.Tx.GetTime(),
"timereceived": first.Tx.GetTime(),
"details": details,
}
if first.Tx.GetBlockHeight() != -1 {
ret["blockindex"] = first.Tx.GetBlockHeight()
ret["blockhash"] = first.Tx.GetBlockHash().String()
ret["blocktime"] = first.Tx.GetBlockTime()
bs, err := GetCurBlock()
if err != nil {
return nil, &btcjson.Error{
Code: btcjson.ErrWallet.Code,
Message: err.Error(),
}
}
ret["confirmations"] = bs.Height - first.Tx.GetBlockHeight() + 1
}
// TODO(oga) if the tx is a coinbase we should set "generated" to true.
// Since we do not mine this currently is never the case.
return ret, nil
}
// ListAccounts handles a listaccounts request by returning a map of account
// names to their balances.
func ListAccounts(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {

View file

@ -108,7 +108,12 @@ type Tx interface {
io.WriterTo
ReadFromVersion(uint32, io.Reader) (int64, error)
TxInfo(string, int32, btcwire.BitcoinNet) []map[string]interface{}
Height() int32
GetBlockHeight() int32
GetBlockHash() *btcwire.ShaHash
GetBlockTime() int64
GetTime() int64
GetTxID() *btcwire.ShaHash
Copy() Tx
}
// TxStore is a slice holding RecvTx and SendTx pointers.
@ -1061,12 +1066,43 @@ func (tx *RecvTx) TxInfo(account string, curheight int32,
return []map[string]interface{}{txInfo}
}
// Height returns the current blockheight of the transaction, implementing the
// Tx interface.
func (tx *RecvTx) Height() int32 {
// GetBlockHeight returns the current blockheight of the transaction,
// implementing the Tx interface.
func (tx *RecvTx) GetBlockHeight() int32 {
return tx.BlockHeight
}
// GetBlockHash return the current blockhash of thet transaction, implementing
// the Tx interface.
func (tx *RecvTx) GetBlockHash() *btcwire.ShaHash {
return &tx.BlockHash
}
// GetBlockTime returns the current block time of the transaction, implementing
// the Tx interface.
func (tx *RecvTx) GetBlockTime() int64 {
return tx.BlockTime
}
// GetTime returns the current ID of the transaction, implementing the Tx
// interface.
func (tx *RecvTx) GetTime() int64 {
return tx.TimeReceived
}
// GetTxID returns the current ID of the transaction, implementing the Tx
// interface.
func (tx *RecvTx) GetTxID() *btcwire.ShaHash {
return &tx.TxID
}
// Copy returns a deep copy of the structure, implementing the Tx interface..
func (tx *RecvTx) Copy() Tx {
copyTx := *tx
return &copyTx
}
func (tx *SendTx) ReadFromVersion(vers uint32, r io.Reader) (n int64, err error) {
var read int64
@ -1205,8 +1241,39 @@ func (tx *SendTx) TxInfo(account string, curheight int32,
return reply
}
// Height returns the current blockheight of the transaction, implementing the
// Tx interface.
func (tx *SendTx) Height() int32 {
// GetBlockHeight returns the current blockheight of the transaction,
// implementing the Tx interface.
func (tx *SendTx) GetBlockHeight() int32 {
return tx.BlockHeight
}
// GetBlockHash return the current blockhash of thet transaction, implementing
// the Tx interface.
func (tx *SendTx) GetBlockHash() *btcwire.ShaHash {
return &tx.BlockHash
}
// GetBlockTime returns the current block time of the transaction, implementing
// the Tx interface.
func (tx *SendTx) GetBlockTime() int64 {
return tx.BlockTime
}
// GetTime returns the current ID of the transaction, implementing the Tx
// interface.
func (tx *SendTx) GetTime() int64 {
return tx.Time
}
// GetTxID returns the current ID of the transaction, implementing the Tx
// interface.
func (tx *SendTx) GetTxID() *btcwire.ShaHash {
return &tx.TxID
}
// Copy returns a deep copy of the structure, implementing the Tx interface..
func (tx *SendTx) Copy() Tx {
copyTx := *tx
return &copyTx
}