Skip to content

Commit 8c3a0f5

Browse files
committed
update for 100sats/kb fees
1 parent 60ddbeb commit 8c3a0f5

File tree

16 files changed

+184
-53
lines changed

16 files changed

+184
-53
lines changed

back/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Stage 1: Build the application
2-
FROM node:20.9.0 AS builder
2+
FROM node:20 AS builder
33
WORKDIR /app
44
COPY package*.json ./
55
RUN npm install\
@@ -8,7 +8,7 @@ COPY . .
88
RUN tsc
99

1010
# Stage 2: Run the application
11-
FROM node:20.9.0
11+
FROM node:20
1212
WORKDIR /app
1313
COPY --from=builder /app/dist ./dist
1414
COPY package*.json ./

back/package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

back/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "truth-machine",
3-
"version": "1.2.0",
3+
"version": "1.3.0",
44
"main": "index.js",
55
"scripts": {
66
"dev": "tsx watch ./src/index.ts",
@@ -12,7 +12,7 @@
1212
"license": "ISC",
1313
"description": "",
1414
"dependencies": {
15-
"@bsv/sdk": "^1.6.24",
15+
"@bsv/sdk": "^1.9.24",
1616
"@bsv/templates": "^1.1.0",
1717
"@types/express": "^5.0.0",
1818
"cors": "^2.8.5",

back/src/functions/allFunds.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default async function (req: Request, res: Response) {
5454
})
5555
}
5656

57-
await fundsTx.fee(new SatoshisPerKilobyte(1))
57+
await fundsTx.fee(new SatoshisPerKilobyte(100))
5858
await fundsTx.sign()
5959
const fundsTxId = fundsTx.id('hex')
6060

@@ -93,7 +93,7 @@ export default async function (req: Request, res: Response) {
9393
lockingScript: new HashPuzzle().lock(pair.hash)
9494
})
9595
})
96-
await tx.fee(new SatoshisPerKilobyte(1))
96+
await tx.fee(new SatoshisPerKilobyte(100))
9797
await tx.sign()
9898
tokenCreationTxs.push(tx)
9999
}
@@ -122,6 +122,7 @@ export default async function (req: Request, res: Response) {
122122
const satoshis = 1
123123
const fileHash = null
124124
const confirmed = false
125+
const spent = false
125126
return {
126127
txid,
127128
vout,
@@ -130,6 +131,7 @@ export default async function (req: Request, res: Response) {
130131
secret,
131132
fileHash,
132133
confirmed,
134+
spent,
133135
}
134136
})
135137

back/src/functions/checkTreasury.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import woc from '../woc'
2525
export default async function (req: Request, res: Response) {
2626
try {
2727
// Count available tokens (UTXOs not yet assigned to files)
28-
const tokens = await db.collection('utxos').countDocuments({ fileHash: null, confirmed: true })
28+
const tokens = await db.collection('utxos').countDocuments({ fileHash: null, confirmed: true, spent: { $ne: true } })
2929

3030
// Get current UTXO set and calculate total balance
3131
const utxos = await woc.getUtxos(address)

back/src/functions/consolidate.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* UTXO Consolidation Endpoint
3+
*
4+
* This endpoint consolidates all available hash-locked UTXOs in the database into a single output.
5+
* It gathers all the tokens created by the fund endpoint and combines them back into one UTXO
6+
* locked to the treasury address.
7+
*
8+
* Process:
9+
* 1. Retrieves all confirmed, unused UTXOs from the database
10+
* 2. Creates a transaction with all UTXOs as inputs
11+
* 3. Unlocks each input using its stored secret
12+
* 4. Creates a single change output locked to the treasury address
13+
* 5. Broadcasts the consolidation transaction
14+
*
15+
* @route POST /api/consolidate
16+
* @returns {Object} Consolidation status
17+
* - txid: Transaction ID of the consolidation
18+
* - utxosConsolidated: Number of UTXOs combined
19+
* - totalSatoshis: Total satoshis consolidated
20+
*
21+
* Use Cases:
22+
* - Reclaim unused tokens
23+
* - Reduce UTXO fragmentation
24+
* - Prepare treasury for new funding rounds
25+
*/
26+
27+
import { Request, Response } from 'express'
28+
import { P2PKH, SatoshisPerKilobyte, Transaction } from '@bsv/sdk'
29+
import HashPuzzle from '../HashPuzzle'
30+
import db from '../db'
31+
import Arc from '../arc'
32+
import { address } from '../functions/address'
33+
34+
export default async function (_req: Request, res: Response) {
35+
try {
36+
// Retrieve all confirmed, unused UTXOs from the database
37+
const utxos = await db.collection('utxos').find({
38+
confirmed: true,
39+
fileHash: null,
40+
invalid: null,
41+
spent: { $ne: true }
42+
}).toArray()
43+
44+
console.log({ utxosFound: utxos.length })
45+
46+
if (utxos.length === 0) {
47+
res.send({ error: 'No UTXOs available to consolidate' })
48+
return
49+
}
50+
51+
// Calculate total satoshis being consolidated
52+
const totalSatoshis = utxos.reduce((sum, utxo) => sum + utxo.satoshis, 0)
53+
54+
// Create consolidation transaction
55+
const tx = new Transaction()
56+
57+
// Add each UTXO as an input
58+
for (const utxo of utxos) {
59+
// Retrieve the source transaction from the database
60+
const sourceTransactionDoc = await db.collection('txs').findOne({ txid: utxo.txid })
61+
62+
if (!sourceTransactionDoc) {
63+
console.error(`Source transaction not found for UTXO ${utxo.txid}:${utxo.vout}`)
64+
continue
65+
}
66+
67+
const sourceTransaction = Transaction.fromHexBEEF(sourceTransactionDoc.beef)
68+
69+
// Add input with hash puzzle unlock
70+
tx.addInput({
71+
sourceTransaction,
72+
sourceOutputIndex: utxo.vout,
73+
unlockingScriptTemplate: new HashPuzzle().unlock(utxo.secret.secret)
74+
})
75+
}
76+
77+
// Add single output back to treasury address
78+
tx.addOutput({
79+
change: true,
80+
lockingScript: new P2PKH().lock(address)
81+
})
82+
83+
// Calculate fees and sign transaction
84+
await tx.fee(new SatoshisPerKilobyte(100))
85+
await tx.sign()
86+
87+
// Broadcast transaction
88+
const initialResponse = await tx.broadcast(Arc)
89+
console.log({ initialResponse })
90+
91+
const txid = tx.id('hex')
92+
93+
// Store consolidation transaction in database
94+
const txDbResponse = await db.collection('txs').insertOne({
95+
txid,
96+
beef: tx.toHexBEEF(),
97+
arc: [initialResponse],
98+
type: 'consolidation',
99+
utxosConsolidated: utxos.length,
100+
totalSatoshis
101+
})
102+
103+
// Mark consolidated UTXOs as spent
104+
await db.collection('utxos').updateMany(
105+
{
106+
_id: { $in: utxos.map(u => u._id) }
107+
},
108+
{
109+
$set: { spent: true, spentInTx: txid }
110+
}
111+
)
112+
113+
res.send({
114+
txid,
115+
utxosConsolidated: utxos.length,
116+
totalSatoshis,
117+
txDbResponse
118+
})
119+
} catch (error) {
120+
console.error('Failed to consolidate UTXOs', error)
121+
res.status(500).json({ error: error.message })
122+
}
123+
}

back/src/functions/fund.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ export default async function (req: Request, res: Response) {
7575
})
7676
for (const pair of secretPairs) {
7777
tx.addOutput({
78-
satoshis: 1,
78+
satoshis: 10,
7979
lockingScript: new HashPuzzle().lock(pair.hash)
8080
})
8181
}
8282
tx.addOutput({
8383
change: true,
8484
lockingScript: new P2PKH().lock(address)
8585
})
86-
await tx.fee(new SatoshisPerKilobyte(1))
86+
await tx.fee(new SatoshisPerKilobyte(100))
8787
await tx.sign()
8888

8989
// Broadcast transaction

back/src/functions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ export { default as upload } from './upload'
2323
export { default as checkTreasury } from './checkTreasury'
2424
export { default as utxoStatusUpdate } from './utxoStatusUptate'
2525
export { default as allFunds } from './allFunds'
26+
export { default as consolidate } from './consolidate'

back/src/functions/upload.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ export default async function (req: Request, res: Response) {
7575
const fileHash = Utils.toHex(Hash.sha256(Utils.toArray(file.toString('hex'), 'hex')))
7676
console.log({ fileHash })
7777

78-
// For a 32 byte hash fees will always be 1
79-
const fees = 1
78+
// For a 32 byte hash fees will always be 10
8079
const utxo = await db.collection('utxos').findOneAndUpdate({ fileHash: null, confirmed: true, invalid: null }, { $set: { fileHash } })
8180

8281
console.log({ utxo })

back/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import express, { Application } from 'express'
2-
import { upload, download, callback, integrity, fund, checkTreasury, utxoStatusUpdate, allFunds } from './functions'
2+
import { upload, download, callback, integrity, fund, checkTreasury, utxoStatusUpdate, allFunds, consolidate } from './functions'
33
import dotenv from 'dotenv'
44
import cors from 'cors'
55
dotenv.config()
@@ -38,6 +38,9 @@ app.use(express.json()).get('/checkTreasury', checkTreasury)
3838
// Update utxo status
3939
app.get('/utxoStatusUpdate', utxoStatusUpdate)
4040

41+
// Consolidate all available utxos into a single output
42+
app.get('/consolidate', consolidate)
43+
4144
app.listen(PORT, () => {
4245
console.log(`http://localhost:${PORT}`);
4346
})

0 commit comments

Comments
 (0)