Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public IActionResult GetAddress([MinLength(4)][MaxLength(100)] string address)

[HttpGet]
[Route("address/{address}/txs")]
public async Task<IActionResult> GetAddressTransactions(string address)
public async Task<IActionResult> GetAddressTransactions(string address, string? after_txid = null)
{
var transactions = storage.AddressHistory(address, null, 50).Items.Select(t => t.TransactionHash).ToList();
List<string> transactions = storage.GetAddressHistory(address,25, 50, after_txid).Items.Select(t => t.TransactionHash).ToList();
List<MempoolTransaction> txns = await storage.GetMempoolTransactionListAsync(transactions);
return Ok(JsonSerializer.Serialize(txns, serializeOption));
}
Expand Down
2 changes: 2 additions & 0 deletions src/Blockcore.Indexer.Core/Storage/IStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Task<List<QueryAddressBalance>> QuickBalancesLookupForAddressesWithHistoryCheckA

QueryResult<QueryAddressItem> AddressHistory(string address, int? offset, int limit);

QueryResult<QueryAddressItem> GetAddressHistory(string address, int limit, int limitMempool, string after_txid = null);

Task<List<MempoolTransaction>> GetMempoolTransactionListAsync(List<string> txids);

QueryResult<QueryMempoolTransactionHashes> GetMemoryTransactionsSlim(int offset, int limit);
Expand Down
155 changes: 152 additions & 3 deletions src/Blockcore.Indexer.Core/Storage/Mongo/MongoData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -766,10 +766,87 @@ public QueryResult<QueryAddressItem> AddressHistory(string address, int? offset,
Total = total
};
}
public QueryResult<QueryAddressItem> GetAddressHistory(string address, int limit, int limitMempool, string after_txid = null)
{
AddressComputedTable addressComputedTable = ComputeAddressBalance(address);
var transaction = after_txid != null ? GetTransaction(after_txid) : new()
{
BlockIndex = 0
};
if (transaction == null)
{
return new QueryResult<QueryAddressItem>
{
Items = Enumerable.Empty<QueryAddressItem>(),
Offset = -1,
Limit = limit,
Total = 0
};
}
IQueryable<AddressHistoryComputedTable> filter = mongoDb.AddressHistoryComputedTable.AsQueryable()
.Where(t => t.Address == address);

SyncBlockInfo storeTip = globalState.StoreTip;
if (storeTip == null)
{
// this can happen if node is in the middle of reorg

return new QueryResult<QueryAddressItem>
{
Items = Enumerable.Empty<QueryAddressItem>(),
Offset = 0,
Limit = limit,
Total = 0
};
}
;

filter = filter.OrderBy(s => s.BlockIndex);
var list = filter.Where(w => w.BlockIndex >= transaction.BlockIndex).Take(limit).ToList();

// Loop all transaction IDs and get the transaction object.
IEnumerable<QueryAddressItem> transactions = list.Select(item => new QueryAddressItem
{
BlockIndex = item.BlockIndex,
Value = item.AmountInOutputs - item.AmountInInputs,
EntryType = item.EntryType,
TransactionHash = item.TransactionId,
Confirmations = storeTip.BlockIndex + 1 - item.BlockIndex
});

IEnumerable<QueryAddressItem> mempoolTransactions = null;

List<MapMempoolAddressBag> mempoolAddressBag = MempoolBalance(address);

mempoolTransactions = mempoolAddressBag.Select(item => new QueryAddressItem
{
TransactionHash = item.Mempool.TransactionId,
BlockIndex = 0,
Value = item.AmountInOutputs - item.AmountInInputs,
EntryType = item.AmountInOutputs > item.AmountInInputs ? "receive" : "send",
}).Take(limitMempool);

List<QueryAddressItem> allTransactions = new();
if (mempoolTransactions != null)
allTransactions.AddRange(mempoolTransactions);

allTransactions.AddRange(transactions);

return new QueryResult<QueryAddressItem>
{
Items = allTransactions,
Offset = -1,
Limit = limit,
Total = list.Count(),
};
}
public async Task<List<MempoolTransaction>> GetMempoolTransactionListAsync(List<string> txids)
{
FilterDefinition<TransactionBlockTable> filter = Builders<TransactionBlockTable>.Filter.In(info => info.TransactionId, txids);
FilterDefinition<MempoolTable> filter_mempool = Builders<MempoolTable>.Filter.In(t => t.TransactionId, txids);
var mempool_trxsCursor = await mongoDb.Mempool.FindAsync(filter_mempool);
var mempool_trxs = await mempool_trxsCursor.ToListAsync();

var trxsCursor = await mongoDb.TransactionBlockTable.FindAsync(filter);
var trxs = await trxsCursor.ToListAsync();

Expand All @@ -793,7 +870,10 @@ public async Task<List<MempoolTransaction>> GetMempoolTransactionListAsync(List<
var tasks = await Task.WhenAll(transactions.Select(async (transaction, index) =>
{
var blk = blks[index];
var outputsTasks = transactionItemsList[index].Inputs.Select(async input => await GetTransactionOutputAsync(input.PreviousTransactionHash, input.PreviousIndex));
var outputsTasks = transactionItemsList[index].Inputs.Select(async input =>
CheckCoinbaseInput(input.PreviousTransactionHash)
? await GetTransactionOutputAsync(input.PreviousTransactionHash, input.PreviousIndex)
: new OutputTable());
var outputs = await Task.WhenAll(outputsTasks);

return new MempoolTransaction
Expand All @@ -814,10 +894,11 @@ public async Task<List<MempoolTransaction>> GetMempoolTransactionListAsync(List<
Vin = transactionItemsList[index].Inputs.Select((input, inputIndex) =>
{
OutputTable output = outputs[inputIndex];
var coinbaseCheck = CheckCoinbaseInput(input.PreviousTransactionHash);
return new Vin()
{
IsCoinbase = input.InputCoinBase != null,
Prevout = new PrevOut()
IsCoinbase = coinbaseCheck,
Prevout = coinbaseCheck ? null : new PrevOut()
{
Value = output.Value,
Scriptpubkey = output.ScriptHex,
Expand All @@ -843,11 +924,79 @@ public async Task<List<MempoolTransaction>> GetMempoolTransactionListAsync(List<
ScriptpubkeyAsm = null,
}).ToList(),
};
}));
var transactionList = tasks.ToList();
var mempoolList = await MapMempoolTableToMempoolTransaction(mempool_trxs);
transactionList.AddRange(mempoolList);
return transactionList;
}
private async Task<List<MempoolTransaction>> MapMempoolTableToMempoolTransaction(List<MempoolTable> transactions)
{
var tasks = await Task.WhenAll(transactions.Select(async (transaction, index) =>
{
var outputsTasks = transaction.Inputs.Select(async input =>
CheckCoinbaseInput(input.Outpoint.TransactionId)
? await GetTransactionOutputAsync(input.Outpoint.TransactionId, input.Outpoint.OutputIndex)
: new OutputTable());
var outputs = await Task.WhenAll(outputsTasks);
// The unavailable fields are set to -1 temporarily
return new MempoolTransaction
{
Txid = transaction.TransactionId,
Version = -1,
Locktime = -1,
Size = -1,
Weight = -1,
Fee = -1,
Status = new()
{
Confirmed = false,
BlockHeight = -1,
BlockHash = null,
BlockTime = -1,
},
Vin = transaction.Inputs.Select((input, inputIndex) =>
{
OutputTable output = outputs[inputIndex];
var coinbaseCheck = CheckCoinbaseInput(input.Outpoint.TransactionId);
return new Vin()
{
IsCoinbase = coinbaseCheck,
Prevout = coinbaseCheck ? null : new PrevOut()
{
Value = output.Value,
Scriptpubkey = output.ScriptHex,
ScriptpubkeyAddress = output.Address,
ScriptpubkeyAsm = null,
ScriptpubkeyType = null
},
Scriptsig = null,
Asm = null,
Sequence = -1,
Txid = input.Outpoint.TransactionId,
Vout = input.Outpoint.OutputIndex,
Witness = null,
InnserRedeemscriptAsm = null,
InnerWitnessscriptAsm = null
};
}).ToList(),
Vout = transaction.Outputs.Select(output => new PrevOut()
{
Value = output.Value,
Scriptpubkey = output.ScriptHex,
ScriptpubkeyAddress = output.Address,
ScriptpubkeyAsm = null,
}).ToList(),
};
}
));
return tasks.ToList();
}

private bool CheckCoinbaseInput(string inputPrevHash)
{
return inputPrevHash == "0000000000000000000000000000000000000000000000000000000000000000";
}
private long TryParseSequenceLock(string sequenceLock)
{
if (long.TryParse(sequenceLock, out long result))
Expand Down
Loading