tx · F6RpxQT5HqcMhmoYwPTaxvfC5AdZ9JNwsdsAU6tCBuGR 3MqtTwie8XKCcUcntgFAFYnfLEnffezju4Y: -0.01500000 Waves 2019.08.09 18:48 [623645] smart account 3MqtTwie8XKCcUcntgFAFYnfLEnffezju4Y > SELF 0.00000000 Waves
{ "type": 13, "id": "F6RpxQT5HqcMhmoYwPTaxvfC5AdZ9JNwsdsAU6tCBuGR", "fee": 1500000, "feeAssetId": null, "timestamp": 1565365776179, "version": 1, "sender": "3MqtTwie8XKCcUcntgFAFYnfLEnffezju4Y", "senderPublicKey": "Gf4eU8JJaQziR7AfnST6tmzhUgFmeKavkwyoceen3PK1", "proofs": [ "32xqDZBvJ4teVG7R7aAh98BiT62Pdxzijb9ZQpPqu7BNWMUFhDSdkKEa4QTLa4DJLgaFczUG7iW3b6tfy2f7KUmE" ], "script": "base64:AAIDAAAAAAAAAAAAAAAaAAAAAAlSU0FQVUJMSUMJAAJbAAAAAQIAAAGPYmFzZTY0Ok1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcWxBaUFOU21CcERIWUtQOXNLZ2VOL2wxYkFiMjhnL3RHbGdEa3dUNUZpTU40WDNwd2R2ZHhFN212U1I4LzQxZFU5cng0akcrNnRacGIxVUxWRFBzNDMxdFIySVJhVFh3NUNqK0FjMnZoTCs1SmFtQ2VyR0QxVVcrYmgvRUdRdHhvOFczWUxEcm9mWEI1UUhKeDRQa3oyS2dmK29TL0M4aEh1Qi9VNGtyTzc2VTA1MDdHVGpaUFA5a1JRMHVMU01lcVFYdDh3WFMrbk1wNXdhanF4UHBETE1hU1JFZ3NLd3YvQUVrUDRkenBUWWJpa0xCWWw0cXRkSnNEODRITEZTa2l3ZDNCaGNPclBqb0lZbUx4UXVCRDVUSU1LVEtEM3NkWmdhWTlyc3lxeDNBMDBpbm55eEQ2enAzYjRnRnBVT1g4SnhLWmRFQzJteUVxbGVOZ2c3R3p3SURBUUFCAAAAAAZTRVJWRVIJAQAAABxAZXh0clVzZXIoYWRkcmVzc0Zyb21TdHJpbmcpAAAAAQIAAAAjM05DaUcyOExtV3lUaWdXRzEzRTVRbnZkSEJzWkZZWFNTMmoAAAAAB1dBVkVMRVQJAABoAAAAAgkAAGgAAAACAAAAAAAAAABkAAAAAAAAAAPoAAAAAAAAAAPoAAAAABBTRVNTSU9OSURGSVhTSVpFAAAAAAAAAAAsAAAAAA5SQU5EQ1lDTEVQUklDRQkAAGkAAAACCQAAaAAAAAIAAAAAAAAAAAUFAAAAB1dBVkVMRVQAAAAAAAAAA+gAAAAAEE1BWFJBTkRTUEVSQ1lDTEUAAAAAAAAAAA4AAAAACVNUQVRFSU5JVAIAAAAESU5JVAAAAAAIREFUQURPTkUCAAAABVJFQURZAAAAAA1TVEFURUZJTklTSEVEAgAAAAhGSU5JU0hFRAAAAAAISWR4U3RhdGUAAAAAAAAAAAAAAAAAD0lkeE9yZ2FuaXplclB1YgAAAAAAAAAAAQAAAAALSWR4UmFuZEZyb20AAAAAAAAAAAIAAAAACUlkeFJhbmRUbwAAAAAAAAAAAwAAAAANSWR4UmFuZHND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NTgJAAGkAAAAAQUAAAAIcmFuZEZyb20JAAGkAAAAAQUAAAAGcmFuZFRvBQAAAA1yYW5kc0NvdW50U3RyBQAAAA1yYW5kc0NvdW50U3RyCQABpAAAAAEFAAAADWRhdGFLZXlzQ291bnQCAAAABG51bGwCAAAAATACAAAAATACAAAAATECAAAAATACAAAAATECAAAAAAkBAAAADFNjcmlwdFJlc3VsdAAAAAIJAQAAAAhXcml0ZVNldAAAAAEJAARMAAAAAgkBAAAACURhdGFFbnRyeQAAAAIFAAAACXNlc3Npb25JZAUAAAAJaW5pdFN0YXRlBQAAAANuaWwJAQAAAAtUcmFuc2ZlclNldAAAAAEJAARMAAAAAgkBAAAADlNjcmlwdFRyYW5zZmVyAAAAAwUAAAAGU0VSVkVSBQAAAAhyYW5kc1BtdAUAAAAEdW5pdAUAAAADbmlsAAAAAWkBAAAABXJlYWR5AAAAAQAAAAlzZXNzaW9uSWQEAAAADmRyYXdQYXJhbXNMaXN0CQEAAAATZXh0cmFjdEdhbWVEYXRhTGlzdAAAAAEFAAAACXNlc3Npb25JZAQAAAAJZHJhd1N0YXRlCQABkQAAAAIFAAAADmRyYXdQYXJhbXNMaXN0BQAAAAhJZHhTdGF0ZQQAAAARb3JnYW5pemVyUHViS2V5NTgJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QFAAAAD0lkeE9yZ2FuaXplclB1YgQAAAANcmFuZHNDb3VudFN0cgkAAZEAAAACBQAAAA5kcmF3UGFyYW1zTGlzdAUAAAANSWR4UmFuZHNDb3VudAQAAAATcmVtYWluUmFuZHNDb3VudFN0cgkAAZEAAAACBQAAAA5kcmF3UGFyYW1zTGlzdAUAAAATSWR4UmVtYWluUmFuZHNDb3VudAQAAAAHZnJvbVN0cgkAAZEAAAACBQAAAA5kcmF3UGFyYW1zTGlzdAUAAAALSWR4UmFuZEZyb20EAAAABXRvU3RyCQABkQAAAAIFAAAADmRyYXdQYXJhbXNMaXN0BQAAAAlJZHhSYW5kVG8EAAAADWRhdGFLZXlzQ291bnQJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QFAAAAEElkeERhdGFLZXlzQ291bnQEAAAAD29yZ2FuaXplclB1YktleQkAAlkAAAABBQAAABFvcmdhbml6ZXJQdWJLZXk1OAMJAQAAAAIhPQAAAAIFAAAACWRyYXdTdGF0ZQUAAAAJU1RBVEVJTklUCQAAAgAAAAEJAAEsAAAAAgUAAAAJc2Vzc2lvbklkAgAAAD4gZHJhdzogbW92aW5nIGludG8gUkVBRFkgc3RhdGUgaXMgYWxsb3dlZCBvbmx5IGZyb20gSU5JVCBzdGF0ZQMJAQAAAAIhPQAAAAIFAAAAD29yZ2FuaXplclB1YktleQgFAAAAAWkAAAAPY2FsbGVyUHVibGljS2V5CQAAAgAAAAEJAAEsAAAAAgUAAAAJc2Vzc2lvbklkAgAAADtkcmF3OiBtb3ZpbmcgaW50byBSRUFEWSBzdGF0ZSBpcyBhbGxvd2VkIGZvciBvcmdhbml6ZXIgb25seQQAAAAKcmVhZHlTdGF0ZQkBAAAAEmZvcm1hdFN0YXRlRGF0YVN0cgAAAA4FAAAACERBVEFET05FBQAAABFvcmdhbml6ZXJQdWJLZXk1OAUAAAAHZnJvbVN0cgUAAAAFdG9TdHIFAAAADXJhbmRzQ291bnRTdHIFAAAAE3JlbWFpblJhbmRzQ291bnRTdHIFAAAADWRhdGFLZXlzQ291bnQJAAJYAAAAAQgFAAAAAWkAAAANdHJhbnNhY3Rpb25JZAIAAAABMAIAAAABMAIAAAABMQIAAAABMAIAAAABMQIAAAAACQEAAAAIV3JpdGVTZXQAAAABCQAETAAAAAIJAQAAAAlEYXRhRW50cnkAAAACBQAAAAlzZXNzaW9uSWQFAAAACnJlYWR5U3RhdGUFAAAAA25pbAAAAAFpAQAAAAZyYW5kb20AAAACAAAACXNlc3Npb25JZAAAAAdyc2FTaWduBAAAAA5kcmF3UGFyYW1zTGlzdAkBAAAAE2V4dHJhY3RHYW1lRGF0YUxpc3QAAAABBQAAAAlzZXNzaW9uSWQEAAAACWRyYXdTdGF0ZQkAAZEAAAACBQAAAA5kcmF3UGFyYW1zTGlzdAUAAAAISWR4U3RhdGUEAAAAEW9yZ2FuaXplclB1YktleTU4CQABkQAAAAIFAAAADmRyYXdQYXJhbXNMaXN0BQAAAA9JZHhPcmdhbml6ZXJQdWIEAAAADXJhbmRzQ291bnRTdHIJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QFAAAADUlkeFJhbmRzQ291bnQEAAAAEHJlbWFpblJhbmRzQ291bnQJAQAAAA1wYXJzZUludFZhbHVlAAAAAQkAAZEAAAACBQAAAA5kcmF3UGFyYW1zTGlzdAUAAAATSWR4UmVtYWluUmFuZHNDb3VudAQAAAAPbGFzdE9mZnNldEJ5dGVzCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QFAAAADUlkeExhc3RPZmZzZXQEAAAADGN1cnJSYW5kc1N0cgkAAZEAAAACBQAAAA5kcmF3UGFyYW1zTGlzdAUAAAAMSWR4Q3VyclJhbmRzBAAAAAdmcm9tU3RyCQABkQAAAAIFAAAADmRyYXdQYXJhbXNMaXN0BQAAAAtJZHhSYW5kRnJvbQQAAAAFdG9TdHIJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QFAAAACUlkeFJhbmRUbwQAAAAMZGF0YURvbmVUeElkCQABkQAAAAIFAAAADmRyYXdQYXJhbXNMaXN0BQAAAA9JZHhEYXRhRG9uZVR4SWQEAAAACHNsb3dJZHgxCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QAAAAAAAAAAAkEAAAACHNsb3dJZHgyCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QAAAAAAAAAAAoEAAAACGZhc3RJZHgxCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QAAAAAAAAAAAsEAAAACGZhc3RJZHgyCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QAAAAAAAAAAAwEAAAABGZyb20JAQAAAA1wYXJzZUludFZhbHVlAAAAAQUAAAAHZnJvbVN0cgQAAAACdG8JAQAAAA1wYXJzZUludFZhbHVlAAAAAQUAAAAFdG9TdHIEAAAAD29yZ2FuaXplclB1YktleQkAAlkAAAABBQAAABFvcmdhbml6ZXJQdWJLZXk1OAMJAQAAAAIhPQAAAAIFAAAACWRyYXdTdGF0ZQUAAAAIREFUQURPTkUJAAACAAAAAQkAASwAAAACBQAAAAlzZXNzaW9uSWQCAAAAOyBkcmF3OiBpdCBtdXN0IGJlIGluIFJFQURZIHN0YXRlIHRvIGdlbmVyYXRlIHJhbmRvbSBudW1iZXJzAwkBAAAAASEAAAABCQAB+AAAAAQFAAAABlNIQTI1NgkAAMsAAAACCQABmwAAAAEFAAAACXNlc3Npb25JZAkAAZsAAAABBQAAAAxkYXRhRG9uZVR4SWQFAAAAB3JzYVNpZ24FAAAACVJTQVBVQkxJQwkAAAIAAAABAgAAABVJbnZhbGlkIFJTQSBzaWduYXR1cmUEAAAAC3JhbmRHZW5JbmZvCQEAAAAMZ2VuZXJhdGVSYW5kAAAACwUAAAAJc2Vzc2lvbklkBQAAAARmcm9tBQAAAAJ0bwUAAAAHcnNhU2lnbgUAAAAMY3VyclJhbmRzU3RyBQAAABByZW1haW5SYW5kc0NvdW50BQAAAA9sYXN0T2Zmc2V0Qnl0ZXMFAAAACHNsb3dJZHgxBQAAAAhzbG93SWR4MgUAAAAIZmFzdElkeDEFAAAACGZhc3RJZHgyBAAAABZuZXdSZW1haW5SYW5kc0NvdW50U3RyCQABkQAAAAIFAAAAC3JhbmRHZW5JbmZvAAAAAAAAAAABBAAAAAhuZXdTdGF0ZQMJAAAAAAAAAgUAAAAWbmV3UmVtYWluUmFuZHNDb3VudFN0cgIAAAABMAUAAAANU1RBVEVGSU5JU0hFRAUAAAAIREFUQURPTkUJAQAAAAhXcml0ZVNldAAAAAEJAARMAAAAAgkBAAAACURhdGFFbnRyeQAAAAIFAAAACXNlc3Npb25JZAkBAAAAEmZvcm1hdFN0YXRlRGF0YVN0cgAAAA4FAAAACG5ld1N0YXRlBQAAABFvcmdhbml6ZXJQdWJLZXk1OAUAAAAHZnJvbVN0cgUAAAAFdG9TdHIFAAAADXJhbmRzQ291bnRTdHIFAAAAFm5ld1JlbWFpblJhbmRzQ291bnRTdHIJAAGRAAAAAgUAAAAOZHJhd1BhcmFtc0xpc3QFAAAAEElkeERhdGFLZXlzQ291bnQFAAAADGRhdGFEb25lVHhJZAkAAZEAAAACBQAAAAtyYW5kR2VuSW5mbwAAAAAAAAAAAgkAAZEAAAACBQAAAAtyYW5kR2VuSW5mbwAAAAAAAAAAAwkAAZEAAAACBQAAAAty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", "chainId": 84, "height": 623645, "spentComplexity": 0 } View: original | compacted Prev: E7kdGeKUvaWAQXHi33iKFajNzK6eyfsMWGXEjtUoJGi3 Next: DBLbhHFaizgYow4i1X3FTKK94mRRCXAeNZzpCxrTc2R9 Diff:
Old | New | Differences | |
---|---|---|---|
31 | 31 | ||
32 | 32 | let IdxRemainRandsCount = 5 | |
33 | 33 | ||
34 | - | let | |
34 | + | let IdxDataKeysCount = 6 | |
35 | 35 | ||
36 | - | let | |
36 | + | let IdxDataDoneTxId = 7 | |
37 | 37 | ||
38 | - | let IdxCurrRands = 8 | |
38 | + | let IdxLastOffset = 8 | |
39 | + | ||
40 | + | let IdxCurrRands = 13 | |
39 | 41 | ||
40 | 42 | func abs (val) = if ((0 > val)) | |
41 | 43 | then -(val) | |
42 | 44 | else val | |
43 | 45 | ||
44 | 46 | ||
45 | - | func formatStateDataStr (drawState,organizerPubKey58,randFrom,randTo,randsCount,remainingRands,dataDoneTxId,lastOffset,randOrEmpty) = { | |
46 | - | let fullStateStr = ((((((((((((((drawState + "_") + organizerPubKey58) + "_") + randFrom) + "_") + randTo) + "_") + randsCount) + "_") + remainingRands) + "_") + dataDoneTxId) + "_") + lastOffset) | |
47 | + | func formatStateDataStr (drawState,organizerPubKey58,randFrom,randTo,randsCount,remainingRands,dataKeysCount,dataDoneTxId,lastOffset,slowIdx1,slowIdx2,fastIdx1,fastIdx2,randOrEmpty) = { | |
48 | + | let fullStateStr = ((((((((((((((((((((((((drawState + "_") + organizerPubKey58) + "_") + randFrom) + "_") + randTo) + "_") + randsCount) + "_") + remainingRands) + "_") + dataKeysCount) + "_") + dataDoneTxId) + "_") + lastOffset) + "_") + slowIdx1) + "_") + slowIdx2) + "_") + fastIdx1) + "_") + fastIdx2) | |
47 | 49 | if ((randOrEmpty == "")) | |
48 | 50 | then ((fullStateStr + "_") + "-") | |
49 | 51 | else ((fullStateStr + "_") + randOrEmpty) | |
68 | 70 | } | |
69 | 71 | ||
70 | 72 | ||
71 | - | func generateRand (sessionId,from,to,rsaSign,currRandsStr,remainingRands,lastOffsetBytes) = { | |
72 | - | let randHash = rsaSign | |
73 | + | func generateRand (sessionId,from,to,rsaSign,currRandsStr,remainingRands,lastOffsetBytes,currSlowIdx1,currSlowIdx2,currFastIdx1,currFastIdx2) = { | |
74 | + | let newIncList = if (((lastOffsetBytes + 1) > 87)) | |
75 | + | then if ((currFastIdx2 > 241)) | |
76 | + | then [0, (currSlowIdx1 + 1), (currSlowIdx2 + 1), 0, 1] | |
77 | + | else [0, currSlowIdx1, currSlowIdx2, (currFastIdx1 + 1), (currFastIdx2 + 1)] | |
78 | + | else [(lastOffsetBytes + 1), currSlowIdx1, currSlowIdx2, currFastIdx1, currFastIdx2] | |
79 | + | let newOffsetBytes = newIncList[0] | |
80 | + | let newSlowIdx1 = newIncList[1] | |
81 | + | let newSlowIdx2 = newIncList[2] | |
82 | + | let newFastIdx1 = newIncList[3] | |
83 | + | let newFastIdx2 = newIncList[4] | |
84 | + | let byteVect = (((((((take(drop(rsaSign, newSlowIdx1), 1) + take(drop(rsaSign, 1), 1)) + take(drop(rsaSign, newFastIdx1), 1)) + take(drop(rsaSign, 3), 1)) + take(drop(rsaSign, newSlowIdx2), 1)) + take(drop(rsaSign, 5), 1)) + take(drop(rsaSign, 6), 1)) + take(drop(rsaSign, newFastIdx2), 1)) | |
85 | + | let randHash = ((sha256(byteVect) + blake2b256(byteVect)) + keccak256(byteVect)) | |
73 | 86 | let div = ((to - from) + 1) | |
74 | - | let rand1 = nextRand(div, from, currRandsStr, remainingRands, drop(randHash, | |
87 | + | let rand1 = nextRand(div, from, currRandsStr, remainingRands, drop(randHash, newOffsetBytes)) | |
75 | 88 | let rem1 = if ((rand1[1] != "")) | |
76 | 89 | then (remainingRands - 1) | |
77 | 90 | else remainingRands | |
78 | - | let rand2 = nextRand(div, from, rand1[0], rem1, drop(randHash, ( | |
91 | + | let rand2 = nextRand(div, from, rand1[0], rem1, drop(randHash, (newOffsetBytes + 1))) | |
79 | 92 | let rem2 = if ((rand2[1] != "")) | |
80 | 93 | then (rem1 - 1) | |
81 | 94 | else rem1 | |
82 | - | let rand3 = nextRand(div, from, rand2[0], rem2, drop(randHash, ( | |
95 | + | let rand3 = nextRand(div, from, rand2[0], rem2, drop(randHash, (newOffsetBytes + 2))) | |
83 | 96 | let rem3 = if ((rand3[1] != "")) | |
84 | 97 | then (rem2 - 1) | |
85 | 98 | else rem2 | |
86 | - | let rand4 = nextRand(div, from, rand3[0], rem3, drop(randHash, ( | |
99 | + | let rand4 = nextRand(div, from, rand3[0], rem3, drop(randHash, (newOffsetBytes + 3))) | |
87 | 100 | let rem4 = if ((rand4[1] != "")) | |
88 | 101 | then (rem3 - 1) | |
89 | 102 | else rem3 | |
90 | - | let rand5 = nextRand(div, from, rand4[0], rem4, drop(randHash, ( | |
103 | + | let rand5 = nextRand(div, from, rand4[0], rem4, drop(randHash, (newOffsetBytes + 4))) | |
91 | 104 | let rem5 = if ((rand5[1] != "")) | |
92 | 105 | then (rem4 - 1) | |
93 | 106 | else rem4 | |
94 | - | let rand6 = nextRand(div, from, rand5[0], rem5, drop(randHash, ( | |
107 | + | let rand6 = nextRand(div, from, rand5[0], rem5, drop(randHash, (newOffsetBytes + 5))) | |
95 | 108 | let rem6 = if ((rand6[1] != "")) | |
96 | 109 | then (rem5 - 1) | |
97 | 110 | else rem5 | |
98 | - | let rand7 = nextRand(div, from, rand6[0], rem6, drop(randHash, ( | |
111 | + | let rand7 = nextRand(div, from, rand6[0], rem6, drop(randHash, (newOffsetBytes + 6))) | |
99 | 112 | let rem7 = if ((rand7[1] != "")) | |
100 | 113 | then (rem6 - 1) | |
101 | 114 | else rem6 | |
102 | - | let rand8 = nextRand(div, from, rand7[0], rem7, drop(randHash, ( | |
115 | + | let rand8 = nextRand(div, from, rand7[0], rem7, drop(randHash, (newOffsetBytes + 7))) | |
103 | 116 | let rem8 = if ((rand8[1] != "")) | |
104 | 117 | then (rem7 - 1) | |
105 | 118 | else rem7 | |
106 | - | let rand9 = nextRand(div, from, rand8[0], rem8, drop(randHash, ( | |
119 | + | let rand9 = nextRand(div, from, rand8[0], rem8, drop(randHash, (newOffsetBytes + 8))) | |
107 | 120 | let rem9 = if ((rand9[1] != "")) | |
108 | 121 | then (rem8 - 1) | |
109 | 122 | else rem8 | |
110 | - | let rand10 = nextRand(div, from, rand9[0], rem9, drop(randHash, ( | |
123 | + | let rand10 = nextRand(div, from, rand9[0], rem9, drop(randHash, (newOffsetBytes + 9))) | |
111 | 124 | let rem10 = if ((rand10[1] != "")) | |
112 | 125 | then (rem9 - 1) | |
113 | 126 | else rem9 | |
114 | - | let rand11 = nextRand(div, from, rand10[0], rem10, drop(randHash, ( | |
127 | + | let rand11 = nextRand(div, from, rand10[0], rem10, drop(randHash, (newOffsetBytes + 10))) | |
115 | 128 | let rem11 = if ((rand11[1] != "")) | |
116 | 129 | then (rem10 - 1) | |
117 | 130 | else rem10 | |
118 | - | let rand12 = nextRand(div, from, rand11[0], rem11, drop(randHash, (lastOffsetBytes + 12))) | |
119 | - | let rem12 = if ((rand12[1] != "")) | |
120 | - | then (rem11 - 1) | |
121 | - | else rem11 | |
122 | - | let rand13 = nextRand(div, from, rand12[0], rem12, drop(randHash, (lastOffsetBytes + 13))) | |
123 | - | let rem13 = if ((rand13[1] != "")) | |
124 | - | then (rem12 - 1) | |
125 | - | else rem12 | |
126 | - | let rand14 = nextRand(div, from, rand13[0], rem13, drop(randHash, (lastOffsetBytes + 14))) | |
127 | - | let rem14 = if ((rand14[1] != "")) | |
128 | - | then (rem13 - 1) | |
129 | - | else rem13 | |
130 | - | [rand14[0], if ((rem14 == 0)) | |
131 | + | [rand11[0], if ((rem11 == 0)) | |
131 | 132 | then "0" | |
132 | - | else toString( | |
133 | + | else toString(rem11), toString((newOffsetBytes + 10)), toString(newSlowIdx1), toString(newSlowIdx2), toString(newFastIdx1), toString(newFastIdx2)] | |
133 | 134 | } | |
134 | 135 | ||
135 | 136 | ||
136 | - | func validateDtxKey (sessionId,data) = if (if (if ((sessionId == take(data.key, SESSIONIDFIXSIZE))) | |
137 | - | then (size(data.key) > SESSIONIDFIXSIZE) | |
138 | - | else false) | |
139 | - | then !(isDefined(getString(this, data.key))) | |
140 | - | else false) | |
141 | - | then match data.value { | |
142 | - | case str: String => | |
143 | - | true | |
144 | - | case _ => | |
145 | - | throw((sessionId + " draw: only String type is accepted for data transactions")) | |
137 | + | func validateDtxKey (sessionId,dataKeysCount,data) = { | |
138 | + | let dataKeyInfo = split(data.key, "_") | |
139 | + | if ((size(dataKeyInfo) != 2)) | |
140 | + | then throw("Invalid data key format. It must follow to ${sessionId}_${num}") | |
141 | + | else { | |
142 | + | let keySessionId = dataKeyInfo[0] | |
143 | + | let keyPostfix = dataKeyInfo[1] | |
144 | + | if ((sessionId != keySessionId)) | |
145 | + | then throw(((("Several data keys have different sessionId: " + sessionId) + " and ") + keySessionId)) | |
146 | + | else if (isDefined(getString(this, data.key))) | |
147 | + | then throw(("One of the data keys has already presented in account state: key=" + data.key)) | |
148 | + | else if ((size(keyPostfix) > 4)) | |
149 | + | then throw("Invalid data key format. It must follow to ${sessionId}_${num} where ${num} length couldn't be greater than 4") | |
150 | + | else if ((take(keyPostfix, 1) == "0")) | |
151 | + | then throw("Invalid data key format. It must follow to ${sessionId}_${num} where ${num} couldn't start from 0") | |
152 | + | else { | |
153 | + | let keyPostfixIntOpt = parseInt(keyPostfix) | |
154 | + | if (isDefined(keyPostfixIntOpt)) | |
155 | + | then { | |
156 | + | let keyPostfixInt = extract(keyPostfixIntOpt) | |
157 | + | if (if ((1 > keyPostfixInt)) | |
158 | + | then true | |
159 | + | else (keyPostfixInt > dataKeysCount)) | |
160 | + | then throw((((("Invalid data key format. It must follow to ${sessionId}_${num}" + " where ${num} must be a valid int value from 1 to ") + toString(dataKeysCount)) + " but actualNum=") + toString(keyPostfixInt))) | |
161 | + | else match data.value { | |
162 | + | case str: String => | |
163 | + | true | |
164 | + | case _ => | |
165 | + | throw((sessionId + " draw: only String type is accepted for data transactions")) | |
166 | + | } | |
167 | + | } | |
168 | + | else throw((("Invalid data key format. It must follow to ${sessionId}_${num}" + " where ${num} must be a valid int value from 1 to 7145 but actualNum=") + keyPostfix)) | |
169 | + | } | |
170 | + | } | |
146 | 171 | } | |
147 | - | else false | |
172 | + | ||
173 | + | ||
174 | + | func validateAndGetRandsPmt (randsCount,pmt,minDataPmt) = { | |
175 | + | let bound1 = 1000 | |
176 | + | let basePrice1 = ((13 * WAVELET) / 100) | |
177 | + | let div1 = 50 | |
178 | + | let diff1 = ((8 * WAVELET) / 100) | |
179 | + | let bound2 = 5000 | |
180 | + | let basePrice2 = ((297 * WAVELET) / 100) | |
181 | + | let div2 = 1000 | |
182 | + | let diff2 = ((143 * WAVELET) / 100) | |
183 | + | let bound3 = 50000 | |
184 | + | let basePrice3 = ((1427 * WAVELET) / 100) | |
185 | + | let div3 = 5000 | |
186 | + | let diff3 = ((705 * WAVELET) / 100) | |
187 | + | let minRandsPmt = if ((bound1 > randsCount)) | |
188 | + | then (basePrice1 + ((randsCount / div1) * diff1)) | |
189 | + | else if ((bound2 > randsCount)) | |
190 | + | then (basePrice2 + (((randsCount / div2) - 1) * diff2)) | |
191 | + | else if ((bound3 > randsCount)) | |
192 | + | then (basePrice3 + (((randsCount / div3) - 1) * diff3)) | |
193 | + | else throw("Please contact our sales team to generate more than 50k rands") | |
194 | + | let minPmt = (minRandsPmt + minDataPmt) | |
195 | + | if (isDefined(pmt.assetId)) | |
196 | + | then throw("Only WAVES can be used as a payment for rands generation") | |
197 | + | else if ((minPmt > pmt.amount)) | |
198 | + | then throw(((((("Attached payment is to small to generate " + toString(randsCount)) + " unique randoms numbers and upload at least 1 data tx: actualPmt=") + toString(pmt.amount)) + " but minPmt is ") + toString(minPmt))) | |
199 | + | else minRandsPmt | |
200 | + | } | |
148 | 201 | ||
149 | 202 | ||
150 | 203 | @Callable(i) | |
151 | 204 | func initDraw (randFrom,randTo,randsCount) = { | |
152 | 205 | let sessionId = toBase58String(i.transactionId) | |
206 | + | let rangeLength = ((randTo - randFrom) + 1) | |
207 | + | let maxRangeLength = (rangeLength / 2) | |
208 | + | let randToLen = size(toString((randTo - 1))) | |
209 | + | let randsCountLen = size(toString(randsCount)) | |
210 | + | let dynamicStatusLen = (((2 * randToLen) + (2 * randsCountLen)) + 4) | |
211 | + | let maxRandsCount = (((5120 - 175) - dynamicStatusLen) / (randToLen + 1)) | |
153 | 212 | if (if ((0 >= randFrom)) | |
154 | 213 | then true | |
155 | 214 | else (0 >= randTo)) | |
156 | 215 | then throw("randFrom and randTo must be greater than 0") | |
157 | 216 | else if ((randFrom >= randTo)) | |
158 | 217 | then throw("randFrom must be strict less then randTo") | |
159 | - | else if ((randsCount > ((randTo - randFrom) + 1))) | |
160 | - | then throw(((((("Impossible to generate " + toString(randsCount)) + " for provided random range - from ") + toString(randFrom)) + " to ") + toString(randTo))) | |
161 | - | else { | |
162 | - | let organizerPubKey58 = toBase58String(i.callerPublicKey) | |
163 | - | let randsCountStr = toString(randsCount) | |
164 | - | let initState = formatStateDataStr(STATEINIT, organizerPubKey58, toString(randFrom), toString(randTo), randsCountStr, randsCountStr, "null", "0", "") | |
165 | - | ScriptResult(WriteSet([DataEntry(sessionId, initState)]), TransferSet([ScriptTransfer(SERVER, 1000, unit)])) | |
166 | - | } | |
218 | + | else if ((randsCount > rangeLength)) | |
219 | + | then throw(((((((("Impossible to generate " + toString(randsCount)) + " unique numbers for provided random range [") + toString(randFrom)) + ", ") + toString(randTo)) + "] with actual size ") + toString(rangeLength))) | |
220 | + | else if ((randsCount > maxRangeLength)) | |
221 | + | then throw(((((((((("randsCount must be less then 50% of passed range length: range=[" + toString(randFrom)) + ", ") + toString(randTo)) + "], rangeLength=") + toString(rangeLength)) + " randsCount=") + toString(randsCount)) + " allowedRandsCount=") + toString(maxRangeLength))) | |
222 | + | else if ((randsCount > maxRandsCount)) | |
223 | + | then throw(((((("randsCount couldn't be kept in 1 data entry: 1_randNumSpace=" + toString((randToLen + 1))) + " maxAllowedRandsCount=") + toString(maxRandsCount)) + " actualRandsCount=") + toString(randsCount))) | |
224 | + | else if (!(isDefined(i.payment))) | |
225 | + | then throw("Please provide payment to generate unique random numbers") | |
226 | + | else { | |
227 | + | let pmt = extract(i.payment) | |
228 | + | let minDataPmt = ((5 * WAVELET) / 1000) | |
229 | + | let randsPmt = validateAndGetRandsPmt(randsCount, pmt, minDataPmt) | |
230 | + | let dataPmt = (pmt.amount - randsPmt) | |
231 | + | let dataTxCount = (dataPmt / minDataPmt) | |
232 | + | let dataKeysCount = if ((7145 >= (dataTxCount * 5))) | |
233 | + | then (dataTxCount * 5) | |
234 | + | else 7145 | |
235 | + | let organizerPubKey58 = toBase58String(i.callerPublicKey) | |
236 | + | let randsCountStr = toString(randsCount) | |
237 | + | let initState = formatStateDataStr(STATEINIT, organizerPubKey58, toString(randFrom), toString(randTo), randsCountStr, randsCountStr, toString(dataKeysCount), "null", "0", "0", "1", "0", "1", "") | |
238 | + | ScriptResult(WriteSet([DataEntry(sessionId, initState)]), TransferSet([ScriptTransfer(SERVER, randsPmt, unit)])) | |
239 | + | } | |
167 | 240 | } | |
168 | 241 | ||
169 | 242 | ||
177 | 250 | let remainRandsCountStr = drawParamsList[IdxRemainRandsCount] | |
178 | 251 | let fromStr = drawParamsList[IdxRandFrom] | |
179 | 252 | let toStr = drawParamsList[IdxRandTo] | |
253 | + | let dataKeysCount = drawParamsList[IdxDataKeysCount] | |
180 | 254 | let organizerPubKey = fromBase58String(organizerPubKey58) | |
181 | 255 | if ((drawState != STATEINIT)) | |
182 | 256 | then throw((sessionId + " draw: moving into READY state is allowed only from INIT state")) | |
183 | 257 | else if ((organizerPubKey != i.callerPublicKey)) | |
184 | 258 | then throw((sessionId + "draw: moving into READY state is allowed for organizer only")) | |
185 | 259 | else { | |
186 | - | let readyState = formatStateDataStr(DATADONE, organizerPubKey58, fromStr, toStr, randsCountStr, remainRandsCountStr, toBase58String(i.transactionId), "0", "") | |
260 | + | let readyState = formatStateDataStr(DATADONE, organizerPubKey58, fromStr, toStr, randsCountStr, remainRandsCountStr, dataKeysCount, toBase58String(i.transactionId), "0", "0", "1", "0", "1", "") | |
187 | 261 | WriteSet([DataEntry(sessionId, readyState)]) | |
188 | 262 | } | |
189 | 263 | } | |
202 | 276 | let fromStr = drawParamsList[IdxRandFrom] | |
203 | 277 | let toStr = drawParamsList[IdxRandTo] | |
204 | 278 | let dataDoneTxId = drawParamsList[IdxDataDoneTxId] | |
279 | + | let slowIdx1 = parseIntValue(drawParamsList[9]) | |
280 | + | let slowIdx2 = parseIntValue(drawParamsList[10]) | |
281 | + | let fastIdx1 = parseIntValue(drawParamsList[11]) | |
282 | + | let fastIdx2 = parseIntValue(drawParamsList[12]) | |
205 | 283 | let from = parseIntValue(fromStr) | |
206 | 284 | let to = parseIntValue(toStr) | |
207 | 285 | let organizerPubKey = fromBase58String(organizerPubKey58) | |
210 | 288 | else if (!(rsaVerify(SHA256, (toBytes(sessionId) + toBytes(dataDoneTxId)), rsaSign, RSAPUBLIC))) | |
211 | 289 | then throw("Invalid RSA signature") | |
212 | 290 | else { | |
213 | - | let randGenInfo = generateRand(sessionId, from, to, rsaSign, currRandsStr, remainRandsCount, lastOffsetBytes) | |
214 | - | let newRandsStr = randGenInfo[0] | |
291 | + | let randGenInfo = generateRand(sessionId, from, to, rsaSign, currRandsStr, remainRandsCount, lastOffsetBytes, slowIdx1, slowIdx2, fastIdx1, fastIdx2) | |
215 | 292 | let newRemainRandsCountStr = randGenInfo[1] | |
216 | - | let newOffsetBytes = randGenInfo[2] | |
217 | 293 | let newState = if ((newRemainRandsCountStr == "0")) | |
218 | 294 | then STATEFINISHED | |
219 | 295 | else DATADONE | |
220 | - | WriteSet([DataEntry(sessionId, formatStateDataStr(newState, organizerPubKey58, fromStr, toStr, randsCountStr, newRemainRandsCountStr, dataDoneTxId, | |
296 | + | WriteSet([DataEntry(sessionId, formatStateDataStr(newState, organizerPubKey58, fromStr, toStr, randsCountStr, newRemainRandsCountStr, drawParamsList[IdxDataKeysCount], dataDoneTxId, randGenInfo[2], randGenInfo[3], randGenInfo[4], randGenInfo[5], randGenInfo[6], randGenInfo[0]))]) | |
221 | 297 | } | |
222 | 298 | } | |
223 | 299 | ||
228 | 304 | let data0 = dtx.data[0] | |
229 | 305 | let sessionId = take(data0.key, SESSIONIDFIXSIZE) | |
230 | 306 | let drawParamsList = extractGameDataList(sessionId) | |
231 | - | let drawState = drawParamsList[0] | |
232 | - | let organizerPubKey58 = drawParamsList[1] | |
307 | + | let drawState = drawParamsList[IdxState] | |
308 | + | let organizerPubKey58 = drawParamsList[IdxOrganizerPub] | |
309 | + | let dataKeysCount = parseIntValue(drawParamsList[IdxDataKeysCount]) | |
233 | 310 | let organizerPubKey = fromBase58String(organizerPubKey58) | |
234 | 311 | let dataEntriesCount = size(dtx.data) | |
235 | 312 | let sigValid = sigVerify(tx.bodyBytes, tx.proofs[0], organizerPubKey) | |
236 | - | let dataSizeValid = if ((dataEntriesCount > 0)) | |
237 | - | then (5 >= dataEntriesCount) | |
313 | + | let dataSizeValid = (dataEntriesCount == 5) | |
314 | + | let feeValid = (dtx.fee == ((WAVELET * 5) / 1000)) | |
315 | + | let keysValid = if (if (if (if (validateDtxKey(sessionId, dataKeysCount, data0)) | |
316 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[1]) | |
317 | + | else false) | |
318 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[2]) | |
319 | + | else false) | |
320 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[3]) | |
321 | + | else false) | |
322 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[4]) | |
238 | 323 | else false | |
239 | - | let keysValid = if (if (if (if (if (validateDtxKey(sessionId, data0)) | |
240 | - | then if ((1 >= dataEntriesCount)) | |
241 | - | then true | |
242 | - | else validateDtxKey(sessionId, dtx.data[1]) | |
324 | + | if (if (if (if ((drawState == STATEINIT)) | |
325 | + | then sigValid | |
243 | 326 | else false) | |
244 | - | then if ((2 >= dataEntriesCount)) | |
245 | - | then true | |
246 | - | else validateDtxKey(sessionId, dtx.data[2]) | |
247 | - | else false) | |
248 | - | then if ((3 >= dataEntriesCount)) | |
249 | - | then true | |
250 | - | else validateDtxKey(sessionId, dtx.data[3]) | |
251 | - | else false) | |
252 | - | then if ((4 >= dataEntriesCount)) | |
253 | - | then true | |
254 | - | else validateDtxKey(sessionId, dtx.data[4]) | |
255 | - | else false) | |
256 | - | then if ((5 >= dataEntriesCount)) | |
257 | - | then true | |
258 | - | else validateDtxKey(sessionId, dtx.data[5]) | |
259 | - | else false | |
260 | - | if (if (if ((drawState == STATEINIT)) | |
261 | - | then sigValid | |
327 | + | then feeValid | |
262 | 328 | else false) | |
263 | 329 | then dataSizeValid | |
264 | 330 | else false) |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 3 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | 4 | let RSAPUBLIC = fromBase64String("base64:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqlAiANSmBpDHYKP9sKgeN/l1bAb28g/tGlgDkwT5FiMN4X3pwdvdxE7mvSR8/41dU9rx4jG+6tZpb1ULVDPs431tR2IRaTXw5Cj+Ac2vhL+5JamCerGD1UW+bh/EGQtxo8W3YLDrofXB5QHJx4Pkz2Kgf+oS/C8hHuB/U4krO76U0507GTjZPP9kRQ0uLSMeqQXt8wXS+nMp5wajqxPpDLMaSREgsKwv/AEkP4dzpTYbikLBYl4qtdJsD84HLFSkiwd3BhcOrPjoIYmLxQuBD5TIMKTKD3sdZgaY9rsyqx3A00innyxD6zp3b4gFpUOX8JxKZdEC2myEqleNgg7GzwIDAQAB") | |
5 | 5 | ||
6 | 6 | let SERVER = addressFromStringValue("3NCiG28LmWyTigWG13E5QnvdHBsZFYXSS2j") | |
7 | 7 | ||
8 | 8 | let WAVELET = ((100 * 1000) * 1000) | |
9 | 9 | ||
10 | 10 | let SESSIONIDFIXSIZE = 44 | |
11 | 11 | ||
12 | 12 | let RANDCYCLEPRICE = ((5 * WAVELET) / 1000) | |
13 | 13 | ||
14 | 14 | let MAXRANDSPERCYCLE = 14 | |
15 | 15 | ||
16 | 16 | let STATEINIT = "INIT" | |
17 | 17 | ||
18 | 18 | let DATADONE = "READY" | |
19 | 19 | ||
20 | 20 | let STATEFINISHED = "FINISHED" | |
21 | 21 | ||
22 | 22 | let IdxState = 0 | |
23 | 23 | ||
24 | 24 | let IdxOrganizerPub = 1 | |
25 | 25 | ||
26 | 26 | let IdxRandFrom = 2 | |
27 | 27 | ||
28 | 28 | let IdxRandTo = 3 | |
29 | 29 | ||
30 | 30 | let IdxRandsCount = 4 | |
31 | 31 | ||
32 | 32 | let IdxRemainRandsCount = 5 | |
33 | 33 | ||
34 | - | let | |
34 | + | let IdxDataKeysCount = 6 | |
35 | 35 | ||
36 | - | let | |
36 | + | let IdxDataDoneTxId = 7 | |
37 | 37 | ||
38 | - | let IdxCurrRands = 8 | |
38 | + | let IdxLastOffset = 8 | |
39 | + | ||
40 | + | let IdxCurrRands = 13 | |
39 | 41 | ||
40 | 42 | func abs (val) = if ((0 > val)) | |
41 | 43 | then -(val) | |
42 | 44 | else val | |
43 | 45 | ||
44 | 46 | ||
45 | - | func formatStateDataStr (drawState,organizerPubKey58,randFrom,randTo,randsCount,remainingRands,dataDoneTxId,lastOffset,randOrEmpty) = { | |
46 | - | let fullStateStr = ((((((((((((((drawState + "_") + organizerPubKey58) + "_") + randFrom) + "_") + randTo) + "_") + randsCount) + "_") + remainingRands) + "_") + dataDoneTxId) + "_") + lastOffset) | |
47 | + | func formatStateDataStr (drawState,organizerPubKey58,randFrom,randTo,randsCount,remainingRands,dataKeysCount,dataDoneTxId,lastOffset,slowIdx1,slowIdx2,fastIdx1,fastIdx2,randOrEmpty) = { | |
48 | + | let fullStateStr = ((((((((((((((((((((((((drawState + "_") + organizerPubKey58) + "_") + randFrom) + "_") + randTo) + "_") + randsCount) + "_") + remainingRands) + "_") + dataKeysCount) + "_") + dataDoneTxId) + "_") + lastOffset) + "_") + slowIdx1) + "_") + slowIdx2) + "_") + fastIdx1) + "_") + fastIdx2) | |
47 | 49 | if ((randOrEmpty == "")) | |
48 | 50 | then ((fullStateStr + "_") + "-") | |
49 | 51 | else ((fullStateStr + "_") + randOrEmpty) | |
50 | 52 | } | |
51 | 53 | ||
52 | 54 | ||
53 | 55 | func extractGameDataList (sessionId) = { | |
54 | 56 | let rawDataStr = getStringValue(this, sessionId) | |
55 | 57 | split(rawDataStr, "_") | |
56 | 58 | } | |
57 | 59 | ||
58 | 60 | ||
59 | 61 | func nextRand (div,min,currRandsStr,remainingRands,remainingHash) = { | |
60 | 62 | let nextRandInt = ((abs(toInt(remainingHash)) % div) + min) | |
61 | 63 | let nextRandStr = toString(nextRandInt) | |
62 | 64 | let duplicate = isDefined(indexOf(currRandsStr, nextRandStr)) | |
63 | 65 | if (if (!(duplicate)) | |
64 | 66 | then (remainingRands > 0) | |
65 | 67 | else false) | |
66 | 68 | then [((currRandsStr + "-") + nextRandStr), "yes"] | |
67 | 69 | else [currRandsStr, ""] | |
68 | 70 | } | |
69 | 71 | ||
70 | 72 | ||
71 | - | func generateRand (sessionId,from,to,rsaSign,currRandsStr,remainingRands,lastOffsetBytes) = { | |
72 | - | let randHash = rsaSign | |
73 | + | func generateRand (sessionId,from,to,rsaSign,currRandsStr,remainingRands,lastOffsetBytes,currSlowIdx1,currSlowIdx2,currFastIdx1,currFastIdx2) = { | |
74 | + | let newIncList = if (((lastOffsetBytes + 1) > 87)) | |
75 | + | then if ((currFastIdx2 > 241)) | |
76 | + | then [0, (currSlowIdx1 + 1), (currSlowIdx2 + 1), 0, 1] | |
77 | + | else [0, currSlowIdx1, currSlowIdx2, (currFastIdx1 + 1), (currFastIdx2 + 1)] | |
78 | + | else [(lastOffsetBytes + 1), currSlowIdx1, currSlowIdx2, currFastIdx1, currFastIdx2] | |
79 | + | let newOffsetBytes = newIncList[0] | |
80 | + | let newSlowIdx1 = newIncList[1] | |
81 | + | let newSlowIdx2 = newIncList[2] | |
82 | + | let newFastIdx1 = newIncList[3] | |
83 | + | let newFastIdx2 = newIncList[4] | |
84 | + | let byteVect = (((((((take(drop(rsaSign, newSlowIdx1), 1) + take(drop(rsaSign, 1), 1)) + take(drop(rsaSign, newFastIdx1), 1)) + take(drop(rsaSign, 3), 1)) + take(drop(rsaSign, newSlowIdx2), 1)) + take(drop(rsaSign, 5), 1)) + take(drop(rsaSign, 6), 1)) + take(drop(rsaSign, newFastIdx2), 1)) | |
85 | + | let randHash = ((sha256(byteVect) + blake2b256(byteVect)) + keccak256(byteVect)) | |
73 | 86 | let div = ((to - from) + 1) | |
74 | - | let rand1 = nextRand(div, from, currRandsStr, remainingRands, drop(randHash, | |
87 | + | let rand1 = nextRand(div, from, currRandsStr, remainingRands, drop(randHash, newOffsetBytes)) | |
75 | 88 | let rem1 = if ((rand1[1] != "")) | |
76 | 89 | then (remainingRands - 1) | |
77 | 90 | else remainingRands | |
78 | - | let rand2 = nextRand(div, from, rand1[0], rem1, drop(randHash, ( | |
91 | + | let rand2 = nextRand(div, from, rand1[0], rem1, drop(randHash, (newOffsetBytes + 1))) | |
79 | 92 | let rem2 = if ((rand2[1] != "")) | |
80 | 93 | then (rem1 - 1) | |
81 | 94 | else rem1 | |
82 | - | let rand3 = nextRand(div, from, rand2[0], rem2, drop(randHash, ( | |
95 | + | let rand3 = nextRand(div, from, rand2[0], rem2, drop(randHash, (newOffsetBytes + 2))) | |
83 | 96 | let rem3 = if ((rand3[1] != "")) | |
84 | 97 | then (rem2 - 1) | |
85 | 98 | else rem2 | |
86 | - | let rand4 = nextRand(div, from, rand3[0], rem3, drop(randHash, ( | |
99 | + | let rand4 = nextRand(div, from, rand3[0], rem3, drop(randHash, (newOffsetBytes + 3))) | |
87 | 100 | let rem4 = if ((rand4[1] != "")) | |
88 | 101 | then (rem3 - 1) | |
89 | 102 | else rem3 | |
90 | - | let rand5 = nextRand(div, from, rand4[0], rem4, drop(randHash, ( | |
103 | + | let rand5 = nextRand(div, from, rand4[0], rem4, drop(randHash, (newOffsetBytes + 4))) | |
91 | 104 | let rem5 = if ((rand5[1] != "")) | |
92 | 105 | then (rem4 - 1) | |
93 | 106 | else rem4 | |
94 | - | let rand6 = nextRand(div, from, rand5[0], rem5, drop(randHash, ( | |
107 | + | let rand6 = nextRand(div, from, rand5[0], rem5, drop(randHash, (newOffsetBytes + 5))) | |
95 | 108 | let rem6 = if ((rand6[1] != "")) | |
96 | 109 | then (rem5 - 1) | |
97 | 110 | else rem5 | |
98 | - | let rand7 = nextRand(div, from, rand6[0], rem6, drop(randHash, ( | |
111 | + | let rand7 = nextRand(div, from, rand6[0], rem6, drop(randHash, (newOffsetBytes + 6))) | |
99 | 112 | let rem7 = if ((rand7[1] != "")) | |
100 | 113 | then (rem6 - 1) | |
101 | 114 | else rem6 | |
102 | - | let rand8 = nextRand(div, from, rand7[0], rem7, drop(randHash, ( | |
115 | + | let rand8 = nextRand(div, from, rand7[0], rem7, drop(randHash, (newOffsetBytes + 7))) | |
103 | 116 | let rem8 = if ((rand8[1] != "")) | |
104 | 117 | then (rem7 - 1) | |
105 | 118 | else rem7 | |
106 | - | let rand9 = nextRand(div, from, rand8[0], rem8, drop(randHash, ( | |
119 | + | let rand9 = nextRand(div, from, rand8[0], rem8, drop(randHash, (newOffsetBytes + 8))) | |
107 | 120 | let rem9 = if ((rand9[1] != "")) | |
108 | 121 | then (rem8 - 1) | |
109 | 122 | else rem8 | |
110 | - | let rand10 = nextRand(div, from, rand9[0], rem9, drop(randHash, ( | |
123 | + | let rand10 = nextRand(div, from, rand9[0], rem9, drop(randHash, (newOffsetBytes + 9))) | |
111 | 124 | let rem10 = if ((rand10[1] != "")) | |
112 | 125 | then (rem9 - 1) | |
113 | 126 | else rem9 | |
114 | - | let rand11 = nextRand(div, from, rand10[0], rem10, drop(randHash, ( | |
127 | + | let rand11 = nextRand(div, from, rand10[0], rem10, drop(randHash, (newOffsetBytes + 10))) | |
115 | 128 | let rem11 = if ((rand11[1] != "")) | |
116 | 129 | then (rem10 - 1) | |
117 | 130 | else rem10 | |
118 | - | let rand12 = nextRand(div, from, rand11[0], rem11, drop(randHash, (lastOffsetBytes + 12))) | |
119 | - | let rem12 = if ((rand12[1] != "")) | |
120 | - | then (rem11 - 1) | |
121 | - | else rem11 | |
122 | - | let rand13 = nextRand(div, from, rand12[0], rem12, drop(randHash, (lastOffsetBytes + 13))) | |
123 | - | let rem13 = if ((rand13[1] != "")) | |
124 | - | then (rem12 - 1) | |
125 | - | else rem12 | |
126 | - | let rand14 = nextRand(div, from, rand13[0], rem13, drop(randHash, (lastOffsetBytes + 14))) | |
127 | - | let rem14 = if ((rand14[1] != "")) | |
128 | - | then (rem13 - 1) | |
129 | - | else rem13 | |
130 | - | [rand14[0], if ((rem14 == 0)) | |
131 | + | [rand11[0], if ((rem11 == 0)) | |
131 | 132 | then "0" | |
132 | - | else toString( | |
133 | + | else toString(rem11), toString((newOffsetBytes + 10)), toString(newSlowIdx1), toString(newSlowIdx2), toString(newFastIdx1), toString(newFastIdx2)] | |
133 | 134 | } | |
134 | 135 | ||
135 | 136 | ||
136 | - | func validateDtxKey (sessionId,data) = if (if (if ((sessionId == take(data.key, SESSIONIDFIXSIZE))) | |
137 | - | then (size(data.key) > SESSIONIDFIXSIZE) | |
138 | - | else false) | |
139 | - | then !(isDefined(getString(this, data.key))) | |
140 | - | else false) | |
141 | - | then match data.value { | |
142 | - | case str: String => | |
143 | - | true | |
144 | - | case _ => | |
145 | - | throw((sessionId + " draw: only String type is accepted for data transactions")) | |
137 | + | func validateDtxKey (sessionId,dataKeysCount,data) = { | |
138 | + | let dataKeyInfo = split(data.key, "_") | |
139 | + | if ((size(dataKeyInfo) != 2)) | |
140 | + | then throw("Invalid data key format. It must follow to ${sessionId}_${num}") | |
141 | + | else { | |
142 | + | let keySessionId = dataKeyInfo[0] | |
143 | + | let keyPostfix = dataKeyInfo[1] | |
144 | + | if ((sessionId != keySessionId)) | |
145 | + | then throw(((("Several data keys have different sessionId: " + sessionId) + " and ") + keySessionId)) | |
146 | + | else if (isDefined(getString(this, data.key))) | |
147 | + | then throw(("One of the data keys has already presented in account state: key=" + data.key)) | |
148 | + | else if ((size(keyPostfix) > 4)) | |
149 | + | then throw("Invalid data key format. It must follow to ${sessionId}_${num} where ${num} length couldn't be greater than 4") | |
150 | + | else if ((take(keyPostfix, 1) == "0")) | |
151 | + | then throw("Invalid data key format. It must follow to ${sessionId}_${num} where ${num} couldn't start from 0") | |
152 | + | else { | |
153 | + | let keyPostfixIntOpt = parseInt(keyPostfix) | |
154 | + | if (isDefined(keyPostfixIntOpt)) | |
155 | + | then { | |
156 | + | let keyPostfixInt = extract(keyPostfixIntOpt) | |
157 | + | if (if ((1 > keyPostfixInt)) | |
158 | + | then true | |
159 | + | else (keyPostfixInt > dataKeysCount)) | |
160 | + | then throw((((("Invalid data key format. It must follow to ${sessionId}_${num}" + " where ${num} must be a valid int value from 1 to ") + toString(dataKeysCount)) + " but actualNum=") + toString(keyPostfixInt))) | |
161 | + | else match data.value { | |
162 | + | case str: String => | |
163 | + | true | |
164 | + | case _ => | |
165 | + | throw((sessionId + " draw: only String type is accepted for data transactions")) | |
166 | + | } | |
167 | + | } | |
168 | + | else throw((("Invalid data key format. It must follow to ${sessionId}_${num}" + " where ${num} must be a valid int value from 1 to 7145 but actualNum=") + keyPostfix)) | |
169 | + | } | |
170 | + | } | |
146 | 171 | } | |
147 | - | else false | |
172 | + | ||
173 | + | ||
174 | + | func validateAndGetRandsPmt (randsCount,pmt,minDataPmt) = { | |
175 | + | let bound1 = 1000 | |
176 | + | let basePrice1 = ((13 * WAVELET) / 100) | |
177 | + | let div1 = 50 | |
178 | + | let diff1 = ((8 * WAVELET) / 100) | |
179 | + | let bound2 = 5000 | |
180 | + | let basePrice2 = ((297 * WAVELET) / 100) | |
181 | + | let div2 = 1000 | |
182 | + | let diff2 = ((143 * WAVELET) / 100) | |
183 | + | let bound3 = 50000 | |
184 | + | let basePrice3 = ((1427 * WAVELET) / 100) | |
185 | + | let div3 = 5000 | |
186 | + | let diff3 = ((705 * WAVELET) / 100) | |
187 | + | let minRandsPmt = if ((bound1 > randsCount)) | |
188 | + | then (basePrice1 + ((randsCount / div1) * diff1)) | |
189 | + | else if ((bound2 > randsCount)) | |
190 | + | then (basePrice2 + (((randsCount / div2) - 1) * diff2)) | |
191 | + | else if ((bound3 > randsCount)) | |
192 | + | then (basePrice3 + (((randsCount / div3) - 1) * diff3)) | |
193 | + | else throw("Please contact our sales team to generate more than 50k rands") | |
194 | + | let minPmt = (minRandsPmt + minDataPmt) | |
195 | + | if (isDefined(pmt.assetId)) | |
196 | + | then throw("Only WAVES can be used as a payment for rands generation") | |
197 | + | else if ((minPmt > pmt.amount)) | |
198 | + | then throw(((((("Attached payment is to small to generate " + toString(randsCount)) + " unique randoms numbers and upload at least 1 data tx: actualPmt=") + toString(pmt.amount)) + " but minPmt is ") + toString(minPmt))) | |
199 | + | else minRandsPmt | |
200 | + | } | |
148 | 201 | ||
149 | 202 | ||
150 | 203 | @Callable(i) | |
151 | 204 | func initDraw (randFrom,randTo,randsCount) = { | |
152 | 205 | let sessionId = toBase58String(i.transactionId) | |
206 | + | let rangeLength = ((randTo - randFrom) + 1) | |
207 | + | let maxRangeLength = (rangeLength / 2) | |
208 | + | let randToLen = size(toString((randTo - 1))) | |
209 | + | let randsCountLen = size(toString(randsCount)) | |
210 | + | let dynamicStatusLen = (((2 * randToLen) + (2 * randsCountLen)) + 4) | |
211 | + | let maxRandsCount = (((5120 - 175) - dynamicStatusLen) / (randToLen + 1)) | |
153 | 212 | if (if ((0 >= randFrom)) | |
154 | 213 | then true | |
155 | 214 | else (0 >= randTo)) | |
156 | 215 | then throw("randFrom and randTo must be greater than 0") | |
157 | 216 | else if ((randFrom >= randTo)) | |
158 | 217 | then throw("randFrom must be strict less then randTo") | |
159 | - | else if ((randsCount > ((randTo - randFrom) + 1))) | |
160 | - | then throw(((((("Impossible to generate " + toString(randsCount)) + " for provided random range - from ") + toString(randFrom)) + " to ") + toString(randTo))) | |
161 | - | else { | |
162 | - | let organizerPubKey58 = toBase58String(i.callerPublicKey) | |
163 | - | let randsCountStr = toString(randsCount) | |
164 | - | let initState = formatStateDataStr(STATEINIT, organizerPubKey58, toString(randFrom), toString(randTo), randsCountStr, randsCountStr, "null", "0", "") | |
165 | - | ScriptResult(WriteSet([DataEntry(sessionId, initState)]), TransferSet([ScriptTransfer(SERVER, 1000, unit)])) | |
166 | - | } | |
218 | + | else if ((randsCount > rangeLength)) | |
219 | + | then throw(((((((("Impossible to generate " + toString(randsCount)) + " unique numbers for provided random range [") + toString(randFrom)) + ", ") + toString(randTo)) + "] with actual size ") + toString(rangeLength))) | |
220 | + | else if ((randsCount > maxRangeLength)) | |
221 | + | then throw(((((((((("randsCount must be less then 50% of passed range length: range=[" + toString(randFrom)) + ", ") + toString(randTo)) + "], rangeLength=") + toString(rangeLength)) + " randsCount=") + toString(randsCount)) + " allowedRandsCount=") + toString(maxRangeLength))) | |
222 | + | else if ((randsCount > maxRandsCount)) | |
223 | + | then throw(((((("randsCount couldn't be kept in 1 data entry: 1_randNumSpace=" + toString((randToLen + 1))) + " maxAllowedRandsCount=") + toString(maxRandsCount)) + " actualRandsCount=") + toString(randsCount))) | |
224 | + | else if (!(isDefined(i.payment))) | |
225 | + | then throw("Please provide payment to generate unique random numbers") | |
226 | + | else { | |
227 | + | let pmt = extract(i.payment) | |
228 | + | let minDataPmt = ((5 * WAVELET) / 1000) | |
229 | + | let randsPmt = validateAndGetRandsPmt(randsCount, pmt, minDataPmt) | |
230 | + | let dataPmt = (pmt.amount - randsPmt) | |
231 | + | let dataTxCount = (dataPmt / minDataPmt) | |
232 | + | let dataKeysCount = if ((7145 >= (dataTxCount * 5))) | |
233 | + | then (dataTxCount * 5) | |
234 | + | else 7145 | |
235 | + | let organizerPubKey58 = toBase58String(i.callerPublicKey) | |
236 | + | let randsCountStr = toString(randsCount) | |
237 | + | let initState = formatStateDataStr(STATEINIT, organizerPubKey58, toString(randFrom), toString(randTo), randsCountStr, randsCountStr, toString(dataKeysCount), "null", "0", "0", "1", "0", "1", "") | |
238 | + | ScriptResult(WriteSet([DataEntry(sessionId, initState)]), TransferSet([ScriptTransfer(SERVER, randsPmt, unit)])) | |
239 | + | } | |
167 | 240 | } | |
168 | 241 | ||
169 | 242 | ||
170 | 243 | ||
171 | 244 | @Callable(i) | |
172 | 245 | func ready (sessionId) = { | |
173 | 246 | let drawParamsList = extractGameDataList(sessionId) | |
174 | 247 | let drawState = drawParamsList[IdxState] | |
175 | 248 | let organizerPubKey58 = drawParamsList[IdxOrganizerPub] | |
176 | 249 | let randsCountStr = drawParamsList[IdxRandsCount] | |
177 | 250 | let remainRandsCountStr = drawParamsList[IdxRemainRandsCount] | |
178 | 251 | let fromStr = drawParamsList[IdxRandFrom] | |
179 | 252 | let toStr = drawParamsList[IdxRandTo] | |
253 | + | let dataKeysCount = drawParamsList[IdxDataKeysCount] | |
180 | 254 | let organizerPubKey = fromBase58String(organizerPubKey58) | |
181 | 255 | if ((drawState != STATEINIT)) | |
182 | 256 | then throw((sessionId + " draw: moving into READY state is allowed only from INIT state")) | |
183 | 257 | else if ((organizerPubKey != i.callerPublicKey)) | |
184 | 258 | then throw((sessionId + "draw: moving into READY state is allowed for organizer only")) | |
185 | 259 | else { | |
186 | - | let readyState = formatStateDataStr(DATADONE, organizerPubKey58, fromStr, toStr, randsCountStr, remainRandsCountStr, toBase58String(i.transactionId), "0", "") | |
260 | + | let readyState = formatStateDataStr(DATADONE, organizerPubKey58, fromStr, toStr, randsCountStr, remainRandsCountStr, dataKeysCount, toBase58String(i.transactionId), "0", "0", "1", "0", "1", "") | |
187 | 261 | WriteSet([DataEntry(sessionId, readyState)]) | |
188 | 262 | } | |
189 | 263 | } | |
190 | 264 | ||
191 | 265 | ||
192 | 266 | ||
193 | 267 | @Callable(i) | |
194 | 268 | func random (sessionId,rsaSign) = { | |
195 | 269 | let drawParamsList = extractGameDataList(sessionId) | |
196 | 270 | let drawState = drawParamsList[IdxState] | |
197 | 271 | let organizerPubKey58 = drawParamsList[IdxOrganizerPub] | |
198 | 272 | let randsCountStr = drawParamsList[IdxRandsCount] | |
199 | 273 | let remainRandsCount = parseIntValue(drawParamsList[IdxRemainRandsCount]) | |
200 | 274 | let lastOffsetBytes = parseIntValue(drawParamsList[IdxLastOffset]) | |
201 | 275 | let currRandsStr = drawParamsList[IdxCurrRands] | |
202 | 276 | let fromStr = drawParamsList[IdxRandFrom] | |
203 | 277 | let toStr = drawParamsList[IdxRandTo] | |
204 | 278 | let dataDoneTxId = drawParamsList[IdxDataDoneTxId] | |
279 | + | let slowIdx1 = parseIntValue(drawParamsList[9]) | |
280 | + | let slowIdx2 = parseIntValue(drawParamsList[10]) | |
281 | + | let fastIdx1 = parseIntValue(drawParamsList[11]) | |
282 | + | let fastIdx2 = parseIntValue(drawParamsList[12]) | |
205 | 283 | let from = parseIntValue(fromStr) | |
206 | 284 | let to = parseIntValue(toStr) | |
207 | 285 | let organizerPubKey = fromBase58String(organizerPubKey58) | |
208 | 286 | if ((drawState != DATADONE)) | |
209 | 287 | then throw((sessionId + " draw: it must be in READY state to generate random numbers")) | |
210 | 288 | else if (!(rsaVerify(SHA256, (toBytes(sessionId) + toBytes(dataDoneTxId)), rsaSign, RSAPUBLIC))) | |
211 | 289 | then throw("Invalid RSA signature") | |
212 | 290 | else { | |
213 | - | let randGenInfo = generateRand(sessionId, from, to, rsaSign, currRandsStr, remainRandsCount, lastOffsetBytes) | |
214 | - | let newRandsStr = randGenInfo[0] | |
291 | + | let randGenInfo = generateRand(sessionId, from, to, rsaSign, currRandsStr, remainRandsCount, lastOffsetBytes, slowIdx1, slowIdx2, fastIdx1, fastIdx2) | |
215 | 292 | let newRemainRandsCountStr = randGenInfo[1] | |
216 | - | let newOffsetBytes = randGenInfo[2] | |
217 | 293 | let newState = if ((newRemainRandsCountStr == "0")) | |
218 | 294 | then STATEFINISHED | |
219 | 295 | else DATADONE | |
220 | - | WriteSet([DataEntry(sessionId, formatStateDataStr(newState, organizerPubKey58, fromStr, toStr, randsCountStr, newRemainRandsCountStr, dataDoneTxId, | |
296 | + | WriteSet([DataEntry(sessionId, formatStateDataStr(newState, organizerPubKey58, fromStr, toStr, randsCountStr, newRemainRandsCountStr, drawParamsList[IdxDataKeysCount], dataDoneTxId, randGenInfo[2], randGenInfo[3], randGenInfo[4], randGenInfo[5], randGenInfo[6], randGenInfo[0]))]) | |
221 | 297 | } | |
222 | 298 | } | |
223 | 299 | ||
224 | 300 | ||
225 | 301 | @Verifier(tx) | |
226 | 302 | func verify () = match tx { | |
227 | 303 | case dtx: DataTransaction => | |
228 | 304 | let data0 = dtx.data[0] | |
229 | 305 | let sessionId = take(data0.key, SESSIONIDFIXSIZE) | |
230 | 306 | let drawParamsList = extractGameDataList(sessionId) | |
231 | - | let drawState = drawParamsList[0] | |
232 | - | let organizerPubKey58 = drawParamsList[1] | |
307 | + | let drawState = drawParamsList[IdxState] | |
308 | + | let organizerPubKey58 = drawParamsList[IdxOrganizerPub] | |
309 | + | let dataKeysCount = parseIntValue(drawParamsList[IdxDataKeysCount]) | |
233 | 310 | let organizerPubKey = fromBase58String(organizerPubKey58) | |
234 | 311 | let dataEntriesCount = size(dtx.data) | |
235 | 312 | let sigValid = sigVerify(tx.bodyBytes, tx.proofs[0], organizerPubKey) | |
236 | - | let dataSizeValid = if ((dataEntriesCount > 0)) | |
237 | - | then (5 >= dataEntriesCount) | |
313 | + | let dataSizeValid = (dataEntriesCount == 5) | |
314 | + | let feeValid = (dtx.fee == ((WAVELET * 5) / 1000)) | |
315 | + | let keysValid = if (if (if (if (validateDtxKey(sessionId, dataKeysCount, data0)) | |
316 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[1]) | |
317 | + | else false) | |
318 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[2]) | |
319 | + | else false) | |
320 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[3]) | |
321 | + | else false) | |
322 | + | then validateDtxKey(sessionId, dataKeysCount, dtx.data[4]) | |
238 | 323 | else false | |
239 | - | let keysValid = if (if (if (if (if (validateDtxKey(sessionId, data0)) | |
240 | - | then if ((1 >= dataEntriesCount)) | |
241 | - | then true | |
242 | - | else validateDtxKey(sessionId, dtx.data[1]) | |
324 | + | if (if (if (if ((drawState == STATEINIT)) | |
325 | + | then sigValid | |
243 | 326 | else false) | |
244 | - | then if ((2 >= dataEntriesCount)) | |
245 | - | then true | |
246 | - | else validateDtxKey(sessionId, dtx.data[2]) | |
247 | - | else false) | |
248 | - | then if ((3 >= dataEntriesCount)) | |
249 | - | then true | |
250 | - | else validateDtxKey(sessionId, dtx.data[3]) | |
251 | - | else false) | |
252 | - | then if ((4 >= dataEntriesCount)) | |
253 | - | then true | |
254 | - | else validateDtxKey(sessionId, dtx.data[4]) | |
255 | - | else false) | |
256 | - | then if ((5 >= dataEntriesCount)) | |
257 | - | then true | |
258 | - | else validateDtxKey(sessionId, dtx.data[5]) | |
259 | - | else false | |
260 | - | if (if (if ((drawState == STATEINIT)) | |
261 | - | then sigValid | |
327 | + | then feeValid | |
262 | 328 | else false) | |
263 | 329 | then dataSizeValid | |
264 | 330 | else false) | |
265 | 331 | then keysValid | |
266 | 332 | else false | |
267 | 333 | case sstx: SetScriptTransaction => | |
268 | 334 | true | |
269 | 335 | case ttx: TransferTransaction => | |
270 | 336 | true | |
271 | 337 | case _ => | |
272 | 338 | false | |
273 | 339 | } | |
274 | 340 |
github/deemru/w8io/026f985 119.94 ms ◑