mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-20 13:37:28 +02:00
QT: Carbon copy 0.14-based transaction accounting
This commit is contained in:
parent
6333166320
commit
003fb1933e
3 changed files with 120 additions and 158 deletions
|
|
@ -79,6 +79,7 @@ static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LO
|
||||||
result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
|
result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
|
||||||
IsMine(wallet, result.txout_address.back()) :
|
IsMine(wallet, result.txout_address.back()) :
|
||||||
ISMINE_NO);
|
ISMINE_NO);
|
||||||
|
result.txout_is_change.push_back(wallet.IsChange(txout));
|
||||||
}
|
}
|
||||||
// ELEMENTS: Retrieve unblinded information about outputs
|
// ELEMENTS: Retrieve unblinded information about outputs
|
||||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
|
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
|
||||||
|
|
|
||||||
|
|
@ -337,6 +337,7 @@ struct WalletTx
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
std::vector<isminetype> txin_is_mine;
|
std::vector<isminetype> txin_is_mine;
|
||||||
std::vector<isminetype> txout_is_mine;
|
std::vector<isminetype> txout_is_mine;
|
||||||
|
std::vector<bool> txout_is_change;
|
||||||
std::vector<CTxDestination> txout_address;
|
std::vector<CTxDestination> txout_address;
|
||||||
std::vector<isminetype> txout_address_is_mine;
|
std::vector<isminetype> txout_address_is_mine;
|
||||||
std::vector<CAmount> txout_amounts;
|
std::vector<CAmount> txout_amounts;
|
||||||
|
|
|
||||||
|
|
@ -36,16 +36,116 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||||
uint256 hash = wtx.tx->GetHash();
|
uint256 hash = wtx.tx->GetHash();
|
||||||
std::map<std::string, std::string> mapValue = wtx.value_map;
|
std::map<std::string, std::string> mapValue = wtx.value_map;
|
||||||
|
|
||||||
if (nNet > 0 || wtx.is_coinbase)
|
bool involvesWatchAddress = false;
|
||||||
|
isminetype fAllFromMe = ISMINE_SPENDABLE;
|
||||||
|
bool any_from_me = false;
|
||||||
|
std::set<CAsset> assets_issued_to_me_only;
|
||||||
|
if (wtx.is_coinbase) {
|
||||||
|
fAllFromMe = ISMINE_NO;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
//
|
CAmountMap assets_received_by_me_only;
|
||||||
// Credit
|
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
||||||
//
|
{
|
||||||
|
if (wtx.tx->vout[i].IsFee()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const CAsset& asset = wtx.txout_assets[i];
|
||||||
|
if (assets_received_by_me_only.count(asset) && assets_received_by_me_only.at(asset) < 0) {
|
||||||
|
// Already known to be received by not-me
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
isminetype mine = wtx.txout_address_is_mine[i];
|
||||||
|
if (!mine) {
|
||||||
|
assets_received_by_me_only[asset] = -1;
|
||||||
|
} else {
|
||||||
|
assets_received_by_me_only[asset] += wtx.txout_amounts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
any_from_me = false;
|
||||||
|
for (size_t i = 0; i < wtx.tx->vin.size(); ++i)
|
||||||
|
{
|
||||||
|
/* Issuance detection */
|
||||||
|
isminetype mine = wtx.txin_is_mine[i];
|
||||||
|
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
|
||||||
|
if(fAllFromMe > mine) fAllFromMe = mine;
|
||||||
|
if (mine) any_from_me = true;
|
||||||
|
CAmountMap assets;
|
||||||
|
assets[wtx.txin_issuance_asset[i]] = wtx.txin_issuance_asset_amount[i];
|
||||||
|
assets[wtx.txin_issuance_token[i]] = wtx.txin_issuance_token_amount[i];
|
||||||
|
for (const auto& asset : assets) {
|
||||||
|
if (!asset.first.IsNull()) {
|
||||||
|
if (assets_received_by_me_only.count(asset.first) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (asset.second == assets_received_by_me_only.at(asset.first)) {
|
||||||
|
// Special case: collapse the chain of issue, send, receive to just an issue
|
||||||
|
assets_issued_to_me_only.insert(asset.first);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
TransactionRecord sub(hash, nTime);
|
||||||
|
sub.involvesWatchAddress = involvesWatchAddress;
|
||||||
|
sub.asset = asset.first;
|
||||||
|
sub.amount = asset.second;
|
||||||
|
sub.type = TransactionRecord::IssuedAsset;
|
||||||
|
parts.append(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fAllFromMe || !any_from_me) {
|
||||||
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
||||||
{
|
{
|
||||||
|
const CTxOut& txout = wtx.tx->vout[i];
|
||||||
|
const CAsset& asset = wtx.txout_assets[i];
|
||||||
|
if (txout.IsFee()) {
|
||||||
|
// explicit fee; ignore
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fAllFromMe && assets_issued_to_me_only.count(asset) == 0) {
|
||||||
|
// Change is only really possible if we're the sender
|
||||||
|
// Otherwise, someone just sent bitcoins to a change address, which should be shown
|
||||||
|
|
||||||
|
if (wtx.txout_is_change[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Debit
|
||||||
|
//
|
||||||
|
TransactionRecord sub(hash, nTime);
|
||||||
|
sub.idx = i;
|
||||||
|
sub.involvesWatchAddress = involvesWatchAddress;
|
||||||
|
sub.amount = -wtx.txout_amounts[i];
|
||||||
|
sub.asset = asset;
|
||||||
|
|
||||||
|
if (!boost::get<CNoDestination>(&wtx.txout_address[i]))
|
||||||
|
{
|
||||||
|
// Sent to Bitcoin Address
|
||||||
|
sub.type = TransactionRecord::SendToAddress;
|
||||||
|
sub.address = EncodeDestination(wtx.txout_address[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Sent to IP, or other non-address transaction like OP_EVAL
|
||||||
|
sub.type = TransactionRecord::SendToOther;
|
||||||
|
sub.address = mapValue["to"];
|
||||||
|
}
|
||||||
|
parts.append(sub);
|
||||||
|
}
|
||||||
|
|
||||||
isminetype mine = wtx.txout_is_mine[i];
|
isminetype mine = wtx.txout_is_mine[i];
|
||||||
if(mine)
|
if(mine)
|
||||||
{
|
{
|
||||||
|
//
|
||||||
|
// Credit
|
||||||
|
//
|
||||||
|
|
||||||
TransactionRecord sub(hash, nTime);
|
TransactionRecord sub(hash, nTime);
|
||||||
CTxDestination address;
|
CTxDestination address;
|
||||||
sub.idx = i; // vout index
|
sub.idx = i; // vout index
|
||||||
|
|
@ -56,7 +156,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||||
// Received by Bitcoin Address
|
// Received by Bitcoin Address
|
||||||
sub.type = TransactionRecord::RecvWithAddress;
|
sub.type = TransactionRecord::RecvWithAddress;
|
||||||
sub.address = EncodeDestination(wtx.txout_address[i]);
|
sub.address = EncodeDestination(wtx.txout_address[i]);
|
||||||
sub.asset = wtx.txout_assets[i];
|
sub.asset = asset;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -71,158 +171,18 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||||
sub.type = TransactionRecord::Generated;
|
sub.type = TransactionRecord::Generated;
|
||||||
sub.asset = wtx.txout_assets[i];
|
sub.asset = wtx.txout_assets[i];
|
||||||
}
|
}
|
||||||
|
if (assets_issued_to_me_only.count(wtx.txout_assets[i])) {
|
||||||
parts.append(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool involvesWatchAddress = false;
|
|
||||||
isminetype fAllFromMe = ISMINE_SPENDABLE;
|
|
||||||
std::set<CAsset> assets_issued_to_me_only;
|
|
||||||
CAmountMap assets_received_by_me_only;
|
|
||||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
|
||||||
{
|
|
||||||
const CAsset& asset = wtx.txout_assets[i];
|
|
||||||
if (assets_received_by_me_only.count(asset) && assets_received_by_me_only.at(asset) < 0) {
|
|
||||||
// Already known to be received by not-me
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
isminetype mine = wtx.txout_address_is_mine[i];
|
|
||||||
if (!mine) {
|
|
||||||
assets_received_by_me_only[asset] = -1;
|
|
||||||
} else {
|
|
||||||
assets_received_by_me_only[asset] += wtx.txout_amounts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < wtx.tx->vin.size(); ++i)
|
|
||||||
{
|
|
||||||
isminetype mine = wtx.txin_is_mine[i];
|
|
||||||
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
|
|
||||||
if(fAllFromMe > mine) fAllFromMe = mine;
|
|
||||||
const CAsset& asset = wtx.txin_issuance_asset[i];
|
|
||||||
const CAmount& asset_amount = wtx.txin_issuance_asset_amount[i];
|
|
||||||
const CAsset& token = wtx.txin_issuance_token[i];
|
|
||||||
const CAmount& token_amount = wtx.txin_issuance_token_amount[i];
|
|
||||||
if (!asset.IsNull()) {
|
|
||||||
if (assets_received_by_me_only.count(asset) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (asset_amount == assets_received_by_me_only.at(asset)) {
|
|
||||||
// Special case: collapse the chain of issue, send, receive to just an issue
|
|
||||||
assets_issued_to_me_only.insert(asset);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionRecord sub(hash, nTime);
|
|
||||||
sub.involvesWatchAddress = involvesWatchAddress;
|
|
||||||
sub.asset = asset;
|
|
||||||
sub.amount = asset_amount;
|
|
||||||
sub.type = TransactionRecord::IssuedAsset;
|
|
||||||
parts.append(sub);
|
|
||||||
}
|
|
||||||
if (!token.IsNull()) {
|
|
||||||
if (assets_received_by_me_only.count(token) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (token_amount == assets_received_by_me_only.at(asset)) {
|
|
||||||
// Special case: collapse the chain of issue, send, receive to just an issue
|
|
||||||
assets_issued_to_me_only.insert(asset);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionRecord sub(hash, nTime);
|
|
||||||
sub.involvesWatchAddress = involvesWatchAddress;
|
|
||||||
sub.asset = token;
|
|
||||||
sub.amount = token_amount;
|
|
||||||
sub.type = TransactionRecord::IssuedAsset;
|
|
||||||
parts.append(sub);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wtx.txin_issuance_token[i].IsNull()) {
|
|
||||||
TransactionRecord sub(hash, nTime);
|
|
||||||
sub.involvesWatchAddress = involvesWatchAddress;
|
|
||||||
sub.asset = wtx.txin_issuance_token[i];
|
|
||||||
sub.amount = wtx.txin_issuance_token_amount[i];
|
|
||||||
sub.type = TransactionRecord::IssuedAsset;
|
|
||||||
parts.append(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isminetype fAllToMe = ISMINE_SPENDABLE;
|
|
||||||
for (unsigned int i = 0; i < wtx.txout_is_mine.size(); ++i) {
|
|
||||||
const isminetype mine = wtx.txout_is_mine[i];
|
|
||||||
const CTxOut txout = wtx.tx->vout[i];
|
|
||||||
if (txout.IsFee()) {
|
|
||||||
// explicit fee; ignore
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
|
|
||||||
if(fAllToMe > mine) fAllToMe = mine;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fAllFromMe && fAllToMe)
|
|
||||||
{
|
|
||||||
// Payment to self
|
|
||||||
CAmount nChange = valueFor(wtx.change, ::policyAsset);
|
|
||||||
|
|
||||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
|
|
||||||
-(nDebit - nChange) + (nCredit - nChange), ::policyAsset));
|
|
||||||
parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument
|
|
||||||
}
|
|
||||||
else if (fAllFromMe)
|
|
||||||
{
|
|
||||||
|
|
||||||
//
|
|
||||||
// Debit
|
|
||||||
//
|
|
||||||
|
|
||||||
for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++)
|
|
||||||
{
|
|
||||||
|
|
||||||
const CTxOut& txout = wtx.tx->vout[nOut];
|
|
||||||
const CAsset& asset = wtx.txout_assets[nOut];
|
|
||||||
|
|
||||||
if(wtx.txout_is_mine[nOut] || txout.IsFee())
|
|
||||||
{
|
|
||||||
// Ignore parts sent to self, as this is usually the change
|
|
||||||
// from a transaction sent back to our own address.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short-circuit when it's an issuance to self
|
|
||||||
if (assets_issued_to_me_only.count(asset) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionRecord sub(hash, nTime);
|
|
||||||
sub.idx = nOut;
|
|
||||||
sub.involvesWatchAddress = involvesWatchAddress;
|
|
||||||
sub.amount = -wtx.txout_amounts[nOut];
|
|
||||||
sub.asset = asset;
|
|
||||||
|
|
||||||
if (!boost::get<CNoDestination>(&wtx.txout_address[nOut]))
|
|
||||||
{
|
|
||||||
// Sent to Bitcoin Address
|
|
||||||
sub.type = TransactionRecord::SendToAddress;
|
|
||||||
sub.address = EncodeDestination(wtx.txout_address[nOut]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Sent to IP, or other non-address transaction like OP_EVAL
|
|
||||||
sub.type = TransactionRecord::SendToOther;
|
|
||||||
sub.address = mapValue["to"];
|
|
||||||
}
|
|
||||||
if (assets_issued_to_me_only.count(asset)) {
|
|
||||||
sub.type = TransactionRecord::IssuedAsset;
|
sub.type = TransactionRecord::IssuedAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
parts.append(sub);
|
parts.append(sub);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fAllFromMe) {
|
||||||
for (const auto& tx_fee : GetFeeMap(*wtx.tx)) {
|
for (const auto& tx_fee : GetFeeMap(*wtx.tx)) {
|
||||||
if (!tx_fee.second) continue;
|
if (!tx_fee.second) continue;
|
||||||
|
|
||||||
TransactionRecord sub(hash, nTime);
|
TransactionRecord sub(hash, nTime);
|
||||||
sub.type = TransactionRecord::Fee;
|
sub.type = TransactionRecord::Fee;
|
||||||
sub.asset = tx_fee.first;
|
sub.asset = tx_fee.first;
|
||||||
|
|
@ -230,14 +190,14 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||||
parts.append(sub);
|
parts.append(sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
//
|
{
|
||||||
// Mixed debit transaction, can't break down payees
|
//
|
||||||
//
|
// Mixed debit transaction, can't break down payees
|
||||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, CAsset()));
|
//
|
||||||
parts.last().involvesWatchAddress = involvesWatchAddress;
|
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, CAsset()));
|
||||||
}
|
parts.last().involvesWatchAddress = involvesWatchAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts;
|
return parts;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue