diff --git a/package.json b/package.json index ceccd48..9eebc71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "l1-lottery-contracts", - "version": "0.7.41", + "version": "0.7.43", "description": "", "author": "", "license": "Apache-2.0", diff --git a/random_request_cid.js b/random_request_cid.js index adc2d6f..4f2dfa8 100644 --- a/random_request_cid.js +++ b/random_request_cid.js @@ -1 +1 @@ -export const cidBuffer = 'bafkreiawtijhfijbc6qflpakv5j6tm4xzpkfy72uahbvre33ybztejr5em' \ No newline at end of file +export const cidBuffer = 'bafkreiemddwsrxjiy34ktrqolqbjggidkqhgevvfzss6ayp2clcczcuebi' \ No newline at end of file diff --git a/random_request_file b/random_request_file index 356581f..b14d3c6 100644 --- a/random_request_file +++ b/random_request_file @@ -2,5 +2,5 @@ "method": "GET", "baseURL": "https://random-data-api.com/api/number/random_number", "path": "number", - "zkapp": "import{Field as c,MerkleMap as D,Poseidon as M,PublicKey as L,SmartContract as W,State as h,Struct as G,UInt32 as V,ZkProgram as H,method as R,state as g}from\"o1js\";import{PublicKey as v,UInt64 as T}from\"o1js\";var Q=T.from(10*10**9),I=480;var Y=v.fromBase58(\"B62qnBkcyABfjz2cqJPzNZKjVt9M9kx1vgoiWLbkJUnk16Cz8KX8qC4\"),q=v.fromBase58(\"B62qnmsn4Bm4MzPujKeN1faxedz4p1cCAwA9mKAWzDjfb4c1ysVvWeK\");import{ZkonZkProgram as J,ZkonRequestCoordinator as S,ExternalRequestEvent as F}from\"zkon-zkapp\";import{Field as y,Provable as E,provable as j,Struct as _,UInt32 as m,UInt64 as st}from\"o1js\";import{StringCircuitValue as P}from\"zkon-zkapp\";var x=\"bafkreif2uuhoeskvfumqoxy7ylmjylnipg4ftm3dgslsheud7rim42b5eq\";var N=254n,U=7,B=32n;function Z(o,t,n){if(n*BigInt(t)>N)throw new Error(\"The Packing Plant is only accepting orders that can fit into one Field, try using MultiPackingPlant\");class r extends _({packed:y}){constructor(e){super({packed:e})}static extractField(e){throw new Error(\"Must implement extractField\")}static sizeInBits(){throw new Error(\"Must implement sizeInBits\")}static unpack(e){throw new Error(\"Must implement unpack\")}static checkPack(e){if(e.length>t)throw new Error(`Input of size ${e.length} is larger than expected size of ${t}`)}static pack(e){this.checkPack(e);let i=this.extractField(e[0]),l=Math.min(e.length,t);for(let d=1;d>=this.sizeInBits();return i}toFields(){return[this.packed]}assertEquals(e){this.packed.assertEquals(e.packed)}}return r.type=j({packed:y},{}),r.l=t,r.bitSize=n,r}function K(o=U){class t extends Z(m,o,B){static extractField(r){return r.value}static sizeInBits(){return B}static unpack(r){let s=E.witness(E.Array(m,o),()=>this.unpackToBigints(r).map(i=>m.from(i)));return r.assertEquals(t.pack(s)),s}static fromUInt32s(r){let s=t.pack(r);return new t(s)}static fromBigInts(r){let s=r.map(e=>m.from(e));return t.fromUInt32s(s)}toBigInts(){return t.unpack(this.packed).map(r=>r.toBigint())}}return t}var w=class extends K(){};var C=()=>{function o(t){let n=t.slice(0,30),r=t.slice(30),s=new P(n).toField(),e=new P(r).toField();return{hashPart1:s,hashPart2:e}}return o(x.toString())};var p=function(o,t,n,r){var s=arguments.length,e=s<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,i;if(typeof Reflect==\"object\"&&typeof Reflect.decorate==\"function\")e=Reflect.decorate(o,t,n,r);else for(var l=o.length-1;l>=0;l--)(i=o[l])&&(e=(s<3?i(e):s>3?i(t,n,e):i(t,n))||e);return s>3&&e&&Object.defineProperty(t,n,e),e},a=function(o,t){if(typeof Reflect==\"object\"&&typeof Reflect.metadata==\"function\")return Reflect.metadata(o,t)},dt=new D().getRoot(),X=H.Proof(J),k=class extends X{},f=class extends G({value:c,salt:c}){hash(){return M.hash([this.value,this.salt])}},{hashPart1:A,hashPart2:b}=C(),O=q,pt=L.fromBase58(\"B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn\"),u=class extends W{constructor(){super(...arguments),this.startSlot=h(),this.commit=h(),this.result=h(),this.curRandomValue=h(),this.events={requested:F}}async commitValue(t){this.permissionCheck(),this.commit.getAndRequireEquals().assertEquals(c(0),\"Already committed\"),this.commit.set(t.hash())}async reveal(t){this.permissionCheck(),this.result.getAndRequireEquals().assertEquals(c(0),\"reveal: Result already computed\");let r=this.curRandomValue.getAndRequireEquals();this.commit.getAndRequireEquals().assertEquals(t.hash(),\"reveal: wrong commit value\"),this.checkRoundPass();let e=M.hash([t.value,r]);this.result.set(e)}async callZkon(){this.curRandomValue.getAndRequireEquals().assertEquals(c(0),\"random value have already been computed\");let r=await new S(O).sendRequest(this.address,A,b),s=new F({id:r,hash1:A,hash2:b});this.emitEvent(\"requested\",s)}async receiveZkonResponse(t,n){this.curRandomValue.getAndRequireEquals().assertEquals(c(0),\"receiveZkonResponse: prev random value was not consumed. Call reveal first\"),await new S(O).recordRequestFullfillment(t,n),this.curRandomValue.set(n.publicInput.dataField)}permissionCheck(){}checkRoundPass(){let t=this.startSlot.getAndRequireEquals();this.network.globalSlotSinceGenesis.requireBetween(t.add(I),V.MAXINT())}};p([g(V),a(\"design:type\",Object)],u.prototype,\"startSlot\",void 0);p([g(c),a(\"design:type\",Object)],u.prototype,\"commit\",void 0);p([g(c),a(\"design:type\",Object)],u.prototype,\"result\",void 0);p([g(c),a(\"design:type\",Object)],u.prototype,\"curRandomValue\",void 0);p([R,a(\"design:type\",Function),a(\"design:paramtypes\",[f]),a(\"design:returntype\",Promise)],u.prototype,\"commitValue\",null);p([R,a(\"design:type\",Function),a(\"design:paramtypes\",[f]),a(\"design:returntype\",Promise)],u.prototype,\"reveal\",null);p([R,a(\"design:type\",Function),a(\"design:paramtypes\",[c,k]),a(\"design:returntype\",Promise)],u.prototype,\"receiveZkonResponse\",null);var mt=u;export{f as CommitValue,u as RandomManager,k as ZkonProof,X as ZkonProof_,mt as default};\n" + "zkapp": "import{Field as c,MerkleMap as D,Poseidon as M,PublicKey as L,SmartContract as W,State as h,Struct as G,UInt32 as z,ZkProgram as H,method as R,state as g}from\"o1js\";import{PublicKey as v,UInt64 as j}from\"o1js\";var Q=j.from(10*10**9),I=480;var Y=v.fromBase58(\"B62qnBkcyABfjz2cqJPzNZKjVt9M9kx1vgoiWLbkJUnk16Cz8KX8qC4\"),q=v.fromBase58(\"B62qnmsn4Bm4MzPujKeN1faxedz4p1cCAwA9mKAWzDjfb4c1ysVvWeK\");import{ZkonZkProgram as J,ZkonRequestCoordinator as S,ExternalRequestEvent as F}from\"zkon-zkapp\";import{Field as k,Provable as E,provable as T,Struct as _,UInt32 as m,UInt64 as st}from\"o1js\";import{StringCircuitValue as P}from\"zkon-zkapp\";var x=\"bafkreibjk5hg5nd7j6uow62yv445wafadoqr7klvkmz7qakugx42fwt53y\";var N=254n,U=7,B=32n;function Z(o,t,n){if(n*BigInt(t)>N)throw new Error(\"The Packing Plant is only accepting orders that can fit into one Field, try using MultiPackingPlant\");class r extends _({packed:k}){constructor(e){super({packed:e})}static extractField(e){throw new Error(\"Must implement extractField\")}static sizeInBits(){throw new Error(\"Must implement sizeInBits\")}static unpack(e){throw new Error(\"Must implement unpack\")}static checkPack(e){if(e.length>t)throw new Error(`Input of size ${e.length} is larger than expected size of ${t}`)}static pack(e){this.checkPack(e);let a=this.extractField(e[0]),l=Math.min(e.length,t);for(let d=1;d>=this.sizeInBits();return a}toFields(){return[this.packed]}assertEquals(e){this.packed.assertEquals(e.packed)}}return r.type=T({packed:k},{}),r.l=t,r.bitSize=n,r}function K(o=U){class t extends Z(m,o,B){static extractField(r){return r.value}static sizeInBits(){return B}static unpack(r){let s=E.witness(E.Array(m,o),()=>this.unpackToBigints(r).map(a=>m.from(a)));return r.assertEquals(t.pack(s)),s}static fromUInt32s(r){let s=t.pack(r);return new t(s)}static fromBigInts(r){let s=r.map(e=>m.from(e));return t.fromUInt32s(s)}toBigInts(){return t.unpack(this.packed).map(r=>r.toBigint())}}return t}var w=class extends K(){};var C=()=>{function o(t){let n=t.slice(0,30),r=t.slice(30),s=new P(n).toField(),e=new P(r).toField();return{hashPart1:s,hashPart2:e}}return o(x.toString())};var p=function(o,t,n,r){var s=arguments.length,e=s<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,a;if(typeof Reflect==\"object\"&&typeof Reflect.decorate==\"function\")e=Reflect.decorate(o,t,n,r);else for(var l=o.length-1;l>=0;l--)(a=o[l])&&(e=(s<3?a(e):s>3?a(t,n,e):a(t,n))||e);return s>3&&e&&Object.defineProperty(t,n,e),e},i=function(o,t){if(typeof Reflect==\"object\"&&typeof Reflect.metadata==\"function\")return Reflect.metadata(o,t)},dt=new D().getRoot(),X=H.Proof(J),y=class extends X{},f=class extends G({value:c,salt:c}){hash(){return M.hash([this.value,this.salt])}},{hashPart1:A,hashPart2:b}=C(),O=q,pt=L.fromBase58(\"B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn\"),u=class extends W{constructor(){super(...arguments),this.startSlot=h(),this.commit=h(),this.result=h(),this.curRandomValue=h(),this.events={requested:F}}async commitValue(t){this.permissionCheck(),this.checkRoundPass(),this.commit.getAndRequireEquals().assertEquals(c(0),\"Already committed\"),this.commit.set(t.hash())}async reveal(t){this.permissionCheck(),this.result.getAndRequireEquals().assertEquals(c(0),\"reveal: Result already computed\");let r=this.curRandomValue.getAndRequireEquals();this.commit.getAndRequireEquals().assertEquals(t.hash(),\"reveal: wrong commit value\"),this.checkRoundPass();let e=M.hash([t.value,r]);this.result.set(e)}async callZkon(){this.curRandomValue.getAndRequireEquals().assertEquals(c(0),\"random value have already been computed\");let r=await new S(O).sendRequest(this.address,A,b),s=new F({id:r,hash1:A,hash2:b});this.emitEvent(\"requested\",s)}async receiveZkonResponse(t,n){this.curRandomValue.getAndRequireEquals().assertEquals(c(0),\"receiveZkonResponse: prev random value was not consumed. Call reveal first\"),await new S(O).recordRequestFullfillment(t,n),this.curRandomValue.set(n.publicInput.dataField)}permissionCheck(){}checkRoundPass(){let t=this.startSlot.getAndRequireEquals();this.network.globalSlotSinceGenesis.requireBetween(t.add(I),z.MAXINT())}};p([g(z),i(\"design:type\",Object)],u.prototype,\"startSlot\",void 0);p([g(c),i(\"design:type\",Object)],u.prototype,\"commit\",void 0);p([g(c),i(\"design:type\",Object)],u.prototype,\"result\",void 0);p([g(c),i(\"design:type\",Object)],u.prototype,\"curRandomValue\",void 0);p([R,i(\"design:type\",Function),i(\"design:paramtypes\",[f]),i(\"design:returntype\",Promise)],u.prototype,\"commitValue\",null);p([R,i(\"design:type\",Function),i(\"design:paramtypes\",[f]),i(\"design:returntype\",Promise)],u.prototype,\"reveal\",null);p([R,i(\"design:type\",Function),i(\"design:paramtypes\",[c,y]),i(\"design:returntype\",Promise)],u.prototype,\"receiveZkonResponse\",null);var mt=u;export{f as CommitValue,u as RandomManager,y as ZkonProof,X as ZkonProof_,mt as default};\n" } \ No newline at end of file diff --git a/src/PLottery.ts b/src/PLottery.ts index e206f6d..faba205 100644 --- a/src/PLottery.ts +++ b/src/PLottery.ts @@ -74,6 +74,8 @@ export class BuyTicketEvent extends Struct({ export class ProduceResultEvent extends Struct({ result: Field, + totalScore: UInt64, + bank: Field, }) {} export class GetRewardEvent extends Struct({ @@ -226,9 +228,11 @@ export class PLottery extends SmartContract { amount: convertToUInt64(bankValue.mul(COMMISSION).div(PRECISION)), }); + const newBankValue = bankValue.mul(PRECISION - COMMISSION).div(PRECISION); + // Update onchain values this.ticketRoot.set(reduceProof.publicOutput.newTicketRoot); - this.bank.set(bankValue.mul(PRECISION - COMMISSION).div(PRECISION)); + this.bank.set(newBankValue); this.totalScore.set(reduceProof.publicOutput.totalScore); this.result.set(winningNumbersPacked); @@ -238,6 +242,8 @@ export class PLottery extends SmartContract { 'produce-result', new ProduceResultEvent({ result: winningNumbersPacked, + bank: newBankValue, + totalScore: reduceProof.publicOutput.totalScore, }) ); } diff --git a/src/Random/RandomManager.ts b/src/Random/RandomManager.ts index 3e851e3..abe1d7c 100644 --- a/src/Random/RandomManager.ts +++ b/src/Random/RandomManager.ts @@ -72,8 +72,7 @@ export class RandomManager extends SmartContract { @method async commitValue(commitValue: CommitValue) { this.permissionCheck(); - // Add time check - // this.checkRoundPass(); + this.checkRoundPass(); const currentCommit = this.commit.getAndRequireEquals(); currentCommit.assertEquals(Field(0), 'Already committed'); diff --git a/src/Tests/PLottery.test.ts b/src/Tests/PLottery.test.ts index 7693b63..33cdb86 100644 --- a/src/Tests/PLottery.test.ts +++ b/src/Tests/PLottery.test.ts @@ -267,10 +267,10 @@ describe('Add', () => { checkConsistency(); console.log('Commiting value'); - // Commit value for random - await commitValue(curRound); // Wait next round mineNBlocks(BLOCK_PER_ROUND); + // Commit value for random + await commitValue(curRound); console.log('Revealing'); await produceResultInRM(curRound); @@ -343,10 +343,10 @@ describe('Add', () => { }); await tx1_1.prove(); await tx1_1.sign([senderKey]).send(); - // Commit value for random - await commitValue(curRound); // Wait 3 more rounds mineNBlocks(3 * BLOCK_PER_ROUND + 1); + // Commit value for random + await commitValue(curRound); // Reduce tickets // let winningNumbers = getWinningCombinationPacked(curRound); let reduceProof = await state.reduceTickets(Field(0)); @@ -436,10 +436,10 @@ describe('Add', () => { expect(balanceBefore.sub(balanceAfter)).toEqual(TICKET_PRICE); checkConsistency(); } - // Commit value for random - await commitValue(round); // Wait for the end of round mineNBlocks(BLOCK_PER_ROUND); + // Commit value for random + await commitValue(round); // Produce random value await produceResultInRM(round); diff --git a/src/constants.ts b/src/constants.ts index 44ed94b..8058574 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,7 +3,7 @@ import { PublicKey, UInt64 } from 'o1js'; export const NUMBERS_IN_TICKET = 6; export const TICKET_PRICE = UInt64.from(10 * 10 ** 9); -export const BLOCK_PER_ROUND = 480; // Approximate blocks per 2 hour +export const BLOCK_PER_ROUND = 480; // Approximate blocks per 1 day export const SCORE_COEFFICIENTS = [0, 90, 324, 2187, 26244, 590490, 31886460]; // Should be updated with appropriate probability diff --git a/vk.js b/vk.js index 76fe89b..bb10b16 100644 --- a/vk.js +++ b/vk.js @@ -1,7 +1,7 @@ export const vkJSON = { "randomManagerVK": { - "hash": "28786785555641963023530609809742524982811664498970312156658979961256337860131", - "data": "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" + "hash": "16506868150889189367725732371730986703239625164369267642787925978896650301756", + "data": "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" }, "PLotteryVK": { "hash": "11165524916679703107090517872097495948130702800031061928835812466885703559006",