# Transaction with segwit v1 spends and outputs.
# Outputs size in bytes and weight units.
def tx_size(n_inputs, n_outputs):
assert(n_inputs <= 252 and n_outputs <= 252)
return list(map(lambda mult: (
mult*(
4 # version
+ 1 # number of inputs
+ n_inputs * (
32 + 4 # prevout
+ 1 # script len
+ 4) # sequence
+ 1 # number of outputs
+ n_outputs * (
8 # amount
+ 1 # script len
+ 34) # script
+ 4) # locktime
+ 2 # witness flag & marker
+ n_inputs * (
1 # witness stack items
+ 1 # witness item len
+ 64)), # BIP-340 sig
[1, 4]))
# 365 day moving average according to
# - https://transactionfee.info/charts/inputs-per-transaction/?avg=365&start=2025-02-26&end=2025-02-27 and
# - https://transactionfee.info/charts/outputs-per-transaction/?avg=365&start=2025-02-26&end=2025-02-27
# retrieved 2025-02-28.
n_inputs = 2.26
n_outputs = 2.69
size = tx_size(n_inputs, n_outputs)
half_agged_tx = map(lambda s: s - (n_inputs - 1)*32, size)
half_agged_block = map(lambda s: s - n_inputs*32, size)
full_agged_tx = map(lambda s: s - (n_inputs-1)*64, size)
full_agged_tx_half_agged_block = map(lambda s: s - (n_inputs-1)*64 - 32, size)
max_agged = map(lambda s: s - n_inputs*64, size)
def savings(name, agged_sizes):
return [name] + ["%.1f%%" % ((1 - a/b)*100) for (a,b) in zip(agged_sizes, size)]
[
[ "", "bytes", "weight units" ],
None,
savings("half aggregation across tx", half_agged_tx),
savings("half aggregation across block", half_agged_block),
savings("full aggregation across tx", full_agged_tx),
savings("full aggregation across tx & half aggregation across block", full_agged_tx_half_agged_block),
savings("max (like infinite large fully aggregated coinjoin)", max_agged)
]| bytes | weight units | |
|---|---|---|
| half aggregation across tx | 10.9% | 3.9% |
| half aggregation across block | 19.6% | 7.1% |
| full aggregation across tx | 21.8% | 7.9% |
| full aggregation across tx & half aggregation across block | 30.5% | 11.0% |
| max (like infinite large fully aggregated coinjoin) | 39.1% | 14.1% |
Spending a taproot-like output via graftroot requires a 64-byte signature of the script that the signers delegate control to. In comparison, opening a taproot commitment only requires revealing the 32-byte “internal” pubkey (if the committed script is at depth zero). With half- or full-aggregation of signatures outside script, the graftroot signature can be aggregated just like key-spend signatures. As a result, graftroot spends would be equal to or more efficient that taproot script spends.