logos-blockchain-circuits/blend/generate_inputs_for_poq.py
thomaslavaur bd4fe8bad3
empower the poq (#51)
* empower the poq

* update test inputs

* Enhance GMP download process in CI workflow

Updated GMP download step to try multiple URLs with retries.

* revert ci

* fix test

* bump version

* add cargo lock
2026-08-04 13:44:26 +02:00

330 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from sage.all import *
p = 21888242871839275222246405745257275088548364400416034343698204186575808495617
F = FiniteField(p)
def poseidon2_hash(data):
return PoseidonSponge(data,2,1)[0]
def Poseidon2_sponge_hash_rate_1(data, n):
return PoseidonSponge(data,3,2,n,1)
def Poseidon2_sponge_hash_rate_2(data, n):
return PoseidonSponge(data,3,1,n,1)
def SBox(inp):
return inp**5
def InternalRound(inp, i):
round_consts = [
0x1a1d063e54b1e764b63e1855bff015b8cedd192f47308731499573f23597d4b5,
0x26abc66f3fdf8e68839d10956259063708235dccc1aa3793b91b002c5b257c37,
0x0c7c64a9d887385381a578cfed5aed370754427aabca92a70b3c2b12ff4d7be8,
0x1cf5998769e9fab79e17f0b6d08b2d1eba2ebac30dc386b0edd383831354b495,
0x0f5e3a8566be31b7564ca60461e9e08b19828764a9669bc17aba0b97e66b0109,
0x18df6a9d19ea90d895e60e4db0794a01f359a53a180b7d4b42bf3d7a531c976e,
0x04f7bf2c5c0538ac6e4b782c3c6e601ad0ea1d3a3b9d25ef4e324055fa3123dc,
0x29c76ce22255206e3c40058523748531e770c0584aa2328ce55d54628b89ebe6,
0x198d425a45b78e85c053659ab4347f5d65b1b8e9c6108dbe00e0e945dbc5ff15,
0x25ee27ab6296cd5e6af3cc79c598a1daa7ff7f6878b3c49d49d3a9a90c3fdf74,
0x138ea8e0af41a1e024561001c0b6eb1505845d7d0c55b1b2c0f88687a96d1381,
0x306197fb3fab671ef6e7c2cba2eefd0e42851b5b9811f2ca4013370a01d95687,
0x1a0c7d52dc32a4432b66f0b4894d4f1a21db7565e5b4250486419eaf00e8f620,
0x2b46b418de80915f3ff86a8e5c8bdfccebfbe5f55163cd6caa52997da2c54a9f,
0x12d3e0dc0085873701f8b777b9673af9613a1af5db48e05bfb46e312b5829f64,
0x263390cf74dc3a8870f5002ed21d089ffb2bf768230f648dba338a5cb19b3a1f,
0x0a14f33a5fe668a60ac884b4ca607ad0f8abb5af40f96f1d7d543db52b003dcd,
0x28ead9c586513eab1a5e86509d68b2da27be3a4f01171a1dd847df829bc683b9,
0x1c6ab1c328c3c6430972031f1bdb2ac9888f0ea1abe71cffea16cda6e1a7416c,
0x1fc7e71bc0b819792b2500239f7f8de04f6decd608cb98a932346015c5b42c94,
0x03e107eb3a42b2ece380e0d860298f17c0c1e197c952650ee6dd85b93a0ddaa8,
0x2d354a251f381a4669c0d52bf88b772c46452ca57c08697f454505f6941d78cd,
0x094af88ab05d94baf687ef14bc566d1c522551d61606eda3d14b4606826f794b,
0x19705b783bf3d2dc19bcaeabf02f8ca5e1ab5b6f2e3195a9d52b2d249d1396f7,
0x09bf4acc3a8bce3f1fcc33fee54fc5b28723b16b7d740a3e60cef6852271200e,
0x1803f8200db6013c50f83c0c8fab62843413732f301f7058543a073f3f3b5e4e,
0x0f80afb5046244de30595b160b8d1f38bf6fb02d4454c0add41f7fef2faf3e5c,
0x126ee1f8504f15c3d77f0088c1cfc964abcfcf643f4a6fea7dc3f98219529d78,
0x23c203d10cfcc60f69bfb3d919552ca10ffb4ee63175ddf8ef86f991d7d0a591,
0x2a2ae15d8b143709ec0d09705fa3a6303dec1ee4eec2cf747c5a339f7744fb94,
0x07b60dee586ed6ef47e5c381ab6343ecc3d3b3006cb461bbb6b5d89081970b2b,
0x27316b559be3edfd885d95c494c1ae3d8a98a320baa7d152132cfe583c9311bd,
0x1d5c49ba157c32b8d8937cb2d3f84311ef834cc2a743ed662f5f9af0c0342e76,
0x2f8b124e78163b2f332774e0b850b5ec09c01bf6979938f67c24bd5940968488,
0x1e6843a5457416b6dc5b7aa09a9ce21b1d4cba6554e51d84665f75260113b3d5,
0x11cdf00a35f650c55fca25c9929c8ad9a68daf9ac6a189ab1f5bc79f21641d4b,
0x21632de3d3bbc5e42ef36e588158d6d4608b2815c77355b7e82b5b9b7eb560bc,
0x0de625758452efbd97b27025fbd245e0255ae48ef2a329e449d7b5c51c18498a,
0x2ad253c053e75213e2febfd4d976cc01dd9e1e1c6f0fb6b09b09546ba0838098,
0x1d6b169ed63872dc6ec7681ec39b3be93dd49cdd13c813b7d35702e38d60b077,
0x1660b740a143664bb9127c4941b67fed0be3ea70a24d5568c3a54e706cfef7fe,
0x0065a92d1de81f34114f4ca2deef76e0ceacdddb12cf879096a29f10376ccbfe,
0x1f11f065202535987367f823da7d672c353ebe2ccbc4869bcf30d50a5871040d,
0x26596f5c5dd5a5d1b437ce7b14a2c3dd3bd1d1a39b6759ba110852d17df0693e,
0x16f49bc727e45a2f7bf3056efcf8b6d38539c4163a5f1e706743db15af91860f,
0x1abe1deb45b3e3119954175efb331bf4568feaf7ea8b3dc5e1a4e7438dd39e5f,
0x0e426ccab66984d1d8993a74ca548b779f5db92aaec5f102020d34aea15fba59,
0x0e7c30c2e2e8957f4933bd1942053f1f0071684b902d534fa841924303f6a6c6,
0x0812a017ca92cf0a1622708fc7edff1d6166ded6e3528ead4c76e1f31d3fc69d,
0x21a5ade3df2bc1b5bba949d1db96040068afe5026edd7a9c2e276b47cf010d54,
0x01f3035463816c84ad711bf1a058c6c6bd101945f50e5afe72b1a5233f8749ce,
0x0b115572f038c0e2028c2aafc2d06a5e8bf2f9398dbd0fdf4dcaa82b0f0c1c8b,
0x1c38ec0b99b62fd4f0ef255543f50d2e27fc24db42bc910a3460613b6ef59e2f,
0x1c89c6d9666272e8425c3ff1f4ac737b2f5d314606a297d4b1d0b254d880c53e,
0x03326e643580356bf6d44008ae4c042a21ad4880097a5eb38b71e2311bb88f8f,
0x268076b0054fb73f67cee9ea0e51e3ad50f27a6434b5dceb5bdde2299910a4c9,
]
sb = SBox(inp[0] + round_consts[i])
out = [F(0) for i in range(3)]
out[0] = 2*sb + inp[1] + inp[2];
out[1] = sb + 2*inp[1] + inp[2];
out[2] = sb + inp[1] + 3*inp[2];
return out
def ExternalRound(inp, i):
out = [F(0) for j in range(3)]
round_consts = [ [F(0x1d066a255517b7fd8bddd3a93f7804ef7f8fcde48bb4c37a59a09a1a97052816)
, F(0x29daefb55f6f2dc6ac3f089cebcc6120b7c6fef31367b68eb7238547d32c1610)
, F(0x1f2cb1624a78ee001ecbd88ad959d7012572d76f08ec5c4f9e8b7ad7b0b4e1d1)
]
, [ F(0x0aad2e79f15735f2bd77c0ed3d14aa27b11f092a53bbc6e1db0672ded84f31e5)
, F(0x2252624f8617738cd6f661dd4094375f37028a98f1dece66091ccf1595b43f28)
, F(0x1a24913a928b38485a65a84a291da1ff91c20626524b2b87d49f4f2c9018d735)
]
, [ F(0x22fc468f1759b74d7bfc427b5f11ebb10a41515ddff497b14fd6dae1508fc47a)
, F(0x1059ca787f1f89ed9cd026e9c9ca107ae61956ff0b4121d5efd65515617f6e4d)
, F(0x02be9473358461d8f61f3536d877de982123011f0bf6f155a45cbbfae8b981ce)
]
, [ F(0x0ec96c8e32962d462778a749c82ed623aba9b669ac5b8736a1ff3a441a5084a4)
, F(0x292f906e073677405442d9553c45fa3f5a47a7cdb8c99f9648fb2e4d814df57e)
, F(0x274982444157b86726c11b9a0f5e39a5cc611160a394ea460c63f0b2ffe5657e)
]
, [ F(0x1acd63c67fbc9ab1626ed93491bda32e5da18ea9d8e4f10178d04aa6f8747ad0)
, F(0x19f8a5d670e8ab66c4e3144be58ef6901bf93375e2323ec3ca8c86cd2a28b5a5)
, F(0x1c0dc443519ad7a86efa40d2df10a011068193ea51f6c92ae1cfbb5f7b9b6893)
]
, [ F(0x14b39e7aa4068dbe50fe7190e421dc19fbeab33cb4f6a2c4180e4c3224987d3d)
, F(0x1d449b71bd826ec58f28c63ea6c561b7b820fc519f01f021afb1e35e28b0795e)
, F(0x1ea2c9a89baaddbb60fa97fe60fe9d8e89de141689d1252276524dc0a9e987fc)
]
, [ F(0x0478d66d43535a8cb57e9c1c3d6a2bd7591f9a46a0e9c058134d5cefdb3c7ff1)
, F(0x19272db71eece6a6f608f3b2717f9cd2662e26ad86c400b21cde5e4a7b00bebe)
, F(0x14226537335cab33c749c746f09208abb2dd1bd66a87ef75039be846af134166)
]
, [ F(0x01fd6af15956294f9dfe38c0d976a088b21c21e4a1c2e823f912f44961f9a9ce)
, F(0x18e5abedd626ec307bca190b8b2cab1aaee2e62ed229ba5a5ad8518d4e5f2a57)
, F(0x0fc1bbceba0590f5abbdffa6d3b35e3297c021a3a409926d0e2d54dc1c84fda6)
]]
sb = [F(0) for j in range(3)]
for j in range(3):
sb[j] = SBox(F(inp[j] + round_consts[i][j]))
out = [F(0) for j in range(3)]
out[0] = 2*sb[0] + sb[1] + sb[2]
out[1] = sb[0] + 2*sb[1] + sb[2]
out[2] = sb[0]+ sb[1] + 2*sb[2]
return out
def LinearLayer(inp):
out = [F(0) for i in range(3)]
out[0] = 2*inp[0] + inp[1] + inp[2]
out[1] = inp[0] + 2*inp[1] + inp[2]
out[2] = inp[0] + inp[1] + 2*inp[2]
return out
def Permutation(inp):
out = [F(0) for i in range(3)]
state = LinearLayer(inp)
for k in range(4):
state = ExternalRound(state, k)
for k in range(56):
state = InternalRound(state, k)
for k in range(4):
state = ExternalRound(state, k+4)
return state
def Compression(inp):
return Permutation([inp[0],inp[1],F(0)])[0]
def PoseidonSponge(data, capacity, output_len):
rate = 3 - capacity;
output = [F(0) for i in range(output_len)]
assert( capacity > 0 )
assert( rate > 0 )
assert( capacity < 3 )
assert( rate < 3 )
# round up to rate the input + 1 field element ("10*" padding)
nblocks = ((len(data) + 1) + (rate-1)) // rate;
nout = (output_len + (rate-1)) // rate;
padded_len = nblocks * rate;
padded = []
for i in range(len(data)):
padded.append(F(data[i]))
padded.append(F(1))
for i in range(len(data)+1,padded_len):
padded.append(F(0))
civ = F(0)
state = [F(0),F(0),F(civ)]
sorbed = [F(0) for j in range(rate)]
for m in range(nblocks):
for i in range(rate):
a = state[i]
b = padded[m*rate+i]
sorbed[i] = a + b
state = Permutation(sorbed[0:rate] + state[rate:3])
q = min(rate, output_len)
for i in range(q):
output[i] = state[i]
out_ptr = rate
for n in range(1,nout):
state[nblocks+n] = Permutation(state[nblocks+n-1])
q = min(rate, output_len-out_ptr)
for i in range(q):
output[out_ptr+i] = state[nblocks+n][i]
out_ptr += rate
return output
# ———————————————————————
# Main
# ———————————————————————
if len(sys.argv) != 5:
print("Usage: python3 generate_inputs_for_poq.py <core_quota> <leader_quota> <pow_quota> <core (0) or leader (1) or pow (2)>")
sys.exit(1)
Qc = int(sys.argv[1])
Ql = int(sys.argv[2])
Qp = int(sys.argv[3])
role_selector = int(sys.argv[4])
if not role_selector in [0,1,2]:
print("core or leader or pow must be 0 or 1 or 2")
sys.exit(1)
# 1) Corenode registry Merkleproof
# pick a random core_sk and derive its public key
core_sk = F(randrange(0,p,1))
pk_core = Compression([ F(4605003), core_sk ])
core_selectors = randrange(0,2**20,1)
core_selectors = format(int(core_selectors),'020b')
core_nodes = [F(randrange(0,p,1)) for i in range(20)]
core_root = pk_core
for i in range(20):
if int(core_selectors[19-i]) == 0:
core_root = Compression([core_root,core_nodes[i]])
else:
core_root = Compression([core_nodes[i],core_root])
#pk_root, core_path, core_selectors = merkle_root_and_path(pk_core, 20)
# 2) PoL inputs (broadened from pol script)
epoch_nonce = F(randrange(0, p,1))
slot_number = F(randrange(0, 2**32,1))
total_stake = int(5000)
t0_constant = F(0x1a3fb997fd58374772808c13d1c2ddacb5ab3ea77413f86fd6e0d3d978e5438)
t1_constant = F(0x71e790b41991052e30c93934b5612412e7958837bac8b1c524c24d84cc7d0)
t0 = F(int(t0_constant) // total_stake)
t1 = F(p- (int(t1_constant) // total_stake**2))
value = F(total_stake / 100)
threshold = (t0 + t1 * value) * value
sk = F(randrange(0,p,1))
tx_hash = F(randrange(0,p,1))
output_number = F(randrange(0,50,1))
pk = Compression([F(4605003),sk])
note_id = poseidon2_hash([F(232989242343357190262606),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
while(ticket > threshold):
output_number += 1
note_id = poseidon2_hash([F(232989242343357190262606),tx_hash,output_number,value,pk])
ticket = poseidon2_hash([F(13887241025832268),F(epoch_nonce),F(slot_number),note_id,sk])
aged_nodes = [F(randrange(0,p,1)) for i in range(32)]
aged_selectors = randrange(0,2**32,1)
aged_selectors = format(aged_selectors,'032b')
aged_root = note_id
for i in range(32):
if int(aged_selectors[31-i]) == 0:
aged_root = Compression([aged_root,aged_nodes[i]])
else:
aged_root = Compression([aged_nodes[i],aged_root])
# PoW inputs
pow_block_hash = F(randrange(0,p,1))
pow_sk = F(randrange(0,p,1))
pow_pk = Compression([F(4605003),pow_sk])
pow_ticket = poseidon2_hash([F(epoch_nonce),F(pow_block_hash), F(pow_pk)])
if role_selector == 2:
pow_difficulty = pow_ticket + 1
else:
pow_difficulty = pow_ticket - 1
# 3) Choose branch & index
index = randrange(0, Ql if role_selector == 0 else (Qc if role_selector == 1 else Qp),1)
# 4) Onetime key
K_one = F(123456)
K_two = F(654321)
# 5) Assemble JSON
inp = {
"core_quota": str(Qc),
"leader_quota": str(Ql),
"pow_quota": str(Qp),
"core_root": str(core_root),
"pol_ledger_aged": str(aged_root),
"K_part_one": str(K_one),
"K_part_two": str(K_two),
"selector": str(role_selector),
"index": str(index),
"core_sk": str(core_sk),
"core_path": [str(x) for x in core_nodes],
"core_path_selectors": [str(x) for x in core_selectors],
"pol_sl": str(slot_number),
"pol_epoch_nonce": str(epoch_nonce),
"pol_t0": str(t0),
"pol_t1": str(t1),
"pol_secret_key": str(sk),
"pol_noteid_path": [str(x) for x in aged_nodes],
"pol_noteid_path_selectors": [str(x) for x in aged_selectors],
"pol_note_tx_hash": str(tx_hash),
"pol_note_output_number": str(output_number),
"pol_note_value": str(value),
"pow_block_hash": str(pow_block_hash),
"pow_blend_difficulty": str(pow_difficulty),
"pow_sk": str(pow_sk)
}
if role_selector != 1:
inp["pol_ledger_aged"] = str(randrange(0,p,1))
if role_selector != 0:
inp["core_root"] = str(randrange(0,p,1))
import json
with open("input.json","w") as f:
json.dump(inp, f, indent=2)
print("Wrote input_poq.json")