consensus/cmd/web/views/transaction-lookup.qtpl
DataHoarder 55661a12da
All checks were successful
continuous-integration/drone/push Build is passing
WIP: Bootstrap-based responsive interface, CSS only
2024-03-20 13:37:26 +01:00

187 lines
9.5 KiB
Plaintext

{% import "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/index" %}
{% import "git.gammaspectra.live/P2Pool/p2pool-observer/types" %}
{% import cmdutils "git.gammaspectra.live/P2Pool/p2pool-observer/cmd/utils" %}
{% code
type TransactionLookupPage struct {
// inherit from base page, so its' title is used in error page.
BasePage
TransactionId types.Hash
Result *cmdutils.TransactionLookupResult
Miner *index.TransactionInputQueryResultsMatch
LikelyMiner bool
MinerCount uint64
NoMinerCount uint64
OtherMinerCount uint64
MinerRatio float64
NoMinerRatio float64
OtherMinerRatio float64
TopTimestamp uint64
BottomTimestamp uint64
Positions struct {
MinerCoinbase *cmdutils.PositionChart
OtherMinerCoinbase *cmdutils.PositionChart
MinerSweep *cmdutils.PositionChart
OtherMinerSweep *cmdutils.PositionChart
NoMiner *cmdutils.PositionChart
}
}
func (p *TransactionLookupPage) Name() string {
return "transaction-lookup"
}
%}
{% func (p *TransactionLookupPage) Title() %}
{%= p.BasePage.Title() %} - Sweep Transaction Lookup
{% endfunc %}
{% func (p *TransactionLookupPage) Content() %}
<div style="text-align: center">
<form action="/transaction-lookup" method="get">
<h2>Sweep Transaction Lookup</h2>
<p>
This tool looks up inputs used in the transaction decoys and correlates it against known P2Pool outputs. It then displays the likeliness of the transaction being a <em>sweep transaction</em> by a specific p.Miner
<br/>
<small>Do note false positives can be found. However, large sweep transactions with many inputs will most likely be correct.</small>
<br/>
If available, this tool will query other P2Pool SideChains.
</p>
<p>
<a href="/sweeps">[Recent Likely Sweep Transactions]</a>
</p>
<div>
<label for="txid">Transaction Id</label><br/>
<input type="text" name="txid" id="txid" placeholder="e1c88cd5fb5538edfcb4814c286cb0af02b7800f50a86e50626014124aec2f1e" size="64" maxlength="64" class="mono" value="{% if p.TransactionId != types.ZeroHash %}{%= hex(p.Context(), p.TransactionId) %}{% endif %}"/>
</div>
<div style="margin-top: 10px">
<input type="submit" value="Lookup" style="width: 20em;"/>
</div>
</form>
</div>
{% if p.TransactionId != types.ZeroHash %}
<hr/>
<div style="text-align: center">
<h2>Transaction Lookup Result</h2>
{% if p.Miner == nil %}
<p>Not enough miner results found.</p>
{% else %}
<table class="center" style="max-width: calc(8em + 15em + 15em + 15em + 15em + 15em + 15em);">
<tr>
<td colspan="7">
<p><strong>Transaction Id:</strong> <a class="mono small" href="/t/{%= henc(p.TransactionId) %}">{%= hex(p.Context(), p.TransactionId) %}</a></p>
{% code minerAddress := p.Miner.Address.ToBase58() %}
<p><strong>Miner Payout Address:</strong> <a href="/miner/{%z= minerAddress %}"><span class="mono small">{%z= minerAddress %}</span></a></p>
<p><strong>Likely:</strong> {% if p.LikelyMiner %}Yes{% else %}Not likely{% endif %}</p>
</td>
</tr>
<tr style="line-height: 1.5;">
<td style="width: 8em"><strong>Total Inputs</strong><br/>{%d len(p.Result.Inputs) %}</td>
<td style="width: 8em"><strong>Decoys per Input</strong><br/>{%d len(p.Result.Inputs[0].Input.KeyOffsets) %}</td>
<td style="width: 15em"><strong>Unknown Inputs</strong><br/>{%dul p.NoMinerCount %} ({%f.3 p.NoMinerRatio %}%)</td>
<td style="width: 15em"><strong>Other Miner Inputs</strong><br/>{%dul p.OtherMinerCount %} ({%f.3 p.OtherMinerRatio %}%)</td>
<td style="width: 15em"><strong>Top Miner Inputs</strong><br/>{%dul p.MinerCount %} ({%f.3 p.MinerRatio %}%)</td>
<td style="width: 15em"><strong>Coinbase Inputs Amount</strong><br/>{%s monero_to_xmr(p.Miner.CoinbaseAmount) %} XMR</td>
<td style="width: 15em"><strong>Coinbase / Sweep Inputs</strong><br/>{%dul p.Miner.CoinbaseCount %} / {%dul p.Miner.SweepCount %}</td>
</tr>
<tr><th colspan="7">&nbsp;</th></tr>
<tr><th colspan="7">
<h3>Miner inputs time scale (from {%s utc_date(p.BottomTimestamp) %} to {%s utc_date(p.TopTimestamp) %})</h3>
<code class="position-chart">{%s p.Positions.MinerCoinbase.String() %}</code>
<code class="position-chart">{%s p.Positions.MinerSweep.String() %}</code>
</th></tr>
<tr><th colspan="7">
<h3>Other Miner inputs time scale (from {%s utc_date(p.BottomTimestamp) %} to {%s utc_date(p.TopTimestamp) %})</h3>
<code class="position-chart">{%s p.Positions.OtherMinerCoinbase.String() %}</code>
<code class="position-chart">{%s p.Positions.OtherMinerSweep.String() %}</code>
</th></tr>
<tr><th colspan="7">
<h3>Unknown inputs time scale (from {%s utc_date(p.BottomTimestamp) %} to {%s utc_date(p.TopTimestamp) %})</h3>
<code class="position-chart">{%s p.Positions.NoMiner.String() %}</code>
</th></tr>
<tr><th colspan="7">&nbsp;</th></tr>
<tr>
<th>Input Index</th>
<th>Input Key Image</th>
<th>Decoy Owner</th>
<th>Source Transaction</th>
<th>Global Output Index</th>
<th>Source Type</th>
<th>Source Value</th>
</tr>
{% code totalInputCount := 0 %}
{% for index, input := range p.Result.Inputs %}
{% code outputCount := 0 %}
{% for oindex, o := range input.MatchedOutputs %}
{% if o == nil %}
{% if outputCount > 0 %}
</tr><tr><td colspan="2"></td>
{% else %}
<tr>
<td>{%d index %}</td>
<td class="mono small" title="{%= hex(p.Context(), input.Input.KeyImage) %}">{%= shorten(p.Context(), input.Input.KeyImage, 10) %}</td>
{% endif %}
<td class="small">-</td>
{% code out := p.Result.Outs[totalInputCount] %}
<td title="{%= hex(p.Context(), out.TransactionId) %}" class="mono small"><a href="/t/{%= henc(out.TransactionId) %}">{%= shorten(p.Context(), out.TransactionId, 10) %}</a></td>
<td class="small">{%dul input.Input.KeyOffsets[oindex] %}</td>
<td class="small">-</td>
<td class="small">-</td>
</tr>
{% else %}
{% if outputCount > 0 %}
</tr><tr {% if o.Address.Compare(p.Miner.Address) == 0 %} class="hl-found"{% endif %}><td colspan="2"></td>
{% else %}
<tr {% if o.Address.Compare(p.Miner.Address) == 0 %} class="hl-found"{% endif %}>
<td>{%d index %}</td>
<td class="mono small" title="{%= hex(p.Context(), input.Input.KeyImage) %}">{%= shorten(p.Context(), input.Input.KeyImage, 10) %}</td>
{% endif %}
{% code
addr := o.Address.ToBase58()
%}
<td title="{%z= addr %}" class="mono small"><a href="/miner/{%z= addr %}">{%= shorten(p.Context(), addr, 10) %}</a></td>
{% if o.Coinbase != nil %}
<td title="{%= hex(p.Context(), o.Coinbase.Id) %}" class="mono small"><a href="/t/{%= henc(o.Coinbase.Id) %}">{%= shorten(p.Context(), o.Coinbase.Id, 10) %}</a> #{%dul uint64(o.Coinbase.Index) %}</td>
<td class="small">{%dul o.GlobalOutputIndex %}</td>
<td class="small" title="This input was seen as an output to a mined Monero block via P2Pool">Coinbase</td>
<td class="small mono">{%s monero_to_xmr(o.Coinbase.Value) %} XMR</td>
{% elseif o.Sweep != nil %}
<td title="{%= hex(p.Context(), o.Sweep.Id) %}" class="mono small"><a href="/transaction-lookup?txid={%= hex(p.Context(), o.Sweep.Id) %}">{%= shorten(p.Context(), o.Sweep.Id, 10) %}</a>
{% for oindex, goi := range o.Sweep.GlobalOutputIndices %}
{% if goi == o.GlobalOutputIndex %}
#{%d oindex %}/{%d len(o.Sweep.GlobalOutputIndices) %}
{% endif %}
{% endfor %}</td>
<td class="small">{%dul o.GlobalOutputIndex %}</td>
<td class="small" title="This input was seen as an output to a likely previous sweep by this miner">Previous Sweep</td>
<td class="small mono"><span title="Previous Sweep had {%d len(o.Sweep.GlobalOutputIndices) %} outputs. This value denotes the whole previous coinbase amount and will probably be wrong.">(?) {%s monero_to_xmr(o.Sweep.Value) %} XMR</span></td>
{% else %}
<td class="small">Unknown</td>
<td>{%dul o.GlobalOutputIndex %}</td>
<td class="small">-</td>
<td class="small">-</td>
{% endif %}
</tr>
{% endif %}
{% code outputCount++ %}
{% code totalInputCount++ %}
{% endfor %}
<tr><th colspan="6">&nbsp;</th></tr>
{% endfor %}
</table>
{% endif %}
</div>
{% endif %}
{% endfunc %}