UNPKG

l1-lottery-contracts

Version:

This repo contains smart contracts and related scripts for ZkNoid L1 Lottery proposed [here](https://forums.minaprotocol.com/t/zknoid-l1-lottery/6269)

420 lines (366 loc) 11.6 kB
// import { Field, MerkleList, Mina, PrivateKey, PublicKey, UInt32 } from 'o1js'; // import { // NumberPacked, // PLotteryType, // PStateManager, // Ticket, // generateNumbersSeed, // } from '../../src'; // import { randomInt } from 'crypto'; // import { LotteryAction } from '../../src/Proofs/TicketReduceProof.js'; // import { RandomManagerType } from '../../src/Random/RandomManager.js'; // import { RandomManagerManager } from '../../src/StateManager/RandomManagerManager.js'; // const PLAYERS_AMOUNT = 10; // const generatePlayers = () => { // return [...Array(PLAYERS_AMOUNT)].map(() => PrivateKey.randomKeypair()); // }; // const players = generatePlayers(); // interface TicketInfo { // ticket: Ticket; // round: Field; // } // class Context { // lottery: PLotteryType; // randomManager: RandomManagerType; // lotterySM: PStateManager; // randomManagerSM: RandomManagerManager; // boughtTickets: TicketInfo[]; // usedTickets: TicketInfo[]; // } // class Deviation { // name: string; // field: string; // probability: number; // expectedError: string | undefined; // apply: () => Promise<void>; // } // type Account = { // publicKey: PublicKey; // privateKey: PrivateKey; // }; // abstract class TestEvent { // activeDeviations: Deviation[]; // sender: Account; // context: Context; // constructor(context: Context, sender: Account) { // this.context = context; // this.sender = sender; // } // static async randomValid(context: Context): Promise<TestEvent> { // throw Error('unimplemented'); // } // abstract getDeviations(): Deviation[]; // addDeviation(deviation: Deviation) { // this.activeDeviations.push(deviation); // deviation.apply(); // } // async safeInvoke() { // try { // await this.invoke(); // } catch (e) { // if (e != this.activeDeviations[0].expectedError) { // throw Error('Unhandled error'); // } // } // } // abstract invoke(): Promise<void>; // async checkedInoke() { // let shouldFail = this.activeDeviations.length > 0; // try { // await this.invoke(); // } catch (e) { // if (shouldFail) { // console.log(`Expected error ${e} occured`); // return; // } // throw Error(`Unexpected error ${e} occured`); // } // if (shouldFail) { // throw Error(`Expected error, but nothing heppen`); // return; // } // } // } // class BuyTicketEvent extends TestEvent { // ownerIndex: number; // ticket: Ticket; // round: Field; // constructor(context: Context, ownerIndex: number, ticket: Ticket) { // super(context, players[ownerIndex]); // this.ownerIndex = ownerIndex; // this.ticket = ticket; // } // static override async randomValid(context: Context): Promise<BuyTicketEvent> { // const ownerIndex = randomInt(PLAYERS_AMOUNT); // const owner = players[ownerIndex]; // const ticket = Ticket.random(owner.publicKey); // return new BuyTicketEvent(context, ownerIndex, ticket); // } // getDeviations(): Deviation[] { // return [ // { // name: 'wrong numbers', // field: 'ticket', // probability: 0.1, // apply: async () => { // this.ticket.numbers[randomInt(6)] = UInt32.from( // randomInt(10, +UInt32.MAXINT) // ); // }, // expectedError: '????', // }, // { // name: 'wrong owner', // field: 'sender', // probability: 0.1, // apply: async () => { // this.sender = players[(this.ownerIndex + randomInt(1, 10)) % 10]; // }, // expectedError: '????', // }, // { // name: 'wrong round', // field: 'round', // probability: 0.1, // apply: async () => { // // 50/50 less or greater // if (Math.random() > 0.5) { // this.round = Field(randomInt(+this.round + 1, +this.round + 1000)); // } else { // this.round = Field(randomInt(+this.round)); // } // }, // expectedError: '????', // }, // ]; // } // async invoke() { // let tx = Mina.transaction(this.sender.publicKey, async () => { // await this.context.lottery.buyTicket(this.ticket, this.round); // }); // await tx.prove(); // await tx.sign([this.sender.privateKey]).send(); // } // } // // class RefundTicketEvent extends TestEvent {} // class RedeemTicketEvent extends TestEvent { // round: Field; // ticket: Ticket; // constructor(context: Context, round: Field, ticket: Ticket) { // super(context, players[randomInt(players.length)]); // this.round = round; // this.ticket = ticket; // } // static override async randomValid( // context: Context // ): Promise<RedeemTicketEvent> { // const { ticket, round } = // context.boughtTickets[randomInt(context.boughtTickets.length)]; // return new RedeemTicketEvent(context, round, ticket); // } // getDeviations(): Deviation[] { // return [ // { // name: 'used ticket', // field: 'ticket', // probability: 0.1, // apply: async () => { // const { ticket, round } = // this.context.boughtTickets[ // randomInt(this.context.boughtTickets.length) // ]; // this.round = round; // this.ticket = ticket; // }, // expectedError: '????', // }, // ]; // } // async invoke() { // const rp = await this.context.lotterySM.getReward(+this.round, this.ticket); // let tx = await Mina.transaction(this.sender.publicKey, async () => { // await this.context.lottery.getReward( // this.ticket, // rp.roundWitness, // rp.roundTicketWitness, // rp.dp, // rp.winningNumbers, // rp.resultWitness, // rp.bankValue, // rp.bankWitness, // rp.nullifierWitness // ); // }); // await tx.prove(); // await tx.sign([this.sender.privateKey]).send(); // // Change bought ticket to used one // } // } // // class RandomValueGenerationEvent extends TestEvent {} // class ProduceResultEvent extends TestEvent { // round: Field; // randomRound: Field; // constructor(context: Context, round: Field) { // super(context, players[randomInt(players.length)]); // this.round = round; // this.randomRound = round; // } // getDeviations(): Deviation[] { // return [ // { // name: 'round-have-not-started', // field: 'round', // probability: 0.1, // expectedError: '???', // apply: async () => { // this.activeDeviations = this.activeDeviations.filter( // (v) => v.field != 'round' // ); // this.round = Field(randomInt(+this.round + 1, +this.round + 1000)); // }, // }, // { // name: 'round-has-result', // field: 'round', // probability: 0.1, // expectedError: '???', // apply: async () => { // this.activeDeviations = this.activeDeviations.filter( // (v) => v.field != 'round' // ); // this.round = Field(randomInt(+this.round)); // }, // }, // { // name: 'wrong-random-round', // field: 'randomRound', // probability: 0.1, // expectedError: '???', // apply: async () => { // let deviantRound = randomInt(+this.round * 2); // while (deviantRound == +this.round) { // deviantRound = randomInt(+this.round * 2); // } // this.randomRound = Field(deviantRound); // }, // }, // ]; // } // async invoke() { // const resultWV = this.context.randomManagerSM.getResultWitness( // this.randomRound // ); // const { resultWitness, bankValue, bankWitness } = // this.context.lotterySM.updateResult( // this.round, // NumberPacked.pack(generateNumbersSeed(resultWV.value)) // ); // let tx = Mina.transaction(this.sender.publicKey, async () => { // await this.context.lottery.produceResult( // resultWitness, // bankValue, // bankWitness, // resultWV.witness, // resultWV.value // ); // }); // await tx.prove(); // await tx.sign([this.sender.privateKey]).send(); // } // } // class ReduceTicketsEvent extends TestEvent { // fromState: Field; // toState: Field; // actions: LotteryAction[][]; // constructor( // context: Context, // sender: Account, // fromState: Field, // toState: Field, // actions: LotteryAction[][] // ) { // super(context, sender); // this.fromState = fromState; // this.toState = toState; // this.actions = actions; // } // static override async randomValid( // context: Context // ): Promise<ReduceTicketsEvent> { // const randomSender = players[randomInt(players.length)]; // const fromState = context.lottery.lastProcessedState.get(); // const toState = context.lottery.account.actionState.get(); // const actions = await context.lottery.reducer.fetchActions({ // fromActionState: fromState, // endActionState: toState, // }); // return new ReduceTicketsEvent( // context, // randomSender, // fromState, // toState, // actions // ); // } // getDeviations(): Deviation[] { // return [ // { // name: 'wrong fromState', // field: 'fromState', // probability: 0.1, // expectedError: '???', // apply: async () => { // this.fromState = this.toState; // Chenge to one step jump // this.actions = await this.context.lottery.reducer.fetchActions({ // fromActionState: this.fromState, // endActionState: this.toState, // }); // }, // }, // { // name: 'wrong actions', // field: 'actions', // probability: 0.1, // expectedError: '???', // apply: async () => { // let firstIndex = randomInt(this.actions.length); // let secondIndex = randomInt( // this.actions[randomInt(firstIndex)].length // ); // this.actions[firstIndex][secondIndex].ticket.numbers[0] = // this.actions[firstIndex][secondIndex].ticket.numbers[0] // .add(1) // .mod(10) // .add(1); // }, // }, // ]; // } // async invoke() { // const shouldFail = this.activeDeviations.length > 0; // let reduceProof = await this.context.lotterySM.reduceTickets( // this.fromState, // this.actions, // !shouldFail // ); // let tx = Mina.transaction(this.sender.publicKey, async () => { // await this.context.lottery.reduceTickets(reduceProof); // }); // await tx.prove(); // await tx.sign([this.sender.privateKey]).send(); // } // } // const events = [BuyTicketEvent, ReduceTicketsEvent]; // export class TestOperator { // async invokeNextEvent(context: Context) { // const eventType = events[randomInt(events.length)]; // const event = await eventType.randomValid(context); // const deviations = event.getDeviations(); // deviations.forEach((deviation) => { // if (deviation.probability > Math.random()) { // event.addDeviation(deviation); // } // }); // await event.invoke(); // } // }