Skip to content
Merged
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
8 changes: 8 additions & 0 deletions src/lib/TransactionInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
hasApo,
hasCtv,
hasCat,
hasCsfs,
hasIkey,
}: SerializedTransactionInput = $props()

let scriptLines = $derived(scriptAsm.split(/ (?=OP_)/))
Expand Down Expand Up @@ -42,6 +44,12 @@
{#if hasCat}
<span class="badge text-bg-success">OP_CAT</span>
{/if}
{#if hasCsfs}
<span class="badge text-bg-success">OP_CHECKSIGFROMSTACK</span>
{/if}
{#if hasIkey}
<span class="badge text-bg-success">OP_INTERNALKEY</span>
{/if}

<p
class="mt-2 mb-1"
Expand Down
2 changes: 2 additions & 0 deletions src/lib/TransactionList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
<option value="apo">SIGHASH_ANYPREVOUT</option>
<option value="ctv">OP_CHECKTEMPLATEVERIFY</option>
<option value="cat">OP_CAT</option>
<option value="csfs">OP_CHECKSIGFROMSTACK</option>
<option value="ikey">OP_INTERNALKEY</option>
</Input>
</FormGroup>

Expand Down
18 changes: 14 additions & 4 deletions src/lib/server/bitcoin/processing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,20 @@ export const getTransactionEntity = (
})

const hasApo =
type === 'p2tr' &&
/(^| )01([\da-f]{64})? OP_CHECKSIG(VERIFY|ADD|)($| )/.test(scriptAsm)
type === InputType.p2tr &&
/\b01([\da-f]{64})? OP_CHECKSIG(VERIFY|ADD|)\b/.test(scriptAsm)

const hasCtv = scriptAsm.includes('OP_CHECKTEMPLATEVERIFY')

const hasCat = type === 'p2tr' && scriptAsm.includes('OP_CAT')
const hasCat = type === InputType.p2tr && scriptAsm.includes('OP_CAT')

if (hasApo || hasCtv || hasCat) {
const hasCsfs =
type === InputType.p2tr && scriptAsm.includes('OP_CHECKSIGFROMSTACK')

const hasIkey =
type === InputType.p2tr && scriptAsm.includes('OP_INTERNALKEY')

if (hasApo || hasCtv || hasCat || hasCsfs || hasIkey) {
transactionInputs.push(
wrap(new TransactionInput()).assign({
inputIndex,
Expand All @@ -115,6 +121,8 @@ export const getTransactionEntity = (
hasApo,
hasCtv,
hasCat,
hasCsfs,
hasIkey,
}),
)
}
Expand All @@ -129,6 +137,8 @@ export const getTransactionEntity = (
hasApo: transactionInputs.some((input) => input.hasApo),
hasCtv: transactionInputs.some((input) => input.hasCtv),
hasCat: transactionInputs.some((input) => input.hasCat),
hasCsfs: transactionInputs.some((input) => input.hasCsfs),
hasIkey: transactionInputs.some((input) => input.hasIkey),
})
} else {
return undefined
Expand Down
3 changes: 3 additions & 0 deletions src/lib/server/bitcoin/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ const opcodes = new Map<number, string>([
// Opcode added by BIP 342 (Tapscript)
[0xba, 'OP_CHECKSIGADD'],

[0xcb, 'OP_INTERNALKEY'],
[0xcc, 'OP_CHECKSIGFROMSTACK'],

[0xff, 'OP_INVALIDOPCODE'],
])

Expand Down
4 changes: 4 additions & 0 deletions src/lib/server/db/entities/transaction-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export class TransactionInput extends CustomBaseEntity {
public hasCtv: boolean
@Property({ type: 'boolean' })
public hasCat: boolean
@Property({ type: 'boolean' })
public hasCsfs: boolean
@Property({ type: 'boolean' })
public hasIkey: boolean

@Property({ type: 'string', persist: false })
public get scriptAsm(): string {
Expand Down
6 changes: 6 additions & 0 deletions src/lib/server/db/entities/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export class Transaction extends CustomBaseEntity {
@Property({ type: 'boolean' })
@Index()
public hasCat: boolean
@Property({ type: 'boolean' })
@Index()
public hasCsfs: boolean
@Property({ type: 'boolean' })
@Index()
public hasIkey: boolean

@ManyToOne(() => Block, { deleteRule: 'cascade' })
public block: Block
Expand Down
2 changes: 2 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ export interface SoftForkFlags {
hasApo: boolean
hasCtv: boolean
hasCat: boolean
hasCsfs: boolean
hasIkey: boolean
}
22 changes: 22 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,28 @@
>BIP347</a
>)
</li>
<li>
{data.csfs} transactions that use <code>OP_CHECKSIGFROMSTACK</code> (<code
>OP_CSFS</code
>,
<a
target="_blank"
rel="noreferrer"
href="https://github.com/bitcoin/bips/blob/master/bip-0348.mediawiki"
>BIP348</a
>)
</li>
<li>
{data.ikey} transactions that use <code>OP_INTERNALKEY</code> (<code
>OP_IKEY</code
>,
<a
target="_blank"
rel="noreferrer"
href="https://github.com/bitcoin/bips/blob/master/bip-0349.mediawiki"
>BIP349</a
>)
</li>
</ul>

<TransactionList />
2 changes: 2 additions & 0 deletions src/routes/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface Counts {
apo: number
ctv: number
cat: number
csfs: number
ikey: number
}

export const load: PageLoad = async ({ fetch }) => {
Expand Down
4 changes: 4 additions & 0 deletions src/routes/api/count/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ export const GET: RequestHandler = async () => {
const apo = await transactionRepository.count({ hasApo: true })
const ctv = await transactionRepository.count({ hasCtv: true })
const cat = await transactionRepository.count({ hasCat: true })
const csfs = await transactionRepository.count({ hasCsfs: true })
const ikey = await transactionRepository.count({ hasIkey: true })

return json({
blocks,
transactions,
apo,
ctv,
cat,
csfs,
ikey,
})
}
6 changes: 6 additions & 0 deletions src/routes/api/latest/[page]/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ export const GET: RequestHandler = async ({ params, request }) => {
case 'cat':
filter = { hasCat: true }
break
case 'csfs':
filter = { hasCsfs: true }
break
case 'ikey':
filter = { hasIkey: true }
break
default:
throw error(400, 'Invalid type')
}
Expand Down
Loading