Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- SRA
- Optimization enabled
- false
- Compiler version
- v0.8.28+commit.7893614a
- EVM Version
- Verified at
- 2026-08-20T06:38:31.029535Z
Constructor Arguments
contracts/lib/SRA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/// @title SRA — commutative-encryption verification primitives
/// @notice On-chain half of the SRA mental-poker protocol (SPEC §2). All
/// deck values are 1024-bit big-endian byte strings (quadratic
/// residues mod P); all exponent checks run forward through the
/// MODEXP precompile. Mirrors web/js/sra.mjs.
library SRA {
/// RFC 2409 Oakley Group 2 prime (1024-bit safe prime), big-endian words.
uint256 internal constant P0 = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74;
uint256 internal constant P1 = 0x020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437;
uint256 internal constant P2 = 0x4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED;
uint256 internal constant P3 = 0xEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF;
uint256 internal constant VALUE_BYTES = 128;
uint256 internal constant DECK = 52;
/// @dev P as a 128-byte string (for the precompile modulus field).
function pBytes() internal pure returns (bytes memory out) {
out = new bytes(128);
assembly ("memory-safe") {
mstore(add(out, 32), P0)
mstore(add(out, 64), P1)
mstore(add(out, 96), P2)
mstore(add(out, 128), P3)
}
}
/// @notice base^exp mod P with a 32-byte exponent; returns 128-byte result.
function modexp32(bytes memory base128, uint256 exp) public view returns (bytes memory) {
return _modexp(base128, abi.encodePacked(exp));
}
/// @notice base^(e1*e2) mod P (512-bit exponent product of two scalars).
function modexpMul(bytes memory base128, uint256 e1, uint256 e2)
public view returns (bytes memory)
{
(uint256 hi, uint256 lo) = mul512(e1, e2);
return _modexp(base128, abi.encodePacked(hi, lo));
}
/// @dev Full 512-bit product of two uint256.
function mul512(uint256 a, uint256 b) internal pure returns (uint256 hi, uint256 lo) {
unchecked {
lo = a * b;
uint256 mm = mulmod(a, b, type(uint256).max);
hi = mm - lo;
if (mm < lo) hi--;
}
}
/// @dev MODEXP precompile call; base must be 128 bytes; mod = P.
function _modexp(bytes memory base128, bytes memory exp)
private view returns (bytes memory result)
{
require(base128.length == 128, "base len");
bytes memory input = abi.encodePacked(
uint256(128), uint256(exp.length), uint256(128), base128, exp, pBytes()
);
result = new bytes(128);
bool ok;
assembly ("memory-safe") {
ok := staticcall(gas(), 0x05, add(input, 32), mload(input), add(result, 32), 128)
}
require(ok, "modexp failed");
}
/// @notice keccak of base^exp mod P — cheap equality comparisons.
function modexpHash32(bytes memory base128, uint256 exp) public view returns (bytes32) {
return keccak256(modexp32(base128, exp));
}
/// @notice base^exp mod P with an arbitrary-length exponent (used for
/// 1024-bit inverse exponents in dispute round-trips).
function modexpBytes(bytes memory base128, bytes memory exp) public view returns (bytes memory) {
return _modexp(base128, exp);
}
/// @notice Deterministic card code for k in [0,52): full-size QR mod P.
/// @dev h = BE(keccak(TAG‖k‖0)‖…‖keccak(TAG‖k‖3)); code = h^2 mod P.
/// Mirrors sra.mjs cardCode().
function cardCode(uint8 k) public view returns (bytes memory) {
require(k < DECK, "bad card");
bytes memory h = new bytes(128);
bytes memory tag = "PLSHOLDEM_CARD_V1";
for (uint8 ctr = 0; ctr < 4; ctr++) {
bytes32 part = keccak256(abi.encodePacked(tag, k, ctr));
assembly ("memory-safe") {
mstore(add(add(h, 32), mul(ctr, 32)), part)
}
}
return _modexp(h, hex"02");
}
/// @notice keccak of cardCode(k).
function cardCodeHash(uint8 k) internal view returns (bytes32) {
return keccak256(cardCode(k));
}
/// @notice Find the card index whose code hashes to `h`; 255 if none.
function cardFromHash(bytes32 h) public view returns (uint8) {
for (uint8 k = 0; k < DECK; k++) {
if (cardCodeHash(k) == h) return k;
}
return 255;
}
/// @notice Extract 128-byte value i from a packed 52-value deck blob.
function deckValue(bytes calldata deck, uint256 i) internal pure returns (bytes memory v) {
v = deck[i * VALUE_BYTES:(i + 1) * VALUE_BYTES];
}
/// @notice Validate a posted deck stage: exactly 52 values, each in
/// (1, P-1), all distinct. Reverts otherwise.
function checkDeck(bytes calldata deck) public pure {
require(deck.length == DECK * VALUE_BYTES, "deck len");
bytes32[DECK] memory hashes;
for (uint256 i = 0; i < DECK; i++) {
uint256 off = i * VALUE_BYTES;
uint256 w0;
uint256 w1;
uint256 w2;
uint256 w3;
assembly ("memory-safe") {
w0 := calldataload(add(deck.offset, off))
w1 := calldataload(add(deck.offset, add(off, 32)))
w2 := calldataload(add(deck.offset, add(off, 64)))
w3 := calldataload(add(deck.offset, add(off, 96)))
}
require(inRange(w0, w1, w2, w3), "value range");
bytes32 h = keccak256(abi.encodePacked(w0, w1, w2, w3));
for (uint256 j = 0; j < i; j++) {
require(hashes[j] != h, "duplicate value");
}
hashes[i] = h;
}
}
/// @dev true iff 1 < v < P-1 for v given as 4 big-endian words.
function inRange(uint256 w0, uint256 w1, uint256 w2, uint256 w3) internal pure returns (bool) {
// v > 1
if (w0 == 0 && w1 == 0 && w2 == 0 && w3 <= 1) return false;
// v < P - 1 (P3 ends in ...FFFF, so P-1 differs only in last word)
if (w0 < P0) return true;
if (w0 > P0) return false;
if (w1 < P1) return true;
if (w1 > P1) return false;
if (w2 < P2) return true;
if (w2 > P2) return false;
return w3 < P3 - 1;
}
/// @notice Range-check a 128-byte memory value (used for proven values).
function inRangeMem(bytes memory v) internal pure returns (bool) {
require(v.length == 128, "len");
uint256 w0;
uint256 w1;
uint256 w2;
uint256 w3;
assembly ("memory-safe") {
w0 := mload(add(v, 32))
w1 := mload(add(v, 64))
w2 := mload(add(v, 96))
w3 := mload(add(v, 128))
}
return inRange(w0, w1, w2, w3);
}
/// @dev Scalar exponent sanity: odd (invertible mod P-1) and nonzero.
function validScalar(uint256 e) internal pure returns (bool) {
return e & 1 == 1 && e > 1;
}
}
Compiler Settings
{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":1,"enabled":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"cardCode","inputs":[{"type":"uint8","name":"k","internalType":"uint8"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"cardFromHash","inputs":[{"type":"bytes32","name":"h","internalType":"bytes32"}]},{"type":"function","stateMutability":"pure","outputs":[],"name":"checkDeck","inputs":[{"type":"bytes","name":"deck","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"modexp32","inputs":[{"type":"bytes","name":"base128","internalType":"bytes"},{"type":"uint256","name":"exp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"modexpBytes","inputs":[{"type":"bytes","name":"base128","internalType":"bytes"},{"type":"bytes","name":"exp","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"modexpHash32","inputs":[{"type":"bytes","name":"base128","internalType":"bytes"},{"type":"uint256","name":"exp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes","name":"","internalType":"bytes"}],"name":"modexpMul","inputs":[{"type":"bytes","name":"base128","internalType":"bytes"},{"type":"uint256","name":"e1","internalType":"uint256"},{"type":"uint256","name":"e2","internalType":"uint256"}]}]
Contract Creation Code
0x6080806040523460195761095f908161001f823930815050f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c9081626a007d146101cb575080633c8dfb44146101af57806340604be71461013557806349b11d3714610110578063631ec63f146100fb578063d325c5b1146100945763ee307cb41461006b57600080fd5b602036600319011261008f576020610084600435610600565b60ff60405191168152f35b600080fd5b604036600319011261008f576004356001600160401b03811161008f576100bf9036906004016103c7565b6024356001600160401b03811161008f576100f7916100e56100eb9236906004016103c7565b90610760565b60405191829182610471565b0390f35b6100f76100eb61010a3661041d565b906105e3565b602036600319011261008f5760043560ff8116810361008f576100eb6100f7916104c4565b606036600319011261008f576004356001600160401b03811161008f576100eb6101666100f79236906004016103c7565b60443560243581810292916000199109918080840393106101a4575b60405192602084015260408301526040825261019f60608361038e565b610760565b916000190191610182565b60206101bd61010a3661041d565b818151910120604051908152f35b602036600319011261008f57600435906001600160401b03821161008f573660238301121561008f5760048201356001600160401b03811161008f57366024828501011161008f57611a000361036157506040519061068061022d818461038e565b3683376000905b6034821061023e57005b8160071b8281046080148315171561034b578101602481013590604481013590608460648201359101359061027582828587610643565b15610318576040519260208401948552604084015260608301526080820152608081526102a360a08261038e565b5190209160005b8181106102c85750600191926102c0828661049d565b520190610234565b836102d3828761049d565b51146102e1576001016102aa565b60405162461bcd60e51b815260206004820152600f60248201526e6475706c69636174652076616c756560881b6044820152606490fd5b60405162461bcd60e51b815260206004820152600b60248201526a76616c75652072616e676560a81b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b62461bcd60e51b81526020600482015260086024820152673232b1b5903632b760c11b6044820152606490fd5b601f909101601f19168101906001600160401b038211908210176103b157604052565b634e487b7160e01b600052604160045260246000fd5b81601f8201121561008f578035906001600160401b0382116103b157604051926103fb601f8401601f19166020018561038e565b8284526020838301011161008f57816000926020809301838601378301015290565b604060031982011261008f57600435906001600160401b03821161008f57610447916004016103c7565b9060243590565b60005b8381106104615750506000910152565b8181015183820152602001610451565b60409160208252610491815180928160208601526020868601910161044e565b601f01601f1916010190565b9060348110156104ae5760051b0190565b634e487b7160e01b600052603260045260246000fd5b90603460ff831610156105b3576040516104df60a08261038e565b60808082523660208301376040908151936104fa838661038e565b601185526020850170504c53484f4c44454d5f434152445f563160781b815260005b600460ff8216106105525750505061054f92935061053c8251928361038e565b60018252600160f91b6020830152610760565b90565b600160ff9188875161059f6002602083818101955161057281888d61044e565b81018960f81b8c60f81b16838201528960f81b8860f81b1660218201520301601d1981018452018261038e565b51902060208260051b88010152011661051c565b60405162461bcd60e51b81526020600482015260086024820152671898590818d85c9960c21b6044820152606490fd5b9061054f916040519160208301526020825261019f60408361038e565b60005b603460ff82161061061557505060ff90565b8161061f826104c4565b602081519101201461063e5760ff1660ff811461034b57600101610603565b905090565b801580610758575b80610750575b80610745575b61073c577736f0255dde973dcb3b399d747f23e32ed6fdb1f77598338b198110610733577736f0255dde973dcb3b399d747f23e32ed6fdb1f77598338b191061072b576000805160206108ea8339815191528110610723576000805160206108ea8339815191521061071c5760008051602061090a83398151915281106107155760008051602061090a8339815191521061070f57600277771c35fdad44cfd2d74f9208be258ff324943328f67329c160411b031190565b50600090565b5050600190565b5050600090565b505050600190565b505050600090565b50505050600190565b50505050600090565b506001841115610657565b508215610651565b50811561064b565b91909160808151036108b957608090835160a061085e60405193610784838661038e565b85855260208501601f19840195863683377736f0255dde973dcb3b399d747f23e32ed6fdb1f77598338b1982526000805160206108ea833981519152604082015260008051602061090a8339815191526060820152600177771c35fdad44cfd2d74f9208be258ff324943328f67329c160411b038882015261084f886040519b8c956020870199838b52604088015282606088015261082c815180926020868b01910161044e565b8601610841825180936020868501910161044e565b01019283925193849161044e565b0103601f19810188528761038e565b61086b604051918261038e565b838152602081019236843794519060055afa1561088457565b60405162461bcd60e51b815260206004820152600d60248201526c1b5bd9195e1c0819985a5b1959609a1b6044820152606490fd5b60405162461bcd60e51b81526020600482015260086024820152673130b9b2903632b760c11b6044820152606490fdfe020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7eda2646970667358221220fe4da8e805a981b6b28046ad1f5f57fddb372485082facd8fe6163e78b0cab1f64736f6c634300081c0033
Deployed ByteCode
0x608080604052600436101561001357600080fd5b60003560e01c9081626a007d146101cb575080633c8dfb44146101af57806340604be71461013557806349b11d3714610110578063631ec63f146100fb578063d325c5b1146100945763ee307cb41461006b57600080fd5b602036600319011261008f576020610084600435610600565b60ff60405191168152f35b600080fd5b604036600319011261008f576004356001600160401b03811161008f576100bf9036906004016103c7565b6024356001600160401b03811161008f576100f7916100e56100eb9236906004016103c7565b90610760565b60405191829182610471565b0390f35b6100f76100eb61010a3661041d565b906105e3565b602036600319011261008f5760043560ff8116810361008f576100eb6100f7916104c4565b606036600319011261008f576004356001600160401b03811161008f576100eb6101666100f79236906004016103c7565b60443560243581810292916000199109918080840393106101a4575b60405192602084015260408301526040825261019f60608361038e565b610760565b916000190191610182565b60206101bd61010a3661041d565b818151910120604051908152f35b602036600319011261008f57600435906001600160401b03821161008f573660238301121561008f5760048201356001600160401b03811161008f57366024828501011161008f57611a000361036157506040519061068061022d818461038e565b3683376000905b6034821061023e57005b8160071b8281046080148315171561034b578101602481013590604481013590608460648201359101359061027582828587610643565b15610318576040519260208401948552604084015260608301526080820152608081526102a360a08261038e565b5190209160005b8181106102c85750600191926102c0828661049d565b520190610234565b836102d3828761049d565b51146102e1576001016102aa565b60405162461bcd60e51b815260206004820152600f60248201526e6475706c69636174652076616c756560881b6044820152606490fd5b60405162461bcd60e51b815260206004820152600b60248201526a76616c75652072616e676560a81b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b62461bcd60e51b81526020600482015260086024820152673232b1b5903632b760c11b6044820152606490fd5b601f909101601f19168101906001600160401b038211908210176103b157604052565b634e487b7160e01b600052604160045260246000fd5b81601f8201121561008f578035906001600160401b0382116103b157604051926103fb601f8401601f19166020018561038e565b8284526020838301011161008f57816000926020809301838601378301015290565b604060031982011261008f57600435906001600160401b03821161008f57610447916004016103c7565b9060243590565b60005b8381106104615750506000910152565b8181015183820152602001610451565b60409160208252610491815180928160208601526020868601910161044e565b601f01601f1916010190565b9060348110156104ae5760051b0190565b634e487b7160e01b600052603260045260246000fd5b90603460ff831610156105b3576040516104df60a08261038e565b60808082523660208301376040908151936104fa838661038e565b601185526020850170504c53484f4c44454d5f434152445f563160781b815260005b600460ff8216106105525750505061054f92935061053c8251928361038e565b60018252600160f91b6020830152610760565b90565b600160ff9188875161059f6002602083818101955161057281888d61044e565b81018960f81b8c60f81b16838201528960f81b8860f81b1660218201520301601d1981018452018261038e565b51902060208260051b88010152011661051c565b60405162461bcd60e51b81526020600482015260086024820152671898590818d85c9960c21b6044820152606490fd5b9061054f916040519160208301526020825261019f60408361038e565b60005b603460ff82161061061557505060ff90565b8161061f826104c4565b602081519101201461063e5760ff1660ff811461034b57600101610603565b905090565b801580610758575b80610750575b80610745575b61073c577736f0255dde973dcb3b399d747f23e32ed6fdb1f77598338b198110610733577736f0255dde973dcb3b399d747f23e32ed6fdb1f77598338b191061072b576000805160206108ea8339815191528110610723576000805160206108ea8339815191521061071c5760008051602061090a83398151915281106107155760008051602061090a8339815191521061070f57600277771c35fdad44cfd2d74f9208be258ff324943328f67329c160411b031190565b50600090565b5050600190565b5050600090565b505050600190565b505050600090565b50505050600190565b50505050600090565b506001841115610657565b508215610651565b50811561064b565b91909160808151036108b957608090835160a061085e60405193610784838661038e565b85855260208501601f19840195863683377736f0255dde973dcb3b399d747f23e32ed6fdb1f77598338b1982526000805160206108ea833981519152604082015260008051602061090a8339815191526060820152600177771c35fdad44cfd2d74f9208be258ff324943328f67329c160411b038882015261084f886040519b8c956020870199838b52604088015282606088015261082c815180926020868b01910161044e565b8601610841825180936020868501910161044e565b01019283925193849161044e565b0103601f19810188528761038e565b61086b604051918261038e565b838152602081019236843794519060055afa1561088457565b60405162461bcd60e51b815260206004820152600d60248201526c1b5bd9195e1c0819985a5b1959609a1b6044820152606490fd5b60405162461bcd60e51b81526020600482015260086024820152673130b9b2903632b760c11b6044820152606490fdfe020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7eda2646970667358221220fe4da8e805a981b6b28046ad1f5f57fddb372485082facd8fe6163e78b0cab1f64736f6c634300081c0033