feat: RLN (dynamic)

This commit is contained in:
Richard Ramos 2022-07-28 10:04:33 -04:00
parent 587fd148ca
commit 132ac128e5
14 changed files with 2209 additions and 31 deletions

10
waku.go
View File

@ -317,15 +317,17 @@ func main() {
Usage: "Rln relay identity commitment key as a Hex string",
Destination: &options.RLNRelay.IDCommitment,
},
// TODO: this is a good candidate option for subcommands
// TODO: consider accepting a private key file and passwd
&cli.StringFlag{
Name: "eth-account-address",
Usage: "Ethereum testnet account address",
Destination: &options.RLNRelay.ETHAccount,
Name: "eth-private-key",
Usage: "Ethereum testnet account private key used for registering in member contract",
Destination: &options.RLNRelay.ETHPrivateKey,
},
&cli.StringFlag{
Name: "eth-client-address",
Usage: "Ethereum testnet client address",
Value: "ws://localhost:8540",
Value: "ws://localhost:8545",
Destination: &options.RLNRelay.ETHClientAddress,
},
&cli.StringFlag{

View File

@ -15,6 +15,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/enode"
dssql "github.com/ipfs/go-ds-sql"
@ -262,6 +263,24 @@ func Execute(options Options) {
if !options.RLNRelay.Dynamic {
nodeOpts = append(nodeOpts, node.WithStaticRLNRelay(options.RLNRelay.PubsubTopic, options.RLNRelay.ContentTopic, rln.MembershipIndex(options.RLNRelay.MembershipIndex), nil))
} else {
var ethPrivKey *ecdsa.PrivateKey
if options.RLNRelay.ETHPrivateKey != "" {
k, err := crypto.HexToECDSA(options.RLNRelay.ETHPrivateKey)
failOnErr(err, "Invalid private key")
ethPrivKey = k
}
nodeOpts = append(nodeOpts, node.WithDynamicRLNRelay(
options.RLNRelay.PubsubTopic,
options.RLNRelay.ContentTopic,
rln.MembershipIndex(options.RLNRelay.MembershipIndex),
nil,
options.RLNRelay.ETHClientAddress,
ethPrivKey,
common.HexToAddress(options.RLNRelay.MembershipContractAddress),
))
}
}

View File

@ -47,7 +47,7 @@ type RLNRelayOptions struct {
Dynamic bool
IDKey string
IDCommitment string
ETHAccount string
ETHPrivateKey string
ETHClientAddress string
MembershipContractAddress string
}

View File

@ -685,7 +685,7 @@ func (w *WakuNode) mountRlnRelay() error {
}
if !w.opts.rlnRelayDynamic {
w.log.Info("setting up waku-rln-relay in off-chain mode... ")
w.log.Info("setting up waku-rln-relay in off-chain mode")
// set up rln relay inputs
groupKeys, memKeyPair, memIndex, err := rln.StaticSetup(w.opts.rlnRelayMemIndex)
if err != nil {
@ -693,7 +693,7 @@ func (w *WakuNode) mountRlnRelay() error {
}
// mount rlnrelay in off-chain mode with a static group of users
wakuRLNRelay, err := rln.RlnRelayStatic(w.relay, groupKeys, memKeyPair, memIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, nil, w.log)
wakuRLNRelay, err := rln.RlnRelayStatic(w.ctx, w.relay, groupKeys, memKeyPair, memIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.log)
if err != nil {
return err
}
@ -703,7 +703,6 @@ func (w *WakuNode) mountRlnRelay() error {
// check the correct construction of the tree by comparing the calculated root against the expected root
// no error should happen as it is already captured in the unit tests
root, err := wakuRLNRelay.RLN.GetMerkleRoot()
if err != nil {
return err
@ -714,12 +713,28 @@ func (w *WakuNode) mountRlnRelay() error {
return errors.New("root mismatch: something went wrong not in Merkle tree construction")
}
w.rlnRelay = wakuRLNRelay
w.log.Info("the calculated root", zap.String("root", hex.EncodeToString(root[:])))
w.log.Info("mounted waku RLN relay", zap.String("pubsubTopic", w.opts.rlnRelayPubsubTopic), zap.String("contentTopic", w.opts.rlnRelayContentTopic))
} else {
return errors.New("TODO: not implemented yet")
w.log.Info("setting up waku-rln-relay in on-chain mode")
// check if the peer has provided its rln credentials
var memKeyPair *r.MembershipKeyPair
if w.opts.rlnRelayIDCommitment != nil && w.opts.rlnRelayIDKey != nil {
memKeyPair = &r.MembershipKeyPair{
IDCommitment: *w.opts.rlnRelayIDCommitment,
IDKey: *w.opts.rlnRelayIDKey,
}
}
// mount the rln relay protocol in the on-chain/dynamic mode
var err error
w.rlnRelay, err = rln.RlnRelayDynamic(context.Background(), w.relay, w.opts.rlnETHClientAddress, w.opts.rlnETHPrivateKey, w.opts.rlnMembershipContractAddress, memKeyPair, w.opts.rlnRelayMemIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.log)
if err != nil {
return err
}
}
w.log.Info("mounted waku RLN relay", zap.String("pubsubTopic", w.opts.rlnRelayPubsubTopic), zap.String("contentTopic", w.opts.rlnRelayContentTopic))
return nil
}

View File

@ -8,6 +8,7 @@ import (
"net"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/crypto"
@ -84,12 +85,17 @@ type WakuNodeParameters struct {
discV5Opts []pubsub.DiscoverOpt
discV5autoUpdate bool
enableRLN bool
rlnRelayMemIndex r.MembershipIndex
rlnRelayPubsubTopic string
rlnRelayContentTopic string
rlnRelayDynamic bool
rlnSpamHandler rln.SpamHandler
enableRLN bool
rlnRelayMemIndex r.MembershipIndex
rlnRelayPubsubTopic string
rlnRelayContentTopic string
rlnRelayDynamic bool
rlnSpamHandler rln.SpamHandler
rlnRelayIDKey *r.IDKey
rlnRelayIDCommitment *r.IDCommitment
rlnETHPrivateKey *ecdsa.PrivateKey
rlnETHClientAddress string
rlnMembershipContractAddress common.Address
keepAliveInterval time.Duration
@ -428,6 +434,22 @@ func WithStaticRLNRelay(pubsubTopic string, contentTopic string, memberIndex r.M
}
}
func WithDynamicRLNRelay(pubsubTopic string, contentTopic string, memberIndex r.MembershipIndex, spamHandler rln.SpamHandler, ethClientAddress string, ethPrivateKey *ecdsa.PrivateKey, membershipContractAddress common.Address) WakuNodeOption {
return func(params *WakuNodeParameters) error {
params.enableRLN = true
params.rlnRelayDynamic = true
params.rlnRelayMemIndex = memberIndex
params.rlnRelayPubsubTopic = pubsubTopic
params.rlnRelayContentTopic = contentTopic
params.rlnSpamHandler = spamHandler
params.rlnETHClientAddress = ethClientAddress
params.rlnETHPrivateKey = ethPrivateKey
params.rlnMembershipContractAddress = membershipContractAddress
return nil
}
}
// Default options used in the libp2p node
var DefaultLibP2POptions = []libp2p.Option{
libp2p.ChainOptions(

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,98 @@
// https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/RLN.sol
pragma solidity 0.7.4;
import { IPoseidonHasher } from "./crypto/PoseidonHasher.sol";
contract RLN {
uint256 public immutable MEMBERSHIP_DEPOSIT;
uint256 public immutable DEPTH;
uint256 public immutable SET_SIZE;
uint256 public pubkeyIndex = 0;
mapping(uint256 => uint256) public members;
IPoseidonHasher public poseidonHasher;
event MemberRegistered(uint256 indexed pubkey, uint256 indexed index);
event MemberWithdrawn(uint256 indexed pubkey, uint256 indexed index);
constructor(
uint256 membershipDeposit,
uint256 depth,
address _poseidonHasher
) public {
MEMBERSHIP_DEPOSIT = membershipDeposit;
DEPTH = depth;
SET_SIZE = 1 << depth;
poseidonHasher = IPoseidonHasher(_poseidonHasher);
}
function register(uint256 pubkey) external payable {
require(pubkeyIndex < SET_SIZE, "RLN, register: set is full");
require(msg.value == MEMBERSHIP_DEPOSIT, "RLN, register: membership deposit is not satisfied");
_register(pubkey);
}
function registerBatch(uint256[] calldata pubkeys) external payable {
require(pubkeyIndex + pubkeys.length <= SET_SIZE, "RLN, registerBatch: set is full");
require(msg.value == MEMBERSHIP_DEPOSIT * pubkeys.length, "RLN, registerBatch: membership deposit is not satisfied");
for (uint256 i = 0; i < pubkeys.length; i++) {
_register(pubkeys[i]);
}
}
function _register(uint256 pubkey) internal {
members[pubkeyIndex] = pubkey;
emit MemberRegistered(pubkey, pubkeyIndex);
pubkeyIndex += 1;
}
function withdrawBatch(
uint256[] calldata secrets,
uint256[] calldata pubkeyIndexes,
address payable[] calldata receivers
) external {
uint256 batchSize = secrets.length;
require(batchSize != 0, "RLN, withdrawBatch: batch size zero");
require(batchSize == pubkeyIndexes.length, "RLN, withdrawBatch: batch size mismatch pubkey indexes");
require(batchSize == receivers.length, "RLN, withdrawBatch: batch size mismatch receivers");
for (uint256 i = 0; i < batchSize; i++) {
_withdraw(secrets[i], pubkeyIndexes[i], receivers[i]);
}
}
function withdraw(
uint256 secret,
uint256 _pubkeyIndex,
address payable receiver
) external {
_withdraw(secret, _pubkeyIndex, receiver);
}
function _withdraw(
uint256 secret,
uint256 _pubkeyIndex,
address payable receiver
) internal {
require(_pubkeyIndex < SET_SIZE, "RLN, _withdraw: invalid pubkey index");
require(members[_pubkeyIndex] != 0, "RLN, _withdraw: member doesn't exist");
require(receiver != address(0), "RLN, _withdraw: empty receiver address");
// derive public key
uint256 pubkey = hash([secret, 0]);
require(members[_pubkeyIndex] == pubkey, "RLN, _withdraw: not verified");
// delete member
members[_pubkeyIndex] = 0;
// refund deposit
receiver.transfer(MEMBERSHIP_DEPOSIT);
emit MemberWithdrawn(pubkey, _pubkeyIndex);
}
function hash(uint256[2] memory input) internal view returns (uint256) {
return poseidonHasher.hash(input);
}
}

View File

@ -0,0 +1,535 @@
// https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/crypto/PoseidonHasher.sol
pragma solidity 0.7.4;
interface IPoseidonHasher {
function hash(uint256[2] memory input) external pure returns (uint256 result);
function identity() external pure returns (uint256);
}
contract PoseidonHasher is IPoseidonHasher {
uint256 constant Q = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant C0 = 2768970367241139781802170833007148522174069744848643939234067191877771423371;
uint256 constant C1 = 6468574107656347268529237497718336587709069456280471580424455063721832719771;
uint256 constant C2 = 18794671303815708509784162368644133457448839260573151733485130900959816547847;
uint256 constant C3 = 21495479230478098685877232964279351350768322664786520160088149860043275231591;
uint256 constant C4 = 13499990804456294376331975175669076043666672782766959528979369127842395667962;
uint256 constant C5 = 11128727858753328237562379127901473551876426168355298792273038389175814738273;
uint256 constant C6 = 6174623859769212836350518599861056828848352474434521101649917228563284157731;
uint256 constant C7 = 731429061815452030222739129549549044853294286077603862495031070831228988506;
uint256 constant C8 = 18691825946094184197601861261718857711439992232978967087150603050561229972879;
uint256 constant C9 = 6901715409529528992021081413007214967207374659260807285380744444309443019002;
uint256 constant C10 = 7889774703159213060685393199720756541539275646424166159037924832893670779999;
uint256 constant C11 = 19148164181730036907570646123652401621475121269795476659255226436085540432812;
uint256 constant C12 = 356519004282890736138910009474234839367086357569508205089190200287135015343;
uint256 constant C13 = 5070064330380484074844995745007722918545766490350556589468108281662282971512;
uint256 constant C14 = 17531425645708334340342421865584088711059286683026910061769879104052748058500;
uint256 constant C15 = 920858801565935944623869688790191117976818337296182581403952541435643043455;
uint256 constant C16 = 7671542183617251806500810220803333911317855932989746628699368932279406768167;
uint256 constant C17 = 7881690674485945577862776772342991782415236259307258648854817257894786877801;
uint256 constant C18 = 19155896751615806549889067080381563929649717724892491357642801041215501837659;
uint256 constant C19 = 9657176770419678097838646349393281787292209556252142440714968703151552794280;
uint256 constant C20 = 1376534476338443314506167934949939673567644510933411653555577922098640496938;
uint256 constant C21 = 17373401671868429549652772677366856294616993437583002599910365256220696563780;
uint256 constant C22 = 9746128003951157537728271629364357225604237114762257721895149249266834505625;
uint256 constant C23 = 17782316906978487011972931426261039391542241330560756508577882755911543517908;
uint256 constant C24 = 6589224737393540076643416156354549730831080306137040791236143083907274321011;
uint256 constant C25 = 17321856211701727875100310423669922181823762220382619531116210312384063527081;
uint256 constant C26 = 21321909001724533038371153813693276559530291455431564073497502239396933319818;
uint256 constant C27 = 17849794853142289789836828064662594833303692772683927435966421769162340090898;
uint256 constant C28 = 3469446763321625523201767743510820386262850368964003752018684310306935672122;
uint256 constant C29 = 4411200946062535851397814394775457104021582693964231752948256822578636459939;
uint256 constant C30 = 18626579782117576204277882219675177684589838126472487652629778677837675278662;
uint256 constant C31 = 764261930639154699767774045099363658161006905585481283735008957581415860662;
uint256 constant C32 = 6122983345977564957406231576949193286198709429730716375547595362175192593123;
uint256 constant C33 = 2469188694307740124657403582315280809174737971010077914159812443136587941756;
uint256 constant C34 = 5382605971470296415808536561267917764719132018437742878552512928718168489695;
uint256 constant C35 = 6589667679759352998039311059993721585330589021804485020627575514256149305666;
uint256 constant C36 = 20661471625156349582050865201555079410137144327315643708284763422133754381325;
uint256 constant C37 = 18694584274137621868895160209707042629719656780873050479100267791928497600376;
uint256 constant C38 = 13822590562029956399621741895244911145933359734099212387467069390727913142666;
uint256 constant C39 = 694674849457359992382704784272941309716157308424480468618786287819256378917;
uint256 constant C40 = 18763037144982179742008371677750585549220053470184500098542470704194556690623;
uint256 constant C41 = 18486457976264107916834430437402981813449917200007719339429151501964183082936;
uint256 constant C42 = 6556747954001777118869362753998468478855885460502383141235961204400638463294;
uint256 constant C43 = 12385133754714463216711399717325449316164682530612784512290690756146937377664;
uint256 constant C44 = 19895348036861519723386861870711743699604138662509897355377030959682349375552;
uint256 constant C45 = 16830822151531046474312344756361243376821272897299262546129244900502111682734;
uint256 constant C46 = 20875209030695524214960780268667166557156788659940284813793529164900666593068;
uint256 constant C47 = 8777606742656153264634386709062440729894098167014885703560793183003141571957;
uint256 constant C48 = 3404497983786933300887052921900023151252717521554164668384012034556256626067;
uint256 constant C49 = 2555107629226010512992356367848094165962022522393505508367085820070687884355;
uint256 constant C50 = 15113919336730148565553505760820732613676431799289611544548947520407297443700;
uint256 constant C51 = 12109280671570307231007703707720632582709786369013364642897329602318304275337;
uint256 constant C52 = 14968914070423010632399365015358049892440762915276845426568596163422798377730;
uint256 constant C53 = 19061189532957010364709072574663003871548558794774753349573267371990486123152;
uint256 constant C54 = 8682801373989521576522508447484143687434748406228624907362693631377691698421;
uint256 constant C55 = 10836672514863795455996193088790111107823297731722043988991810099345596059914;
uint256 constant C56 = 15783196349960742821266383686606772431598902255698169764526487081837237805029;
uint256 constant C57 = 2908608383546665417899486637528022511748691900266764564499649456344035921401;
uint256 constant C58 = 11329727236614532496067886659074662970462346457991275919233250048151273297377;
uint256 constant C59 = 5116653167814666817290578004214535117470888177090558374943095066718387153691;
uint256 constant C60 = 13540152826778549413479417002435847356268346234631393477044136590757331815911;
uint256 constant C61 = 7947441526608065582264952807420254797193034288774248212171921641395121801332;
uint256 constant C62 = 1547483282147791156522335107475157206253201614573941966672490820147450725004;
uint256 constant M0 = 10115680371401748607263639966297231210785101245789087039760048681884121897698;
uint256 constant M1 = 7682529184580308813007726368887641784100790067958820768670102409394963579396;
uint256 constant M2 = 3036380094837744536618704667164758501777151816139077990926623931426168668495;
uint256 constant M3 = 462021688665431536448264191536285854244979957674130050775621906384644169421;
uint256 constant M4 = 62665112908715427992420108025470785827183241813778563254523765114425841603;
uint256 constant M5 = 534236519266104271325123542701236478972353175554309786393016883839422407419;
uint256 constant M6 = 5922526147398848214826466482573128257954044394758836008121691281747299315999;
uint256 constant M7 = 10778490508693548114587990025762035374898560218037495485187480124708600063292;
uint256 constant M8 = 13853145901042782779715203529626320031162484246107565810821674712070680357632;
function hash(uint256[2] memory input) external pure override returns (uint256 result) {
return _hash(input);
}
function _hash(uint256[2] memory input) internal pure returns (uint256 result) {
assembly {
let q := Q
let pos := mload(0x40)
mstore(pos, M0)
mstore(add(pos, 32), M1)
mstore(add(pos, 64), M2)
mstore(add(pos, 96), M3)
mstore(add(pos, 128), M4)
// mstore(add(pos, 160), M5)
// mstore(add(pos, 192), M6)
// mstore(add(pos, 224), M7)
// mstore(add(pos, 256), M8)
// use stack intensively
let m5 := M5
let m6 := M6
let m7 := M7
let m8 := M8
let s0 := add(mload(input), C0)
let s1 := add(mload(add(input, 32)), C0)
let s2 := C0
let t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := mulmod(s1, s1, q)
s1 := mulmod(mulmod(t, t, q), s1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
t := C1
let z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
let z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := mulmod(z1, z1, q)
z1 := mulmod(mulmod(t, t, q), z1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
t := C2
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := mulmod(s1, s1, q)
s1 := mulmod(mulmod(t, t, q), s1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
t := C3
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := mulmod(z1, z1, q)
z1 := mulmod(mulmod(t, t, q), z1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
t := C4
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C5
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C6
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C7
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C8
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C9
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C10
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C11
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C12
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C13
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C14
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C15
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C16
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C17
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C18
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C19
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C20
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C21
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C22
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C23
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C24
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C25
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C26
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C27
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C28
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C29
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C30
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C31
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C32
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C33
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C34
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C35
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C36
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C37
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C38
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C39
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C40
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C41
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C42
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C43
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C44
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C45
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C46
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C47
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C48
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C49
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C50
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C51
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C52
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C53
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C54
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C55
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C56
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C57
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := C58
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := C59
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := mulmod(z1, z1, q)
z1 := mulmod(mulmod(t, t, q), z1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
t := C60
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := mulmod(s1, s1, q)
s1 := mulmod(mulmod(t, t, q), s1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
t := C61
z0 := add(add(add(mulmod(s0, mload(pos), q), mulmod(s1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
z1 := add(add(add(mulmod(s0, mload(add(pos, 96)), q), mulmod(s1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(s0, m6, q), mulmod(s1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(z0, z0, q)
z0 := mulmod(mulmod(t, t, q), z0, q)
t := mulmod(z1, z1, q)
z1 := mulmod(mulmod(t, t, q), z1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
t := C62
s0 := add(add(add(mulmod(z0, mload(pos), q), mulmod(z1, mload(add(pos, 32)), q)), mulmod(s2, mload(add(pos, 64)), q)), t)
s1 := add(add(add(mulmod(z0, mload(add(pos, 96)), q), mulmod(z1, mload(add(pos, 128)), q)), mulmod(s2, m5, q)), t)
s2 := add(add(add(mulmod(z0, m6, q), mulmod(z1, m7, q)), mulmod(s2, m8, q)), t)
t := mulmod(s0, s0, q)
s0 := mulmod(mulmod(t, t, q), s0, q)
t := mulmod(s1, s1, q)
s1 := mulmod(mulmod(t, t, q), s1, q)
t := mulmod(s2, s2, q)
s2 := mulmod(mulmod(t, t, q), s2, q)
result := s0
}
}
function identity() external pure override returns (uint256) {
return _identity();
}
function _identity() internal pure returns (uint256) {
return 0x2ff267fd23782a5625e6d804f0a7fa700b8dc6084e2e7a5aff7cd4b1c506d30b;
}
}

View File

@ -0,0 +1,3 @@
package contracts
//go:generate abigen -sol ./RLN.sol -pkg contracts -out ./RLN.go

View File

@ -1,14 +1,18 @@
package rln
import (
"context"
"crypto/ecdsa"
"errors"
"github.com/ethereum/go-ethereum/common"
r "github.com/status-im/go-rln/rln"
"github.com/status-im/go-waku/waku/v2/protocol/relay"
"go.uber.org/zap"
)
func RlnRelayStatic(
ctx context.Context,
relay *relay.WakuRelay,
group []r.IDCommitment,
memKeyPair r.MembershipKeyPair,
@ -47,6 +51,7 @@ func RlnRelayStatic(
// create the WakuRLNRelay
rlnPeer := &WakuRLNRelay{
ctx: ctx,
membershipKeyPair: memKeyPair,
membershipIndex: memIndex,
RLN: rlnInstance,
@ -68,3 +73,92 @@ func RlnRelayStatic(
return rlnPeer, nil
}
func RlnRelayDynamic(
ctx context.Context,
relay *relay.WakuRelay,
ethClientAddr string,
ethAccountPrivateKey *ecdsa.PrivateKey,
memContractAddr common.Address,
memKeyPair *r.MembershipKeyPair,
memIndex r.MembershipIndex,
pubsubTopic string,
contentTopic string,
spamHandler SpamHandler,
log *zap.Logger,
) (*WakuRLNRelay, error) {
log = log.Named("rln-dynamic")
log.Info("mounting rln-relay in onchain/dynamic mode")
// create an RLN instance
parameters, err := parametersKeyBytes()
if err != nil {
return nil, err
}
rlnInstance, err := r.NewRLN(parameters)
if err != nil {
return nil, err
}
// create the WakuRLNRelay
rlnPeer := &WakuRLNRelay{
ctx: ctx,
membershipIndex: memIndex,
membershipContractAddress: memContractAddr,
ethClientAddress: ethClientAddr,
ethAccountPrivateKey: ethAccountPrivateKey,
RLN: rlnInstance,
pubsubTopic: pubsubTopic,
contentTopic: contentTopic,
log: log,
nullifierLog: make(map[r.Epoch][]r.ProofMetadata),
}
// prepare rln membership key pair
if memKeyPair == nil {
log.Debug("no rln-relay key is provided, generating one")
memKeyPair, err = rlnInstance.MembershipKeyGen()
if err != nil {
return nil, err
}
// register the rln-relay peer to the membership contract
membershipIndex, err := rlnPeer.Register(ctx)
if err != nil {
return nil, err
}
rlnPeer.membershipKeyPair = *memKeyPair
rlnPeer.membershipIndex = *membershipIndex
log.Info("registered peer into the membership contract")
} else {
rlnPeer.membershipKeyPair = *memKeyPair
}
handler := func(pubkey r.IDCommitment, index r.MembershipIndex) error {
log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]))
// assuming all the members arrive in order
if !rlnInstance.InsertMember(pubkey) {
return errors.New("couldn't insert member")
}
return nil
}
go rlnPeer.HandleGroupUpdates(handler)
// adds a topic validator for the supplied pubsub topic at the relay protocol
// messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped
// the topic validator checks for the correct non-spamming proof of the message
err = rlnPeer.addValidator(relay, pubsubTopic, contentTopic, spamHandler)
if err != nil {
return nil, err
}
log.Info("rln relay topic validator mounted", zap.String("pubsubTopic", pubsubTopic), zap.String("contentTopic", contentTopic))
return rlnPeer, nil
}

View File

@ -52,7 +52,7 @@ func (s *WakuRLNRelaySuite) TestOffchainMode() {
// index also represents the index of the leaf in the Merkle tree that contains node's commitment key
index := r.MembershipIndex(5)
wakuRLNRelay, err := RlnRelayStatic(relay, groupIDCommitments, groupKeyPairs[index], index, RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, utils.Logger())
wakuRLNRelay, err := RlnRelayStatic(context.TODO(), relay, groupIDCommitments, groupKeyPairs[index], index, RLNRELAY_PUBSUB_TOPIC, RLNRELAY_CONTENT_TOPIC, nil, utils.Logger())
s.NoError(err)
// get the root of Merkle tree which is constructed inside the mountRlnRelay proc

View File

@ -1,16 +1,5 @@
package rln
/*
# inputs of the membership contract constructor
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils
const
MEMBERSHIP_FEE* = 5.u256
*/
// TODO the ETH_CLIENT should be an input to the rln-relay, though hardcoded for now
// the current address is the address of ganache-cli when run locally
const ETH_CLIENT = "ws://localhost:8540/"
type MessageValidationResult int
const (

View File

@ -26,6 +26,8 @@ const MAX_CLOCK_GAP_SECONDS = 20
const MAX_EPOCH_GAP = int64(MAX_CLOCK_GAP_SECONDS / r.EPOCH_UNIT_SECONDS)
type WakuRLNRelay struct {
ctx context.Context
membershipKeyPair r.MembershipKeyPair
// membershipIndex denotes the index of a leaf in the Merkle tree
@ -34,7 +36,6 @@ type WakuRLNRelay struct {
membershipIndex r.MembershipIndex
membershipContractAddress common.Address
ethClientAddress string
ethAccountAddress common.Address
// ethAccountPrivateKey is required for signing transactions
// TODO may need to erase this ethAccountPrivateKey when is not used
// TODO may need to make ethAccountPrivateKey mandatory

View File

@ -0,0 +1,178 @@
package rln
import (
"context"
"crypto/ecdsa"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
r "github.com/status-im/go-rln/rln"
"github.com/status-im/go-waku/waku/v2/protocol/rln/contracts"
"go.uber.org/zap"
)
var MEMBERSHIP_FEE = big.NewInt(5) // wei
func toBigInt(i []byte) *big.Int {
result := new(big.Int)
result.SetBytes(i[:])
return result
}
func register(ctx context.Context, idComm r.IDCommitment, ethAccountPrivateKey *ecdsa.PrivateKey, ethClientAddress string, membershipContractAddress common.Address, log *zap.Logger) (*r.MembershipIndex, error) {
backend, err := ethclient.Dial(ethClientAddress)
if err != nil {
return nil, err
}
defer backend.Close()
chainID, err := backend.ChainID(context.Background())
if err != nil {
return nil, err
}
auth, err := bind.NewKeyedTransactorWithChainID(ethAccountPrivateKey, chainID)
if err != nil {
return nil, err
}
auth.Value = MEMBERSHIP_FEE
auth.Context = ctx
rlnContract, err := contracts.NewRLN(membershipContractAddress, backend)
if err != nil {
return nil, err
}
log.Debug("registering an id commitment", zap.Binary("idComm", idComm[:]))
// registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress
tx, err := rlnContract.Register(auth, toBigInt(idComm[:]))
if err != nil {
return nil, err
}
log.Info("transaction broadcasted", zap.String("transactionHash", tx.Hash().Hex()))
txReceipt, err := bind.WaitMined(ctx, backend, tx)
if err != nil {
return nil, err
}
if txReceipt.Status != types.ReceiptStatusSuccessful {
return nil, errors.New("transaction reverted")
}
// the receipt topic holds the hash of signature of the raised events
evt, err := rlnContract.ParseMemberRegistered(*txReceipt.Logs[0])
if err != nil {
return nil, err
}
var eventIdComm r.IDCommitment
copy(eventIdComm[:], evt.Pubkey.Bytes())
log.Debug("the identity commitment key extracted from tx log", zap.Binary("eventIdComm", eventIdComm[:]))
if eventIdComm != idComm {
return nil, errors.New("invalid id commitment key")
}
result := new(r.MembershipIndex)
*result = r.MembershipIndex(uint(evt.Index.Int64()))
// debug "the index of registered identity commitment key", eventIndex=eventIndex
log.Debug("the index of registered identity commitment key", zap.Uint("eventIndex", uint(*result)))
return result, nil
}
// Register registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey
// into the membership contract whose address is in rlnPeer.membershipContractAddress
func (rln *WakuRLNRelay) Register(ctx context.Context) (*r.MembershipIndex, error) {
pk := rln.membershipKeyPair.IDCommitment
return register(ctx, pk, rln.ethAccountPrivateKey, rln.ethClientAddress, rln.membershipContractAddress, rln.log)
}
// the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
type RegistrationEventHandler = func(pubkey r.IDCommitment, index r.MembershipIndex) error
func processLogs(evt *contracts.RLNMemberRegistered, handler RegistrationEventHandler) {
if evt == nil {
return
}
var pubkey r.IDCommitment
copy(pubkey[:], evt.Pubkey.Bytes())
index := r.MembershipIndex(uint(evt.Index.Int64()))
handler(pubkey, index)
}
// HandleGroupUpdates mounts the supplied handler for the registration events emitting from the membership contract
// It connects to the eth client, subscribes to the `MemberRegistered` event emitted from the `MembershipContract`
// and collects all the events, for every received event, it calls the `handler`
func (rln *WakuRLNRelay) HandleGroupUpdates(handler RegistrationEventHandler) error {
backend, err := ethclient.Dial(rln.ethClientAddress)
if err != nil {
return err
}
defer backend.Close()
rlnContract, err := contracts.NewRLN(rln.membershipContractAddress, backend)
if err != nil {
return err
}
// TODO: process log should have a channel that consumes logs and has a buffer to receive a lot of events
// TODO: an error channel is required
rln.loadOldEvents(rlnContract, handler)
rln.watchNewEvents(rlnContract, handler, rln.log)
return nil
}
func (rln *WakuRLNRelay) loadOldEvents(rlnContract *contracts.RLN, handler RegistrationEventHandler) error {
// Get old events should
logIterator, err := rlnContract.FilterMemberRegistered(&bind.FilterOpts{Start: 0, End: nil, Context: rln.ctx}, []*big.Int{}, []*big.Int{})
if err != nil {
return err
}
for {
if !logIterator.Next() || logIterator.Error() != nil {
break
}
go processLogs(logIterator.Event, handler)
}
return nil
}
func (rln *WakuRLNRelay) watchNewEvents(rlnContract *contracts.RLN, handler RegistrationEventHandler, log *zap.Logger) error {
// Watch for new events
logSink := make(chan *contracts.RLNMemberRegistered)
subs, err := rlnContract.WatchMemberRegistered(&bind.WatchOpts{Context: rln.ctx, Start: nil}, logSink, []*big.Int{}, []*big.Int{})
if err != nil {
return err
}
for {
select {
case evt := <-logSink:
go processLogs(evt, handler)
case <-rln.ctx.Done():
subs.Unsubscribe()
close(logSink)
return nil
case err := <-subs.Err():
log.Error("watching new events", zap.Error(err))
close(logSink)
return nil
}
}
}