Add transaction accelerator (prioritisetransaction UI + API)

Full-stack feature for boosting transactions via bitcoind's
prioritisetransaction RPC:

Backend:
- New accelerator package with Accelerate, Cancel, MaxFeerate, List,
  and background Cleanup goroutine (removes confirmed/dropped txs)
- RPC wrappers: GetMempoolEntry, PrioritiseTransaction,
  GetRawMempoolVerbose, IsRPCError helpers
- SQLite boosted_txs table for persistence across restarts
- Revenue impact measured via getblocktemplate before/after comparison
- Hard cap at 2000 sat/vB; MaxFeerate uses fees.base (not modified)
  to ignore our own prior priority adjustments

Frontend:
- Rocket icon in header with 10s jiggle animation
- AcceleratorPage with flame-gradient border, txid input, feerate
  input, "Prioritize above all" button with spinner, boost list with
  cancel buttons, and dismissible error/success messages
- Hash-based routing (#/accelerator)
- SharesBar font size bump
This commit is contained in:
satoshi
2026-05-11 03:54:35 +03:00
parent 72890d900b
commit dc7da6ab19
12 changed files with 1247 additions and 25 deletions
+70
View File
@@ -276,3 +276,73 @@ func (c *RPC) GetBlockTemplate(ctx context.Context) (*BlockTemplate, error) {
}
return &out, nil
}
// ---- mempool / priority methods ----
// MempoolEntryFees mirrors the "fees" object inside getmempoolentry.
type MempoolEntryFees struct {
Base float64 `json:"base"`
Modified float64 `json:"modified"`
Ancestor float64 `json:"ancestor"`
Descendant float64 `json:"descendant"`
}
// MempoolEntry is the result of getmempoolentry <txid>.
type MempoolEntry struct {
Vsize int64 `json:"vsize"`
Fees MempoolEntryFees `json:"fees"`
}
// GetMempoolEntry returns the mempool entry for a transaction.
// Returns rpcError code -5 if the tx is not in the mempool.
func (c *RPC) GetMempoolEntry(ctx context.Context, txid string) (*MempoolEntry, error) {
var out MempoolEntry
if err := c.Call(ctx, "getmempoolentry", []any{txid}, &out); err != nil {
return nil, err
}
return &out, nil
}
// PrioritiseTransaction adjusts a transaction's apparent fee for block
// template selection. feeDelta is in satoshis (positive = boost,
// negative = de-prioritise).
func (c *RPC) PrioritiseTransaction(ctx context.Context, txid string, feeDelta int64) error {
var ok bool
if err := c.Call(ctx, "prioritisetransaction", []any{txid, 0, feeDelta}, &ok); err != nil {
return err
}
return nil
}
// RawMempoolEntry is the verbose form of a mempool entry from getrawmempool true.
type RawMempoolEntry struct {
Vsize int64 `json:"vsize"`
Fees MempoolEntryFees `json:"fees"`
}
// GetRawMempoolVerbose returns all mempool transactions with their details.
func (c *RPC) GetRawMempoolVerbose(ctx context.Context) (map[string]RawMempoolEntry, error) {
var out map[string]RawMempoolEntry
if err := c.Call(ctx, "getrawmempool", []any{true}, &out); err != nil {
return nil, err
}
return out, nil
}
// IsRPCError checks if err is an rpcError with a specific code.
func IsRPCError(err error, code int) bool {
var rerr *rpcError
if errors.As(err, &rerr) {
return rerr.Code == code
}
return false
}
// RPCErrorMessage extracts the message from an rpcError, or the error string.
func RPCErrorMessage(err error) string {
var rerr *rpcError
if errors.As(err, &rerr) {
return rerr.Message
}
return err.Error()
}