Skip to content

feat: add padding keyword to increase program size#226

Draft
stringhandler wants to merge 1 commit intoBlockstreamResearch:masterfrom
stringhandler:feat/add-padding
Draft

feat: add padding keyword to increase program size#226
stringhandler wants to merge 1 commit intoBlockstreamResearch:masterfrom
stringhandler:feat/add-padding

Conversation

@stringhandler
Copy link

This might be controversial, but I wanted to see if this was possible and what it might look like.

The problem I'm aiming to solve is the annex padding issue, which basically boils down to the transaction being padded with some data increase the budget for the CPU cost of the program.

It doesn't really make sense to increase the simplicity program size, however, developers who are unaware of this problem might add random code to the simplicity program. This adds formal syntax to SimplicityHL in the hopes that developers will find this keyword instead of adding random code. This allows us to remove this padding in future compilers cleanly.

A very common set of programs that need padding are ones using for_while.

Here is the example hashloop program

fn main() {
    padding::<7369>();
 
   // Hash bytes 0x00 to 0xff
    let ctx: Ctx8 = jet::sha_256_ctx_8_init();
    let out: Either<u256, Ctx8> = for_while::<hash_counter_8>(ctx, ());
    let expected: u256 = 0x40aff2e9d2d8922e47afd4648e6967497158785fbd1da870e7110266bf944880;
    assert!(jet::eq_256(expected, unwrap_left::<Ctx8>(out)));
    

The addition of padding in this case avoids the error of "Program's execution cost could exceed budget" , however it is still not relayed due to RPC error -26: min relay fee not met, 100 < 1226, which I'll investigate further. This could just be some setting in hal-simplicity I am missing.

The instructions that padding adds at the moment are fairly arbitrary. If we decide to go ahead with this, I'm open to suggestions.

For this program (p2pkh.simf):

fn sha2(string: u256) -> u256 {
    let hasher: Ctx8 = jet::sha_256_ctx_8_init();
    let hasher: Ctx8 = jet::sha_256_ctx_8_add_32(hasher, string);
    jet::sha_256_ctx_8_finalize(hasher)
}

fn main() {
    padding::<10>();
    let pk: Pubkey = witness::PK;
    let expected_pk_hash: u256 = 0x132f39a98c31baaddba6525f5d43f2954472097fa15265f45130bfdb70e51def; // sha2(1 * G)
    let pk_hash: u256 = sha2(pk);
    assert!(jet::eq_256(pk_hash, expected_pk_hash));

    let msg: u256 = jet::sig_all_hash();
    jet::bip_0340_verify((pk, msg), witness::SIG)
}

The program without padding:

flowchart TD
  node0["witness(0)"]
  node1["iden"]
  node2["pair"]
  node2 --> node0
  node2 --> node1
  node3["unit"]
  node4["word(0x132...1def)"]
  node5["comp"]
  node5 --> node3
  node5 --> node4
  node6["iden"]
  node7["pair"]
  node7 --> node5
  node7 --> node6
  node8["iden"]
  node9["take"]
  node9 --> node8
  node10["drop"]
  node10 --> node9
  node11["unit"]
  node12["jet(sha_256_ctx_8_init)"]
  node13["comp"]
  node13 --> node11
  node13 --> node12
  node14["iden"]
  node15["pair"]
  node15 --> node13
  node15 --> node14
  node16["iden"]
  node17["take"]
  node17 --> node16
  node18["iden"]
  node19["drop"]
  node19 --> node18
  node20["pair"]
  node20 --> node17
  node20 --> node19
  node21["jet(sha_256_ctx_8_add_32)"]
  node22["comp"]
  node22 --> node20
  node22 --> node21
  node23["iden"]
  node24["pair"]
  node24 --> node22
  node24 --> node23
  node25["iden"]
  node26["take"]
  node26 --> node25
  node27["jet(sha_256_ctx_8_finalize)"]
  node28["comp"]
  node28 --> node26
  node28 --> node27
  node29["comp"]
  node29 --> node24
  node29 --> node28
  node30["comp"]
  node30 --> node15
  node30 --> node29
  node31["comp"]
  node31 --> node10
  node31 --> node30
  node32["iden"]
  node33["pair"]
  node33 --> node31
  node33 --> node32
  node34["iden"]
  node35["take"]
  node35 --> node34
  node36["iden"]
  node37["take"]
  node37 --> node36
  node38["drop"]
  node38 --> node37
  node39["pair"]
  node39 --> node35
  node39 --> node38
  node40["jet(eq_256)"]
  node41["comp"]
  node41 --> node39
  node41 --> node40
  node42["jet(verify)"]
  node43["comp"]
  node43 --> node41
  node43 --> node42
  node44["unit"]
  node45["jet(sig_all_hash)"]
  node46["comp"]
  node46 --> node44
  node46 --> node45
  node47["iden"]
  node48["pair"]
  node48 --> node46
  node48 --> node47
  node49["iden"]
  node50["take"]
  node50 --> node49
  node51["drop"]
  node51 --> node50
  node52["drop"]
  node52 --> node51
  node53["drop"]
  node53 --> node52
  node54["iden"]
  node55["take"]
  node55 --> node54
  node56["pair"]
  node56 --> node53
  node56 --> node55
  node57["witness(57)"]
  node58["pair"]
  node58 --> node56
  node58 --> node57
  node59["jet(bip_0340_verify)"]
  node60["comp"]
  node60 --> node58
  node60 --> node59
  node61["comp"]
  node61 --> node48
  node61 --> node60
  node62["pair"]
  node62 --> node43
  node62 --> node61
  node63["iden"]
  node64["drop"]
  node64 --> node63
  node65["comp"]
  node65 --> node62
  node65 --> node64
  node66["comp"]
  node66 --> node33
  node66 --> node65
  node67["comp"]
  node67 --> node7
  node67 --> node66
  node68["comp"]
  node68 --> node2
  node68 --> node67

Loading

And with padding:

flowchart TD
  node0["unit"]
  node1["unit"]
  node2["unit"]
  node3["unit"]
  node4["unit"]
  node5["unit"]
  node6["unit"]
  node7["unit"]
  node8["unit"]
  node9["unit"]
  node10["unit"]
  node11["pair"]
  node11 --> node9
  node11 --> node10
  node12["iden"]
  node13["drop"]
  node13 --> node12
  node14["comp"]
  node14 --> node11
  node14 --> node13
  node15["pair"]
  node15 --> node8
  node15 --> node14
  node16["iden"]
  node17["drop"]
  node17 --> node16
  node18["comp"]
  node18 --> node15
  node18 --> node17
  node19["pair"]
  node19 --> node7
  node19 --> node18
  node20["iden"]
  node21["drop"]
  node21 --> node20
  node22["comp"]
  node22 --> node19
  node22 --> node21
  node23["pair"]
  node23 --> node6
  node23 --> node22
  node24["iden"]
  node25["drop"]
  node25 --> node24
  node26["comp"]
  node26 --> node23
  node26 --> node25
  node27["pair"]
  node27 --> node5
  node27 --> node26
  node28["iden"]
  node29["drop"]
  node29 --> node28
  node30["comp"]
  node30 --> node27
  node30 --> node29
  node31["pair"]
  node31 --> node4
  node31 --> node30
  node32["iden"]
  node33["drop"]
  node33 --> node32
  node34["comp"]
  node34 --> node31
  node34 --> node33
  node35["pair"]
  node35 --> node3
  node35 --> node34
  node36["iden"]
  node37["drop"]
  node37 --> node36
  node38["comp"]
  node38 --> node35
  node38 --> node37
  node39["pair"]
  node39 --> node2
  node39 --> node38
  node40["iden"]
  node41["drop"]
  node41 --> node40
  node42["comp"]
  node42 --> node39
  node42 --> node41
  node43["pair"]
  node43 --> node1
  node43 --> node42
  node44["iden"]
  node45["drop"]
  node45 --> node44
  node46["comp"]
  node46 --> node43
  node46 --> node45
  node47["pair"]
  node47 --> node0
  node47 --> node46
  node48["iden"]
  node49["drop"]
  node49 --> node48
  node50["comp"]
  node50 --> node47
  node50 --> node49
  node51["witness(51)"]
  node52["iden"]
  node53["pair"]
  node53 --> node51
  node53 --> node52
  node54["unit"]
  node55["word(0x132...1def)"]
  node56["comp"]
  node56 --> node54
  node56 --> node55
  node57["iden"]
  node58["pair"]
  node58 --> node56
  node58 --> node57
  node59["iden"]
  node60["take"]
  node60 --> node59
  node61["drop"]
  node61 --> node60
  node62["unit"]
  node63["jet(sha_256_ctx_8_init)"]
  node64["comp"]
  node64 --> node62
  node64 --> node63
  node65["iden"]
  node66["pair"]
  node66 --> node64
  node66 --> node65
  node67["iden"]
  node68["take"]
  node68 --> node67
  node69["iden"]
  node70["drop"]
  node70 --> node69
  node71["pair"]
  node71 --> node68
  node71 --> node70
  node72["jet(sha_256_ctx_8_add_32)"]
  node73["comp"]
  node73 --> node71
  node73 --> node72
  node74["iden"]
  node75["pair"]
  node75 --> node73
  node75 --> node74
  node76["iden"]
  node77["take"]
  node77 --> node76
  node78["jet(sha_256_ctx_8_finalize)"]
  node79["comp"]
  node79 --> node77
  node79 --> node78
  node80["comp"]
  node80 --> node75
  node80 --> node79
  node81["comp"]
  node81 --> node66
  node81 --> node80
  node82["comp"]
  node82 --> node61
  node82 --> node81
  node83["iden"]
  node84["pair"]
  node84 --> node82
  node84 --> node83
  node85["iden"]
  node86["take"]
  node86 --> node85
  node87["iden"]
  node88["take"]
  node88 --> node87
  node89["drop"]
  node89 --> node88
  node90["pair"]
  node90 --> node86
  node90 --> node89
  node91["jet(eq_256)"]
  node92["comp"]
  node92 --> node90
  node92 --> node91
  node93["jet(verify)"]
  node94["comp"]
  node94 --> node92
  node94 --> node93
  node95["unit"]
  node96["jet(sig_all_hash)"]
  node97["comp"]
  node97 --> node95
  node97 --> node96
  node98["iden"]
  node99["pair"]
  node99 --> node97
  node99 --> node98
  node100["iden"]
  node101["take"]
  node101 --> node100
  node102["drop"]
  node102 --> node101
  node103["drop"]
  node103 --> node102
  node104["drop"]
  node104 --> node103
  node105["iden"]
  node106["take"]
  node106 --> node105
  node107["pair"]
  node107 --> node104
  node107 --> node106
  node108["witness(108)"]
  node109["pair"]
  node109 --> node107
  node109 --> node108
  node110["jet(bip_0340_verify)"]
  node111["comp"]
  node111 --> node109
  node111 --> node110
  node112["comp"]
  node112 --> node99
  node112 --> node111
  node113["pair"]
  node113 --> node94
  node113 --> node112
  node114["iden"]
  node115["drop"]
  node115 --> node114
  node116["comp"]
  node116 --> node113
  node116 --> node115
  node117["comp"]
  node117 --> node84
  node117 --> node116
  node118["comp"]
  node118 --> node58
  node118 --> node117
  node119["comp"]
  node119 --> node53
  node119 --> node118
  node120["pair"]
  node120 --> node50
  node120 --> node119
  node121["iden"]
  node122["drop"]
  node122 --> node121
  node123["comp"]
  node123 --> node120
  node123 --> node122


Loading

@delta1
Copy link
Collaborator

delta1 commented Mar 3, 2026

I'm not wild about it, but do see how it could be useful 🤷

RPC error -26: min relay fee not met, 100 < 1226

It seems maybe the fee output value is being hardcoded either here or in hal-simplicity, while it should be calculated from feerate (0.1 sats/vb on liquid) multiplied by vsize of the transaction (which is tx-weight / 4)

If you're using hal-simplicity pset create then your fee output value just needs to be increased

@stringhandler
Copy link
Author

Thanks, I managed to adjust the fee

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants