├── .gitignore ├── README.md ├── eosdacrandom.abi ├── eosdacrandom.cpp ├── eosdacrandom.hpp ├── eosdacrandom.wasm ├── eosdacrandom.wast ├── gen.sh └── requester.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EOSDACRandom 2 | A random number generator smart contract. 3 | 4 | ## Attention 5 | ``` 6 | EOSDACRandom is related to oracleserver, an oracle market contract implemented by oraclechain, which 7 | currently is not open-sourced. Therefore building this project would be failed, the suggestion is remove 8 | all oracleserver related code. 9 | ``` 10 | 11 | ### participator 12 | 1. random contract (aka contract), an oracle instance. 13 | 2. data feeder 14 | 3. consumer 15 | 16 | ### procedure 17 | #### contract 18 | * set seed size, e.g n. 19 | 20 | #### data feeders 21 | * all n data feeders send hash of seed, e.g h1. 22 | * all data feeders send seed, e.g s, (MUST wait for h1 sent, otherwise this will fail). 23 | * calculate hash of seed s, get h2, compare h1 and h2, if equals, we put seed s in seed pool, otherwise, put provider in blacklist. 24 | 25 | #### consumers 26 | * first need implment a `getrandom` interface. 27 | * want a random number, register himself to random contract. 28 | * random contract generator a random number using seed pool. After it's done, clear pool, wait for next round. 29 | * send consumer this random number using `getrandom` by `SEND_INLINE_ACTION`. After it's done, clear geters table, wait for next round. 30 | 31 | ---- 32 | 33 | ### test 34 | 35 | #### How to test EOSDACRandom contract? 36 | 37 | ``` 38 | 1. deploy EOSDACToken contract 39 | 2. create OCT 40 | 3. issue OCT 41 | 4. create account 42 | 4.1 eosdacrandom 43 | 4.2 requester 44 | 4.3 seeder1, seeder2, seeder3 45 | 5. transfer OCT to requester, seeder1, seeder2, seeder3 46 | 6. deploy EOSDACRandom contract 47 | 7. deploy requester contract 48 | 8. push action eosdacrandom setsize '[3]' -p eosdacrandom 49 | 9. push action eosdacrandom regrequest '["requester", 1]' -p requester 50 | 10. push action eosdacrandom sendhash '["seeder1", "A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3", "OCT"]' -p seeder1 51 | 11. push action eosdacrandom sendhash '["seeder2", "A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3", "OCT"]' -p seeder2 52 | 12. push action eosdacrandom sendhash '["seeder3", "A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3", "OCT"]' -p seeder3 53 | 13. push action eosdacrandom seedseed '["seeder1", 1, "OCT"]' -p seeder1 54 | 14. push action eosdacrandom sendseed '["seeder2", 1, "OCT"]' -p seeder2 55 | 15. push action eosdacrandom sendseed '["seeder3", 1, "OCT"]' -p seeder3 56 | 16. here requester should get the random number. 57 | ``` 58 | 59 | #### example 60 | 61 | ``` 62 | $ cleos system newaccount eosio octtothemoon EOS6RLatEKdW3LGZ4Kfftf7xAEmqVkkFjsjG4uEefuatSZyDLk261 EOS6RLatEKdW3LGZ4Kfftf7xAEmqVkkFjsjG4uEefuatSZyDLk261 --stake-cpu "1.0000 SYS" --stake-net "1.0000 SYS" --buy-ram "1.0000 SYS" 63 | 64 | $ cleos system newaccount eosio eosdacrandom EOS62wJ1teiN23sdB2ynQE2za715FRwuXC7XN5xa1hu16QLs8EkV9 EOS62wJ1teiN23sdB2ynQE2za715FRwuXC7XN5xa1hu16QLs8EkV9 --stake-cpu "1.0000 SYS" --stake-net "1.0000 SYS" --buy-ram "1.0000 SYS" 65 | 66 | cleos set account permission eosdacrandom active '{"threshold": 1,"keys": [{"key": "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","weight": 1}],"accounts": [{"permission":{"actor":"eosdacrandom","permission":"eosio.code"},"weight":1}]}' owner -p eosdacrandom 67 | 68 | cleos create account eosio requester EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 69 | 70 | cleos create account eosio seeder1 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 71 | 72 | cleos create account eosio seeder2 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 73 | 74 | cleos create account eosio seeder3 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 75 | 76 | $ cleos set contract octtothemoon ./ eosdactoken.wasm eosdactoken.abi 77 | 78 | $ cleos set contract eosdacrandom ./ eosdacrandom.wasm eosdacrandom.abi 79 | 80 | $ cleos push action octtothemoon create '["octtothemoon", "100000000.0000 OCT"]' -p octtothemoon 81 | 82 | $ cleos push action octtothemoon issue '["octtothemoon", "100000000.0000 OCT"]' -p octtothemoon 83 | 84 | $ cleos push action oracleserver registerorc '["eosdacrandom", "random", 10, 10]' -p oracleserver 85 | 86 | cleos push action eosdactoken transfer '["eosdactoken", "eosdacrandom", "1000.0000 OCT", ""]' -p eosdactoken 87 | 88 | cleos push action eosdactoken transfer '["eosdactoken", "requester", "1.0000 OCT", ""]' -p eosdactoken 89 | 90 | cleos push action eosdactoken transfer '["eosdactoken", "seeder1", "1.0000 OCT", ""]' -p eosdactoken 91 | 92 | cleos push action eosdactoken transfer '["eosdactoken", "seeder2", "1.0000 OCT", ""]' -p eosdactoken 93 | 94 | cleos push action eosdactoken transfer '["eosdactoken", "seeder3", "1.0000 OCT", ""]' -p eosdactoken 95 | 96 | 97 | cleos set contract eosdacrandom ./eosdacrandom -p eosdacrandom 98 | 99 | cleos set contract requester ./requester -p requester 100 | 101 | cleos push action eosdacrandom setsize '[1]' -p eosdacrandom 102 | cleos push action eosdacrandom setsize '[1]' -p eosdacrandom 103 | 104 | cleos push action eosdacrandom regrequest '["requester", 1]' -p requester 105 | 106 | cleos push action eosdacrandom sendhash '["seeder1", "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b", "OCT"]' -p seeder1 107 | 108 | cleos push action eosdacrandom sendseed '["seeder1", 1, "OCT"]' -p seeder1 109 | ``` 110 | -------------------------------------------------------------------------------- /eosdacrandom.abi: -------------------------------------------------------------------------------- 1 | { 2 | "____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-08-30T04:05:09", 3 | "version": "eosio::abi/1.0", 4 | "types": [], 5 | "structs": [{ 6 | "name": "seedinfo", 7 | "base": "", 8 | "fields": [{ 9 | "name": "datafeeder", 10 | "type": "name" 11 | },{ 12 | "name": "seed", 13 | "type": "string" 14 | },{ 15 | "name": "hash", 16 | "type": "string" 17 | } 18 | ] 19 | },{ 20 | "name": "requestinfo", 21 | "base": "", 22 | "fields": [{ 23 | "name": "timestamp", 24 | "type": "uint64" 25 | },{ 26 | "name": "orderid", 27 | "type": "string" 28 | } 29 | ] 30 | },{ 31 | "name": "geterinfo", 32 | "base": "", 33 | "fields": [{ 34 | "name": "consumer", 35 | "type": "name" 36 | },{ 37 | "name": "requestinfos", 38 | "type": "requestinfo[]" 39 | } 40 | ] 41 | },{ 42 | "name": "seedconfig", 43 | "base": "", 44 | "fields": [{ 45 | "name": "owner", 46 | "type": "name" 47 | },{ 48 | "name": "target_size", 49 | "type": "uint64" 50 | },{ 51 | "name": "hash_count", 52 | "type": "uint64" 53 | },{ 54 | "name": "seed_match", 55 | "type": "uint64" 56 | } 57 | ] 58 | },{ 59 | "name": "setsize", 60 | "base": "", 61 | "fields": [{ 62 | "name": "size", 63 | "type": "uint64" 64 | } 65 | ] 66 | },{ 67 | "name": "setreserved", 68 | "base": "", 69 | "fields": [{ 70 | "name": "dfs", 71 | "type": "name[]" 72 | } 73 | ] 74 | },{ 75 | "name": "sendseed", 76 | "base": "", 77 | "fields": [{ 78 | "name": "datafeeder", 79 | "type": "name" 80 | },{ 81 | "name": "seed", 82 | "type": "string" 83 | } 84 | ] 85 | },{ 86 | "name": "sendhash", 87 | "base": "", 88 | "fields": [{ 89 | "name": "datafeeder", 90 | "type": "name" 91 | },{ 92 | "name": "hash", 93 | "type": "string" 94 | } 95 | ] 96 | },{ 97 | "name": "regrequest", 98 | "base": "", 99 | "fields": [{ 100 | "name": "consumer", 101 | "type": "name" 102 | },{ 103 | "name": "orderid", 104 | "type": "string" 105 | } 106 | ] 107 | },{ 108 | "name": "investor", 109 | "base": "", 110 | "fields": [{ 111 | "name": "name", 112 | "type": "name" 113 | },{ 114 | "name": "balance", 115 | "type": "asset" 116 | } 117 | ] 118 | },{ 119 | "name": "dfreputation", 120 | "base": "", 121 | "fields": [{ 122 | "name": "owner", 123 | "type": "name" 124 | },{ 125 | "name": "reputation", 126 | "type": "int64" 127 | } 128 | ] 129 | },{ 130 | "name": "dfregistery", 131 | "base": "", 132 | "fields": [{ 133 | "name": "oracle", 134 | "type": "name" 135 | },{ 136 | "name": "available", 137 | "type": "asset" 138 | },{ 139 | "name": "staked", 140 | "type": "asset" 141 | } 142 | ] 143 | },{ 144 | "name": "method", 145 | "base": "", 146 | "fields": [{ 147 | "name": "methodname", 148 | "type": "name" 149 | },{ 150 | "name": "price", 151 | "type": "asset" 152 | },{ 153 | "name": "stake", 154 | "type": "asset" 155 | } 156 | ] 157 | },{ 158 | "name": "service", 159 | "base": "", 160 | "fields": [{ 161 | "name": "oracle", 162 | "type": "name" 163 | },{ 164 | "name": "queryfee", 165 | "type": "asset" 166 | },{ 167 | "name": "prespan", 168 | "type": "int64" 169 | },{ 170 | "name": "lifespan", 171 | "type": "int64" 172 | },{ 173 | "name": "methods", 174 | "type": "method[]" 175 | } 176 | ] 177 | },{ 178 | "name": "order", 179 | "base": "", 180 | "fields": [{ 181 | "name": "creation", 182 | "type": "uint64" 183 | },{ 184 | "name": "orderid", 185 | "type": "string" 186 | },{ 187 | "name": "consumer", 188 | "type": "name" 189 | },{ 190 | "name": "oracle", 191 | "type": "name" 192 | },{ 193 | "name": "method", 194 | "type": "name" 195 | },{ 196 | "name": "status", 197 | "type": "int64" 198 | },{ 199 | "name": "treasury", 200 | "type": "asset" 201 | },{ 202 | "name": "expiration", 203 | "type": "int64" 204 | } 205 | ] 206 | },{ 207 | "name": "record", 208 | "base": "", 209 | "fields": [{ 210 | "name": "usageid", 211 | "type": "int64" 212 | },{ 213 | "name": "orderid", 214 | "type": "string" 215 | },{ 216 | "name": "oracle", 217 | "type": "name" 218 | },{ 219 | "name": "datafeeder", 220 | "type": "name" 221 | },{ 222 | "name": "staked", 223 | "type": "asset" 224 | },{ 225 | "name": "status", 226 | "type": "int64" 227 | } 228 | ] 229 | },{ 230 | "name": "dfreg", 231 | "base": "", 232 | "fields": [{ 233 | "name": "df", 234 | "type": "name" 235 | },{ 236 | "name": "oracle", 237 | "type": "name" 238 | },{ 239 | "name": "balance", 240 | "type": "asset" 241 | } 242 | ] 243 | },{ 244 | "name": "dfrefund", 245 | "base": "", 246 | "fields": [{ 247 | "name": "df", 248 | "type": "name" 249 | },{ 250 | "name": "oracle", 251 | "type": "name" 252 | },{ 253 | "name": "balance", 254 | "type": "asset" 255 | } 256 | ] 257 | },{ 258 | "name": "makeorder", 259 | "base": "", 260 | "fields": [{ 261 | "name": "consumer", 262 | "type": "name" 263 | },{ 264 | "name": "oracle", 265 | "type": "name" 266 | },{ 267 | "name": "method", 268 | "type": "name" 269 | },{ 270 | "name": "payment", 271 | "type": "asset" 272 | } 273 | ] 274 | },{ 275 | "name": "regorc", 276 | "base": "", 277 | "fields": [{ 278 | "name": "oracle", 279 | "type": "name" 280 | },{ 281 | "name": "method", 282 | "type": "name" 283 | },{ 284 | "name": "price", 285 | "type": "asset" 286 | },{ 287 | "name": "stake", 288 | "type": "asset" 289 | } 290 | ] 291 | },{ 292 | "name": "unregorc", 293 | "base": "", 294 | "fields": [{ 295 | "name": "oracle", 296 | "type": "name" 297 | },{ 298 | "name": "method", 299 | "type": "name" 300 | } 301 | ] 302 | },{ 303 | "name": "freezedf", 304 | "base": "", 305 | "fields": [{ 306 | "name": "oracle", 307 | "type": "name" 308 | },{ 309 | "name": "datafeeder", 310 | "type": "name" 311 | },{ 312 | "name": "stake", 313 | "type": "asset" 314 | } 315 | ] 316 | },{ 317 | "name": "unfreezedf", 318 | "base": "", 319 | "fields": [{ 320 | "name": "oracle", 321 | "type": "name" 322 | },{ 323 | "name": "datafeeder", 324 | "type": "name" 325 | },{ 326 | "name": "stake", 327 | "type": "asset" 328 | } 329 | ] 330 | },{ 331 | "name": "claimreward", 332 | "base": "", 333 | "fields": [{ 334 | "name": "oracle", 335 | "type": "name" 336 | },{ 337 | "name": "consumer", 338 | "type": "name" 339 | },{ 340 | "name": "orderid", 341 | "type": "string" 342 | } 343 | ] 344 | },{ 345 | "name": "feedback", 346 | "base": "", 347 | "fields": [{ 348 | "name": "receiver", 349 | "type": "name" 350 | },{ 351 | "name": "payment", 352 | "type": "asset" 353 | },{ 354 | "name": "reputation", 355 | "type": "int64" 356 | } 357 | ] 358 | },{ 359 | "name": "feedbackdf", 360 | "base": "", 361 | "fields": [{ 362 | "name": "oracle", 363 | "type": "name" 364 | },{ 365 | "name": "feedbacks", 366 | "type": "feedback[]" 367 | } 368 | ] 369 | },{ 370 | "name": "transfer", 371 | "base": "", 372 | "fields": [{ 373 | "name": "from", 374 | "type": "name" 375 | },{ 376 | "name": "to", 377 | "type": "name" 378 | },{ 379 | "name": "quantity", 380 | "type": "asset" 381 | },{ 382 | "name": "memo", 383 | "type": "string" 384 | } 385 | ] 386 | } 387 | ], 388 | "actions": [{ 389 | "name": "setsize", 390 | "type": "setsize", 391 | "ricardian_contract": "" 392 | },{ 393 | "name": "setreserved", 394 | "type": "setreserved", 395 | "ricardian_contract": "" 396 | },{ 397 | "name": "sendseed", 398 | "type": "sendseed", 399 | "ricardian_contract": "" 400 | },{ 401 | "name": "sendhash", 402 | "type": "sendhash", 403 | "ricardian_contract": "" 404 | },{ 405 | "name": "regrequest", 406 | "type": "regrequest", 407 | "ricardian_contract": "" 408 | },{ 409 | "name": "dfreg", 410 | "type": "dfreg", 411 | "ricardian_contract": "" 412 | },{ 413 | "name": "dfrefund", 414 | "type": "dfrefund", 415 | "ricardian_contract": "" 416 | },{ 417 | "name": "makeorder", 418 | "type": "makeorder", 419 | "ricardian_contract": "" 420 | },{ 421 | "name": "regorc", 422 | "type": "regorc", 423 | "ricardian_contract": "" 424 | },{ 425 | "name": "unregorc", 426 | "type": "unregorc", 427 | "ricardian_contract": "" 428 | },{ 429 | "name": "freezedf", 430 | "type": "freezedf", 431 | "ricardian_contract": "" 432 | },{ 433 | "name": "unfreezedf", 434 | "type": "unfreezedf", 435 | "ricardian_contract": "" 436 | },{ 437 | "name": "claimreward", 438 | "type": "claimreward", 439 | "ricardian_contract": "" 440 | },{ 441 | "name": "feedbackdf", 442 | "type": "feedbackdf", 443 | "ricardian_contract": "" 444 | },{ 445 | "name": "transfer", 446 | "type": "transfer", 447 | "ricardian_contract": "" 448 | } 449 | ], 450 | "tables": [{ 451 | "name": "seedinfo", 452 | "index_type": "i64", 453 | "key_names": [ 454 | "datafeeder" 455 | ], 456 | "key_types": [ 457 | "name" 458 | ], 459 | "type": "seedinfo" 460 | },{ 461 | "name": "requestinfo", 462 | "index_type": "i64", 463 | "key_names": [ 464 | "timestamp" 465 | ], 466 | "key_types": [ 467 | "uint64" 468 | ], 469 | "type": "requestinfo" 470 | },{ 471 | "name": "geterinfo", 472 | "index_type": "i64", 473 | "key_names": [ 474 | "consumer" 475 | ], 476 | "key_types": [ 477 | "name" 478 | ], 479 | "type": "geterinfo" 480 | },{ 481 | "name": "seedconfig", 482 | "index_type": "i64", 483 | "key_names": [ 484 | "owner" 485 | ], 486 | "key_types": [ 487 | "name" 488 | ], 489 | "type": "seedconfig" 490 | },{ 491 | "name": "investor", 492 | "index_type": "i64", 493 | "key_names": [ 494 | "name" 495 | ], 496 | "key_types": [ 497 | "name" 498 | ], 499 | "type": "investor" 500 | },{ 501 | "name": "dfreputation", 502 | "index_type": "i64", 503 | "key_names": [ 504 | "owner" 505 | ], 506 | "key_types": [ 507 | "name" 508 | ], 509 | "type": "dfreputation" 510 | },{ 511 | "name": "dfregistery", 512 | "index_type": "i64", 513 | "key_names": [ 514 | "oracle" 515 | ], 516 | "key_types": [ 517 | "name" 518 | ], 519 | "type": "dfregistery" 520 | },{ 521 | "name": "service", 522 | "index_type": "i64", 523 | "key_names": [ 524 | "oracle" 525 | ], 526 | "key_types": [ 527 | "name" 528 | ], 529 | "type": "service" 530 | },{ 531 | "name": "order", 532 | "index_type": "i64", 533 | "key_names": [ 534 | "creation" 535 | ], 536 | "key_types": [ 537 | "uint64" 538 | ], 539 | "type": "order" 540 | },{ 541 | "name": "record", 542 | "index_type": "i64", 543 | "key_names": [ 544 | "usageid" 545 | ], 546 | "key_types": [ 547 | "int64" 548 | ], 549 | "type": "record" 550 | } 551 | ], 552 | "ricardian_clauses": [], 553 | "error_messages": [], 554 | "abi_extensions": [] 555 | } -------------------------------------------------------------------------------- /eosdacrandom.cpp: -------------------------------------------------------------------------------- 1 | #include "eosdacrandom.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../oracleserver/oracleserver.hpp" 7 | 8 | eosdacrandom::eosdacrandom(account_name name) 9 | : contract(name) 10 | { 11 | } 12 | 13 | eosdacrandom::~eosdacrandom() 14 | { 15 | } 16 | 17 | void eosdacrandom::setsize(uint64_t size) 18 | { 19 | require_auth(_self); 20 | 21 | seedconfig_table config(_self, _self); 22 | auto existing = config.find(_self); 23 | if (existing != config.end()) { 24 | if (existing->hash_count == existing->target_size) { 25 | if (existing->hash_count != existing->seed_match) { 26 | eosio_assert(false, "do not set size during sendseed period"); 27 | } 28 | } 29 | 30 | config.modify(existing, _self, [&](auto& c){ 31 | c.target_size = size; 32 | }); 33 | } else { 34 | config.emplace(_self, [&](auto& c){ 35 | c.owner = _self; 36 | c.target_size = size; 37 | }); 38 | } 39 | } 40 | 41 | void eosdacrandom::setreserved(vector dfs) 42 | { 43 | require_auth(_self); 44 | 45 | if (dfs.empty()) { 46 | return; 47 | } 48 | 49 | seedconfig_table config(_self, _self); 50 | auto existing = config.find(_self); 51 | eosio_assert(existing != config.end(), "target size must set first"); 52 | eosio_assert(dfs.size() <= existing->target_size, "reserved data feeders' number bigger than target size"); 53 | eosio_assert(dfs.size() <= existing->target_size - existing->hash_count, "too much reserved data feeders"); 54 | 55 | seed_table seeds(_self, _self); 56 | name n; 57 | n.value = _self; 58 | for (const auto& df : dfs) { 59 | dfreg_table dfregs(N(oracleserver), df); 60 | auto df_existing = dfregs.find(n); 61 | eosio_assert(df_existing != dfregs.end(), "data feeder must register first"); 62 | 63 | auto sd = seeds.find(df); 64 | if (sd == seeds.end()) { 65 | seeds.emplace(_self, [&](auto& a){ 66 | a.datafeeder = df; 67 | }); 68 | 69 | // do we have to set config's state? like hash_count? I think we do. 70 | config.modify(*existing, _self, [&](auto& c){ 71 | c.hash_count++; 72 | }); 73 | } 74 | } 75 | } 76 | 77 | void eosdacrandom::sendseed(name datafeeder, string seed) 78 | { 79 | require_auth(datafeeder); 80 | 81 | name n; 82 | n.value = _self; 83 | 84 | dfreg_table dfregs(N(oracleserver), datafeeder); 85 | auto df_existing = dfregs.find(n); 86 | eosio_assert(df_existing != dfregs.end(), "data feeder is not registered to oracleserver"); 87 | 88 | seedconfig_table config(_self, _self); 89 | auto existing = config.find(_self); 90 | eosio_assert(existing != config.end(), "target size must set first"); 91 | const auto& cfg = *existing; 92 | 93 | seed_table seeds(_self, _self); 94 | const auto& sd = seeds.find(datafeeder); 95 | if (cfg.hash_count < cfg.target_size) { 96 | if (sd != seeds.end()) { 97 | seeds.erase(sd); 98 | config.modify(cfg, _self, [&](auto& c){ 99 | c.hash_count --; 100 | }); 101 | } 102 | eosio_assert(false, "hash size not fulled"); 103 | } 104 | 105 | string h = cal_sha256_str(seed); 106 | eosio_assert(sd->seed != seed, "you have already send seed"); 107 | if (sd->hash != h) { 108 | // datafeeder is evil, kick him out, whole procedure start over. 109 | for (auto itr = seeds.cbegin(); itr != seeds.cend(); ) { 110 | itr = seeds.erase(itr); 111 | } 112 | config.modify(cfg, _self, [&](auto& c){ 113 | c.hash_count = 0; 114 | c.seed_match = 0; 115 | }); 116 | return; 117 | } 118 | 119 | seeds.modify(sd, _self, [&](auto& a){ 120 | a.seed = seed; 121 | }); 122 | 123 | config.modify(cfg, _self, [&](auto& c){ 124 | c.seed_match = c.seed_match + 1; 125 | print(c.seed_match); 126 | }); 127 | 128 | // if all seeds match 129 | if (seedsmatch()) { 130 | // commit random to consumer. 131 | dispatch_request(); 132 | 133 | vector fbs; 134 | for (auto itr = seeds.begin(); itr != seeds.end(); ++itr) { 135 | // unfreeze datafeeder's stake balance. 136 | dispatch_inline(N(oracleserver), N(unfreezedf), 137 | {permission_level(_self, N(active))}, 138 | std::make_tuple(_self, itr->datafeeder, asset(100000, S(4,OCT)))); 139 | 140 | feedback fb{itr->datafeeder, asset(10000, S(4,OCT)), 1}; 141 | fbs.push_back(fb); 142 | } 143 | 144 | // feedback about datafeeders. 145 | dispatch_inline(N(oracleserver), N(feedbackdf), 146 | {permission_level(_self, N(active))}, 147 | std::make_tuple(_self, fbs)); 148 | } 149 | } 150 | 151 | void eosdacrandom::sendhash(name datafeeder, string hash) 152 | { 153 | require_auth(datafeeder); 154 | 155 | name n; 156 | n.value = _self; 157 | 158 | dfreg_table dfregs(N(oracleserver), datafeeder); 159 | auto df_existing = dfregs.find(n); 160 | eosio_assert(df_existing != dfregs.end(), "data feeder is not registered to oracleserver"); 161 | 162 | seedconfig_table config(_self, _self); 163 | auto existing = config.find(_self); 164 | eosio_assert(existing != config.end(), "target size must set first"); 165 | const auto& cfg = *existing; 166 | 167 | eosio_assert(cfg.hash_count < cfg.target_size, "seeds is full"); 168 | 169 | seed_table seeds(_self, _self); 170 | auto s = seeds.find(datafeeder); 171 | if (s == seeds.end()) { 172 | seeds.emplace(_self, [&](auto& a){ 173 | a.datafeeder = datafeeder; 174 | a.hash = hash; 175 | }); 176 | 177 | config.modify(cfg, _self, [&](auto& c){ 178 | c.hash_count++; 179 | }); 180 | } else { 181 | seeds.modify(s, _self, [&](auto& a){ 182 | a.hash = hash; 183 | }); 184 | } 185 | } 186 | 187 | void eosdacrandom::regrequest(name consumer, string orderid) 188 | { 189 | eosio_assert(is_account(consumer), "Invalid account"); 190 | 191 | // here we should query consumer and orderid from oracleserver for answer, whether the orderid is valid. 192 | // if true, then go ahead, otherwise it stops. 193 | 194 | order_table orders(N(oracleserver), consumer); 195 | bool found = false; 196 | for (const auto& o : orders) { 197 | if (o.orderid == orderid) { 198 | found = true; 199 | } 200 | } 201 | eosio_assert(found, "order id is not exist"); 202 | 203 | geter_table geters(_self, _self); 204 | auto it = geters.find(consumer); 205 | uint64_t cur = current_time(); 206 | requestinfo req {cur, orderid}; 207 | if (it == geters.end()) { 208 | geters.emplace(_self, [&](auto& a){ 209 | a.consumer = consumer; 210 | a.requestinfos.push_back(req); 211 | }); 212 | } else { 213 | geters.modify(it, _self, [&](auto& a){ 214 | bool same_order = false; 215 | for (auto ri = a.requestinfos.begin(); ri != a.requestinfos.end(); ++ri) { 216 | if (ri->orderid == orderid) { // if orderid equals former orderid. 217 | *ri = req; 218 | same_order = true; 219 | break; 220 | } 221 | } 222 | 223 | if (!same_order) { 224 | a.requestinfos.push_back(req); 225 | } 226 | }); 227 | } 228 | } 229 | 230 | string eosdacrandom::one_seed() 231 | { 232 | // use seeds to generate random number 233 | seedconfig_table config(_self, _self); 234 | auto existing = config.find(_self); 235 | eosio_assert(existing != config.end(), "target size must set first"); 236 | eosio_assert(existing->hash_count == existing->target_size, "seed is not full"); 237 | 238 | // how to generate random number? 239 | string seed; 240 | seed_table seeds(_self, _self); 241 | for (auto it = seeds.cbegin(); it != seeds.cend();) { 242 | seed += it->seed; 243 | it = seeds.erase(it); 244 | } 245 | 246 | config.modify(existing, _self, [&](auto& c){ 247 | c.hash_count = 0; 248 | c.seed_match = 0; 249 | }); 250 | 251 | return seed; 252 | } 253 | 254 | bool eosdacrandom::seedsmatch() 255 | { 256 | seedconfig_table config(_self, _self); 257 | auto existing = config.find(_self); 258 | eosio_assert(existing != config.end(), "target size must set first"); 259 | const auto& cfg = *existing; 260 | 261 | if (cfg.hash_count != cfg.target_size) { 262 | return false; 263 | } 264 | 265 | if (cfg.hash_count == cfg.seed_match) { 266 | return true; 267 | } 268 | 269 | return false; 270 | } 271 | 272 | string eosdacrandom::cal_sha256_str(string seed) 273 | { 274 | string h; 275 | char d[255] = { 0 }; 276 | snprintf(d, sizeof(d) - 1, "%s", seed.c_str()); 277 | checksum256 cs = { 0 }; 278 | sha256(d, strlen(d), &cs); 279 | for (int i = 0; i < sizeof(cs.hash); ++i) { 280 | char hex[3] = { 0 }; 281 | snprintf(hex, sizeof(hex), "%02x", static_cast(cs.hash[i])); 282 | h += hex; 283 | } 284 | 285 | return h; 286 | } 287 | 288 | string eosdacrandom::make_sha256_str(int index, string orderid, string seed) 289 | { 290 | string h; 291 | char str[255] = { 0 }; 292 | snprintf(str, sizeof(str), "%d%s%s", index, orderid.c_str(), seed.c_str()); 293 | checksum256 cs = { 0 }; 294 | sha256(str, strlen(str), &cs); 295 | 296 | for (int i = 0; i < sizeof(cs.hash); ++i) { 297 | char hex[3] = { 0 }; 298 | snprintf(hex, sizeof(hex), "%02x", static_cast(cs.hash[i])); 299 | h += hex; 300 | } 301 | 302 | return h; 303 | } 304 | 305 | void eosdacrandom::dispatch_request() 306 | { 307 | print("all seeds matched."); 308 | uint64_t cur = current_time(); 309 | string seed = one_seed(); 310 | static int expiraion = 3000; // ms 311 | std::vector> reqs; 312 | 313 | geter_table geters(_self, _self); 314 | for (auto i = geters.cbegin(); i != geters.cend();) { 315 | auto tmp = i->requestinfos; 316 | int index = 0; 317 | for (auto j = tmp.begin(); j != tmp.end();) { 318 | if (cur - j->timestamp >= expiraion) { 319 | string random = make_sha256_str(index++, j->orderid, seed); 320 | dispatch_inline(i->consumer, N(genrandom), 321 | {permission_level(_self, N(active))}, 322 | std::make_tuple(j->orderid, random)); 323 | j = tmp.erase(j); 324 | } else { 325 | ++j; 326 | } 327 | } 328 | 329 | if (tmp.size()) { 330 | reqs.push_back(tmp); 331 | } 332 | 333 | i = geters.erase(i); 334 | } 335 | } 336 | 337 | EOSIO_ABI( eosdacrandom, (setsize) (setreserved) (sendseed) (sendhash) (regrequest) ) 338 | -------------------------------------------------------------------------------- /eosdacrandom.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace eosio; 8 | using namespace std; 9 | 10 | // @abi table 11 | struct seedinfo 12 | { 13 | name datafeeder; 14 | string seed; 15 | string hash; 16 | 17 | uint64_t primary_key() const { return datafeeder.value; } 18 | 19 | EOSLIB_SERIALIZE( seedinfo, (datafeeder) (seed) (hash)) 20 | }; 21 | 22 | typedef eosio::multi_index seed_table; 23 | 24 | // @abi table 25 | struct requestinfo 26 | { 27 | uint64_t timestamp; 28 | string orderid; 29 | 30 | EOSLIB_SERIALIZE( requestinfo, (timestamp) (orderid) ) 31 | }; 32 | 33 | // @abi table 34 | struct geterinfo 35 | { 36 | name consumer; 37 | vector requestinfos; 38 | 39 | uint64_t primary_key() const { return consumer.value; } 40 | 41 | EOSLIB_SERIALIZE( geterinfo, (consumer) (requestinfos) ) 42 | }; 43 | 44 | typedef eosio::multi_index geter_table; 45 | 46 | // void getrandom(string orderid, int64_t number) {} 47 | 48 | class eosdacrandom : public eosio::contract 49 | { 50 | public: 51 | eosdacrandom(account_name name); 52 | ~eosdacrandom(); 53 | 54 | // @abi action 55 | void setsize(uint64_t size); 56 | 57 | // @abi action 58 | void setreserved(vector dfs); 59 | 60 | // @abi action 61 | void sendseed(name datafeeder, string seed); 62 | 63 | // @abi action 64 | void sendhash(name datafeeder, string hash); 65 | 66 | // @abi action 67 | void regrequest(name consumer, string orderid); 68 | 69 | public: 70 | 71 | // @abi table 72 | struct seedconfig 73 | { 74 | account_name owner; 75 | uint64_t target_size; 76 | uint64_t hash_count; 77 | uint64_t seed_match; 78 | 79 | uint64_t primary_key() const { return owner; } 80 | 81 | EOSLIB_SERIALIZE( seedconfig, (owner) (target_size) (hash_count) (seed_match) ) 82 | }; 83 | 84 | typedef eosio::multi_index seedconfig_table; 85 | 86 | private: 87 | string one_seed(); 88 | bool seedsmatch(); 89 | 90 | string cal_sha256_str(string word); 91 | string make_sha256_str(int index, string orderid, string seed); 92 | 93 | void dispatch_request(); 94 | }; 95 | -------------------------------------------------------------------------------- /eosdacrandom.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OracleChain/EOSDACRandom/bc86b4d40f565acfa3e7dfb2c9c0bd1dc2ab3177/eosdacrandom.wasm -------------------------------------------------------------------------------- /gen.sh: -------------------------------------------------------------------------------- 1 | rm -rf eosdacrandom.abi 2 | rm -rf eosdacrandom.wast 3 | eosiocpp -g eosdacrandom.abi eosdacrandom.cpp 4 | eosiocpp -o eosdacrandom.wast eosdacrandom.cpp 5 | -------------------------------------------------------------------------------- /requester.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace eosio; 3 | 4 | class requester : public eosio::contract { 5 | public: 6 | using contract::contract; 7 | 8 | // @abi action 9 | void genrandom(string orderid, uint64_t num) { 10 | print("Order id: "); 11 | print(orderid); 12 | print("Random number: "); 13 | print(num); 14 | } 15 | }; 16 | 17 | EOSIO_ABI( requester, (genrandom) ) 18 | --------------------------------------------------------------------------------