Having every tx greater than 100kvB be treated as being ~1000kvB seems like it would encourage stuffing – “I need a 120kvB tx, but I have to pay for 880kvB extra anyway, so might as well find some garbage to fill that up with”. Perhaps you could tweak this somewhat so that other people’s txs could still what’s used to fill up the block. Rough idea:
Consensus rules
- both the coinbase and the last tx in a block can be arbitrarily large, but every other tx must have a weight less than 400000
- if the last tx in a block has weight more than 400,000:
- it cannot spend an output that was created in the block (ie, it’s treated as if it had an relative lock time of 1 block)
- its weight is rounded up to 5000 below the next multiple of 100k, ie w' \equiv 95000 \pmod{100000}. This makes the max tx size 3,995,000 weight units or 998,750vB.
Mempool acceptance, storage
When accepting large txs to the mempool, they cannot have in-mempool ancestors or descendants, so always have a cluster size of one.
Because their weights are rounded up for consensus purposes, each large tx in the mempool can be put into one of 36 buckets matching its rounded up weight (495,000 weight units through 3,995,000), and only the highest fee tx in each of those buckets needs to be considered at any point in time. Further, if the best tx in a higher weight unit bucket has lower fee than the best tx in a lower weight unit bucket, it can be ignored. Keep track of these ~36 non-ignored txs.
Mining
When mining, take the ~36 non-ignored large txs from your mempool ordered from largest to smallest, put them in a large_buckets list, and run something like this algorithm:
block = []
ignored_txs = 0
large_bucket = 0
while ignored_txs < 1000:
chunk = mempool.get_next_chunk()
if not chunk: break
if block.weight() + chunk.weight() > MAX_WEIGHT:
mempool.ignored_chunk()
ignored_txs += 1
continue
while large_bucket < large_buckets.size()
if block.weight() + chunk.weight() + large_buckets[large_bucket].weight() > MAX_WEIGHT:
large_buckets[large_bucket].block = block.copy()
large_bucket += 1
else:
break
block.add(chunk)
mempool.accepted_chunk()
ignored_txs = 0
if large_bucket < large_buckets.size():
large_buckets[large_bucket].block = block.copy()
for large_bucket in range(large_buckets):
if not lb.block: break
lb = large_buckets[large_bucket]
if lb.block.fee + lb.tx.fee > block.fee:
block = lb.block + lb.tx
return block
That misses out on getting the best possible final 25kvB into a block that includes a large tx, but otherwise seems fairly feasible?