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:
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user