├── .gitignore ├── README.md ├── bitcoincomp ├── README.md ├── bitcoincomp.cpp └── provable │ └── eos_api.hpp ├── checkqueryid ├── README.md ├── checkqueryid.cpp └── provable │ └── eos_api.hpp ├── dieselprice ├── README.md ├── dieselprice.cpp └── provable │ └── eos_api.hpp ├── encryptedqry ├── README.md ├── encryptedqry.cpp └── provable │ └── eos_api.hpp ├── eosusdprice ├── README.md ├── eosusdprice.cpp └── provable │ └── eos_api.hpp ├── randomsample ├── README.md ├── provable │ └── eos_api.hpp └── randomsample.cpp ├── urlrequests ├── README.md ├── provable │ ├── README.md │ └── eos_api.hpp └── urlrequests.cpp └── wolframrand ├── README.md ├── provable ├── README.md └── eos_api.hpp └── wolframrand.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | *.abi 3 | *.wasm 4 | *.wast 5 | *.sh 6 | *.pwd 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## __Provable EOS Examples__ - CDT 1.7 [![Join the chat at https://gitter.im/provable/eos-api](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/provable/eos-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docs@Provable.xyz](https://camo.githubusercontent.com/5e89710c6ae9ce0da822eec138ee1a2f08b34453/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f63732d536c6174652d627269676874677265656e2e737667)](http://docs.provable.xyz) [![Contributions Welcome!](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/provable-things/eos-examples/issues) [![HitCount](http://hits.dwyl.io/provable-things/eos-examples.svg)](http://hits.dwyl.io/provable-things/eos-examples) 2 | 3 | Here you can find some code examples showing just how __easy__ it is to integrate the __Provable__ Service into your EOS smart-contracts! Thanks to our CDT 1.7 [__EOS API__](https://github.com/provable-things/eos-api) using __Provable__ in your projects couldn't be more straightforward. 4 | In C++ it is as simple as importing __`provable/eos_api.hpp`__ in your contract like so: 5 | 6 | ```c++ 7 | #include "provable/eos_api.hpp" 8 | ``` 9 | 10 | This provisions your contract with the __`provable_query()`__ function (and many others!), which makes it trivial to leverage our technology straight away. 11 | 12 | :tada: Happy developing! 13 | 14 | *** 15 | 16 | ### :globe_with_meridians: __Network Availability__ 17 | 18 | The oracle service is currently available on: 19 | 20 | * EOS Mainnet 21 | * EOS Jungle 3 Testnet 22 | * EOS Kylin Testnet 23 | 24 | ### :computer: __CDT Availability__ 25 | 26 | The `master` branch is currently referring to EOSIO.CDT 1.7, please check the releases for the specific CDT version: 27 | 28 | * CDT 1.7 29 | * CDT 1.6 30 | * CDT 1.4 31 | 32 | ### :black_nib: __Notes__ 33 | 34 | This is a list of the features you will find amongst the examples, along with a reference to the relevant example: 35 | 36 | * Sending simple __URL Queries!__ [#1](./eosusdprice/eosusdprice.cpp) [#2](./checkqueryid/checkqueryid.cpp) [#3](./dieselprice/dieselprice.cpp) 37 | * Scheduling a query for a __future date!__ [#1](./wolframrand/wolframrand.cpp) [#2](./dieselprice/dieselprice.cpp) 38 | * Sending calls __recursively__! [#1](./wolframrand/wolframrand.cpp) 39 | * Requesting __Android__ authenticity __proofs!__ [#1](./eosusdprice/eosusdprice.cpp) 40 | * Requesting __IPFS storage__ for the authenticity proofs! [#1](./eosusdprice/eosusdprice.cpp) 41 | * Leveraging __JSONPATH__ parsing helpers! [#1](./eosusdprice/eosusdprice.cpp) 42 | * Leveraging __XPATH__ parsing helpers! [#1](./dieselprice/dieselprice.cpp) 43 | * Using the __random datasouce__! [#1](./randomsample/randomsample.cpp) 44 | * Using the __computation datasouce__! [#1](./bitcoincomp/bitcoincomp.cpp) [#2](./urlrequests/urlrequests.cpp) 45 | * Using the __WolframAlpha__ datasource! [#1](./wolframrand/wolframrand.cpp) 46 | * Using the __encrypted__ query! [#1](./encryptedqry/encryptedqry.cpp) 47 | 48 | ### :loudspeaker: __Support__ 49 | 50 | If you need any help working with the examples here, you can get timely support in the [__Provable Gitter__](https://gitter.im/provable/eos-api) channel. 51 | -------------------------------------------------------------------------------- /bitcoincomp/README.md: -------------------------------------------------------------------------------- 1 | # Provable's Bitcoin Computation Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **computation** datasource. 4 | 5 | The `bitcoincomp.cpp` example allows you to *retrieve the balance of a Bitcoin address*. 6 | 7 | ## :page_with_curl: *Instructions* 8 | 9 | **1)** Compile your contract (launch this command **inside** the folder `bitcoin-computation`): 10 | 11 | **`❍ eosio-cpp -abigen bitcoincomp.cpp -o bitcoincomp.wasm`** 12 | 13 | **2)** Unlock your wallet: 14 | 15 | **`❍ cleos wallet unlock -n --password `** 16 | 17 | **3)** Deploy your contract on EOS (launch this command **outside** the folder `bitcoin-computation`): 18 | 19 | **`❍ cleos set contract bitcoin-computation bitcoincomp.wasm bitcoincomp.abi -p @`** 20 | 21 | **4)** Call the `compute` action of the contract `bitcoincomp`: 22 | 23 | **`❍ cleos push action compute '[]' -p @`** 24 | 25 | ## :pen: Notes 26 | 27 | Provable replies to your `provable_query` by calling your `callback(...)` with the *balance result* . 28 | You can search for your transaction ID in one of the following links to verify it: 29 | 30 | * :mag_right::ledger: [Jungle 3 Blocks.io](https://jungle3.bloks.io/): A block explorer for the Jungle 3.0 testnet. 31 | 32 | * :mag_right::ledger: [Kylin Blocks.io](https://kylin.bloks.io/): A block explorer for the Kylin testnet. 33 | 34 | ## :ambulance: Support 35 | 36 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel to get timely support! 37 | 38 | ***Happy developing!*** 39 | 40 | -------------------------------------------------------------------------------- /bitcoincomp/bitcoincomp.cpp: -------------------------------------------------------------------------------- 1 | #include "provable/eos_api.hpp" 2 | 3 | class bitcoincomp : public eosio::contract 4 | { 5 | public: 6 | using contract::contract; 7 | 8 | bitcoincomp(eosio::name receiver, eosio::name code, datastream ds) : contract(receiver, code, ds) {} 9 | 10 | [[eosio::action]] 11 | void compute() 12 | { 13 | // Prepare computation arguments: IPFS Multihash and Bitcoin Address 14 | std::string bitcoinAddress = "3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r"; 15 | std::vector> args = { 16 | string_to_vector("QmYe37uvAUvZZ8ksV726BZt6dJFWP764sTPisNQtuDZVom"), 17 | string_to_vector(bitcoinAddress) 18 | }; 19 | std::vector query = provable_set_computation_args(args); 20 | print("Sending query to Provable..."); 21 | provable_query("computation", query); 22 | } 23 | 24 | [[eosio::action]] 25 | void callback( 26 | const eosio::checksum256 queryId, 27 | const std::vector result, 28 | const std::vector proof 29 | ) 30 | { 31 | require_auth(provable_cbAddress()); 32 | // Print the account balance of the Bitcoin account 33 | const std::string result_str = vector_to_string(result); 34 | print("Account balance:", result_str); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /bitcoincomp/provable/eos_api.hpp: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * Provable API * 3 | * * 4 | * Copyright (c) 2015-2016 Provable SRL * 5 | * Copyright (c) 2016 Provable LTD * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy * 7 | * of this software and associated documentation files (the "Software"), to deal * 8 | * in the Software without restriction, including without limitation the rights * 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * 10 | * copies of the Software, and to permit persons to whom the Software is * 11 | * furnished to do so, subject to the following conditions: * 12 | * The above copyright notice and this permission notice shall be included in * 13 | * all copies or substantial portions of the Software. * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * 20 | * THE SOFTWARE. * 21 | *********************************************************************************/ 22 | 23 | #ifndef PROVABLEAPI_H 24 | #define PROVABLEAPI_H 25 | 26 | 27 | /************************************************** 28 | * INCLUDE * 29 | * Libraries * 30 | **************************************************/ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | /************************************************** 40 | * MACRO * 41 | * Definitions * 42 | **************************************************/ 43 | #ifndef PROVABLE_NETWORK_NAME 44 | #warning PROVABLE_NETWORK_NAME is not set, setting it to "eosio_unknown".. [possible values are "eosio_mainnet"/"eosio_testnet_jungle"/"eosio_testnet_kylin"/"eosio_unknown"] 45 | #define PROVABLE_NETWORK_NAME "eosio_unknown" 46 | #endif // PROVABLE_NETWORK_NAME 47 | 48 | #ifndef CONTRACT_NAME 49 | #warning CONTRACT_NAME is not set, setting it to "".. [write the "contract.name" not the account name] 50 | #define CONTRACT_NAME "unknown" 51 | #endif // CONTRACT_NAME 52 | 53 | #ifndef PROVABLE_PAYER 54 | #define PROVABLE_PAYER _self 55 | #endif // PROVABLE_PAYER 56 | 57 | #define provable_query(...) __provable_query(PROVABLE_PAYER, __VA_ARGS__, _self) 58 | #define provable_newRandomDSQuery(...) __provable_newRandomDSQuery(PROVABLE_PAYER, __VA_ARGS__, _self) 59 | #define provable_queryId_localEmplace(...) __provable_queryId_localEmplace(__VA_ARGS__, _self) 60 | #define provable_queryId_match(...) __provable_queryId_match(__VA_ARGS__, _self) 61 | 62 | 63 | /************************************************** 64 | * NAMESPACES * 65 | * Declarations * 66 | **************************************************/ 67 | using namespace eosio; 68 | using namespace std; 69 | 70 | 71 | /************************************************** 72 | * CONSTANTS * 73 | * Proof Types * 74 | **************************************************/ 75 | const uint8_t proofType_NONE = 0x00; 76 | const uint8_t proofType_TLSNotary = 0x10; 77 | const uint8_t proofType_Ledger = 0x30; 78 | const uint8_t proofType_Android = 0x40; 79 | const uint8_t proofType_Native = 0xF0; 80 | const uint8_t proofStorage_IPFS = 0x01; 81 | 82 | const uint8_t CODE_HASH_RANDOMDS[32] = { 83 | 253, 148, 250, 113, 188, 11, 161, 13, 57, 212, 100, 208, 216, 244, 101, 239, 238, 240, 162, 118, 78, 56, 135, 252, 201, 223, 65, 222, 210, 15, 80, 92 84 | }; 85 | const uint8_t LEDGERKEY[64] = { 86 | 127, 185, 86, 70, 156, 92, 155, 137, 132, 13, 85, 180, 53, 55, 230, 106, 152, 221, 72, 17, 234, 10, 39, 34, 66, 114, 194, 229, 98, 41, 17, 232, 83, 122, 47, 142, 134, 164, 107, 174, 200, 40, 100, 233, 141, 208, 30, 156, 204, 47, 139, 197, 223, 201, 203, 229, 169, 26, 41, 4, 152, 221, 150, 228 87 | }; 88 | 89 | 90 | /************************************************** 91 | * PROVABLE TABLE * 92 | * Definition * 93 | **************************************************/ 94 | struct [[eosio::table, eosio::contract("provableconn")]] snonce 95 | { 96 | name sender; 97 | uint32_t nonce; 98 | 99 | uint64_t primary_key() const { return sender.value; } 100 | }; 101 | 102 | struct [[eosio::table, eosio::contract("provableconn")]] cbaddr 103 | { 104 | name sender; 105 | 106 | uint64_t primary_key() const { return sender.value; } 107 | }; 108 | 109 | struct [[eosio::table, eosio::contract("provableconn")]] spubkey 110 | { 111 | name key; 112 | eosio::checksum256 randomDS_lastSessionPubkeyHash; 113 | 114 | uint64_t primary_key() const { return key.value; } 115 | eosio::checksum256 get_randomDS_lastSessionPubkeyHash() const { return randomDS_lastSessionPubkeyHash; } 116 | }; 117 | 118 | 119 | /************************************************** 120 | * PROVABLE TABLE * 121 | * Definition * 122 | **************************************************/ 123 | 124 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] scommitment 125 | { 126 | name shortqueryid; 127 | eosio::checksum256 queryid; 128 | eosio::checksum256 commitment; 129 | 130 | uint64_t primary_key() const { return shortqueryid.value; } 131 | }; 132 | 133 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] queryid 134 | { 135 | name key; 136 | eosio::checksum256 qid; 137 | uint8_t active; 138 | 139 | uint64_t primary_key() const { return key.value; } 140 | }; 141 | 142 | typedef eosio::multi_index ds_snonce; 143 | typedef eosio::multi_index ds_cbaddr; 144 | typedef eosio::multi_index ds_spubkey; 145 | typedef eosio::multi_index ds_scommitment; 146 | typedef eosio::multi_index ds_queryid; 147 | 148 | 149 | /************************************************** 150 | * PUBLIC FUNCTIONS * 151 | * Implementation * 152 | **************************************************/ 153 | eosio::name provable_cbAddress() 154 | { 155 | ds_cbaddr cb_addrs("provableconn"_n, "provableconn"_n.value); // go to the connector table which identify the sender 156 | auto itr = cb_addrs.begin(); // point to the first element of the table 157 | const uint64_t cbaddr_uint64t = (itr != cb_addrs.end()) ? itr->sender.value : 0; 158 | eosio::name cbaddr_name = eosio::name(cbaddr_uint64t); 159 | return cbaddr_name; 160 | } 161 | 162 | std::string vector_uc_to_string(const std::vector v) 163 | { 164 | std::string v_str(v.begin(), v.end()); 165 | return v_str; 166 | } 167 | 168 | std::vector provable_set_computation_args( 169 | const std::vector> _args 170 | ) 171 | { 172 | // [args_number][first_arg_len][[first_arg][second_arg_len][[second_arg][...][...][last_arg_len][last_arg] 173 | const unsigned char args_size = _args.size(); 174 | std::vector query; 175 | // Prepare computation query 176 | query.push_back(args_size); // Max arguments allowed are 255 177 | for (int i = 0; i < args_size; i++) 178 | { 179 | query.push_back(_args[i].size()); // argument length 180 | query.insert(query.end(), _args[i].begin(), _args[i].end()); // argument 181 | } 182 | return query; 183 | } 184 | 185 | eosio::checksum256 invert_checksum256(const eosio::checksum256 _cs) 186 | { 187 | uint8_t *first_word = (uint8_t *) &_cs.get_array()[0]; 188 | uint8_t *second_word = (uint8_t *) &_cs.get_array()[1]; 189 | eosio::checksum256 cs_inverted; 190 | uint8_t *first_word_inverted = (uint8_t *) &cs_inverted.get_array()[0]; 191 | uint8_t *second_word_inverted = (uint8_t *) &cs_inverted.get_array()[1]; 192 | for (int i = 0; i < 16; i++) 193 | { 194 | first_word_inverted[15 - i] = first_word[i]; 195 | second_word_inverted[15 - i] = second_word[i]; 196 | } 197 | return cs_inverted; 198 | } 199 | 200 | 201 | bool checksum256_is_empty(const eosio::checksum256 cs) 202 | { 203 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 204 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 205 | for(int i = 0; i < 16; i++) 206 | if(first_word[i] != 0 && second_word[i] != 0) 207 | return false; 208 | return true; 209 | } 210 | 211 | std::vector string_to_vector(const std::string s) 212 | { 213 | std::vector s_v(s.begin(), s.end()); 214 | return s_v; 215 | } 216 | 217 | std::string vector_to_string(const std::vector v) 218 | { 219 | std::string v_str(v.begin(), v.end()); 220 | return v_str; 221 | } 222 | 223 | std::vector hexstring_to_vector32(std::string s) 224 | { 225 | std::vector v32; 226 | for(unsigned int i = 0; i < 32; i++) 227 | { 228 | unsigned int ui; 229 | sscanf(s.data() + (i * 2), "%02x", &ui); 230 | unsigned char uc = (unsigned char) ui; 231 | v32.push_back(uc); 232 | } 233 | return v32; 234 | } 235 | 236 | eosio::checksum256 hexstring_to_checksum256(const std::string hs) 237 | { 238 | std::vector hs_bytes = hexstring_to_vector32(hs); 239 | eosio::checksum256 cs; 240 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 241 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 242 | for (int i = 0; i < 16; i++) 243 | { 244 | first_word[i] = hs_bytes[i]; 245 | second_word[i] = hs_bytes[i + 16]; 246 | } 247 | return cs; 248 | } 249 | 250 | std::string checksum256_to_string(const eosio::checksum256 cs) 251 | { 252 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 253 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 254 | char hexstr[64]; 255 | for (int i = 0; i < 16; i++) 256 | sprintf(hexstr + i * 2, "%02x", first_word[i]); 257 | for (int i = 16; i < 32; i++) 258 | sprintf(hexstr + i * 2, "%02x", second_word[i - 16]); 259 | std::string c_str = std::string(hexstr); 260 | return c_str; 261 | } 262 | 263 | std::string chara_to_hexstring(uint8_t *input, const int size) 264 | { 265 | char hexstr[size * 2]; 266 | for (int i = 0; i < size; i++) 267 | sprintf(hexstr + i * 2, "%02x", input[i]); 268 | std::string c_str = std::string(hexstr); 269 | return c_str; 270 | } 271 | 272 | std::string vector_to_hexstring(std::vector *input) 273 | { 274 | int size = input->size(); 275 | char hexstr[size * 2]; 276 | for (int i = 0; i < size; i++) 277 | sprintf(hexstr + i * 2, "%02x", input->at(i)); 278 | std::string c_str = std::string(hexstr); 279 | return c_str; 280 | } 281 | 282 | std::vector uint32_to_vector8(uint32_t num) 283 | { 284 | std::vector ba(8); 285 | const uint32_t mask = 0xFF; 286 | for(int i = 0; i < 8; i++) 287 | { 288 | ba[i] = num & mask; 289 | num = num >> 8; 290 | } 291 | return ba; 292 | } 293 | 294 | std::vector uint32_to_vector32_bigendian(uint32_t num) 295 | { 296 | std::vector ba(32); 297 | const uint32_t mask = 0xFF; 298 | for(int i = 31; i > -1; i--) 299 | { 300 | ba[i] = num & mask; 301 | num = num >> 8; 302 | } 303 | return ba; 304 | } 305 | 306 | std::vector checksum256_to_vector32(const eosio::checksum256 cs) 307 | { 308 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 309 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 310 | std::vector ba(32); 311 | for (int i = 0; i < 16; i++) 312 | { 313 | ba[i] = first_word[i]; 314 | } 315 | for (int i = 16; i < 32; i++) 316 | { 317 | ba[i] = second_word[i - 16]; 318 | } 319 | return ba; 320 | } 321 | 322 | 323 | /************************************************** 324 | * INTERNAL FUNCTIONS * 325 | * Definitions * 326 | **************************************************/ 327 | eosio::checksum256 __provable_randomDS_getSessionPubkeyHash() 328 | { 329 | ds_spubkey spubkeys("provableconn"_n, "provableconn"_n.value); 330 | name index = "1"_n; // only one value in the table with key = 1 331 | auto itr = spubkeys.find(index.value); 332 | eosio::checksum256 sessionPubkeyHash; 333 | if (itr != spubkeys.end()) 334 | { 335 | sessionPubkeyHash = itr->get_randomDS_lastSessionPubkeyHash(); 336 | } 337 | return sessionPubkeyHash; 338 | } 339 | 340 | uint32_t __provable_getSenderNonce(name sender) 341 | { 342 | ds_snonce last_nonces("provableconn"_n, "provableconn"_n.value); 343 | auto itr = last_nonces.find(sender.value); 344 | uint32_t nonce = 0; 345 | if (itr != last_nonces.end()) 346 | { 347 | nonce = itr->nonce; 348 | } 349 | return nonce; 350 | } 351 | 352 | eosio::checksum256 __provable_getNextQueryId(const name sender) 353 | { 354 | const uint32_t nonce = __provable_getSenderNonce(sender); // get values to generate the queryId 355 | const size_t tx_size = transaction_size(); 356 | uint8_t tbh[sizeof(sender) + sizeof(nonce) + sizeof(tx_size)]; // calculate the hash of the previous values 357 | std::memcpy(tbh, &sender, sizeof(sender)); 358 | std::memcpy(tbh + sizeof(sender), &nonce, sizeof(nonce)); 359 | std::memcpy(tbh + sizeof(sender) + sizeof(nonce), &tx_size, sizeof(tx_size)); 360 | eosio::checksum256 calc_hash = sha256((char *)tbh, sizeof(tbh)); 361 | return calc_hash; 362 | } 363 | 364 | // Check that the queryId being passed matches with the one in the customer local table, return true/false accordingly 365 | bool __provable_queryId_match(const eosio::checksum256 queryId, const name sender) 366 | { 367 | name myQueryId_short; 368 | std::memcpy(&myQueryId_short, &queryId, sizeof(myQueryId_short)); 369 | // Access the local query table and find the right row 370 | ds_queryid queryids(sender, sender.value); 371 | auto itr = queryids.find(myQueryId_short.value); 372 | // Get the local queryId value 373 | eosio::checksum256 queryId_expected; 374 | std::memcpy(&queryId_expected, 0, sizeof(queryId_expected)); 375 | // Get the queryId from the table 376 | if (itr != queryids.end()) 377 | queryId_expected = itr->qid; 378 | else 379 | return false; 380 | // Check if the value retrieved is only 0 381 | if(checksum256_is_empty(queryId_expected)) 382 | return false; 383 | // Convert queryId and queryId_expected to string, to compare them 384 | const std::string queryId_str__expected = checksum256_to_string(queryId_expected); 385 | const std::string queryId_str = checksum256_to_string(queryId); 386 | // Compare the queryids and check if string values are empty 387 | if (queryId_str != queryId_str__expected || queryId_str.empty() || queryId_str__expected.empty()) 388 | return false; 389 | else 390 | return true; 391 | } 392 | 393 | void __provable_queryId_localEmplace(const eosio::checksum256 myQueryId, const name sender) 394 | { 395 | // Retreive the short queryId to use it as an index 396 | name myQueryId_short; 397 | std::memcpy(&myQueryId_short, &myQueryId, sizeof(myQueryId_short)); 398 | // Save the queryId in the local table 399 | ds_queryid queryids(sender, sender.value); 400 | queryids.emplace(sender, [&](auto& o) { 401 | o.key = myQueryId_short; 402 | o.qid = myQueryId; 403 | o.active = true; 404 | }); 405 | } 406 | 407 | 408 | /************************************************** 409 | * Provable Query * 410 | * Strings * 411 | **************************************************/ 412 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 413 | { 414 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 415 | action(permission_level{user, "active"_n}, 416 | "provableconn"_n, 417 | "querystr"_n, 418 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 419 | ).send(); 420 | return queryId; 421 | } 422 | 423 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const name sender) 424 | { 425 | return __provable_query(user, 0, datasource, query, 0, sender); 426 | } 427 | 428 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const name sender) 429 | { 430 | return __provable_query(user, timestamp, datasource, query, 0, sender); 431 | } 432 | 433 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 434 | { 435 | return __provable_query(user, 0, datasource, query, prooftype, sender); 436 | } 437 | 438 | 439 | /************************************************** 440 | * Provable Query * 441 | * Bytearrays * 442 | **************************************************/ 443 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 444 | { 445 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 446 | printhex(query.data(), query.size()); 447 | auto n = name{user}; 448 | const std::string str = n.to_string(); 449 | action(permission_level{user, "active"_n}, 450 | "provableconn"_n, 451 | "queryba"_n, 452 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 453 | ).send(); 454 | return queryId; 455 | } 456 | 457 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const name sender) 458 | { 459 | return __provable_query(user, 0, datasource, query, 0, sender); 460 | } 461 | 462 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const name sender) 463 | { 464 | return __provable_query(user, timestamp, datasource, query, 0, sender); 465 | } 466 | 467 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 468 | { 469 | return __provable_query(user, 0, datasource, query, prooftype, sender); 470 | } 471 | 472 | 473 | /************************************************** 474 | * Provable Query * 475 | * Random DS * 476 | **************************************************/ 477 | void __provable_randomDS_setCommitment(const eosio::checksum256 queryId, const eosio::checksum256 commitment, const name payer) 478 | { 479 | name myQueryId_short; // Calculate the short queryId, to use it as a key of the table 480 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 481 | ds_scommitment last_commitments(payer, payer.value); // Set the commitment in the eos table of the caller 482 | last_commitments.emplace(payer, [&](auto &o) { // The key must be a uint64_t so a short query Id is used 483 | o.shortqueryid = myQueryId_short; 484 | o.queryid = queryId; 485 | o.commitment = commitment; 486 | }); 487 | } 488 | 489 | eosio::checksum256 __provable_newRandomDSQuery(const name user, const uint32_t _delay, const uint8_t _nbytes, const name sender) 490 | { 491 | // 1. NBYTES - Convert nbytes to bytearray 492 | std::vector nbytesBa(1); 493 | nbytesBa[0] = _nbytes; 494 | 495 | // 2. SESSIONKEYHASH - Get the sessionKeyHash from the ledger public key. 496 | const eosio::checksum256 sessionPubkeyHash = __provable_randomDS_getSessionPubkeyHash(); 497 | std::vector sessionPubkeyHashBa(32); 498 | std::vector sessionPubkeyHashBa2(32); 499 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 500 | 501 | // 3. UNONCE - Need something block dependent so we decided to perform the hash of those 4 block dependent fields. This value have to be unpredictable from Provable 502 | const size_t tx_size = transaction_size(); 503 | const int tapos_block_num_ = tapos_block_num(); 504 | const int tapos_block_prefix_ = tapos_block_prefix(); 505 | uint8_t unonce[sizeof(tx_size) + sizeof(tapos_block_num_) + sizeof(tapos_block_prefix_)]; // Fill the unonce array: now() + transaction_size + tapos_block_num + tapos_block_prefix 506 | std::memcpy(unonce, &tx_size, sizeof(tx_size)); 507 | std::memcpy(unonce + sizeof(tx_size), &tapos_block_num_, sizeof(tapos_block_num_)); 508 | std::memcpy(unonce + sizeof(tx_size) + sizeof(tapos_block_num_), &tapos_block_prefix_, sizeof(tapos_block_prefix_)); 509 | eosio::checksum256 unonceHash = sha256((char *)unonce, sizeof(unonce)); // Container for the unonce hash 510 | std::vector unonceHashBa(32); // Convert the unonce hash in bytearray 511 | unonceHashBa = checksum256_to_vector32(unonceHash); 512 | 513 | // 4. DELAY - Delay converted in a big endian bytearray 514 | const uint32_t delayLedgerTime = _delay * 10; // Convert from seconds to ledger timer ticks 515 | std::vector delayBaBigEndian(32); 516 | delayBaBigEndian = uint32_to_vector32_bigendian(delayLedgerTime); 517 | 518 | // Set args2 to be passed as params of the provable "random" query 519 | std::vector> args; 520 | args.push_back(unonceHashBa); 521 | args.push_back(nbytesBa); 522 | args.push_back(sessionPubkeyHashBa); 523 | args.push_back(delayBaBigEndian); 524 | std::vector args2; 525 | for(auto && a : args) 526 | args2.insert(args2.end(), a.begin(), a.end()); 527 | 528 | // Call the provable_query and get the queryId 529 | const eosio::checksum256 queryId = __provable_query(user,"random", args2, proofType_Ledger, sender); // proofType and datasource are always fixed in this function 530 | 531 | // Calculate the commitment and call a function to set it 532 | std::vector delayBa(8); // delay converted to 8 byte 533 | delayBa = uint32_to_vector8(delayLedgerTime); 534 | uint8_t* charArray = &unonceHashBa[0]; 535 | eosio::checksum256 unonceHashBaHash = invert_checksum256(sha256((char *) charArray, unonceHashBa.size())); // unonce has to be passed hashed 536 | uint8_t commitmentTbh[8 + 1 + 32 + args[2].size()]; // Calculate the commitment to be hashed with the size of: 8 + 1 + 32 + 32 537 | std::memcpy(commitmentTbh, &delayBa[0], 8); // 8 538 | std::memcpy(commitmentTbh + 8, &args[1][0], 1); // 8 + 1 539 | std::memcpy(commitmentTbh + 8 + 1, &unonceHashBaHash.get_array()[0], 16); // 8 + 1 + 16 540 | std::memcpy(commitmentTbh + 8 + 1 + 16, &unonceHashBaHash.get_array()[1], 16); // 8 + 1 + 32 == commitmentSlice1 541 | std::memcpy(commitmentTbh + delayBa.size() + args[1].size() + 32, &args[2][0], args[2].size()); // 8 + 1 + 32 + 32 (commitmentSlice1 + sessionPubkeyHashBa) 542 | eosio::checksum256 commitment = sha256((char *)commitmentTbh, sizeof(commitmentTbh)); // Container for the commitment hash 543 | const name payer = user; // Payer for setting the commitment 544 | __provable_randomDS_setCommitment(queryId, commitment, payer); // Call the function to set query Id and commitment in the table 545 | 546 | return queryId; 547 | } 548 | 549 | void __provable_randomDS_get_signature_component(uint8_t component[32], const uint8_t signature[], const uint8_t signature_len, const uint8_t length_idx) 550 | { 551 | eosio::internal_use_do_not_use::eosio_assert(signature_len > length_idx, "Invalid index"); 552 | uint8_t component_len = signature[length_idx]; 553 | uint8_t byte_to_jump = component_len % 32; 554 | std::memcpy(component, &signature[length_idx + 1 + byte_to_jump], component_len - byte_to_jump); 555 | } 556 | 557 | bool __provable_randomDS_matchBytes32Prefix(const eosio::checksum256 content, const uint8_t prefix[], const uint8_t prefix_len, const uint8_t n_random_bytes) 558 | { 559 | eosio::internal_use_do_not_use::eosio_assert(prefix_len == n_random_bytes, "Prefix length and random bytes number should match."); 560 | const eosio::checksum256 content_inverted = invert_checksum256(content); 561 | uint8_t *first_word = (uint8_t *)&content_inverted.get_array()[0]; 562 | uint8_t *second_word = (uint8_t *)&content_inverted.get_array()[1]; 563 | for (int i = 0; i < n_random_bytes; ++i) 564 | { 565 | if (i < 16) 566 | { 567 | if (first_word[i] != prefix[i]) 568 | return false; 569 | } 570 | else 571 | { 572 | if (second_word[i] != prefix[i]) 573 | return false; 574 | } 575 | } 576 | return true; 577 | } 578 | 579 | bool __provable_randomDS_test_pubkey_signature(const uint8_t whatever, const uint8_t v, const uint8_t r[32], const uint8_t s[32], const eosio::checksum256 digest, const uint8_t pubkey[64]) 580 | { 581 | eosio::webauthn_signature sig; 582 | sig.auth_data[0] = v; 583 | for (int i = 0; i < 32; i++) 584 | sig.auth_data[i + 1] = r[i]; 585 | for (int i = 0; i < 32; i++) 586 | sig.auth_data[i + 1 + 32] = s[i]; 587 | 588 | const eosio::webauthn_public_key pubkey_recovered = get(recover_key(digest, sig)); 589 | 590 | if (pubkey_recovered.key.size() != 33) 591 | return false; 592 | if (pubkey_recovered.key[0] != 0x02 && pubkey_recovered.key[0] != 0x03) 593 | return false; 594 | // Discard the first (0x00) and the second byte (0x02 or 0x03) 595 | for (int i = 0; i < 32; i++) 596 | if ((uint8_t)pubkey_recovered.key[i + 1] != pubkey[i]) 597 | return false; 598 | return true; 599 | } 600 | 601 | bool __provable_randomDS_verifySig(const eosio::checksum256 digest, const uint8_t der_signature[], const uint8_t der_signature_len, const uint8_t pubkey[64]) 602 | { 603 | uint8_t r[32]; 604 | uint8_t s[32]; 605 | __provable_randomDS_get_signature_component(r, der_signature, der_signature_len, 3); 606 | __provable_randomDS_get_signature_component(s, der_signature, der_signature_len, 4 + der_signature[3] + 1); 607 | // We try either with v=27 or with v=28 608 | bool test_v27 = __provable_randomDS_test_pubkey_signature(0, 27, r, s, digest, pubkey); 609 | bool test_v28 = __provable_randomDS_test_pubkey_signature(0, 28, r, s, digest, pubkey); 610 | return test_v27 || test_v28; 611 | } 612 | 613 | uint8_t provable_randomDS_proofVerify(const eosio::checksum256 queryId, const std::vector result, const std::vector proof, const name payer) 614 | { 615 | /******************************************************************************************* 616 | * * 617 | * Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) * 618 | * * 619 | *******************************************************************************************/ 620 | if (proof[0] != 'L' || proof[1] != 'P' || proof[2] != 1) 621 | return 1; 622 | 623 | 624 | /******************************************************************************************** 625 | * * 626 | * Step 2: the unique keyhash has to match with the sha256 of (context name + queryId) * 627 | * * 628 | ********************************************************************************************/ 629 | const uint8_t ledgerProofLength = 3 + 65 + (proof[3 + 65 + 1] + 2) + 32; 630 | uint8_t keyhash[32]; 631 | std::memcpy(keyhash, &proof.data()[ledgerProofLength], 32); 632 | eosio::checksum256 keyhash_sha = sha256((char *)keyhash, 32); 633 | std::string keyhash_sha_str = checksum256_to_string(keyhash_sha); 634 | uint8_t *first_word = (uint8_t *) &keyhash_sha.get_array()[0]; 635 | uint8_t *second_word = (uint8_t *) &keyhash_sha.get_array()[1]; 636 | std::string context_name_str = PROVABLE_NETWORK_NAME; 637 | char context_name[context_name_str.size()]; 638 | context_name_str.copy(context_name, context_name_str.size()); 639 | uint8_t tbh2[sizeof(context_name) + 32]; 640 | std::memcpy(tbh2, &context_name, sizeof(context_name)); 641 | std::memcpy(tbh2 + sizeof(context_name), &queryId.get_array()[0], 16); 642 | std::memcpy(tbh2 + sizeof(context_name) + 16, &queryId.get_array()[1], 16); 643 | eosio::checksum256 calc_hash = invert_checksum256(sha256((char *)tbh2, sizeof(context_name) + 644 | 16 + 16)); 645 | uint8_t tbh3[32]; 646 | std::memcpy(tbh3, &calc_hash.get_array()[0], 16); 647 | std::memcpy(tbh3 + 16, &calc_hash.get_array()[1], 16); 648 | eosio::checksum256 calc_hash_2 = sha256((char *)tbh3, 16 + 16); 649 | uint8_t *first_word3 = (uint8_t *) &calc_hash_2.get_array()[0]; 650 | uint8_t *second_word3 = (uint8_t *) &calc_hash_2.get_array()[1]; 651 | if(checksum256_to_string(keyhash_sha) != checksum256_to_string(calc_hash_2)) 652 | return 2; 653 | 654 | 655 | /******************************************************************************************** 656 | * * 657 | * Step 3: we assume sig1 is valid (it will be verified during step 5) * 658 | * and we verify if 'result' is the prefix of sha256(sig1) * 659 | * * 660 | ********************************************************************************************/ 661 | const uint8_t sig1_len = proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1] + 2; 662 | uint8_t sig1[sig1_len]; 663 | std::memcpy(sig1, &proof.data()[ledgerProofLength + (32 + 8 + 1 + 32)], sig1_len); 664 | eosio::checksum256 sig1_hash = sha256((char *)sig1, sizeof(sig1)); 665 | if (!__provable_randomDS_matchBytes32Prefix(sig1_hash, result.data(), result.size(), proof[ledgerProofLength + 32 + 8])) 666 | return 3; 667 | 668 | 669 | /******************************************************************************************** 670 | * * 671 | * Step 4: commitment match verification, * 672 | * sha256(delay, nbytes, unonce, sessionKeyHash) == commitment in table. * 673 | * * 674 | ********************************************************************************************/ 675 | const uint8_t slice_offset = 8 + 1 + 32; // delay + nbytes + unonceHashBa 676 | uint8_t commitmentSlice1[slice_offset]; 677 | std::memcpy(commitmentSlice1, &proof.data()[ledgerProofLength + 32], sizeof(commitmentSlice1)); 678 | // Extract the session public key and calculate the session publick key hash 679 | uint8_t sessionPubKey[64]; 680 | const uint16_t sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1_len + 65; // ledgerProofLength+32+(8+1+32)+sig1.len+65 681 | std::memcpy(sessionPubKey, &proof.data()[sig2offset - 64], sizeof(sessionPubKey)); 682 | eosio::checksum256 sessionPubkeyHash = invert_checksum256(sha256((char *)sessionPubKey, 683 | sizeof(sessionPubKey))); // Calculate the key hash 684 | vector sessionPubkeyHashBa(32); // Convert to bytearray the public key hash 685 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 686 | // Recreate the lastCommitment to compare with the table one 687 | uint8_t tbh[slice_offset + 32]; 688 | std::memcpy(tbh, &commitmentSlice1, slice_offset); 689 | std::memcpy(tbh + slice_offset, &sessionPubkeyHashBa[0], 32); 690 | eosio::checksum256 lastCommitment = sha256((char *)tbh, sizeof(tbh)); 691 | // Retrieve the table commitment 692 | ds_scommitment last_commitments(payer, payer.value); 693 | name myQueryId_short; 694 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 695 | // Check if the key value exists 696 | auto itr = last_commitments.find(myQueryId_short.value); 697 | if (itr == last_commitments.end()) 698 | return 4; 699 | // Check the query id with the one in the table 700 | const std::string queryId_str__expected = checksum256_to_string(itr->queryid); 701 | const std::string queryId_str = checksum256_to_string(queryId); 702 | if (queryId_str != queryId_str__expected) 703 | return 4; 704 | // Check the commitment with the one in the table 705 | const std::string lastCommitment_str__expected = checksum256_to_string(itr->commitment); 706 | const std::string lastCommitment_str = checksum256_to_string(lastCommitment); 707 | if (lastCommitment_str != lastCommitment_str__expected) 708 | return 4; 709 | 710 | 711 | /******************************************************************************************** 712 | * * 713 | * Step 5: validity verification for sig1 (keyhash and args signed with the sessionKey) * 714 | * * 715 | ********************************************************************************************/ 716 | uint8_t toSign1[32 + 8 + 1 + 32]; 717 | std::memcpy(toSign1, &proof.data()[ledgerProofLength], sizeof(toSign1)); 718 | eosio::checksum256 toSign1_hash = sha256((char *)toSign1, sizeof(toSign1)); 719 | if (!__provable_randomDS_verifySig(toSign1_hash, sig1, sizeof(sig1), sessionPubKey)) 720 | return 5; 721 | 722 | 723 | /******************************************************************************************** 724 | * * 725 | * Step 6: verify the attestation signature, * 726 | * APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) * 727 | * * 728 | ********************************************************************************************/ 729 | uint8_t sig2[proof[sig2offset + 1] + 2]; 730 | std::memcpy(sig2, &proof.data()[sig2offset], sizeof(sig2)); 731 | uint8_t appkey_pubkey[64]; 732 | std::memcpy(appkey_pubkey, &proof.data()[3 + 1], sizeof(appkey_pubkey)); 733 | uint8_t toSign2[1 + 65 + 32]; 734 | toSign2[0] = 1; // role 735 | std::memcpy(toSign2 + 1, &proof.data()[sig2offset - 65], 65); 736 | std::memcpy(toSign2 + 65 + 1, CODE_HASH_RANDOMDS, 32); 737 | eosio::checksum256 toSign2_hash = sha256((char *)toSign2, sizeof(toSign2)); 738 | if (!__provable_randomDS_verifySig(toSign2_hash, sig2, sizeof(sig2), appkey_pubkey)) 739 | return 6; 740 | 741 | 742 | /******************************************************************************************** 743 | * * 744 | * Step 7: verify the APPKEY1 provenance (must be signed by Ledger) * 745 | * * 746 | ********************************************************************************************/ 747 | uint8_t toSign3[1 + 65]; 748 | toSign3[0] = 0xfe; 749 | std::memcpy(toSign3 + 1, &proof.data()[3], 65); 750 | uint8_t sig3[proof[3 + 65 + 1] + 2]; 751 | std::memcpy(sig3, &proof.data()[3 + 65], sizeof(sig3)); 752 | eosio::checksum256 toSign3_hash = sha256((char *)toSign3, sizeof(toSign3)); 753 | if (!__provable_randomDS_verifySig(toSign3_hash, sig3, sizeof(sig3), LEDGERKEY)) 754 | return 7; 755 | 756 | 757 | // Erase the commitment after the proof is verified 758 | auto itr2 = last_commitments.find(myQueryId_short.value); 759 | last_commitments.erase(itr2); 760 | return 0; 761 | } 762 | 763 | #endif 764 | -------------------------------------------------------------------------------- /checkqueryid/README.md: -------------------------------------------------------------------------------- 1 | # Provable's Check Query ID Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **URL** datasource. 4 | 5 | The `checkqueryid.cpp` example allows you to *retrieve the eos token price in usd, and check if the `queryId` of 6 | your query matches the one provided by Provable in the callback*. 7 | 8 | ## :page_with_curl: *Instructions* 9 | 10 | **1)** Compile your contract (launch this command **inside** the folder `check-queryid`): 11 | 12 | **`❍ eosio-cpp -abigen checkqueryid.cpp -o checkqueryid.wasm`** 13 | 14 | **2)** Unlock your wallet: 15 | 16 | **`❍ cleos wallet unlock -n --password `** 17 | 18 | **3)** Deploy your contract on EOS (launch this command **outside** the folder `check-queryid`): 19 | 20 | **`❍ cleos set contract check-queryid checkqueryid.wasm checkqueryid.abi -p @`** 21 | 22 | **4)** Call the `checkquery` action of the contract `checkqueryid`: 23 | 24 | **`❍ cleos push action checkquery '[]' -p @`** 25 | 26 | ## :pen: Notes 27 | 28 | Provable replies to your `provable_query` by calling your `callback(...)` with the *price result*. 29 | You can search for your transaction ID in one of the following links to verify it: 30 | 31 | * :mag_right::ledger: [Jungle 3 Blocks.io](https://jungle3.bloks.io/): A block explorer for the Jungle 3.0 testnet. 32 | 33 | * :mag_right::ledger: [Kylin Blocks.io](https://kylin.bloks.io/): A block explorer for the Kylin testnet. 34 | 35 | ## :ambulance: Support 36 | 37 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel to get timely support! 38 | 39 | ***Happy developing!*** 40 | -------------------------------------------------------------------------------- /checkqueryid/checkqueryid.cpp: -------------------------------------------------------------------------------- 1 | #define CONTRACT_NAME "checkqueryid" 2 | 3 | #include "provable/eos_api.hpp" 4 | 5 | class checkqueryid : public eosio::contract 6 | { 7 | public: 8 | using contract::contract; 9 | 10 | checkqueryid(eosio::name receiver, eosio::name code, datastream ds) : contract(receiver, code, ds) {} 11 | 12 | [[eosio::action]] 13 | void checkquery() 14 | { 15 | eosio::checksum256 myQueryId = provable_query("URL", "json(https://api.kraken.com/0/public/Ticker?pair=EOSUSD).result.EOSUSD.l.0"); 16 | provable_queryId_localEmplace(myQueryId); 17 | print(" Provable query was sent & queryId saved in the queryId table as a record, standing by for the answer..."); 18 | } 19 | 20 | [[eosio::action]] 21 | void callback( 22 | const eosio::checksum256 queryId, 23 | const std::vector result, 24 | const std::vector proof 25 | ) 26 | { 27 | require_auth(provable_cbAddress()); 28 | if (!provable_queryId_match(queryId)) 29 | { 30 | // The query Id match has failed, manage this use case... 31 | print(" Unexpected query ID!"); 32 | } 33 | else 34 | { 35 | const std::string result_str = vector_to_string(result); 36 | print(" Result: ", result_str); 37 | } 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /dieselprice/README.md: -------------------------------------------------------------------------------- 1 | # Provable's Diesel Price Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **URL** datasource. 4 | 5 | The `dieselprice.cpp` example allows you to *retrieve the diesel price in usd*. 6 | 7 | ## :page_with_curl: *Instructions* 8 | 9 | **1)** Compile your contract (launch this command **inside** the folder `diesel-price`): 10 | 11 | **`❍ eosio-cpp -abigen dieselprice.cpp -o dieselprice.wasm`** 12 | 13 | **2)** Unlock your wallet: 14 | 15 | **`❍ cleos wallet unlock -n --password `** 16 | 17 | **3)** Deploy your contract on EOS (launch this command **outside** the folder `diesel-price`): 18 | 19 | **`❍ cleos set contract diesel-price dieselprice.wasm dieselprice.abi -p @`** 20 | 21 | **4)** Call the `execquery` action of the contract `dieselprice`: 22 | 23 | **`❍ cleos push action execquery '[]' -p @`** 24 | 25 | ## :pen: Notes 26 | 27 | Provable replies to your `provable_query` by calling your `callback(...)` with the *price result*. 28 | You can search your transaction ID in one of the following links to verify it: 29 | 30 | * :mag_right::ledger: [Jungle 3 Blocks.io](https://jungle3.bloks.io/): A block explorer for the Jungle 3.0 testnet. 31 | 32 | * :mag_right::ledger: [Kylin Blocks.io](https://kylin.bloks.io/): A block explorer for the Kylin testnet. 33 | 34 | ## :ambulance: Support 35 | 36 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel to get timely support! 37 | 38 | ***Happy developing!*** 39 | -------------------------------------------------------------------------------- /dieselprice/dieselprice.cpp: -------------------------------------------------------------------------------- 1 | #include "provable/eos_api.hpp" 2 | 3 | class dieselprice : public eosio::contract 4 | { 5 | public: 6 | using contract::contract; 7 | 8 | dieselprice(eosio::name receiver, eosio::name code, datastream ds) : contract(receiver, code, ds) {} 9 | 10 | [[eosio::action]] 11 | void execquery() 12 | { 13 | print("Sending query to Provable..."); 14 | provable_query(60, "URL", "xml(https://www.fueleconomy.gov/ws/rest/fuelprices).fuelPrices.diesel"); 15 | } 16 | 17 | [[eosio::action]] 18 | void callback( 19 | const eosio::checksum256 queryId, 20 | const std::vector result, 21 | const std::vector proof 22 | ) 23 | { 24 | require_auth(provable_cbAddress()); 25 | const std::string result_str = vector_to_string(result); 26 | print("Diesel Price USD: ", result_str); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /dieselprice/provable/eos_api.hpp: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * Provable API * 3 | * * 4 | * Copyright (c) 2015-2016 Provable SRL * 5 | * Copyright (c) 2016 Provable LTD * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy * 7 | * of this software and associated documentation files (the "Software"), to deal * 8 | * in the Software without restriction, including without limitation the rights * 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * 10 | * copies of the Software, and to permit persons to whom the Software is * 11 | * furnished to do so, subject to the following conditions: * 12 | * The above copyright notice and this permission notice shall be included in * 13 | * all copies or substantial portions of the Software. * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * 20 | * THE SOFTWARE. * 21 | *********************************************************************************/ 22 | 23 | #ifndef PROVABLEAPI_H 24 | #define PROVABLEAPI_H 25 | 26 | 27 | /************************************************** 28 | * INCLUDE * 29 | * Libraries * 30 | **************************************************/ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | /************************************************** 40 | * MACRO * 41 | * Definitions * 42 | **************************************************/ 43 | #ifndef PROVABLE_NETWORK_NAME 44 | #warning PROVABLE_NETWORK_NAME is not set, setting it to "eosio_unknown".. [possible values are "eosio_mainnet"/"eosio_testnet_jungle"/"eosio_testnet_kylin"/"eosio_unknown"] 45 | #define PROVABLE_NETWORK_NAME "eosio_unknown" 46 | #endif // PROVABLE_NETWORK_NAME 47 | 48 | #ifndef CONTRACT_NAME 49 | #warning CONTRACT_NAME is not set, setting it to "".. [write the "contract.name" not the account name] 50 | #define CONTRACT_NAME "unknown" 51 | #endif // CONTRACT_NAME 52 | 53 | #ifndef PROVABLE_PAYER 54 | #define PROVABLE_PAYER _self 55 | #endif // PROVABLE_PAYER 56 | 57 | #define provable_query(...) __provable_query(PROVABLE_PAYER, __VA_ARGS__, _self) 58 | #define provable_newRandomDSQuery(...) __provable_newRandomDSQuery(PROVABLE_PAYER, __VA_ARGS__, _self) 59 | #define provable_queryId_localEmplace(...) __provable_queryId_localEmplace(__VA_ARGS__, _self) 60 | #define provable_queryId_match(...) __provable_queryId_match(__VA_ARGS__, _self) 61 | 62 | 63 | /************************************************** 64 | * NAMESPACES * 65 | * Declarations * 66 | **************************************************/ 67 | using namespace eosio; 68 | using namespace std; 69 | 70 | 71 | /************************************************** 72 | * CONSTANTS * 73 | * Proof Types * 74 | **************************************************/ 75 | const uint8_t proofType_NONE = 0x00; 76 | const uint8_t proofType_TLSNotary = 0x10; 77 | const uint8_t proofType_Ledger = 0x30; 78 | const uint8_t proofType_Android = 0x40; 79 | const uint8_t proofType_Native = 0xF0; 80 | const uint8_t proofStorage_IPFS = 0x01; 81 | 82 | const uint8_t CODE_HASH_RANDOMDS[32] = { 83 | 253, 148, 250, 113, 188, 11, 161, 13, 57, 212, 100, 208, 216, 244, 101, 239, 238, 240, 162, 118, 78, 56, 135, 252, 201, 223, 65, 222, 210, 15, 80, 92 84 | }; 85 | const uint8_t LEDGERKEY[64] = { 86 | 127, 185, 86, 70, 156, 92, 155, 137, 132, 13, 85, 180, 53, 55, 230, 106, 152, 221, 72, 17, 234, 10, 39, 34, 66, 114, 194, 229, 98, 41, 17, 232, 83, 122, 47, 142, 134, 164, 107, 174, 200, 40, 100, 233, 141, 208, 30, 156, 204, 47, 139, 197, 223, 201, 203, 229, 169, 26, 41, 4, 152, 221, 150, 228 87 | }; 88 | 89 | 90 | /************************************************** 91 | * PROVABLE TABLE * 92 | * Definition * 93 | **************************************************/ 94 | struct [[eosio::table, eosio::contract("provableconn")]] snonce 95 | { 96 | name sender; 97 | uint32_t nonce; 98 | 99 | uint64_t primary_key() const { return sender.value; } 100 | }; 101 | 102 | struct [[eosio::table, eosio::contract("provableconn")]] cbaddr 103 | { 104 | name sender; 105 | 106 | uint64_t primary_key() const { return sender.value; } 107 | }; 108 | 109 | struct [[eosio::table, eosio::contract("provableconn")]] spubkey 110 | { 111 | name key; 112 | eosio::checksum256 randomDS_lastSessionPubkeyHash; 113 | 114 | uint64_t primary_key() const { return key.value; } 115 | eosio::checksum256 get_randomDS_lastSessionPubkeyHash() const { return randomDS_lastSessionPubkeyHash; } 116 | }; 117 | 118 | 119 | /************************************************** 120 | * PROVABLE TABLE * 121 | * Definition * 122 | **************************************************/ 123 | 124 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] scommitment 125 | { 126 | name shortqueryid; 127 | eosio::checksum256 queryid; 128 | eosio::checksum256 commitment; 129 | 130 | uint64_t primary_key() const { return shortqueryid.value; } 131 | }; 132 | 133 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] queryid 134 | { 135 | name key; 136 | eosio::checksum256 qid; 137 | uint8_t active; 138 | 139 | uint64_t primary_key() const { return key.value; } 140 | }; 141 | 142 | typedef eosio::multi_index ds_snonce; 143 | typedef eosio::multi_index ds_cbaddr; 144 | typedef eosio::multi_index ds_spubkey; 145 | typedef eosio::multi_index ds_scommitment; 146 | typedef eosio::multi_index ds_queryid; 147 | 148 | 149 | /************************************************** 150 | * PUBLIC FUNCTIONS * 151 | * Implementation * 152 | **************************************************/ 153 | eosio::name provable_cbAddress() 154 | { 155 | ds_cbaddr cb_addrs("provableconn"_n, "provableconn"_n.value); // go to the connector table which identify the sender 156 | auto itr = cb_addrs.begin(); // point to the first element of the table 157 | const uint64_t cbaddr_uint64t = (itr != cb_addrs.end()) ? itr->sender.value : 0; 158 | eosio::name cbaddr_name = eosio::name(cbaddr_uint64t); 159 | return cbaddr_name; 160 | } 161 | 162 | std::string vector_uc_to_string(const std::vector v) 163 | { 164 | std::string v_str(v.begin(), v.end()); 165 | return v_str; 166 | } 167 | 168 | std::vector provable_set_computation_args( 169 | const std::vector> _args 170 | ) 171 | { 172 | // [args_number][first_arg_len][[first_arg][second_arg_len][[second_arg][...][...][last_arg_len][last_arg] 173 | const unsigned char args_size = _args.size(); 174 | std::vector query; 175 | // Prepare computation query 176 | query.push_back(args_size); // Max arguments allowed are 255 177 | for (int i = 0; i < args_size; i++) 178 | { 179 | query.push_back(_args[i].size()); // argument length 180 | query.insert(query.end(), _args[i].begin(), _args[i].end()); // argument 181 | } 182 | return query; 183 | } 184 | 185 | eosio::checksum256 invert_checksum256(const eosio::checksum256 _cs) 186 | { 187 | uint8_t *first_word = (uint8_t *) &_cs.get_array()[0]; 188 | uint8_t *second_word = (uint8_t *) &_cs.get_array()[1]; 189 | eosio::checksum256 cs_inverted; 190 | uint8_t *first_word_inverted = (uint8_t *) &cs_inverted.get_array()[0]; 191 | uint8_t *second_word_inverted = (uint8_t *) &cs_inverted.get_array()[1]; 192 | for (int i = 0; i < 16; i++) 193 | { 194 | first_word_inverted[15 - i] = first_word[i]; 195 | second_word_inverted[15 - i] = second_word[i]; 196 | } 197 | return cs_inverted; 198 | } 199 | 200 | 201 | bool checksum256_is_empty(const eosio::checksum256 cs) 202 | { 203 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 204 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 205 | for(int i = 0; i < 16; i++) 206 | if(first_word[i] != 0 && second_word[i] != 0) 207 | return false; 208 | return true; 209 | } 210 | 211 | std::vector string_to_vector(const std::string s) 212 | { 213 | std::vector s_v(s.begin(), s.end()); 214 | return s_v; 215 | } 216 | 217 | std::string vector_to_string(const std::vector v) 218 | { 219 | std::string v_str(v.begin(), v.end()); 220 | return v_str; 221 | } 222 | 223 | std::vector hexstring_to_vector32(std::string s) 224 | { 225 | std::vector v32; 226 | for(unsigned int i = 0; i < 32; i++) 227 | { 228 | unsigned int ui; 229 | sscanf(s.data() + (i * 2), "%02x", &ui); 230 | unsigned char uc = (unsigned char) ui; 231 | v32.push_back(uc); 232 | } 233 | return v32; 234 | } 235 | 236 | eosio::checksum256 hexstring_to_checksum256(const std::string hs) 237 | { 238 | std::vector hs_bytes = hexstring_to_vector32(hs); 239 | eosio::checksum256 cs; 240 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 241 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 242 | for (int i = 0; i < 16; i++) 243 | { 244 | first_word[i] = hs_bytes[i]; 245 | second_word[i] = hs_bytes[i + 16]; 246 | } 247 | return cs; 248 | } 249 | 250 | std::string checksum256_to_string(const eosio::checksum256 cs) 251 | { 252 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 253 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 254 | char hexstr[64]; 255 | for (int i = 0; i < 16; i++) 256 | sprintf(hexstr + i * 2, "%02x", first_word[i]); 257 | for (int i = 16; i < 32; i++) 258 | sprintf(hexstr + i * 2, "%02x", second_word[i - 16]); 259 | std::string c_str = std::string(hexstr); 260 | return c_str; 261 | } 262 | 263 | std::string chara_to_hexstring(uint8_t *input, const int size) 264 | { 265 | char hexstr[size * 2]; 266 | for (int i = 0; i < size; i++) 267 | sprintf(hexstr + i * 2, "%02x", input[i]); 268 | std::string c_str = std::string(hexstr); 269 | return c_str; 270 | } 271 | 272 | std::string vector_to_hexstring(std::vector *input) 273 | { 274 | int size = input->size(); 275 | char hexstr[size * 2]; 276 | for (int i = 0; i < size; i++) 277 | sprintf(hexstr + i * 2, "%02x", input->at(i)); 278 | std::string c_str = std::string(hexstr); 279 | return c_str; 280 | } 281 | 282 | std::vector uint32_to_vector8(uint32_t num) 283 | { 284 | std::vector ba(8); 285 | const uint32_t mask = 0xFF; 286 | for(int i = 0; i < 8; i++) 287 | { 288 | ba[i] = num & mask; 289 | num = num >> 8; 290 | } 291 | return ba; 292 | } 293 | 294 | std::vector uint32_to_vector32_bigendian(uint32_t num) 295 | { 296 | std::vector ba(32); 297 | const uint32_t mask = 0xFF; 298 | for(int i = 31; i > -1; i--) 299 | { 300 | ba[i] = num & mask; 301 | num = num >> 8; 302 | } 303 | return ba; 304 | } 305 | 306 | std::vector checksum256_to_vector32(const eosio::checksum256 cs) 307 | { 308 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 309 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 310 | std::vector ba(32); 311 | for (int i = 0; i < 16; i++) 312 | { 313 | ba[i] = first_word[i]; 314 | } 315 | for (int i = 16; i < 32; i++) 316 | { 317 | ba[i] = second_word[i - 16]; 318 | } 319 | return ba; 320 | } 321 | 322 | 323 | /************************************************** 324 | * INTERNAL FUNCTIONS * 325 | * Definitions * 326 | **************************************************/ 327 | eosio::checksum256 __provable_randomDS_getSessionPubkeyHash() 328 | { 329 | ds_spubkey spubkeys("provableconn"_n, "provableconn"_n.value); 330 | name index = "1"_n; // only one value in the table with key = 1 331 | auto itr = spubkeys.find(index.value); 332 | eosio::checksum256 sessionPubkeyHash; 333 | if (itr != spubkeys.end()) 334 | { 335 | sessionPubkeyHash = itr->get_randomDS_lastSessionPubkeyHash(); 336 | } 337 | return sessionPubkeyHash; 338 | } 339 | 340 | uint32_t __provable_getSenderNonce(name sender) 341 | { 342 | ds_snonce last_nonces("provableconn"_n, "provableconn"_n.value); 343 | auto itr = last_nonces.find(sender.value); 344 | uint32_t nonce = 0; 345 | if (itr != last_nonces.end()) 346 | { 347 | nonce = itr->nonce; 348 | } 349 | return nonce; 350 | } 351 | 352 | eosio::checksum256 __provable_getNextQueryId(const name sender) 353 | { 354 | const uint32_t nonce = __provable_getSenderNonce(sender); // get values to generate the queryId 355 | const size_t tx_size = transaction_size(); 356 | uint8_t tbh[sizeof(sender) + sizeof(nonce) + sizeof(tx_size)]; // calculate the hash of the previous values 357 | std::memcpy(tbh, &sender, sizeof(sender)); 358 | std::memcpy(tbh + sizeof(sender), &nonce, sizeof(nonce)); 359 | std::memcpy(tbh + sizeof(sender) + sizeof(nonce), &tx_size, sizeof(tx_size)); 360 | eosio::checksum256 calc_hash = sha256((char *)tbh, sizeof(tbh)); 361 | return calc_hash; 362 | } 363 | 364 | // Check that the queryId being passed matches with the one in the customer local table, return true/false accordingly 365 | bool __provable_queryId_match(const eosio::checksum256 queryId, const name sender) 366 | { 367 | name myQueryId_short; 368 | std::memcpy(&myQueryId_short, &queryId, sizeof(myQueryId_short)); 369 | // Access the local query table and find the right row 370 | ds_queryid queryids(sender, sender.value); 371 | auto itr = queryids.find(myQueryId_short.value); 372 | // Get the local queryId value 373 | eosio::checksum256 queryId_expected; 374 | std::memcpy(&queryId_expected, 0, sizeof(queryId_expected)); 375 | // Get the queryId from the table 376 | if (itr != queryids.end()) 377 | queryId_expected = itr->qid; 378 | else 379 | return false; 380 | // Check if the value retrieved is only 0 381 | if(checksum256_is_empty(queryId_expected)) 382 | return false; 383 | // Convert queryId and queryId_expected to string, to compare them 384 | const std::string queryId_str__expected = checksum256_to_string(queryId_expected); 385 | const std::string queryId_str = checksum256_to_string(queryId); 386 | // Compare the queryids and check if string values are empty 387 | if (queryId_str != queryId_str__expected || queryId_str.empty() || queryId_str__expected.empty()) 388 | return false; 389 | else 390 | return true; 391 | } 392 | 393 | void __provable_queryId_localEmplace(const eosio::checksum256 myQueryId, const name sender) 394 | { 395 | // Retreive the short queryId to use it as an index 396 | name myQueryId_short; 397 | std::memcpy(&myQueryId_short, &myQueryId, sizeof(myQueryId_short)); 398 | // Save the queryId in the local table 399 | ds_queryid queryids(sender, sender.value); 400 | queryids.emplace(sender, [&](auto& o) { 401 | o.key = myQueryId_short; 402 | o.qid = myQueryId; 403 | o.active = true; 404 | }); 405 | } 406 | 407 | 408 | /************************************************** 409 | * Provable Query * 410 | * Strings * 411 | **************************************************/ 412 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 413 | { 414 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 415 | action(permission_level{user, "active"_n}, 416 | "provableconn"_n, 417 | "querystr"_n, 418 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 419 | ).send(); 420 | return queryId; 421 | } 422 | 423 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const name sender) 424 | { 425 | return __provable_query(user, 0, datasource, query, 0, sender); 426 | } 427 | 428 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const name sender) 429 | { 430 | return __provable_query(user, timestamp, datasource, query, 0, sender); 431 | } 432 | 433 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 434 | { 435 | return __provable_query(user, 0, datasource, query, prooftype, sender); 436 | } 437 | 438 | 439 | /************************************************** 440 | * Provable Query * 441 | * Bytearrays * 442 | **************************************************/ 443 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 444 | { 445 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 446 | printhex(query.data(), query.size()); 447 | auto n = name{user}; 448 | const std::string str = n.to_string(); 449 | action(permission_level{user, "active"_n}, 450 | "provableconn"_n, 451 | "queryba"_n, 452 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 453 | ).send(); 454 | return queryId; 455 | } 456 | 457 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const name sender) 458 | { 459 | return __provable_query(user, 0, datasource, query, 0, sender); 460 | } 461 | 462 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const name sender) 463 | { 464 | return __provable_query(user, timestamp, datasource, query, 0, sender); 465 | } 466 | 467 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 468 | { 469 | return __provable_query(user, 0, datasource, query, prooftype, sender); 470 | } 471 | 472 | 473 | /************************************************** 474 | * Provable Query * 475 | * Random DS * 476 | **************************************************/ 477 | void __provable_randomDS_setCommitment(const eosio::checksum256 queryId, const eosio::checksum256 commitment, const name payer) 478 | { 479 | name myQueryId_short; // Calculate the short queryId, to use it as a key of the table 480 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 481 | ds_scommitment last_commitments(payer, payer.value); // Set the commitment in the eos table of the caller 482 | last_commitments.emplace(payer, [&](auto &o) { // The key must be a uint64_t so a short query Id is used 483 | o.shortqueryid = myQueryId_short; 484 | o.queryid = queryId; 485 | o.commitment = commitment; 486 | }); 487 | } 488 | 489 | eosio::checksum256 __provable_newRandomDSQuery(const name user, const uint32_t _delay, const uint8_t _nbytes, const name sender) 490 | { 491 | // 1. NBYTES - Convert nbytes to bytearray 492 | std::vector nbytesBa(1); 493 | nbytesBa[0] = _nbytes; 494 | 495 | // 2. SESSIONKEYHASH - Get the sessionKeyHash from the ledger public key. 496 | const eosio::checksum256 sessionPubkeyHash = __provable_randomDS_getSessionPubkeyHash(); 497 | std::vector sessionPubkeyHashBa(32); 498 | std::vector sessionPubkeyHashBa2(32); 499 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 500 | 501 | // 3. UNONCE - Need something block dependent so we decided to perform the hash of those 4 block dependent fields. This value have to be unpredictable from Provable 502 | const size_t tx_size = transaction_size(); 503 | const int tapos_block_num_ = tapos_block_num(); 504 | const int tapos_block_prefix_ = tapos_block_prefix(); 505 | uint8_t unonce[sizeof(tx_size) + sizeof(tapos_block_num_) + sizeof(tapos_block_prefix_)]; // Fill the unonce array: now() + transaction_size + tapos_block_num + tapos_block_prefix 506 | std::memcpy(unonce, &tx_size, sizeof(tx_size)); 507 | std::memcpy(unonce + sizeof(tx_size), &tapos_block_num_, sizeof(tapos_block_num_)); 508 | std::memcpy(unonce + sizeof(tx_size) + sizeof(tapos_block_num_), &tapos_block_prefix_, sizeof(tapos_block_prefix_)); 509 | eosio::checksum256 unonceHash = sha256((char *)unonce, sizeof(unonce)); // Container for the unonce hash 510 | std::vector unonceHashBa(32); // Convert the unonce hash in bytearray 511 | unonceHashBa = checksum256_to_vector32(unonceHash); 512 | 513 | // 4. DELAY - Delay converted in a big endian bytearray 514 | const uint32_t delayLedgerTime = _delay * 10; // Convert from seconds to ledger timer ticks 515 | std::vector delayBaBigEndian(32); 516 | delayBaBigEndian = uint32_to_vector32_bigendian(delayLedgerTime); 517 | 518 | // Set args2 to be passed as params of the provable "random" query 519 | std::vector> args; 520 | args.push_back(unonceHashBa); 521 | args.push_back(nbytesBa); 522 | args.push_back(sessionPubkeyHashBa); 523 | args.push_back(delayBaBigEndian); 524 | std::vector args2; 525 | for(auto && a : args) 526 | args2.insert(args2.end(), a.begin(), a.end()); 527 | 528 | // Call the provable_query and get the queryId 529 | const eosio::checksum256 queryId = __provable_query(user,"random", args2, proofType_Ledger, sender); // proofType and datasource are always fixed in this function 530 | 531 | // Calculate the commitment and call a function to set it 532 | std::vector delayBa(8); // delay converted to 8 byte 533 | delayBa = uint32_to_vector8(delayLedgerTime); 534 | uint8_t* charArray = &unonceHashBa[0]; 535 | eosio::checksum256 unonceHashBaHash = invert_checksum256(sha256((char *) charArray, unonceHashBa.size())); // unonce has to be passed hashed 536 | uint8_t commitmentTbh[8 + 1 + 32 + args[2].size()]; // Calculate the commitment to be hashed with the size of: 8 + 1 + 32 + 32 537 | std::memcpy(commitmentTbh, &delayBa[0], 8); // 8 538 | std::memcpy(commitmentTbh + 8, &args[1][0], 1); // 8 + 1 539 | std::memcpy(commitmentTbh + 8 + 1, &unonceHashBaHash.get_array()[0], 16); // 8 + 1 + 16 540 | std::memcpy(commitmentTbh + 8 + 1 + 16, &unonceHashBaHash.get_array()[1], 16); // 8 + 1 + 32 == commitmentSlice1 541 | std::memcpy(commitmentTbh + delayBa.size() + args[1].size() + 32, &args[2][0], args[2].size()); // 8 + 1 + 32 + 32 (commitmentSlice1 + sessionPubkeyHashBa) 542 | eosio::checksum256 commitment = sha256((char *)commitmentTbh, sizeof(commitmentTbh)); // Container for the commitment hash 543 | const name payer = user; // Payer for setting the commitment 544 | __provable_randomDS_setCommitment(queryId, commitment, payer); // Call the function to set query Id and commitment in the table 545 | 546 | return queryId; 547 | } 548 | 549 | void __provable_randomDS_get_signature_component(uint8_t component[32], const uint8_t signature[], const uint8_t signature_len, const uint8_t length_idx) 550 | { 551 | eosio::internal_use_do_not_use::eosio_assert(signature_len > length_idx, "Invalid index"); 552 | uint8_t component_len = signature[length_idx]; 553 | uint8_t byte_to_jump = component_len % 32; 554 | std::memcpy(component, &signature[length_idx + 1 + byte_to_jump], component_len - byte_to_jump); 555 | } 556 | 557 | bool __provable_randomDS_matchBytes32Prefix(const eosio::checksum256 content, const uint8_t prefix[], const uint8_t prefix_len, const uint8_t n_random_bytes) 558 | { 559 | eosio::internal_use_do_not_use::eosio_assert(prefix_len == n_random_bytes, "Prefix length and random bytes number should match."); 560 | const eosio::checksum256 content_inverted = invert_checksum256(content); 561 | uint8_t *first_word = (uint8_t *)&content_inverted.get_array()[0]; 562 | uint8_t *second_word = (uint8_t *)&content_inverted.get_array()[1]; 563 | for (int i = 0; i < n_random_bytes; ++i) 564 | { 565 | if (i < 16) 566 | { 567 | if (first_word[i] != prefix[i]) 568 | return false; 569 | } 570 | else 571 | { 572 | if (second_word[i] != prefix[i]) 573 | return false; 574 | } 575 | } 576 | return true; 577 | } 578 | 579 | bool __provable_randomDS_test_pubkey_signature(const uint8_t whatever, const uint8_t v, const uint8_t r[32], const uint8_t s[32], const eosio::checksum256 digest, const uint8_t pubkey[64]) 580 | { 581 | eosio::webauthn_signature sig; 582 | sig.auth_data[0] = v; 583 | for (int i = 0; i < 32; i++) 584 | sig.auth_data[i + 1] = r[i]; 585 | for (int i = 0; i < 32; i++) 586 | sig.auth_data[i + 1 + 32] = s[i]; 587 | 588 | const eosio::webauthn_public_key pubkey_recovered = get(recover_key(digest, sig)); 589 | 590 | if (pubkey_recovered.key.size() != 33) 591 | return false; 592 | if (pubkey_recovered.key[0] != 0x02 && pubkey_recovered.key[0] != 0x03) 593 | return false; 594 | // Discard the first (0x00) and the second byte (0x02 or 0x03) 595 | for (int i = 0; i < 32; i++) 596 | if ((uint8_t)pubkey_recovered.key[i + 1] != pubkey[i]) 597 | return false; 598 | return true; 599 | } 600 | 601 | bool __provable_randomDS_verifySig(const eosio::checksum256 digest, const uint8_t der_signature[], const uint8_t der_signature_len, const uint8_t pubkey[64]) 602 | { 603 | uint8_t r[32]; 604 | uint8_t s[32]; 605 | __provable_randomDS_get_signature_component(r, der_signature, der_signature_len, 3); 606 | __provable_randomDS_get_signature_component(s, der_signature, der_signature_len, 4 + der_signature[3] + 1); 607 | // We try either with v=27 or with v=28 608 | bool test_v27 = __provable_randomDS_test_pubkey_signature(0, 27, r, s, digest, pubkey); 609 | bool test_v28 = __provable_randomDS_test_pubkey_signature(0, 28, r, s, digest, pubkey); 610 | return test_v27 || test_v28; 611 | } 612 | 613 | uint8_t provable_randomDS_proofVerify(const eosio::checksum256 queryId, const std::vector result, const std::vector proof, const name payer) 614 | { 615 | /******************************************************************************************* 616 | * * 617 | * Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) * 618 | * * 619 | *******************************************************************************************/ 620 | if (proof[0] != 'L' || proof[1] != 'P' || proof[2] != 1) 621 | return 1; 622 | 623 | 624 | /******************************************************************************************** 625 | * * 626 | * Step 2: the unique keyhash has to match with the sha256 of (context name + queryId) * 627 | * * 628 | ********************************************************************************************/ 629 | const uint8_t ledgerProofLength = 3 + 65 + (proof[3 + 65 + 1] + 2) + 32; 630 | uint8_t keyhash[32]; 631 | std::memcpy(keyhash, &proof.data()[ledgerProofLength], 32); 632 | eosio::checksum256 keyhash_sha = sha256((char *)keyhash, 32); 633 | std::string keyhash_sha_str = checksum256_to_string(keyhash_sha); 634 | uint8_t *first_word = (uint8_t *) &keyhash_sha.get_array()[0]; 635 | uint8_t *second_word = (uint8_t *) &keyhash_sha.get_array()[1]; 636 | std::string context_name_str = PROVABLE_NETWORK_NAME; 637 | char context_name[context_name_str.size()]; 638 | context_name_str.copy(context_name, context_name_str.size()); 639 | uint8_t tbh2[sizeof(context_name) + 32]; 640 | std::memcpy(tbh2, &context_name, sizeof(context_name)); 641 | std::memcpy(tbh2 + sizeof(context_name), &queryId.get_array()[0], 16); 642 | std::memcpy(tbh2 + sizeof(context_name) + 16, &queryId.get_array()[1], 16); 643 | eosio::checksum256 calc_hash = invert_checksum256(sha256((char *)tbh2, sizeof(context_name) + 644 | 16 + 16)); 645 | uint8_t tbh3[32]; 646 | std::memcpy(tbh3, &calc_hash.get_array()[0], 16); 647 | std::memcpy(tbh3 + 16, &calc_hash.get_array()[1], 16); 648 | eosio::checksum256 calc_hash_2 = sha256((char *)tbh3, 16 + 16); 649 | uint8_t *first_word3 = (uint8_t *) &calc_hash_2.get_array()[0]; 650 | uint8_t *second_word3 = (uint8_t *) &calc_hash_2.get_array()[1]; 651 | if(checksum256_to_string(keyhash_sha) != checksum256_to_string(calc_hash_2)) 652 | return 2; 653 | 654 | 655 | /******************************************************************************************** 656 | * * 657 | * Step 3: we assume sig1 is valid (it will be verified during step 5) * 658 | * and we verify if 'result' is the prefix of sha256(sig1) * 659 | * * 660 | ********************************************************************************************/ 661 | const uint8_t sig1_len = proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1] + 2; 662 | uint8_t sig1[sig1_len]; 663 | std::memcpy(sig1, &proof.data()[ledgerProofLength + (32 + 8 + 1 + 32)], sig1_len); 664 | eosio::checksum256 sig1_hash = sha256((char *)sig1, sizeof(sig1)); 665 | if (!__provable_randomDS_matchBytes32Prefix(sig1_hash, result.data(), result.size(), proof[ledgerProofLength + 32 + 8])) 666 | return 3; 667 | 668 | 669 | /******************************************************************************************** 670 | * * 671 | * Step 4: commitment match verification, * 672 | * sha256(delay, nbytes, unonce, sessionKeyHash) == commitment in table. * 673 | * * 674 | ********************************************************************************************/ 675 | const uint8_t slice_offset = 8 + 1 + 32; // delay + nbytes + unonceHashBa 676 | uint8_t commitmentSlice1[slice_offset]; 677 | std::memcpy(commitmentSlice1, &proof.data()[ledgerProofLength + 32], sizeof(commitmentSlice1)); 678 | // Extract the session public key and calculate the session publick key hash 679 | uint8_t sessionPubKey[64]; 680 | const uint16_t sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1_len + 65; // ledgerProofLength+32+(8+1+32)+sig1.len+65 681 | std::memcpy(sessionPubKey, &proof.data()[sig2offset - 64], sizeof(sessionPubKey)); 682 | eosio::checksum256 sessionPubkeyHash = invert_checksum256(sha256((char *)sessionPubKey, 683 | sizeof(sessionPubKey))); // Calculate the key hash 684 | vector sessionPubkeyHashBa(32); // Convert to bytearray the public key hash 685 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 686 | // Recreate the lastCommitment to compare with the table one 687 | uint8_t tbh[slice_offset + 32]; 688 | std::memcpy(tbh, &commitmentSlice1, slice_offset); 689 | std::memcpy(tbh + slice_offset, &sessionPubkeyHashBa[0], 32); 690 | eosio::checksum256 lastCommitment = sha256((char *)tbh, sizeof(tbh)); 691 | // Retrieve the table commitment 692 | ds_scommitment last_commitments(payer, payer.value); 693 | name myQueryId_short; 694 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 695 | // Check if the key value exists 696 | auto itr = last_commitments.find(myQueryId_short.value); 697 | if (itr == last_commitments.end()) 698 | return 4; 699 | // Check the query id with the one in the table 700 | const std::string queryId_str__expected = checksum256_to_string(itr->queryid); 701 | const std::string queryId_str = checksum256_to_string(queryId); 702 | if (queryId_str != queryId_str__expected) 703 | return 4; 704 | // Check the commitment with the one in the table 705 | const std::string lastCommitment_str__expected = checksum256_to_string(itr->commitment); 706 | const std::string lastCommitment_str = checksum256_to_string(lastCommitment); 707 | if (lastCommitment_str != lastCommitment_str__expected) 708 | return 4; 709 | 710 | 711 | /******************************************************************************************** 712 | * * 713 | * Step 5: validity verification for sig1 (keyhash and args signed with the sessionKey) * 714 | * * 715 | ********************************************************************************************/ 716 | uint8_t toSign1[32 + 8 + 1 + 32]; 717 | std::memcpy(toSign1, &proof.data()[ledgerProofLength], sizeof(toSign1)); 718 | eosio::checksum256 toSign1_hash = sha256((char *)toSign1, sizeof(toSign1)); 719 | if (!__provable_randomDS_verifySig(toSign1_hash, sig1, sizeof(sig1), sessionPubKey)) 720 | return 5; 721 | 722 | 723 | /******************************************************************************************** 724 | * * 725 | * Step 6: verify the attestation signature, * 726 | * APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) * 727 | * * 728 | ********************************************************************************************/ 729 | uint8_t sig2[proof[sig2offset + 1] + 2]; 730 | std::memcpy(sig2, &proof.data()[sig2offset], sizeof(sig2)); 731 | uint8_t appkey_pubkey[64]; 732 | std::memcpy(appkey_pubkey, &proof.data()[3 + 1], sizeof(appkey_pubkey)); 733 | uint8_t toSign2[1 + 65 + 32]; 734 | toSign2[0] = 1; // role 735 | std::memcpy(toSign2 + 1, &proof.data()[sig2offset - 65], 65); 736 | std::memcpy(toSign2 + 65 + 1, CODE_HASH_RANDOMDS, 32); 737 | eosio::checksum256 toSign2_hash = sha256((char *)toSign2, sizeof(toSign2)); 738 | if (!__provable_randomDS_verifySig(toSign2_hash, sig2, sizeof(sig2), appkey_pubkey)) 739 | return 6; 740 | 741 | 742 | /******************************************************************************************** 743 | * * 744 | * Step 7: verify the APPKEY1 provenance (must be signed by Ledger) * 745 | * * 746 | ********************************************************************************************/ 747 | uint8_t toSign3[1 + 65]; 748 | toSign3[0] = 0xfe; 749 | std::memcpy(toSign3 + 1, &proof.data()[3], 65); 750 | uint8_t sig3[proof[3 + 65 + 1] + 2]; 751 | std::memcpy(sig3, &proof.data()[3 + 65], sizeof(sig3)); 752 | eosio::checksum256 toSign3_hash = sha256((char *)toSign3, sizeof(toSign3)); 753 | if (!__provable_randomDS_verifySig(toSign3_hash, sig3, sizeof(sig3), LEDGERKEY)) 754 | return 7; 755 | 756 | 757 | // Erase the commitment after the proof is verified 758 | auto itr2 = last_commitments.find(myQueryId_short.value); 759 | last_commitments.erase(itr2); 760 | return 0; 761 | } 762 | 763 | #endif 764 | -------------------------------------------------------------------------------- /encryptedqry/README.md: -------------------------------------------------------------------------------- 1 | # Provable's Encrypted Query Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **encrypted queries**. 4 | 5 | The `encryptedqry.cpp` example allows you to *send an encrypted query*. 6 | 7 |   8 | 9 | ## :lock: _Query Encryption_ 10 | 11 | **1)** Decide the Provable query you want to encrypt, the one in this example is: 12 | 13 | ``` 14 | provable_query( 15 | "URL", 16 | "json(https://min-api.cryptocompare.com/data/price?fsym=EOS&tsyms=USD&sign=true).USD",\ 17 | (proofType_Native) 18 | ) 19 | ``` 20 | 21 | **2)** Fire up your favourite console & clone the Provable encyption tool repo somewhere: 22 | 23 | __`❍ git clone https://github.com/provable-things/encrypted-queries.git`__ 24 | 25 | **3)** Enter the directory and brace yourself to encrypt your query with the Provable public key & install python modules: 26 | 27 | __`❍ cd encrypted-queries/tools && pip install base58`__ 28 | 29 | Provable public key: 30 | 31 | ``` 32 | 044992e9473b7d90ca54d2886c7addd14a61109af202f1c95e218b0c99eb060c7134c4ae46345d0383ac996185762f04997d6fd6c393c86e4325c469741e64eca9 33 | ``` 34 | 35 | **4)** Encrypt the first query argument: 36 | 37 | __`❍ python encrypted_queries_tools.py -e -p 044992e9473b7d90ca54d2886c7addd14a61109af202f1c95e218b0c99eb060c7134c4ae46345d0383ac996185762f04997d6fd6c393c86e4325c469741e64eca9 "json(https://min-api.cryptocompare.com/data/price?fsym=EOS&tsyms=USD&sign=true).USD"`__ 38 | 39 | it will generate a unique encrypted string (each re-run will generate a new unique string, as each encrypted string is meant to be used only by a single contract at a time): 40 | 41 | ``` 42 | BI1E+MtXQJXDFHVm38/YRZHMVe3s99a0rmQbdtUaa3w+EL/JALVJrvAKVIJi8OxON8vTo5K8x9P78qwQXovsDazeyV8tybIbcO6GlEy/tQXvtHClCPFjQREZ7uZfVd+0wWB1dqQO/WxCunCfgj8uiGFNQrlwwmyqc6/A/9lUUfoTf/koCI44sIOvsybELCg0m1ICaGKx1GhC8qJkChQwkACbY6Y= 43 | ``` 44 | 45 | **5)** Use the previous non-deterministic output and plug it into the query function: 46 | 47 | ``` 48 | provable_query( 49 | "URL", 50 | "BI1E+MtXQJXDFHVm38/YRZHMVe3s99a0rmQbdtUaa3w+EL/JALVJrvAKVIJi8OxON8vTo5K8x9P78qwQXovsDazeyV8tybIbcO6GlEy/tQXvtHClCPFjQREZ7uZfVd+0wWB1dqQO/WxCunCfgj8uiGFNQrlwwmyqc6/A/9lUUfoTf/koCI44sIOvsybELCg0m1ICaGKx1GhC8qJkChQwkACbY6Y=",\ 51 | (proofType_Native) 52 | ); 53 | ``` 54 | 55 |   56 | 57 | ## :page_with_curl: *Instructions* 58 | 59 | **1)** Fire up your favourite console & clone this repo somewhere: 60 | 61 | __`❍ git clone https://github.com/provable-things/eos-examples.git`__ 62 | 63 | **2)** Enter this directory & compile your contract: 64 | 65 | __`❍ cd encryptedqry && eosio-cpp -abigen encryptedqry.cpp -o encryptedqry.wasm`__ 66 | 67 | **3)** Unlock your wallet: 68 | 69 | __`❍ cleos wallet unlock -n --password `__ 70 | 71 | **4)** Go back to the root folder & deploy your contract on the EOS blockchain: 72 | 73 | __`❍ cleos set contract encryptedqry encryptedqry.wasm encryptedqry.abi -p @`__ 74 | 75 | **5)** Call the `execquery` action of the contract `encryptedqry`: 76 | 77 | __`❍ cleos push action execquery '[]' -p @`__ 78 | 79 | ## :pen: Notes 80 | 81 | Provable replies to your `provable_query` by calling your `callback(...)` with the *request status result* . 82 | You can search for your transaction ID in one of the following links to verify it: 83 | 84 | * :mag_right::ledger: [Blocks.io](https://jungle3.bloks.io/): A block explorer for the Jungle 3.0 testnet. 85 | 86 | * :palm_tree::lion::palm_tree: [Jungle 3.0](https://monitor3.jungletestnet.io/#home): A transaction explorer is available by selecting *Get TX* on the Jungle 3.0 testnet website. 87 | 88 | ## :ambulance: Support 89 | 90 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel to get timely support! 91 | 92 | ***Happy developing!*** 93 | 94 | -------------------------------------------------------------------------------- /encryptedqry/encryptedqry.cpp: -------------------------------------------------------------------------------- 1 | #include "provable/eos_api.hpp" 2 | 3 | class encryptedqry : public eosio::contract 4 | { 5 | public: 6 | using contract::contract; 7 | 8 | encryptedqry(eosio::name receiver, eosio::name code, datastream ds) : contract(receiver, code, ds) {} 9 | 10 | [[eosio::action]] 11 | void execquery() 12 | { 13 | print("Sending encrypted query to Provable..."); 14 | provable_query( 15 | "URL", 16 | "BI1E+MtXQJXDFHVm38/YRZHMVe3s99a0rmQbdtUaa3w+EL/JALVJrvAKVIJi8OxON8vTo5K8x9P78qwQXovsDazeyV8tybIbcO6GlEy/tQXvtHClCPFjQREZ7uZfVd+0wWB1dqQO/WxCunCfgj8uiGFNQrlwwmyqc6/A/9lUUfoTf/koCI44sIOvsybELCg0m1ICaGKx1GhC8qJkChQwkACbY6Y=",\ 17 | (proofType_Native) 18 | ); 19 | } 20 | 21 | [[eosio::action]] 22 | void callback( 23 | const eosio::checksum256 queryId, 24 | const std::vector result, 25 | const std::vector proof 26 | ) 27 | { 28 | require_auth(provable_cbAddress()); 29 | const std::string result_str = vector_to_string(result); 30 | print(" EOSUSD Price: ", result_str); 31 | print(" Proof length: ", proof.size()); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /eosusdprice/README.md: -------------------------------------------------------------------------------- 1 | # Provable's EOS/USD Price Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **URL** datasource. 4 | 5 | The `eosusdprice.cpp` example allows you to *retrieve the eos token price in usd* and ***provides 6 | you with the authenticity proof of the fetched data***. 7 | 8 | ## :page_with_curl: *Instructions* 9 | 10 | **1)** Compile your contract (launch this command **inside** the folder `eos-usd-price`): 11 | 12 | **`❍ eosio-cpp -abigen eosusdprice.cpp -o eosusdprice.wasm`** 13 | 14 | **2)** Unlock your wallet: 15 | 16 | **`❍ cleos wallet unlock -n --password `** 17 | 18 | **3)** Deploy your contract on EOS (launch this command **outside** the folder `eos-usd-price`): 19 | 20 | **`❍ cleos set contract eos-usd-price eosusdprice.wasm eosusdprice.abi -p @`** 21 | 22 | **4)** Call the `execquery` action of the contract `eosusdprice`: 23 | 24 | **`❍ cleos push action execquery '[]' -p @`** 25 | 26 | ## :pen: Notes 27 | 28 | Provable replies to your `provable_query` by calling your `callback(...)` with the *price result*. 29 | You can search your transaction ID in one of the following links to verify it: 30 | 31 | * :mag_right::ledger: [Jungle 3 Blocks.io](https://jungle3.bloks.io/): A block explorer for the Jungle 3.0 testnet. 32 | 33 | * :mag_right::ledger: [Kylin Blocks.io](https://kylin.bloks.io/): A block explorer for the Kylin testnet. 34 | 35 | ## :ambulance: Support 36 | 37 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel to get timely support! 38 | 39 | ***Happy developing!*** 40 | 41 | -------------------------------------------------------------------------------- /eosusdprice/eosusdprice.cpp: -------------------------------------------------------------------------------- 1 | #include "provable/eos_api.hpp" 2 | 3 | class eosusdprice : public eosio::contract 4 | { 5 | public: 6 | using contract::contract; 7 | 8 | eosusdprice(eosio::name receiver, eosio::name code, datastream ds) : contract(receiver, code, ds) {} 9 | 10 | [[eosio::action]] 11 | void execquery() 12 | { 13 | print("Sending query to Provable..."); 14 | provable_query("URL", "json(https://min-api.cryptocompare.com/data/price?fsym=EOS&tsyms=USD).USD",\ 15 | (proofType_Android | proofStorage_IPFS)); 16 | } 17 | 18 | [[eosio::action]] 19 | void callback( 20 | const eosio::checksum256 queryId, 21 | const std::vector result, 22 | const std::vector proof 23 | ) 24 | { 25 | require_auth(provable_cbAddress()); 26 | const std::string result_str = vector_to_string(result); 27 | print(" Result: ", result_str); 28 | print(" Proof length: ", proof.size()); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /eosusdprice/provable/eos_api.hpp: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * Provable API * 3 | * * 4 | * Copyright (c) 2015-2016 Provable SRL * 5 | * Copyright (c) 2016 Provable LTD * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy * 7 | * of this software and associated documentation files (the "Software"), to deal * 8 | * in the Software without restriction, including without limitation the rights * 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * 10 | * copies of the Software, and to permit persons to whom the Software is * 11 | * furnished to do so, subject to the following conditions: * 12 | * The above copyright notice and this permission notice shall be included in * 13 | * all copies or substantial portions of the Software. * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * 20 | * THE SOFTWARE. * 21 | *********************************************************************************/ 22 | 23 | #ifndef PROVABLEAPI_H 24 | #define PROVABLEAPI_H 25 | 26 | 27 | /************************************************** 28 | * INCLUDE * 29 | * Libraries * 30 | **************************************************/ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | /************************************************** 40 | * MACRO * 41 | * Definitions * 42 | **************************************************/ 43 | #ifndef PROVABLE_NETWORK_NAME 44 | #warning PROVABLE_NETWORK_NAME is not set, setting it to "eosio_unknown".. [possible values are "eosio_mainnet"/"eosio_testnet_jungle"/"eosio_testnet_kylin"/"eosio_unknown"] 45 | #define PROVABLE_NETWORK_NAME "eosio_unknown" 46 | #endif // PROVABLE_NETWORK_NAME 47 | 48 | #ifndef CONTRACT_NAME 49 | #warning CONTRACT_NAME is not set, setting it to "".. [write the "contract.name" not the account name] 50 | #define CONTRACT_NAME "unknown" 51 | #endif // CONTRACT_NAME 52 | 53 | #ifndef PROVABLE_PAYER 54 | #define PROVABLE_PAYER _self 55 | #endif // PROVABLE_PAYER 56 | 57 | #define provable_query(...) __provable_query(PROVABLE_PAYER, __VA_ARGS__, _self) 58 | #define provable_newRandomDSQuery(...) __provable_newRandomDSQuery(PROVABLE_PAYER, __VA_ARGS__, _self) 59 | #define provable_queryId_localEmplace(...) __provable_queryId_localEmplace(__VA_ARGS__, _self) 60 | #define provable_queryId_match(...) __provable_queryId_match(__VA_ARGS__, _self) 61 | 62 | 63 | /************************************************** 64 | * NAMESPACES * 65 | * Declarations * 66 | **************************************************/ 67 | using namespace eosio; 68 | using namespace std; 69 | 70 | 71 | /************************************************** 72 | * CONSTANTS * 73 | * Proof Types * 74 | **************************************************/ 75 | const uint8_t proofType_NONE = 0x00; 76 | const uint8_t proofType_TLSNotary = 0x10; 77 | const uint8_t proofType_Ledger = 0x30; 78 | const uint8_t proofType_Android = 0x40; 79 | const uint8_t proofType_Native = 0xF0; 80 | const uint8_t proofStorage_IPFS = 0x01; 81 | 82 | const uint8_t CODE_HASH_RANDOMDS[32] = { 83 | 253, 148, 250, 113, 188, 11, 161, 13, 57, 212, 100, 208, 216, 244, 101, 239, 238, 240, 162, 118, 78, 56, 135, 252, 201, 223, 65, 222, 210, 15, 80, 92 84 | }; 85 | const uint8_t LEDGERKEY[64] = { 86 | 127, 185, 86, 70, 156, 92, 155, 137, 132, 13, 85, 180, 53, 55, 230, 106, 152, 221, 72, 17, 234, 10, 39, 34, 66, 114, 194, 229, 98, 41, 17, 232, 83, 122, 47, 142, 134, 164, 107, 174, 200, 40, 100, 233, 141, 208, 30, 156, 204, 47, 139, 197, 223, 201, 203, 229, 169, 26, 41, 4, 152, 221, 150, 228 87 | }; 88 | 89 | 90 | /************************************************** 91 | * PROVABLE TABLE * 92 | * Definition * 93 | **************************************************/ 94 | struct [[eosio::table, eosio::contract("provableconn")]] snonce 95 | { 96 | name sender; 97 | uint32_t nonce; 98 | 99 | uint64_t primary_key() const { return sender.value; } 100 | }; 101 | 102 | struct [[eosio::table, eosio::contract("provableconn")]] cbaddr 103 | { 104 | name sender; 105 | 106 | uint64_t primary_key() const { return sender.value; } 107 | }; 108 | 109 | struct [[eosio::table, eosio::contract("provableconn")]] spubkey 110 | { 111 | name key; 112 | eosio::checksum256 randomDS_lastSessionPubkeyHash; 113 | 114 | uint64_t primary_key() const { return key.value; } 115 | eosio::checksum256 get_randomDS_lastSessionPubkeyHash() const { return randomDS_lastSessionPubkeyHash; } 116 | }; 117 | 118 | 119 | /************************************************** 120 | * PROVABLE TABLE * 121 | * Definition * 122 | **************************************************/ 123 | 124 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] scommitment 125 | { 126 | name shortqueryid; 127 | eosio::checksum256 queryid; 128 | eosio::checksum256 commitment; 129 | 130 | uint64_t primary_key() const { return shortqueryid.value; } 131 | }; 132 | 133 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] queryid 134 | { 135 | name key; 136 | eosio::checksum256 qid; 137 | uint8_t active; 138 | 139 | uint64_t primary_key() const { return key.value; } 140 | }; 141 | 142 | typedef eosio::multi_index ds_snonce; 143 | typedef eosio::multi_index ds_cbaddr; 144 | typedef eosio::multi_index ds_spubkey; 145 | typedef eosio::multi_index ds_scommitment; 146 | typedef eosio::multi_index ds_queryid; 147 | 148 | 149 | /************************************************** 150 | * PUBLIC FUNCTIONS * 151 | * Implementation * 152 | **************************************************/ 153 | eosio::name provable_cbAddress() 154 | { 155 | ds_cbaddr cb_addrs("provableconn"_n, "provableconn"_n.value); // go to the connector table which identify the sender 156 | auto itr = cb_addrs.begin(); // point to the first element of the table 157 | const uint64_t cbaddr_uint64t = (itr != cb_addrs.end()) ? itr->sender.value : 0; 158 | eosio::name cbaddr_name = eosio::name(cbaddr_uint64t); 159 | return cbaddr_name; 160 | } 161 | 162 | std::string vector_uc_to_string(const std::vector v) 163 | { 164 | std::string v_str(v.begin(), v.end()); 165 | return v_str; 166 | } 167 | 168 | std::vector provable_set_computation_args( 169 | const std::vector> _args 170 | ) 171 | { 172 | // [args_number][first_arg_len][[first_arg][second_arg_len][[second_arg][...][...][last_arg_len][last_arg] 173 | const unsigned char args_size = _args.size(); 174 | std::vector query; 175 | // Prepare computation query 176 | query.push_back(args_size); // Max arguments allowed are 255 177 | for (int i = 0; i < args_size; i++) 178 | { 179 | query.push_back(_args[i].size()); // argument length 180 | query.insert(query.end(), _args[i].begin(), _args[i].end()); // argument 181 | } 182 | return query; 183 | } 184 | 185 | eosio::checksum256 invert_checksum256(const eosio::checksum256 _cs) 186 | { 187 | uint8_t *first_word = (uint8_t *) &_cs.get_array()[0]; 188 | uint8_t *second_word = (uint8_t *) &_cs.get_array()[1]; 189 | eosio::checksum256 cs_inverted; 190 | uint8_t *first_word_inverted = (uint8_t *) &cs_inverted.get_array()[0]; 191 | uint8_t *second_word_inverted = (uint8_t *) &cs_inverted.get_array()[1]; 192 | for (int i = 0; i < 16; i++) 193 | { 194 | first_word_inverted[15 - i] = first_word[i]; 195 | second_word_inverted[15 - i] = second_word[i]; 196 | } 197 | return cs_inverted; 198 | } 199 | 200 | 201 | bool checksum256_is_empty(const eosio::checksum256 cs) 202 | { 203 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 204 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 205 | for(int i = 0; i < 16; i++) 206 | if(first_word[i] != 0 && second_word[i] != 0) 207 | return false; 208 | return true; 209 | } 210 | 211 | std::vector string_to_vector(const std::string s) 212 | { 213 | std::vector s_v(s.begin(), s.end()); 214 | return s_v; 215 | } 216 | 217 | std::string vector_to_string(const std::vector v) 218 | { 219 | std::string v_str(v.begin(), v.end()); 220 | return v_str; 221 | } 222 | 223 | std::vector hexstring_to_vector32(std::string s) 224 | { 225 | std::vector v32; 226 | for(unsigned int i = 0; i < 32; i++) 227 | { 228 | unsigned int ui; 229 | sscanf(s.data() + (i * 2), "%02x", &ui); 230 | unsigned char uc = (unsigned char) ui; 231 | v32.push_back(uc); 232 | } 233 | return v32; 234 | } 235 | 236 | eosio::checksum256 hexstring_to_checksum256(const std::string hs) 237 | { 238 | std::vector hs_bytes = hexstring_to_vector32(hs); 239 | eosio::checksum256 cs; 240 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 241 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 242 | for (int i = 0; i < 16; i++) 243 | { 244 | first_word[i] = hs_bytes[i]; 245 | second_word[i] = hs_bytes[i + 16]; 246 | } 247 | return cs; 248 | } 249 | 250 | std::string checksum256_to_string(const eosio::checksum256 cs) 251 | { 252 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 253 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 254 | char hexstr[64]; 255 | for (int i = 0; i < 16; i++) 256 | sprintf(hexstr + i * 2, "%02x", first_word[i]); 257 | for (int i = 16; i < 32; i++) 258 | sprintf(hexstr + i * 2, "%02x", second_word[i - 16]); 259 | std::string c_str = std::string(hexstr); 260 | return c_str; 261 | } 262 | 263 | std::string chara_to_hexstring(uint8_t *input, const int size) 264 | { 265 | char hexstr[size * 2]; 266 | for (int i = 0; i < size; i++) 267 | sprintf(hexstr + i * 2, "%02x", input[i]); 268 | std::string c_str = std::string(hexstr); 269 | return c_str; 270 | } 271 | 272 | std::string vector_to_hexstring(std::vector *input) 273 | { 274 | int size = input->size(); 275 | char hexstr[size * 2]; 276 | for (int i = 0; i < size; i++) 277 | sprintf(hexstr + i * 2, "%02x", input->at(i)); 278 | std::string c_str = std::string(hexstr); 279 | return c_str; 280 | } 281 | 282 | std::vector uint32_to_vector8(uint32_t num) 283 | { 284 | std::vector ba(8); 285 | const uint32_t mask = 0xFF; 286 | for(int i = 0; i < 8; i++) 287 | { 288 | ba[i] = num & mask; 289 | num = num >> 8; 290 | } 291 | return ba; 292 | } 293 | 294 | std::vector uint32_to_vector32_bigendian(uint32_t num) 295 | { 296 | std::vector ba(32); 297 | const uint32_t mask = 0xFF; 298 | for(int i = 31; i > -1; i--) 299 | { 300 | ba[i] = num & mask; 301 | num = num >> 8; 302 | } 303 | return ba; 304 | } 305 | 306 | std::vector checksum256_to_vector32(const eosio::checksum256 cs) 307 | { 308 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 309 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 310 | std::vector ba(32); 311 | for (int i = 0; i < 16; i++) 312 | { 313 | ba[i] = first_word[i]; 314 | } 315 | for (int i = 16; i < 32; i++) 316 | { 317 | ba[i] = second_word[i - 16]; 318 | } 319 | return ba; 320 | } 321 | 322 | 323 | /************************************************** 324 | * INTERNAL FUNCTIONS * 325 | * Definitions * 326 | **************************************************/ 327 | eosio::checksum256 __provable_randomDS_getSessionPubkeyHash() 328 | { 329 | ds_spubkey spubkeys("provableconn"_n, "provableconn"_n.value); 330 | name index = "1"_n; // only one value in the table with key = 1 331 | auto itr = spubkeys.find(index.value); 332 | eosio::checksum256 sessionPubkeyHash; 333 | if (itr != spubkeys.end()) 334 | { 335 | sessionPubkeyHash = itr->get_randomDS_lastSessionPubkeyHash(); 336 | } 337 | return sessionPubkeyHash; 338 | } 339 | 340 | uint32_t __provable_getSenderNonce(name sender) 341 | { 342 | ds_snonce last_nonces("provableconn"_n, "provableconn"_n.value); 343 | auto itr = last_nonces.find(sender.value); 344 | uint32_t nonce = 0; 345 | if (itr != last_nonces.end()) 346 | { 347 | nonce = itr->nonce; 348 | } 349 | return nonce; 350 | } 351 | 352 | eosio::checksum256 __provable_getNextQueryId(const name sender) 353 | { 354 | const uint32_t nonce = __provable_getSenderNonce(sender); // get values to generate the queryId 355 | const size_t tx_size = transaction_size(); 356 | uint8_t tbh[sizeof(sender) + sizeof(nonce) + sizeof(tx_size)]; // calculate the hash of the previous values 357 | std::memcpy(tbh, &sender, sizeof(sender)); 358 | std::memcpy(tbh + sizeof(sender), &nonce, sizeof(nonce)); 359 | std::memcpy(tbh + sizeof(sender) + sizeof(nonce), &tx_size, sizeof(tx_size)); 360 | eosio::checksum256 calc_hash = sha256((char *)tbh, sizeof(tbh)); 361 | return calc_hash; 362 | } 363 | 364 | // Check that the queryId being passed matches with the one in the customer local table, return true/false accordingly 365 | bool __provable_queryId_match(const eosio::checksum256 queryId, const name sender) 366 | { 367 | name myQueryId_short; 368 | std::memcpy(&myQueryId_short, &queryId, sizeof(myQueryId_short)); 369 | // Access the local query table and find the right row 370 | ds_queryid queryids(sender, sender.value); 371 | auto itr = queryids.find(myQueryId_short.value); 372 | // Get the local queryId value 373 | eosio::checksum256 queryId_expected; 374 | std::memcpy(&queryId_expected, 0, sizeof(queryId_expected)); 375 | // Get the queryId from the table 376 | if (itr != queryids.end()) 377 | queryId_expected = itr->qid; 378 | else 379 | return false; 380 | // Check if the value retrieved is only 0 381 | if(checksum256_is_empty(queryId_expected)) 382 | return false; 383 | // Convert queryId and queryId_expected to string, to compare them 384 | const std::string queryId_str__expected = checksum256_to_string(queryId_expected); 385 | const std::string queryId_str = checksum256_to_string(queryId); 386 | // Compare the queryids and check if string values are empty 387 | if (queryId_str != queryId_str__expected || queryId_str.empty() || queryId_str__expected.empty()) 388 | return false; 389 | else 390 | return true; 391 | } 392 | 393 | void __provable_queryId_localEmplace(const eosio::checksum256 myQueryId, const name sender) 394 | { 395 | // Retreive the short queryId to use it as an index 396 | name myQueryId_short; 397 | std::memcpy(&myQueryId_short, &myQueryId, sizeof(myQueryId_short)); 398 | // Save the queryId in the local table 399 | ds_queryid queryids(sender, sender.value); 400 | queryids.emplace(sender, [&](auto& o) { 401 | o.key = myQueryId_short; 402 | o.qid = myQueryId; 403 | o.active = true; 404 | }); 405 | } 406 | 407 | 408 | /************************************************** 409 | * Provable Query * 410 | * Strings * 411 | **************************************************/ 412 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 413 | { 414 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 415 | action(permission_level{user, "active"_n}, 416 | "provableconn"_n, 417 | "querystr"_n, 418 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 419 | ).send(); 420 | return queryId; 421 | } 422 | 423 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const name sender) 424 | { 425 | return __provable_query(user, 0, datasource, query, 0, sender); 426 | } 427 | 428 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const name sender) 429 | { 430 | return __provable_query(user, timestamp, datasource, query, 0, sender); 431 | } 432 | 433 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 434 | { 435 | return __provable_query(user, 0, datasource, query, prooftype, sender); 436 | } 437 | 438 | 439 | /************************************************** 440 | * Provable Query * 441 | * Bytearrays * 442 | **************************************************/ 443 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 444 | { 445 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 446 | printhex(query.data(), query.size()); 447 | auto n = name{user}; 448 | const std::string str = n.to_string(); 449 | action(permission_level{user, "active"_n}, 450 | "provableconn"_n, 451 | "queryba"_n, 452 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 453 | ).send(); 454 | return queryId; 455 | } 456 | 457 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const name sender) 458 | { 459 | return __provable_query(user, 0, datasource, query, 0, sender); 460 | } 461 | 462 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const name sender) 463 | { 464 | return __provable_query(user, timestamp, datasource, query, 0, sender); 465 | } 466 | 467 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 468 | { 469 | return __provable_query(user, 0, datasource, query, prooftype, sender); 470 | } 471 | 472 | 473 | /************************************************** 474 | * Provable Query * 475 | * Random DS * 476 | **************************************************/ 477 | void __provable_randomDS_setCommitment(const eosio::checksum256 queryId, const eosio::checksum256 commitment, const name payer) 478 | { 479 | name myQueryId_short; // Calculate the short queryId, to use it as a key of the table 480 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 481 | ds_scommitment last_commitments(payer, payer.value); // Set the commitment in the eos table of the caller 482 | last_commitments.emplace(payer, [&](auto &o) { // The key must be a uint64_t so a short query Id is used 483 | o.shortqueryid = myQueryId_short; 484 | o.queryid = queryId; 485 | o.commitment = commitment; 486 | }); 487 | } 488 | 489 | eosio::checksum256 __provable_newRandomDSQuery(const name user, const uint32_t _delay, const uint8_t _nbytes, const name sender) 490 | { 491 | // 1. NBYTES - Convert nbytes to bytearray 492 | std::vector nbytesBa(1); 493 | nbytesBa[0] = _nbytes; 494 | 495 | // 2. SESSIONKEYHASH - Get the sessionKeyHash from the ledger public key. 496 | const eosio::checksum256 sessionPubkeyHash = __provable_randomDS_getSessionPubkeyHash(); 497 | std::vector sessionPubkeyHashBa(32); 498 | std::vector sessionPubkeyHashBa2(32); 499 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 500 | 501 | // 3. UNONCE - Need something block dependent so we decided to perform the hash of those 4 block dependent fields. This value have to be unpredictable from Provable 502 | const size_t tx_size = transaction_size(); 503 | const int tapos_block_num_ = tapos_block_num(); 504 | const int tapos_block_prefix_ = tapos_block_prefix(); 505 | uint8_t unonce[sizeof(tx_size) + sizeof(tapos_block_num_) + sizeof(tapos_block_prefix_)]; // Fill the unonce array: now() + transaction_size + tapos_block_num + tapos_block_prefix 506 | std::memcpy(unonce, &tx_size, sizeof(tx_size)); 507 | std::memcpy(unonce + sizeof(tx_size), &tapos_block_num_, sizeof(tapos_block_num_)); 508 | std::memcpy(unonce + sizeof(tx_size) + sizeof(tapos_block_num_), &tapos_block_prefix_, sizeof(tapos_block_prefix_)); 509 | eosio::checksum256 unonceHash = sha256((char *)unonce, sizeof(unonce)); // Container for the unonce hash 510 | std::vector unonceHashBa(32); // Convert the unonce hash in bytearray 511 | unonceHashBa = checksum256_to_vector32(unonceHash); 512 | 513 | // 4. DELAY - Delay converted in a big endian bytearray 514 | const uint32_t delayLedgerTime = _delay * 10; // Convert from seconds to ledger timer ticks 515 | std::vector delayBaBigEndian(32); 516 | delayBaBigEndian = uint32_to_vector32_bigendian(delayLedgerTime); 517 | 518 | // Set args2 to be passed as params of the provable "random" query 519 | std::vector> args; 520 | args.push_back(unonceHashBa); 521 | args.push_back(nbytesBa); 522 | args.push_back(sessionPubkeyHashBa); 523 | args.push_back(delayBaBigEndian); 524 | std::vector args2; 525 | for(auto && a : args) 526 | args2.insert(args2.end(), a.begin(), a.end()); 527 | 528 | // Call the provable_query and get the queryId 529 | const eosio::checksum256 queryId = __provable_query(user,"random", args2, proofType_Ledger, sender); // proofType and datasource are always fixed in this function 530 | 531 | // Calculate the commitment and call a function to set it 532 | std::vector delayBa(8); // delay converted to 8 byte 533 | delayBa = uint32_to_vector8(delayLedgerTime); 534 | uint8_t* charArray = &unonceHashBa[0]; 535 | eosio::checksum256 unonceHashBaHash = invert_checksum256(sha256((char *) charArray, unonceHashBa.size())); // unonce has to be passed hashed 536 | uint8_t commitmentTbh[8 + 1 + 32 + args[2].size()]; // Calculate the commitment to be hashed with the size of: 8 + 1 + 32 + 32 537 | std::memcpy(commitmentTbh, &delayBa[0], 8); // 8 538 | std::memcpy(commitmentTbh + 8, &args[1][0], 1); // 8 + 1 539 | std::memcpy(commitmentTbh + 8 + 1, &unonceHashBaHash.get_array()[0], 16); // 8 + 1 + 16 540 | std::memcpy(commitmentTbh + 8 + 1 + 16, &unonceHashBaHash.get_array()[1], 16); // 8 + 1 + 32 == commitmentSlice1 541 | std::memcpy(commitmentTbh + delayBa.size() + args[1].size() + 32, &args[2][0], args[2].size()); // 8 + 1 + 32 + 32 (commitmentSlice1 + sessionPubkeyHashBa) 542 | eosio::checksum256 commitment = sha256((char *)commitmentTbh, sizeof(commitmentTbh)); // Container for the commitment hash 543 | const name payer = user; // Payer for setting the commitment 544 | __provable_randomDS_setCommitment(queryId, commitment, payer); // Call the function to set query Id and commitment in the table 545 | 546 | return queryId; 547 | } 548 | 549 | void __provable_randomDS_get_signature_component(uint8_t component[32], const uint8_t signature[], const uint8_t signature_len, const uint8_t length_idx) 550 | { 551 | eosio::internal_use_do_not_use::eosio_assert(signature_len > length_idx, "Invalid index"); 552 | uint8_t component_len = signature[length_idx]; 553 | uint8_t byte_to_jump = component_len % 32; 554 | std::memcpy(component, &signature[length_idx + 1 + byte_to_jump], component_len - byte_to_jump); 555 | } 556 | 557 | bool __provable_randomDS_matchBytes32Prefix(const eosio::checksum256 content, const uint8_t prefix[], const uint8_t prefix_len, const uint8_t n_random_bytes) 558 | { 559 | eosio::internal_use_do_not_use::eosio_assert(prefix_len == n_random_bytes, "Prefix length and random bytes number should match."); 560 | const eosio::checksum256 content_inverted = invert_checksum256(content); 561 | uint8_t *first_word = (uint8_t *)&content_inverted.get_array()[0]; 562 | uint8_t *second_word = (uint8_t *)&content_inverted.get_array()[1]; 563 | for (int i = 0; i < n_random_bytes; ++i) 564 | { 565 | if (i < 16) 566 | { 567 | if (first_word[i] != prefix[i]) 568 | return false; 569 | } 570 | else 571 | { 572 | if (second_word[i] != prefix[i]) 573 | return false; 574 | } 575 | } 576 | return true; 577 | } 578 | 579 | bool __provable_randomDS_test_pubkey_signature(const uint8_t whatever, const uint8_t v, const uint8_t r[32], const uint8_t s[32], const eosio::checksum256 digest, const uint8_t pubkey[64]) 580 | { 581 | eosio::webauthn_signature sig; 582 | sig.auth_data[0] = v; 583 | for (int i = 0; i < 32; i++) 584 | sig.auth_data[i + 1] = r[i]; 585 | for (int i = 0; i < 32; i++) 586 | sig.auth_data[i + 1 + 32] = s[i]; 587 | 588 | const eosio::webauthn_public_key pubkey_recovered = get(recover_key(digest, sig)); 589 | 590 | if (pubkey_recovered.key.size() != 33) 591 | return false; 592 | if (pubkey_recovered.key[0] != 0x02 && pubkey_recovered.key[0] != 0x03) 593 | return false; 594 | // Discard the first (0x00) and the second byte (0x02 or 0x03) 595 | for (int i = 0; i < 32; i++) 596 | if ((uint8_t)pubkey_recovered.key[i + 1] != pubkey[i]) 597 | return false; 598 | return true; 599 | } 600 | 601 | bool __provable_randomDS_verifySig(const eosio::checksum256 digest, const uint8_t der_signature[], const uint8_t der_signature_len, const uint8_t pubkey[64]) 602 | { 603 | uint8_t r[32]; 604 | uint8_t s[32]; 605 | __provable_randomDS_get_signature_component(r, der_signature, der_signature_len, 3); 606 | __provable_randomDS_get_signature_component(s, der_signature, der_signature_len, 4 + der_signature[3] + 1); 607 | // We try either with v=27 or with v=28 608 | bool test_v27 = __provable_randomDS_test_pubkey_signature(0, 27, r, s, digest, pubkey); 609 | bool test_v28 = __provable_randomDS_test_pubkey_signature(0, 28, r, s, digest, pubkey); 610 | return test_v27 || test_v28; 611 | } 612 | 613 | uint8_t provable_randomDS_proofVerify(const eosio::checksum256 queryId, const std::vector result, const std::vector proof, const name payer) 614 | { 615 | /******************************************************************************************* 616 | * * 617 | * Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) * 618 | * * 619 | *******************************************************************************************/ 620 | if (proof[0] != 'L' || proof[1] != 'P' || proof[2] != 1) 621 | return 1; 622 | 623 | 624 | /******************************************************************************************** 625 | * * 626 | * Step 2: the unique keyhash has to match with the sha256 of (context name + queryId) * 627 | * * 628 | ********************************************************************************************/ 629 | const uint8_t ledgerProofLength = 3 + 65 + (proof[3 + 65 + 1] + 2) + 32; 630 | uint8_t keyhash[32]; 631 | std::memcpy(keyhash, &proof.data()[ledgerProofLength], 32); 632 | eosio::checksum256 keyhash_sha = sha256((char *)keyhash, 32); 633 | std::string keyhash_sha_str = checksum256_to_string(keyhash_sha); 634 | uint8_t *first_word = (uint8_t *) &keyhash_sha.get_array()[0]; 635 | uint8_t *second_word = (uint8_t *) &keyhash_sha.get_array()[1]; 636 | std::string context_name_str = PROVABLE_NETWORK_NAME; 637 | char context_name[context_name_str.size()]; 638 | context_name_str.copy(context_name, context_name_str.size()); 639 | uint8_t tbh2[sizeof(context_name) + 32]; 640 | std::memcpy(tbh2, &context_name, sizeof(context_name)); 641 | std::memcpy(tbh2 + sizeof(context_name), &queryId.get_array()[0], 16); 642 | std::memcpy(tbh2 + sizeof(context_name) + 16, &queryId.get_array()[1], 16); 643 | eosio::checksum256 calc_hash = invert_checksum256(sha256((char *)tbh2, sizeof(context_name) + 644 | 16 + 16)); 645 | uint8_t tbh3[32]; 646 | std::memcpy(tbh3, &calc_hash.get_array()[0], 16); 647 | std::memcpy(tbh3 + 16, &calc_hash.get_array()[1], 16); 648 | eosio::checksum256 calc_hash_2 = sha256((char *)tbh3, 16 + 16); 649 | uint8_t *first_word3 = (uint8_t *) &calc_hash_2.get_array()[0]; 650 | uint8_t *second_word3 = (uint8_t *) &calc_hash_2.get_array()[1]; 651 | if(checksum256_to_string(keyhash_sha) != checksum256_to_string(calc_hash_2)) 652 | return 2; 653 | 654 | 655 | /******************************************************************************************** 656 | * * 657 | * Step 3: we assume sig1 is valid (it will be verified during step 5) * 658 | * and we verify if 'result' is the prefix of sha256(sig1) * 659 | * * 660 | ********************************************************************************************/ 661 | const uint8_t sig1_len = proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1] + 2; 662 | uint8_t sig1[sig1_len]; 663 | std::memcpy(sig1, &proof.data()[ledgerProofLength + (32 + 8 + 1 + 32)], sig1_len); 664 | eosio::checksum256 sig1_hash = sha256((char *)sig1, sizeof(sig1)); 665 | if (!__provable_randomDS_matchBytes32Prefix(sig1_hash, result.data(), result.size(), proof[ledgerProofLength + 32 + 8])) 666 | return 3; 667 | 668 | 669 | /******************************************************************************************** 670 | * * 671 | * Step 4: commitment match verification, * 672 | * sha256(delay, nbytes, unonce, sessionKeyHash) == commitment in table. * 673 | * * 674 | ********************************************************************************************/ 675 | const uint8_t slice_offset = 8 + 1 + 32; // delay + nbytes + unonceHashBa 676 | uint8_t commitmentSlice1[slice_offset]; 677 | std::memcpy(commitmentSlice1, &proof.data()[ledgerProofLength + 32], sizeof(commitmentSlice1)); 678 | // Extract the session public key and calculate the session publick key hash 679 | uint8_t sessionPubKey[64]; 680 | const uint16_t sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1_len + 65; // ledgerProofLength+32+(8+1+32)+sig1.len+65 681 | std::memcpy(sessionPubKey, &proof.data()[sig2offset - 64], sizeof(sessionPubKey)); 682 | eosio::checksum256 sessionPubkeyHash = invert_checksum256(sha256((char *)sessionPubKey, 683 | sizeof(sessionPubKey))); // Calculate the key hash 684 | vector sessionPubkeyHashBa(32); // Convert to bytearray the public key hash 685 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 686 | // Recreate the lastCommitment to compare with the table one 687 | uint8_t tbh[slice_offset + 32]; 688 | std::memcpy(tbh, &commitmentSlice1, slice_offset); 689 | std::memcpy(tbh + slice_offset, &sessionPubkeyHashBa[0], 32); 690 | eosio::checksum256 lastCommitment = sha256((char *)tbh, sizeof(tbh)); 691 | // Retrieve the table commitment 692 | ds_scommitment last_commitments(payer, payer.value); 693 | name myQueryId_short; 694 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 695 | // Check if the key value exists 696 | auto itr = last_commitments.find(myQueryId_short.value); 697 | if (itr == last_commitments.end()) 698 | return 4; 699 | // Check the query id with the one in the table 700 | const std::string queryId_str__expected = checksum256_to_string(itr->queryid); 701 | const std::string queryId_str = checksum256_to_string(queryId); 702 | if (queryId_str != queryId_str__expected) 703 | return 4; 704 | // Check the commitment with the one in the table 705 | const std::string lastCommitment_str__expected = checksum256_to_string(itr->commitment); 706 | const std::string lastCommitment_str = checksum256_to_string(lastCommitment); 707 | if (lastCommitment_str != lastCommitment_str__expected) 708 | return 4; 709 | 710 | 711 | /******************************************************************************************** 712 | * * 713 | * Step 5: validity verification for sig1 (keyhash and args signed with the sessionKey) * 714 | * * 715 | ********************************************************************************************/ 716 | uint8_t toSign1[32 + 8 + 1 + 32]; 717 | std::memcpy(toSign1, &proof.data()[ledgerProofLength], sizeof(toSign1)); 718 | eosio::checksum256 toSign1_hash = sha256((char *)toSign1, sizeof(toSign1)); 719 | if (!__provable_randomDS_verifySig(toSign1_hash, sig1, sizeof(sig1), sessionPubKey)) 720 | return 5; 721 | 722 | 723 | /******************************************************************************************** 724 | * * 725 | * Step 6: verify the attestation signature, * 726 | * APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) * 727 | * * 728 | ********************************************************************************************/ 729 | uint8_t sig2[proof[sig2offset + 1] + 2]; 730 | std::memcpy(sig2, &proof.data()[sig2offset], sizeof(sig2)); 731 | uint8_t appkey_pubkey[64]; 732 | std::memcpy(appkey_pubkey, &proof.data()[3 + 1], sizeof(appkey_pubkey)); 733 | uint8_t toSign2[1 + 65 + 32]; 734 | toSign2[0] = 1; // role 735 | std::memcpy(toSign2 + 1, &proof.data()[sig2offset - 65], 65); 736 | std::memcpy(toSign2 + 65 + 1, CODE_HASH_RANDOMDS, 32); 737 | eosio::checksum256 toSign2_hash = sha256((char *)toSign2, sizeof(toSign2)); 738 | if (!__provable_randomDS_verifySig(toSign2_hash, sig2, sizeof(sig2), appkey_pubkey)) 739 | return 6; 740 | 741 | 742 | /******************************************************************************************** 743 | * * 744 | * Step 7: verify the APPKEY1 provenance (must be signed by Ledger) * 745 | * * 746 | ********************************************************************************************/ 747 | uint8_t toSign3[1 + 65]; 748 | toSign3[0] = 0xfe; 749 | std::memcpy(toSign3 + 1, &proof.data()[3], 65); 750 | uint8_t sig3[proof[3 + 65 + 1] + 2]; 751 | std::memcpy(sig3, &proof.data()[3 + 65], sizeof(sig3)); 752 | eosio::checksum256 toSign3_hash = sha256((char *)toSign3, sizeof(toSign3)); 753 | if (!__provable_randomDS_verifySig(toSign3_hash, sig3, sizeof(sig3), LEDGERKEY)) 754 | return 7; 755 | 756 | 757 | // Erase the commitment after the proof is verified 758 | auto itr2 = last_commitments.find(myQueryId_short.value); 759 | last_commitments.erase(itr2); 760 | return 0; 761 | } 762 | 763 | #endif 764 | -------------------------------------------------------------------------------- /randomsample/README.md: -------------------------------------------------------------------------------- 1 | # Provable's Random Datasource Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **random** datasource. 4 | 5 | The `randomsample.cpp` example allows you to *retrieve a random number*. 6 | 7 | ## :page_with_curl: *Instructions* 8 | 9 | **1)** Compile your contract (launch this command **inside** the folder `random-datasource`): 10 | 11 | **`❍ eosio-cpp -abigen randomsample.cpp -o randomsample.wasm`** 12 | 13 | **2)** Unlock your wallet: 14 | 15 | **`❍ cleos wallet unlock -n --password `** 16 | 17 | **3)** Deploy your contract on EOS (launch this command **outside** the folder `random-datasource`): 18 | 19 | **`❍ cleos set contract random-datasource randomsample.wasm randomsample.abi -p @`** 20 | 21 | **4)** Call the `getrandnum` action of the contract `randomsample`: 22 | 23 | **`❍ cleos push action getrandnum '[]' -p @`** 24 | 25 | ## :pen: Notes 26 | 27 | Provable replies to your `provable_newRandomDSQuery` by calling your `callback(...)` with the *random number result*. 28 | You can search your transaction ID in one of the following links to verify it: 29 | 30 | * :mag_right::ledger: [Jungle 3 Blocks.io](https://jungle3.bloks.io/): A block explorer for the Jungle 3.0 testnet. 31 | 32 | * :mag_right::ledger: [Kylin Blocks.io](https://kylin.bloks.io/): A block explorer for the Kylin testnet. 33 | 34 | ## :ambulance: Support 35 | 36 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel to get timely support! 37 | 38 | ***Happy developing!*** 39 | 40 | -------------------------------------------------------------------------------- /randomsample/randomsample.cpp: -------------------------------------------------------------------------------- 1 | #define PROVABLE_NETWORK_NAME "eosio_testnet_jungle" 2 | #define CONTRACT_NAME "randomsample" 3 | 4 | #include "provable/eos_api.hpp" 5 | 6 | class randomsample : public eosio::contract 7 | { 8 | public: 9 | using contract::contract; 10 | 11 | randomsample(eosio::name receiver, eosio::name code, datastream ds) : contract(receiver, code, ds) {} 12 | 13 | [[eosio::action]] 14 | void getrandnum() 15 | { 16 | print("Sending query to Provable..."); 17 | uint8_t N = 1; // Possible outputs: [0-255] 18 | uint32_t delay = 0; 19 | provable_newRandomDSQuery(delay, N); 20 | } 21 | 22 | [[eosio::action]] 23 | void callback( 24 | const eosio::checksum256 queryId, 25 | const std::vector result, 26 | const std::vector proof 27 | ) 28 | { 29 | require_auth(provable_cbAddress()); 30 | if (provable_randomDS_proofVerify(queryId, result, proof, _self) != 0) 31 | { 32 | // The proof verification has failed, manage this use case... 33 | print("Number: ", result[0]); 34 | print(" Proof failed has failed..."); 35 | } 36 | else 37 | { 38 | print("Number: ", result[0]); 39 | } 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /urlrequests/README.md: -------------------------------------------------------------------------------- 1 | # Provable's URL Requests Computation Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **computation** datasource. 4 | 5 | The `urlrequests.cpp` example allows you *to retrieve the response of several URL requests*. 6 | 7 | ## :page_with_curl: *Instructions* 8 | 9 | **1)** Compile your contract (launch this command **inside** the folder `url-requests-computation`): 10 | 11 | **`❍ eosio-cpp -abigen urlrequests.cpp -o urlrequests.wasm`** 12 | 13 | **2)** Unlock your wallet: 14 | 15 | **`❍ cleos wallet unlock -n --password `** 16 | 17 | **3)** Deploy your contract on EOS (launch this command **outside** the folder `url-requests-computation`): 18 | 19 | **`❍ cleos set contract url-requests-computation urlrequests.wasm urlrequests.abi -p @`** 20 | 21 | **4)** Call the `reqheadscust` action of the contract `urlrequests`: 22 | 23 | **`❍ cleos push action reqheadscust '[]' -p @`** 24 | 25 | **5)** Call the `reqbasauth` action of the contract `urlrequests`: 26 | 27 | **`❍ cleos push action reqbasauth '[]' -p @`** 28 | 29 | **6)** Call the `reqpost` action of the contract `urlrequests`: 30 | 31 | **`❍ cleos push action reqpost '[]' -p @`** 32 | 33 | **7)** Call the `reqput` action of the contract `urlrequests`: 34 | 35 | **`❍ cleos push action reqput '[]' -p @`** 36 | 37 | **8)** Call the `reqcookies` action of the contract `urlrequests`: 38 | 39 | **`❍ cleos push action reqcookies '[]' -p @`** 40 | 41 | ## :pen: Notes 42 | 43 | Provable replies to your `oraclize_query` by calling your `callback(...)` with the *respose body result*. 44 | You can search for your transaction ID in one of the following links to verify it: 45 | 46 | * :mag_right::ledger: [Blocks.io](https://jungle.bloks.io/): A block explorer for the Jungle 2.0 testnet. 47 | 48 | * :palm_tree::lion::palm_tree: [Jungle 2.0](https://monitor.jungletestnet.io/#home): A transaction explorer is available by selecting *Get TX* on the Jungle 2.0 testnet website. 49 | 50 | ## :ambulance: Support 51 | 52 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel 53 | to get timely support! 54 | 55 | ***Happy developing!*** 56 | 57 | -------------------------------------------------------------------------------- /urlrequests/provable/README.md: -------------------------------------------------------------------------------- 1 | # eos-api 2 | 3 | Please refer to [our API documentation](http://docs.oraclize.it/#eos) to know more about the Oraclize API on EOSIO. 4 | 5 | ## Oraclize `eos-api` 6 | 7 | Documentation: [docs.oraclize.it](http://docs.oraclize.it/#eos) 8 | 9 | Gitter public support channel: 10 | [![Join the chat at https://gitter.im/oraclize/eos-api](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/oraclize/eos-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 11 | 12 | [![HitCount](http://hits.dwyl.io/oraclize/eos-api.svg)](http://hits.dwyl.io/oraclize/eos-api) 13 | -------------------------------------------------------------------------------- /urlrequests/provable/eos_api.hpp: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * Provable API * 3 | * * 4 | * Copyright (c) 2015-2016 Provable SRL * 5 | * Copyright (c) 2016 Provable LTD * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy * 7 | * of this software and associated documentation files (the "Software"), to deal * 8 | * in the Software without restriction, including without limitation the rights * 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * 10 | * copies of the Software, and to permit persons to whom the Software is * 11 | * furnished to do so, subject to the following conditions: * 12 | * The above copyright notice and this permission notice shall be included in * 13 | * all copies or substantial portions of the Software. * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * 20 | * THE SOFTWARE. * 21 | *********************************************************************************/ 22 | 23 | #ifndef PROVABLEAPI_H 24 | #define PROVABLEAPI_H 25 | 26 | 27 | /************************************************** 28 | * INCLUDE * 29 | * Libraries * 30 | **************************************************/ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | /************************************************** 40 | * MACRO * 41 | * Definitions * 42 | **************************************************/ 43 | #ifndef PROVABLE_NETWORK_NAME 44 | #warning PROVABLE_NETWORK_NAME is not set, setting it to "eosio_unknown".. [possible values are "eosio_mainnet"/"eosio_testnet_jungle"/"eosio_testnet_kylin"/"eosio_unknown"] 45 | #define PROVABLE_NETWORK_NAME "eosio_unknown" 46 | #endif // PROVABLE_NETWORK_NAME 47 | 48 | #ifndef CONTRACT_NAME 49 | #warning CONTRACT_NAME is not set, setting it to "".. [write the "contract.name" not the account name] 50 | #define CONTRACT_NAME "unknown" 51 | #endif // CONTRACT_NAME 52 | 53 | #ifndef PROVABLE_PAYER 54 | #define PROVABLE_PAYER _self 55 | #endif // PROVABLE_PAYER 56 | 57 | #define provable_query(...) __provable_query(PROVABLE_PAYER, __VA_ARGS__, _self) 58 | #define provable_newRandomDSQuery(...) __provable_newRandomDSQuery(PROVABLE_PAYER, __VA_ARGS__, _self) 59 | #define provable_queryId_localEmplace(...) __provable_queryId_localEmplace(__VA_ARGS__, _self) 60 | #define provable_queryId_match(...) __provable_queryId_match(__VA_ARGS__, _self) 61 | 62 | 63 | /************************************************** 64 | * NAMESPACES * 65 | * Declarations * 66 | **************************************************/ 67 | using namespace eosio; 68 | using namespace std; 69 | 70 | 71 | /************************************************** 72 | * CONSTANTS * 73 | * Proof Types * 74 | **************************************************/ 75 | const uint8_t proofType_NONE = 0x00; 76 | const uint8_t proofType_TLSNotary = 0x10; 77 | const uint8_t proofType_Ledger = 0x30; 78 | const uint8_t proofType_Android = 0x40; 79 | const uint8_t proofType_Native = 0xF0; 80 | const uint8_t proofStorage_IPFS = 0x01; 81 | 82 | const uint8_t CODE_HASH_RANDOMDS[32] = { 83 | 253, 148, 250, 113, 188, 11, 161, 13, 57, 212, 100, 208, 216, 244, 101, 239, 238, 240, 162, 118, 78, 56, 135, 252, 201, 223, 65, 222, 210, 15, 80, 92 84 | }; 85 | const uint8_t LEDGERKEY[64] = { 86 | 127, 185, 86, 70, 156, 92, 155, 137, 132, 13, 85, 180, 53, 55, 230, 106, 152, 221, 72, 17, 234, 10, 39, 34, 66, 114, 194, 229, 98, 41, 17, 232, 83, 122, 47, 142, 134, 164, 107, 174, 200, 40, 100, 233, 141, 208, 30, 156, 204, 47, 139, 197, 223, 201, 203, 229, 169, 26, 41, 4, 152, 221, 150, 228 87 | }; 88 | 89 | 90 | /************************************************** 91 | * PROVABLE TABLE * 92 | * Definition * 93 | **************************************************/ 94 | struct [[eosio::table, eosio::contract("provableconn")]] snonce 95 | { 96 | name sender; 97 | uint32_t nonce; 98 | 99 | uint64_t primary_key() const { return sender.value; } 100 | }; 101 | 102 | struct [[eosio::table, eosio::contract("provableconn")]] cbaddr 103 | { 104 | name sender; 105 | 106 | uint64_t primary_key() const { return sender.value; } 107 | }; 108 | 109 | struct [[eosio::table, eosio::contract("provableconn")]] spubkey 110 | { 111 | name key; 112 | eosio::checksum256 randomDS_lastSessionPubkeyHash; 113 | 114 | uint64_t primary_key() const { return key.value; } 115 | eosio::checksum256 get_randomDS_lastSessionPubkeyHash() const { return randomDS_lastSessionPubkeyHash; } 116 | }; 117 | 118 | 119 | /************************************************** 120 | * PROVABLE TABLE * 121 | * Definition * 122 | **************************************************/ 123 | 124 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] scommitment 125 | { 126 | name shortqueryid; 127 | eosio::checksum256 queryid; 128 | eosio::checksum256 commitment; 129 | 130 | uint64_t primary_key() const { return shortqueryid.value; } 131 | }; 132 | 133 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] queryid 134 | { 135 | name key; 136 | eosio::checksum256 qid; 137 | uint8_t active; 138 | 139 | uint64_t primary_key() const { return key.value; } 140 | }; 141 | 142 | typedef eosio::multi_index ds_snonce; 143 | typedef eosio::multi_index ds_cbaddr; 144 | typedef eosio::multi_index ds_spubkey; 145 | typedef eosio::multi_index ds_scommitment; 146 | typedef eosio::multi_index ds_queryid; 147 | 148 | 149 | /************************************************** 150 | * PUBLIC FUNCTIONS * 151 | * Implementation * 152 | **************************************************/ 153 | eosio::name provable_cbAddress() 154 | { 155 | ds_cbaddr cb_addrs("provableconn"_n, "provableconn"_n.value); // go to the connector table which identify the sender 156 | auto itr = cb_addrs.begin(); // point to the first element of the table 157 | const uint64_t cbaddr_uint64t = (itr != cb_addrs.end()) ? itr->sender.value : 0; 158 | eosio::name cbaddr_name = eosio::name(cbaddr_uint64t); 159 | return cbaddr_name; 160 | } 161 | 162 | std::string vector_uc_to_string(const std::vector v) 163 | { 164 | std::string v_str(v.begin(), v.end()); 165 | return v_str; 166 | } 167 | 168 | std::vector provable_set_computation_args( 169 | const std::vector> _args 170 | ) 171 | { 172 | // [args_number][first_arg_len][[first_arg][second_arg_len][[second_arg][...][...][last_arg_len][last_arg] 173 | const unsigned char args_size = _args.size(); 174 | std::vector query; 175 | // Prepare computation query 176 | query.push_back(args_size); // Max arguments allowed are 255 177 | for (int i = 0; i < args_size; i++) 178 | { 179 | query.push_back(_args[i].size()); // argument length 180 | query.insert(query.end(), _args[i].begin(), _args[i].end()); // argument 181 | } 182 | return query; 183 | } 184 | 185 | eosio::checksum256 invert_checksum256(const eosio::checksum256 _cs) 186 | { 187 | uint8_t *first_word = (uint8_t *) &_cs.get_array()[0]; 188 | uint8_t *second_word = (uint8_t *) &_cs.get_array()[1]; 189 | eosio::checksum256 cs_inverted; 190 | uint8_t *first_word_inverted = (uint8_t *) &cs_inverted.get_array()[0]; 191 | uint8_t *second_word_inverted = (uint8_t *) &cs_inverted.get_array()[1]; 192 | for (int i = 0; i < 16; i++) 193 | { 194 | first_word_inverted[15 - i] = first_word[i]; 195 | second_word_inverted[15 - i] = second_word[i]; 196 | } 197 | return cs_inverted; 198 | } 199 | 200 | 201 | bool checksum256_is_empty(const eosio::checksum256 cs) 202 | { 203 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 204 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 205 | for(int i = 0; i < 16; i++) 206 | if(first_word[i] != 0 && second_word[i] != 0) 207 | return false; 208 | return true; 209 | } 210 | 211 | std::vector string_to_vector(const std::string s) 212 | { 213 | std::vector s_v(s.begin(), s.end()); 214 | return s_v; 215 | } 216 | 217 | std::string vector_to_string(const std::vector v) 218 | { 219 | std::string v_str(v.begin(), v.end()); 220 | return v_str; 221 | } 222 | 223 | std::vector hexstring_to_vector32(std::string s) 224 | { 225 | std::vector v32; 226 | for(unsigned int i = 0; i < 32; i++) 227 | { 228 | unsigned int ui; 229 | sscanf(s.data() + (i * 2), "%02x", &ui); 230 | unsigned char uc = (unsigned char) ui; 231 | v32.push_back(uc); 232 | } 233 | return v32; 234 | } 235 | 236 | eosio::checksum256 hexstring_to_checksum256(const std::string hs) 237 | { 238 | std::vector hs_bytes = hexstring_to_vector32(hs); 239 | eosio::checksum256 cs; 240 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 241 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 242 | for (int i = 0; i < 16; i++) 243 | { 244 | first_word[i] = hs_bytes[i]; 245 | second_word[i] = hs_bytes[i + 16]; 246 | } 247 | return cs; 248 | } 249 | 250 | std::string checksum256_to_string(const eosio::checksum256 cs) 251 | { 252 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 253 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 254 | char hexstr[64]; 255 | for (int i = 0; i < 16; i++) 256 | sprintf(hexstr + i * 2, "%02x", first_word[i]); 257 | for (int i = 16; i < 32; i++) 258 | sprintf(hexstr + i * 2, "%02x", second_word[i - 16]); 259 | std::string c_str = std::string(hexstr); 260 | return c_str; 261 | } 262 | 263 | std::string chara_to_hexstring(uint8_t *input, const int size) 264 | { 265 | char hexstr[size * 2]; 266 | for (int i = 0; i < size; i++) 267 | sprintf(hexstr + i * 2, "%02x", input[i]); 268 | std::string c_str = std::string(hexstr); 269 | return c_str; 270 | } 271 | 272 | std::string vector_to_hexstring(std::vector *input) 273 | { 274 | int size = input->size(); 275 | char hexstr[size * 2]; 276 | for (int i = 0; i < size; i++) 277 | sprintf(hexstr + i * 2, "%02x", input->at(i)); 278 | std::string c_str = std::string(hexstr); 279 | return c_str; 280 | } 281 | 282 | std::vector uint32_to_vector8(uint32_t num) 283 | { 284 | std::vector ba(8); 285 | const uint32_t mask = 0xFF; 286 | for(int i = 0; i < 8; i++) 287 | { 288 | ba[i] = num & mask; 289 | num = num >> 8; 290 | } 291 | return ba; 292 | } 293 | 294 | std::vector uint32_to_vector32_bigendian(uint32_t num) 295 | { 296 | std::vector ba(32); 297 | const uint32_t mask = 0xFF; 298 | for(int i = 31; i > -1; i--) 299 | { 300 | ba[i] = num & mask; 301 | num = num >> 8; 302 | } 303 | return ba; 304 | } 305 | 306 | std::vector checksum256_to_vector32(const eosio::checksum256 cs) 307 | { 308 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 309 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 310 | std::vector ba(32); 311 | for (int i = 0; i < 16; i++) 312 | { 313 | ba[i] = first_word[i]; 314 | } 315 | for (int i = 16; i < 32; i++) 316 | { 317 | ba[i] = second_word[i - 16]; 318 | } 319 | return ba; 320 | } 321 | 322 | 323 | /************************************************** 324 | * INTERNAL FUNCTIONS * 325 | * Definitions * 326 | **************************************************/ 327 | eosio::checksum256 __provable_randomDS_getSessionPubkeyHash() 328 | { 329 | ds_spubkey spubkeys("provableconn"_n, "provableconn"_n.value); 330 | name index = "1"_n; // only one value in the table with key = 1 331 | auto itr = spubkeys.find(index.value); 332 | eosio::checksum256 sessionPubkeyHash; 333 | if (itr != spubkeys.end()) 334 | { 335 | sessionPubkeyHash = itr->get_randomDS_lastSessionPubkeyHash(); 336 | } 337 | return sessionPubkeyHash; 338 | } 339 | 340 | uint32_t __provable_getSenderNonce(name sender) 341 | { 342 | ds_snonce last_nonces("provableconn"_n, "provableconn"_n.value); 343 | auto itr = last_nonces.find(sender.value); 344 | uint32_t nonce = 0; 345 | if (itr != last_nonces.end()) 346 | { 347 | nonce = itr->nonce; 348 | } 349 | return nonce; 350 | } 351 | 352 | eosio::checksum256 __provable_getNextQueryId(const name sender) 353 | { 354 | const uint32_t nonce = __provable_getSenderNonce(sender); // get values to generate the queryId 355 | const size_t tx_size = transaction_size(); 356 | uint8_t tbh[sizeof(sender) + sizeof(nonce) + sizeof(tx_size)]; // calculate the hash of the previous values 357 | std::memcpy(tbh, &sender, sizeof(sender)); 358 | std::memcpy(tbh + sizeof(sender), &nonce, sizeof(nonce)); 359 | std::memcpy(tbh + sizeof(sender) + sizeof(nonce), &tx_size, sizeof(tx_size)); 360 | eosio::checksum256 calc_hash = sha256((char *)tbh, sizeof(tbh)); 361 | return calc_hash; 362 | } 363 | 364 | // Check that the queryId being passed matches with the one in the customer local table, return true/false accordingly 365 | bool __provable_queryId_match(const eosio::checksum256 queryId, const name sender) 366 | { 367 | name myQueryId_short; 368 | std::memcpy(&myQueryId_short, &queryId, sizeof(myQueryId_short)); 369 | // Access the local query table and find the right row 370 | ds_queryid queryids(sender, sender.value); 371 | auto itr = queryids.find(myQueryId_short.value); 372 | // Get the local queryId value 373 | eosio::checksum256 queryId_expected; 374 | std::memcpy(&queryId_expected, 0, sizeof(queryId_expected)); 375 | // Get the queryId from the table 376 | if (itr != queryids.end()) 377 | queryId_expected = itr->qid; 378 | else 379 | return false; 380 | // Check if the value retrieved is only 0 381 | if(checksum256_is_empty(queryId_expected)) 382 | return false; 383 | // Convert queryId and queryId_expected to string, to compare them 384 | const std::string queryId_str__expected = checksum256_to_string(queryId_expected); 385 | const std::string queryId_str = checksum256_to_string(queryId); 386 | // Compare the queryids and check if string values are empty 387 | if (queryId_str != queryId_str__expected || queryId_str.empty() || queryId_str__expected.empty()) 388 | return false; 389 | else 390 | return true; 391 | } 392 | 393 | void __provable_queryId_localEmplace(const eosio::checksum256 myQueryId, const name sender) 394 | { 395 | // Retreive the short queryId to use it as an index 396 | name myQueryId_short; 397 | std::memcpy(&myQueryId_short, &myQueryId, sizeof(myQueryId_short)); 398 | // Save the queryId in the local table 399 | ds_queryid queryids(sender, sender.value); 400 | queryids.emplace(sender, [&](auto& o) { 401 | o.key = myQueryId_short; 402 | o.qid = myQueryId; 403 | o.active = true; 404 | }); 405 | } 406 | 407 | 408 | /************************************************** 409 | * Provable Query * 410 | * Strings * 411 | **************************************************/ 412 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 413 | { 414 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 415 | action(permission_level{user, "active"_n}, 416 | "provableconn"_n, 417 | "querystr"_n, 418 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 419 | ).send(); 420 | return queryId; 421 | } 422 | 423 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const name sender) 424 | { 425 | return __provable_query(user, 0, datasource, query, 0, sender); 426 | } 427 | 428 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const name sender) 429 | { 430 | return __provable_query(user, timestamp, datasource, query, 0, sender); 431 | } 432 | 433 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 434 | { 435 | return __provable_query(user, 0, datasource, query, prooftype, sender); 436 | } 437 | 438 | 439 | /************************************************** 440 | * Provable Query * 441 | * Bytearrays * 442 | **************************************************/ 443 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 444 | { 445 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 446 | printhex(query.data(), query.size()); 447 | auto n = name{user}; 448 | const std::string str = n.to_string(); 449 | action(permission_level{user, "active"_n}, 450 | "provableconn"_n, 451 | "queryba"_n, 452 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 453 | ).send(); 454 | return queryId; 455 | } 456 | 457 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const name sender) 458 | { 459 | return __provable_query(user, 0, datasource, query, 0, sender); 460 | } 461 | 462 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const name sender) 463 | { 464 | return __provable_query(user, timestamp, datasource, query, 0, sender); 465 | } 466 | 467 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 468 | { 469 | return __provable_query(user, 0, datasource, query, prooftype, sender); 470 | } 471 | 472 | 473 | /************************************************** 474 | * Provable Query * 475 | * Random DS * 476 | **************************************************/ 477 | void __provable_randomDS_setCommitment(const eosio::checksum256 queryId, const eosio::checksum256 commitment, const name payer) 478 | { 479 | name myQueryId_short; // Calculate the short queryId, to use it as a key of the table 480 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 481 | ds_scommitment last_commitments(payer, payer.value); // Set the commitment in the eos table of the caller 482 | last_commitments.emplace(payer, [&](auto &o) { // The key must be a uint64_t so a short query Id is used 483 | o.shortqueryid = myQueryId_short; 484 | o.queryid = queryId; 485 | o.commitment = commitment; 486 | }); 487 | } 488 | 489 | eosio::checksum256 __provable_newRandomDSQuery(const name user, const uint32_t _delay, const uint8_t _nbytes, const name sender) 490 | { 491 | // 1. NBYTES - Convert nbytes to bytearray 492 | std::vector nbytesBa(1); 493 | nbytesBa[0] = _nbytes; 494 | 495 | // 2. SESSIONKEYHASH - Get the sessionKeyHash from the ledger public key. 496 | const eosio::checksum256 sessionPubkeyHash = __provable_randomDS_getSessionPubkeyHash(); 497 | std::vector sessionPubkeyHashBa(32); 498 | std::vector sessionPubkeyHashBa2(32); 499 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 500 | 501 | // 3. UNONCE - Need something block dependent so we decided to perform the hash of those 4 block dependent fields. This value have to be unpredictable from Provable 502 | const size_t tx_size = transaction_size(); 503 | const int tapos_block_num_ = tapos_block_num(); 504 | const int tapos_block_prefix_ = tapos_block_prefix(); 505 | uint8_t unonce[sizeof(tx_size) + sizeof(tapos_block_num_) + sizeof(tapos_block_prefix_)]; // Fill the unonce array: now() + transaction_size + tapos_block_num + tapos_block_prefix 506 | std::memcpy(unonce, &tx_size, sizeof(tx_size)); 507 | std::memcpy(unonce + sizeof(tx_size), &tapos_block_num_, sizeof(tapos_block_num_)); 508 | std::memcpy(unonce + sizeof(tx_size) + sizeof(tapos_block_num_), &tapos_block_prefix_, sizeof(tapos_block_prefix_)); 509 | eosio::checksum256 unonceHash = sha256((char *)unonce, sizeof(unonce)); // Container for the unonce hash 510 | std::vector unonceHashBa(32); // Convert the unonce hash in bytearray 511 | unonceHashBa = checksum256_to_vector32(unonceHash); 512 | 513 | // 4. DELAY - Delay converted in a big endian bytearray 514 | const uint32_t delayLedgerTime = _delay * 10; // Convert from seconds to ledger timer ticks 515 | std::vector delayBaBigEndian(32); 516 | delayBaBigEndian = uint32_to_vector32_bigendian(delayLedgerTime); 517 | 518 | // Set args2 to be passed as params of the provable "random" query 519 | std::vector> args; 520 | args.push_back(unonceHashBa); 521 | args.push_back(nbytesBa); 522 | args.push_back(sessionPubkeyHashBa); 523 | args.push_back(delayBaBigEndian); 524 | std::vector args2; 525 | for(auto && a : args) 526 | args2.insert(args2.end(), a.begin(), a.end()); 527 | 528 | // Call the provable_query and get the queryId 529 | const eosio::checksum256 queryId = __provable_query(user,"random", args2, proofType_Ledger, sender); // proofType and datasource are always fixed in this function 530 | 531 | // Calculate the commitment and call a function to set it 532 | std::vector delayBa(8); // delay converted to 8 byte 533 | delayBa = uint32_to_vector8(delayLedgerTime); 534 | uint8_t* charArray = &unonceHashBa[0]; 535 | eosio::checksum256 unonceHashBaHash = invert_checksum256(sha256((char *) charArray, unonceHashBa.size())); // unonce has to be passed hashed 536 | uint8_t commitmentTbh[8 + 1 + 32 + args[2].size()]; // Calculate the commitment to be hashed with the size of: 8 + 1 + 32 + 32 537 | std::memcpy(commitmentTbh, &delayBa[0], 8); // 8 538 | std::memcpy(commitmentTbh + 8, &args[1][0], 1); // 8 + 1 539 | std::memcpy(commitmentTbh + 8 + 1, &unonceHashBaHash.get_array()[0], 16); // 8 + 1 + 16 540 | std::memcpy(commitmentTbh + 8 + 1 + 16, &unonceHashBaHash.get_array()[1], 16); // 8 + 1 + 32 == commitmentSlice1 541 | std::memcpy(commitmentTbh + delayBa.size() + args[1].size() + 32, &args[2][0], args[2].size()); // 8 + 1 + 32 + 32 (commitmentSlice1 + sessionPubkeyHashBa) 542 | eosio::checksum256 commitment = sha256((char *)commitmentTbh, sizeof(commitmentTbh)); // Container for the commitment hash 543 | const name payer = user; // Payer for setting the commitment 544 | __provable_randomDS_setCommitment(queryId, commitment, payer); // Call the function to set query Id and commitment in the table 545 | 546 | return queryId; 547 | } 548 | 549 | void __provable_randomDS_get_signature_component(uint8_t component[32], const uint8_t signature[], const uint8_t signature_len, const uint8_t length_idx) 550 | { 551 | eosio::internal_use_do_not_use::eosio_assert(signature_len > length_idx, "Invalid index"); 552 | uint8_t component_len = signature[length_idx]; 553 | uint8_t byte_to_jump = component_len % 32; 554 | std::memcpy(component, &signature[length_idx + 1 + byte_to_jump], component_len - byte_to_jump); 555 | } 556 | 557 | bool __provable_randomDS_matchBytes32Prefix(const eosio::checksum256 content, const uint8_t prefix[], const uint8_t prefix_len, const uint8_t n_random_bytes) 558 | { 559 | eosio::internal_use_do_not_use::eosio_assert(prefix_len == n_random_bytes, "Prefix length and random bytes number should match."); 560 | const eosio::checksum256 content_inverted = invert_checksum256(content); 561 | uint8_t *first_word = (uint8_t *)&content_inverted.get_array()[0]; 562 | uint8_t *second_word = (uint8_t *)&content_inverted.get_array()[1]; 563 | for (int i = 0; i < n_random_bytes; ++i) 564 | { 565 | if (i < 16) 566 | { 567 | if (first_word[i] != prefix[i]) 568 | return false; 569 | } 570 | else 571 | { 572 | if (second_word[i] != prefix[i]) 573 | return false; 574 | } 575 | } 576 | return true; 577 | } 578 | 579 | bool __provable_randomDS_test_pubkey_signature(const uint8_t whatever, const uint8_t v, const uint8_t r[32], const uint8_t s[32], const eosio::checksum256 digest, const uint8_t pubkey[64]) 580 | { 581 | eosio::webauthn_signature sig; 582 | sig.auth_data[0] = v; 583 | for (int i = 0; i < 32; i++) 584 | sig.auth_data[i + 1] = r[i]; 585 | for (int i = 0; i < 32; i++) 586 | sig.auth_data[i + 1 + 32] = s[i]; 587 | 588 | const eosio::webauthn_public_key pubkey_recovered = get(recover_key(digest, sig)); 589 | 590 | if (pubkey_recovered.key.size() != 33) 591 | return false; 592 | if (pubkey_recovered.key[0] != 0x02 && pubkey_recovered.key[0] != 0x03) 593 | return false; 594 | // Discard the first (0x00) and the second byte (0x02 or 0x03) 595 | for (int i = 0; i < 32; i++) 596 | if ((uint8_t)pubkey_recovered.key[i + 1] != pubkey[i]) 597 | return false; 598 | return true; 599 | } 600 | 601 | bool __provable_randomDS_verifySig(const eosio::checksum256 digest, const uint8_t der_signature[], const uint8_t der_signature_len, const uint8_t pubkey[64]) 602 | { 603 | uint8_t r[32]; 604 | uint8_t s[32]; 605 | __provable_randomDS_get_signature_component(r, der_signature, der_signature_len, 3); 606 | __provable_randomDS_get_signature_component(s, der_signature, der_signature_len, 4 + der_signature[3] + 1); 607 | // We try either with v=27 or with v=28 608 | bool test_v27 = __provable_randomDS_test_pubkey_signature(0, 27, r, s, digest, pubkey); 609 | bool test_v28 = __provable_randomDS_test_pubkey_signature(0, 28, r, s, digest, pubkey); 610 | return test_v27 || test_v28; 611 | } 612 | 613 | uint8_t provable_randomDS_proofVerify(const eosio::checksum256 queryId, const std::vector result, const std::vector proof, const name payer) 614 | { 615 | /******************************************************************************************* 616 | * * 617 | * Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) * 618 | * * 619 | *******************************************************************************************/ 620 | if (proof[0] != 'L' || proof[1] != 'P' || proof[2] != 1) 621 | return 1; 622 | 623 | 624 | /******************************************************************************************** 625 | * * 626 | * Step 2: the unique keyhash has to match with the sha256 of (context name + queryId) * 627 | * * 628 | ********************************************************************************************/ 629 | const uint8_t ledgerProofLength = 3 + 65 + (proof[3 + 65 + 1] + 2) + 32; 630 | uint8_t keyhash[32]; 631 | std::memcpy(keyhash, &proof.data()[ledgerProofLength], 32); 632 | eosio::checksum256 keyhash_sha = sha256((char *)keyhash, 32); 633 | std::string keyhash_sha_str = checksum256_to_string(keyhash_sha); 634 | uint8_t *first_word = (uint8_t *) &keyhash_sha.get_array()[0]; 635 | uint8_t *second_word = (uint8_t *) &keyhash_sha.get_array()[1]; 636 | std::string context_name_str = PROVABLE_NETWORK_NAME; 637 | char context_name[context_name_str.size()]; 638 | context_name_str.copy(context_name, context_name_str.size()); 639 | uint8_t tbh2[sizeof(context_name) + 32]; 640 | std::memcpy(tbh2, &context_name, sizeof(context_name)); 641 | std::memcpy(tbh2 + sizeof(context_name), &queryId.get_array()[0], 16); 642 | std::memcpy(tbh2 + sizeof(context_name) + 16, &queryId.get_array()[1], 16); 643 | eosio::checksum256 calc_hash = invert_checksum256(sha256((char *)tbh2, sizeof(context_name) + 644 | 16 + 16)); 645 | uint8_t tbh3[32]; 646 | std::memcpy(tbh3, &calc_hash.get_array()[0], 16); 647 | std::memcpy(tbh3 + 16, &calc_hash.get_array()[1], 16); 648 | eosio::checksum256 calc_hash_2 = sha256((char *)tbh3, 16 + 16); 649 | uint8_t *first_word3 = (uint8_t *) &calc_hash_2.get_array()[0]; 650 | uint8_t *second_word3 = (uint8_t *) &calc_hash_2.get_array()[1]; 651 | if(checksum256_to_string(keyhash_sha) != checksum256_to_string(calc_hash_2)) 652 | return 2; 653 | 654 | 655 | /******************************************************************************************** 656 | * * 657 | * Step 3: we assume sig1 is valid (it will be verified during step 5) * 658 | * and we verify if 'result' is the prefix of sha256(sig1) * 659 | * * 660 | ********************************************************************************************/ 661 | const uint8_t sig1_len = proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1] + 2; 662 | uint8_t sig1[sig1_len]; 663 | std::memcpy(sig1, &proof.data()[ledgerProofLength + (32 + 8 + 1 + 32)], sig1_len); 664 | eosio::checksum256 sig1_hash = sha256((char *)sig1, sizeof(sig1)); 665 | if (!__provable_randomDS_matchBytes32Prefix(sig1_hash, result.data(), result.size(), proof[ledgerProofLength + 32 + 8])) 666 | return 3; 667 | 668 | 669 | /******************************************************************************************** 670 | * * 671 | * Step 4: commitment match verification, * 672 | * sha256(delay, nbytes, unonce, sessionKeyHash) == commitment in table. * 673 | * * 674 | ********************************************************************************************/ 675 | const uint8_t slice_offset = 8 + 1 + 32; // delay + nbytes + unonceHashBa 676 | uint8_t commitmentSlice1[slice_offset]; 677 | std::memcpy(commitmentSlice1, &proof.data()[ledgerProofLength + 32], sizeof(commitmentSlice1)); 678 | // Extract the session public key and calculate the session publick key hash 679 | uint8_t sessionPubKey[64]; 680 | const uint16_t sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1_len + 65; // ledgerProofLength+32+(8+1+32)+sig1.len+65 681 | std::memcpy(sessionPubKey, &proof.data()[sig2offset - 64], sizeof(sessionPubKey)); 682 | eosio::checksum256 sessionPubkeyHash = invert_checksum256(sha256((char *)sessionPubKey, 683 | sizeof(sessionPubKey))); // Calculate the key hash 684 | vector sessionPubkeyHashBa(32); // Convert to bytearray the public key hash 685 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 686 | // Recreate the lastCommitment to compare with the table one 687 | uint8_t tbh[slice_offset + 32]; 688 | std::memcpy(tbh, &commitmentSlice1, slice_offset); 689 | std::memcpy(tbh + slice_offset, &sessionPubkeyHashBa[0], 32); 690 | eosio::checksum256 lastCommitment = sha256((char *)tbh, sizeof(tbh)); 691 | // Retrieve the table commitment 692 | ds_scommitment last_commitments(payer, payer.value); 693 | name myQueryId_short; 694 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 695 | // Check if the key value exists 696 | auto itr = last_commitments.find(myQueryId_short.value); 697 | if (itr == last_commitments.end()) 698 | return 4; 699 | // Check the query id with the one in the table 700 | const std::string queryId_str__expected = checksum256_to_string(itr->queryid); 701 | const std::string queryId_str = checksum256_to_string(queryId); 702 | if (queryId_str != queryId_str__expected) 703 | return 4; 704 | // Check the commitment with the one in the table 705 | const std::string lastCommitment_str__expected = checksum256_to_string(itr->commitment); 706 | const std::string lastCommitment_str = checksum256_to_string(lastCommitment); 707 | if (lastCommitment_str != lastCommitment_str__expected) 708 | return 4; 709 | 710 | 711 | /******************************************************************************************** 712 | * * 713 | * Step 5: validity verification for sig1 (keyhash and args signed with the sessionKey) * 714 | * * 715 | ********************************************************************************************/ 716 | uint8_t toSign1[32 + 8 + 1 + 32]; 717 | std::memcpy(toSign1, &proof.data()[ledgerProofLength], sizeof(toSign1)); 718 | eosio::checksum256 toSign1_hash = sha256((char *)toSign1, sizeof(toSign1)); 719 | if (!__provable_randomDS_verifySig(toSign1_hash, sig1, sizeof(sig1), sessionPubKey)) 720 | return 5; 721 | 722 | 723 | /******************************************************************************************** 724 | * * 725 | * Step 6: verify the attestation signature, * 726 | * APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) * 727 | * * 728 | ********************************************************************************************/ 729 | uint8_t sig2[proof[sig2offset + 1] + 2]; 730 | std::memcpy(sig2, &proof.data()[sig2offset], sizeof(sig2)); 731 | uint8_t appkey_pubkey[64]; 732 | std::memcpy(appkey_pubkey, &proof.data()[3 + 1], sizeof(appkey_pubkey)); 733 | uint8_t toSign2[1 + 65 + 32]; 734 | toSign2[0] = 1; // role 735 | std::memcpy(toSign2 + 1, &proof.data()[sig2offset - 65], 65); 736 | std::memcpy(toSign2 + 65 + 1, CODE_HASH_RANDOMDS, 32); 737 | eosio::checksum256 toSign2_hash = sha256((char *)toSign2, sizeof(toSign2)); 738 | if (!__provable_randomDS_verifySig(toSign2_hash, sig2, sizeof(sig2), appkey_pubkey)) 739 | return 6; 740 | 741 | 742 | /******************************************************************************************** 743 | * * 744 | * Step 7: verify the APPKEY1 provenance (must be signed by Ledger) * 745 | * * 746 | ********************************************************************************************/ 747 | uint8_t toSign3[1 + 65]; 748 | toSign3[0] = 0xfe; 749 | std::memcpy(toSign3 + 1, &proof.data()[3], 65); 750 | uint8_t sig3[proof[3 + 65 + 1] + 2]; 751 | std::memcpy(sig3, &proof.data()[3 + 65], sizeof(sig3)); 752 | eosio::checksum256 toSign3_hash = sha256((char *)toSign3, sizeof(toSign3)); 753 | if (!__provable_randomDS_verifySig(toSign3_hash, sig3, sizeof(sig3), LEDGERKEY)) 754 | return 7; 755 | 756 | 757 | // Erase the commitment after the proof is verified 758 | auto itr2 = last_commitments.find(myQueryId_short.value); 759 | last_commitments.erase(itr2); 760 | return 0; 761 | } 762 | 763 | #endif 764 | -------------------------------------------------------------------------------- /urlrequests/urlrequests.cpp: -------------------------------------------------------------------------------- 1 | #include "provable/eos_api.hpp" 2 | 3 | class urlrequests : public eosio::contract 4 | { 5 | private: 6 | void request( 7 | const std::string _query, 8 | const std::string _method, 9 | const std::string _url, 10 | const std::string _kwargs 11 | ) 12 | { 13 | std::vector> args = { 14 | string_to_vector(_query), 15 | string_to_vector(_method), 16 | string_to_vector(_url), 17 | string_to_vector(_kwargs) 18 | }; 19 | std::vector myquery = provable_set_computation_args(args); 20 | provable_query("computation", myquery); 21 | } 22 | 23 | public: 24 | using contract::contract; 25 | 26 | urlrequests(eosio::name receiver, eosio::name code, datastream ds) : contract(receiver, code, ds) {} 27 | 28 | [[eosio::action]] 29 | void reqheadscust() 30 | { 31 | print("Sending query to Provable..."); 32 | request("json(QmdKK319Veha83h6AYgQqhx9YRsJ9MJE7y33oCXyZ4MqHE).headers", 33 | "GET", 34 | "http://httpbin.org/headers", 35 | "{'headers': {'content-type': 'json'}}" 36 | ); 37 | } 38 | 39 | [[eosio::action]] 40 | void reqbasauth() 41 | { 42 | request("QmdKK319Veha83h6AYgQqhx9YRsJ9MJE7y33oCXyZ4MqHE", 43 | "GET", 44 | "http://httpbin.org/basic-auth/myuser/secretpass", 45 | "{'auth': ('myuser','secretpass'), 'headers': {'content-type': 'json'}}" 46 | ); 47 | } 48 | 49 | [[eosio::action]] 50 | void reqpost() 51 | { 52 | request("QmdKK319Veha83h6AYgQqhx9YRsJ9MJE7y33oCXyZ4MqHE", 53 | "POST", 54 | "https://api.postcodes.io/postcodes", 55 | "{\"json\": {\"postcodes\" : [\"OX49 5NU\"]}}" 56 | ); 57 | } 58 | 59 | [[eosio::action]] 60 | void reqput() 61 | { 62 | request("QmdKK319Veha83h6AYgQqhx9YRsJ9MJE7y33oCXyZ4MqHE", 63 | "PUT", 64 | "http://httpbin.org/anything", 65 | "{'json' : {'testing':'it works'}}" 66 | ); 67 | } 68 | 69 | [[eosio::action]] 70 | void reqcookies() 71 | { 72 | request("QmdKK319Veha83h6AYgQqhx9YRsJ9MJE7y33oCXyZ4MqHE", 73 | "GET", 74 | "http://httpbin.org/cookies", 75 | "{'cookies' : {'thiscookie':'should be saved and visible :)'}}" 76 | ); 77 | } 78 | 79 | [[eosio::action]] 80 | void callback( 81 | const eosio::checksum256 queryId, 82 | const std::vector result, 83 | const std::vector proof 84 | ) 85 | { 86 | require_auth(provable_cbAddress()); 87 | const std::string result_str = vector_to_string(result); 88 | print("Response: ", result_str); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /wolframrand/README.md: -------------------------------------------------------------------------------- 1 | # Provable's Wolfram Alpha Example 2 | 3 | This repo is to demonstrate how you would work with the Provable **WolframAlpha** datasource. 4 | 5 | The `wolframrand.cpp` example allows you to *retrieve a random number from 1 to 6; and, if the number is 6 | not 6, the `callback` launches a new query*. 7 | 8 | ## :page_with_curl: *Instructions* 9 | 10 | **1)** Compile your contract (launch this command **inside** the folder `wolfram-alpha`): 11 | 12 | **`❍ eosio-cpp -abigen wolframrand.cpp -o wolframrand.wasm`** 13 | 14 | **2)** Unlock your wallet: 15 | 16 | **`❍ cleos wallet unlock -n --password `** 17 | 18 | **3)** Deploy your contract on EOS (launch this command **outside** the folder `wolfram-alpha`): 19 | 20 | **`❍ cleos set contract wolfram-alpha wolframrand.wasm wolframrand.abi -p @`** 21 | 22 | **4)** Call the `getrandomnum` action of the contract `wolframrand`: 23 | 24 | **`❍ cleos push action getrandomnum '[]' -p @`** 25 | 26 | ## :pen: Notes 27 | 28 | Provable replies to your `oraclize_query` by calling your `callback(...)` with the *random number between 1 and 6 result*. 29 | You can search for your transaction ID in one of the following links to verify it: 30 | 31 | * :mag_right::ledger: [Blocks.io](https://jungle.bloks.io/): A block explorer for the Jungle 2.0 testnet. 32 | 33 | * :palm_tree::lion::palm_tree: [Jungle 2.0](https://monitor.jungletestnet.io/#home): A transaction explorer is available by selecting *Get TX* on the Jungle 2.0 testnet website. 34 | 35 | ## :ambulance: Support 36 | 37 | ❍ If you have any issues, head on over to our [Gitter](https://gitter.im/provable/eos-api) channel 38 | to get timely support! 39 | 40 | ***Happy developing!*** 41 | 42 | -------------------------------------------------------------------------------- /wolframrand/provable/README.md: -------------------------------------------------------------------------------- 1 | # eos-api 2 | 3 | Please refer to [our API documentation](http://docs.oraclize.it/#eos) to know more about the Oraclize API on EOSIO. 4 | 5 | ## Oraclize `eos-api` 6 | 7 | Documentation: [docs.oraclize.it](http://docs.oraclize.it/#eos) 8 | 9 | Gitter public support channel: 10 | [![Join the chat at https://gitter.im/oraclize/eos-api](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/oraclize/eos-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 11 | 12 | [![HitCount](http://hits.dwyl.io/oraclize/eos-api.svg)](http://hits.dwyl.io/oraclize/eos-api) 13 | -------------------------------------------------------------------------------- /wolframrand/provable/eos_api.hpp: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * Provable API * 3 | * * 4 | * Copyright (c) 2015-2016 Provable SRL * 5 | * Copyright (c) 2016 Provable LTD * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy * 7 | * of this software and associated documentation files (the "Software"), to deal * 8 | * in the Software without restriction, including without limitation the rights * 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * 10 | * copies of the Software, and to permit persons to whom the Software is * 11 | * furnished to do so, subject to the following conditions: * 12 | * The above copyright notice and this permission notice shall be included in * 13 | * all copies or substantial portions of the Software. * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * 20 | * THE SOFTWARE. * 21 | *********************************************************************************/ 22 | 23 | #ifndef PROVABLEAPI_H 24 | #define PROVABLEAPI_H 25 | 26 | 27 | /************************************************** 28 | * INCLUDE * 29 | * Libraries * 30 | **************************************************/ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | /************************************************** 40 | * MACRO * 41 | * Definitions * 42 | **************************************************/ 43 | #ifndef PROVABLE_NETWORK_NAME 44 | #warning PROVABLE_NETWORK_NAME is not set, setting it to "eosio_unknown".. [possible values are "eosio_mainnet"/"eosio_testnet_jungle"/"eosio_testnet_kylin"/"eosio_unknown"] 45 | #define PROVABLE_NETWORK_NAME "eosio_unknown" 46 | #endif // PROVABLE_NETWORK_NAME 47 | 48 | #ifndef CONTRACT_NAME 49 | #warning CONTRACT_NAME is not set, setting it to "".. [write the "contract.name" not the account name] 50 | #define CONTRACT_NAME "unknown" 51 | #endif // CONTRACT_NAME 52 | 53 | #ifndef PROVABLE_PAYER 54 | #define PROVABLE_PAYER _self 55 | #endif // PROVABLE_PAYER 56 | 57 | #define provable_query(...) __provable_query(PROVABLE_PAYER, __VA_ARGS__, _self) 58 | #define provable_newRandomDSQuery(...) __provable_newRandomDSQuery(PROVABLE_PAYER, __VA_ARGS__, _self) 59 | #define provable_queryId_localEmplace(...) __provable_queryId_localEmplace(__VA_ARGS__, _self) 60 | #define provable_queryId_match(...) __provable_queryId_match(__VA_ARGS__, _self) 61 | 62 | 63 | /************************************************** 64 | * NAMESPACES * 65 | * Declarations * 66 | **************************************************/ 67 | using namespace eosio; 68 | using namespace std; 69 | 70 | 71 | /************************************************** 72 | * CONSTANTS * 73 | * Proof Types * 74 | **************************************************/ 75 | const uint8_t proofType_NONE = 0x00; 76 | const uint8_t proofType_TLSNotary = 0x10; 77 | const uint8_t proofType_Ledger = 0x30; 78 | const uint8_t proofType_Android = 0x40; 79 | const uint8_t proofType_Native = 0xF0; 80 | const uint8_t proofStorage_IPFS = 0x01; 81 | 82 | const uint8_t CODE_HASH_RANDOMDS[32] = { 83 | 253, 148, 250, 113, 188, 11, 161, 13, 57, 212, 100, 208, 216, 244, 101, 239, 238, 240, 162, 118, 78, 56, 135, 252, 201, 223, 65, 222, 210, 15, 80, 92 84 | }; 85 | const uint8_t LEDGERKEY[64] = { 86 | 127, 185, 86, 70, 156, 92, 155, 137, 132, 13, 85, 180, 53, 55, 230, 106, 152, 221, 72, 17, 234, 10, 39, 34, 66, 114, 194, 229, 98, 41, 17, 232, 83, 122, 47, 142, 134, 164, 107, 174, 200, 40, 100, 233, 141, 208, 30, 156, 204, 47, 139, 197, 223, 201, 203, 229, 169, 26, 41, 4, 152, 221, 150, 228 87 | }; 88 | 89 | 90 | /************************************************** 91 | * PROVABLE TABLE * 92 | * Definition * 93 | **************************************************/ 94 | struct [[eosio::table, eosio::contract("provableconn")]] snonce 95 | { 96 | name sender; 97 | uint32_t nonce; 98 | 99 | uint64_t primary_key() const { return sender.value; } 100 | }; 101 | 102 | struct [[eosio::table, eosio::contract("provableconn")]] cbaddr 103 | { 104 | name sender; 105 | 106 | uint64_t primary_key() const { return sender.value; } 107 | }; 108 | 109 | struct [[eosio::table, eosio::contract("provableconn")]] spubkey 110 | { 111 | name key; 112 | eosio::checksum256 randomDS_lastSessionPubkeyHash; 113 | 114 | uint64_t primary_key() const { return key.value; } 115 | eosio::checksum256 get_randomDS_lastSessionPubkeyHash() const { return randomDS_lastSessionPubkeyHash; } 116 | }; 117 | 118 | 119 | /************************************************** 120 | * PROVABLE TABLE * 121 | * Definition * 122 | **************************************************/ 123 | 124 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] scommitment 125 | { 126 | name shortqueryid; 127 | eosio::checksum256 queryid; 128 | eosio::checksum256 commitment; 129 | 130 | uint64_t primary_key() const { return shortqueryid.value; } 131 | }; 132 | 133 | struct [[eosio::table, eosio::contract(CONTRACT_NAME)]] queryid 134 | { 135 | name key; 136 | eosio::checksum256 qid; 137 | uint8_t active; 138 | 139 | uint64_t primary_key() const { return key.value; } 140 | }; 141 | 142 | typedef eosio::multi_index ds_snonce; 143 | typedef eosio::multi_index ds_cbaddr; 144 | typedef eosio::multi_index ds_spubkey; 145 | typedef eosio::multi_index ds_scommitment; 146 | typedef eosio::multi_index ds_queryid; 147 | 148 | 149 | /************************************************** 150 | * PUBLIC FUNCTIONS * 151 | * Implementation * 152 | **************************************************/ 153 | eosio::name provable_cbAddress() 154 | { 155 | ds_cbaddr cb_addrs("provableconn"_n, "provableconn"_n.value); // go to the connector table which identify the sender 156 | auto itr = cb_addrs.begin(); // point to the first element of the table 157 | const uint64_t cbaddr_uint64t = (itr != cb_addrs.end()) ? itr->sender.value : 0; 158 | eosio::name cbaddr_name = eosio::name(cbaddr_uint64t); 159 | return cbaddr_name; 160 | } 161 | 162 | std::string vector_uc_to_string(const std::vector v) 163 | { 164 | std::string v_str(v.begin(), v.end()); 165 | return v_str; 166 | } 167 | 168 | std::vector provable_set_computation_args( 169 | const std::vector> _args 170 | ) 171 | { 172 | // [args_number][first_arg_len][[first_arg][second_arg_len][[second_arg][...][...][last_arg_len][last_arg] 173 | const unsigned char args_size = _args.size(); 174 | std::vector query; 175 | // Prepare computation query 176 | query.push_back(args_size); // Max arguments allowed are 255 177 | for (int i = 0; i < args_size; i++) 178 | { 179 | query.push_back(_args[i].size()); // argument length 180 | query.insert(query.end(), _args[i].begin(), _args[i].end()); // argument 181 | } 182 | return query; 183 | } 184 | 185 | eosio::checksum256 invert_checksum256(const eosio::checksum256 _cs) 186 | { 187 | uint8_t *first_word = (uint8_t *) &_cs.get_array()[0]; 188 | uint8_t *second_word = (uint8_t *) &_cs.get_array()[1]; 189 | eosio::checksum256 cs_inverted; 190 | uint8_t *first_word_inverted = (uint8_t *) &cs_inverted.get_array()[0]; 191 | uint8_t *second_word_inverted = (uint8_t *) &cs_inverted.get_array()[1]; 192 | for (int i = 0; i < 16; i++) 193 | { 194 | first_word_inverted[15 - i] = first_word[i]; 195 | second_word_inverted[15 - i] = second_word[i]; 196 | } 197 | return cs_inverted; 198 | } 199 | 200 | 201 | bool checksum256_is_empty(const eosio::checksum256 cs) 202 | { 203 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 204 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 205 | for(int i = 0; i < 16; i++) 206 | if(first_word[i] != 0 && second_word[i] != 0) 207 | return false; 208 | return true; 209 | } 210 | 211 | std::vector string_to_vector(const std::string s) 212 | { 213 | std::vector s_v(s.begin(), s.end()); 214 | return s_v; 215 | } 216 | 217 | std::string vector_to_string(const std::vector v) 218 | { 219 | std::string v_str(v.begin(), v.end()); 220 | return v_str; 221 | } 222 | 223 | std::vector hexstring_to_vector32(std::string s) 224 | { 225 | std::vector v32; 226 | for(unsigned int i = 0; i < 32; i++) 227 | { 228 | unsigned int ui; 229 | sscanf(s.data() + (i * 2), "%02x", &ui); 230 | unsigned char uc = (unsigned char) ui; 231 | v32.push_back(uc); 232 | } 233 | return v32; 234 | } 235 | 236 | eosio::checksum256 hexstring_to_checksum256(const std::string hs) 237 | { 238 | std::vector hs_bytes = hexstring_to_vector32(hs); 239 | eosio::checksum256 cs; 240 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 241 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 242 | for (int i = 0; i < 16; i++) 243 | { 244 | first_word[i] = hs_bytes[i]; 245 | second_word[i] = hs_bytes[i + 16]; 246 | } 247 | return cs; 248 | } 249 | 250 | std::string checksum256_to_string(const eosio::checksum256 cs) 251 | { 252 | uint8_t *first_word = (uint8_t *) &cs.get_array()[0]; 253 | uint8_t *second_word = (uint8_t *) &cs.get_array()[1]; 254 | char hexstr[64]; 255 | for (int i = 0; i < 16; i++) 256 | sprintf(hexstr + i * 2, "%02x", first_word[i]); 257 | for (int i = 16; i < 32; i++) 258 | sprintf(hexstr + i * 2, "%02x", second_word[i - 16]); 259 | std::string c_str = std::string(hexstr); 260 | return c_str; 261 | } 262 | 263 | std::string chara_to_hexstring(uint8_t *input, const int size) 264 | { 265 | char hexstr[size * 2]; 266 | for (int i = 0; i < size; i++) 267 | sprintf(hexstr + i * 2, "%02x", input[i]); 268 | std::string c_str = std::string(hexstr); 269 | return c_str; 270 | } 271 | 272 | std::string vector_to_hexstring(std::vector *input) 273 | { 274 | int size = input->size(); 275 | char hexstr[size * 2]; 276 | for (int i = 0; i < size; i++) 277 | sprintf(hexstr + i * 2, "%02x", input->at(i)); 278 | std::string c_str = std::string(hexstr); 279 | return c_str; 280 | } 281 | 282 | std::vector uint32_to_vector8(uint32_t num) 283 | { 284 | std::vector ba(8); 285 | const uint32_t mask = 0xFF; 286 | for(int i = 0; i < 8; i++) 287 | { 288 | ba[i] = num & mask; 289 | num = num >> 8; 290 | } 291 | return ba; 292 | } 293 | 294 | std::vector uint32_to_vector32_bigendian(uint32_t num) 295 | { 296 | std::vector ba(32); 297 | const uint32_t mask = 0xFF; 298 | for(int i = 31; i > -1; i--) 299 | { 300 | ba[i] = num & mask; 301 | num = num >> 8; 302 | } 303 | return ba; 304 | } 305 | 306 | std::vector checksum256_to_vector32(const eosio::checksum256 cs) 307 | { 308 | uint8_t *first_word = (uint8_t *)&cs.get_array()[0]; 309 | uint8_t *second_word = (uint8_t *)&cs.get_array()[1]; 310 | std::vector ba(32); 311 | for (int i = 0; i < 16; i++) 312 | { 313 | ba[i] = first_word[i]; 314 | } 315 | for (int i = 16; i < 32; i++) 316 | { 317 | ba[i] = second_word[i - 16]; 318 | } 319 | return ba; 320 | } 321 | 322 | 323 | /************************************************** 324 | * INTERNAL FUNCTIONS * 325 | * Definitions * 326 | **************************************************/ 327 | eosio::checksum256 __provable_randomDS_getSessionPubkeyHash() 328 | { 329 | ds_spubkey spubkeys("provableconn"_n, "provableconn"_n.value); 330 | name index = "1"_n; // only one value in the table with key = 1 331 | auto itr = spubkeys.find(index.value); 332 | eosio::checksum256 sessionPubkeyHash; 333 | if (itr != spubkeys.end()) 334 | { 335 | sessionPubkeyHash = itr->get_randomDS_lastSessionPubkeyHash(); 336 | } 337 | return sessionPubkeyHash; 338 | } 339 | 340 | uint32_t __provable_getSenderNonce(name sender) 341 | { 342 | ds_snonce last_nonces("provableconn"_n, "provableconn"_n.value); 343 | auto itr = last_nonces.find(sender.value); 344 | uint32_t nonce = 0; 345 | if (itr != last_nonces.end()) 346 | { 347 | nonce = itr->nonce; 348 | } 349 | return nonce; 350 | } 351 | 352 | eosio::checksum256 __provable_getNextQueryId(const name sender) 353 | { 354 | const uint32_t nonce = __provable_getSenderNonce(sender); // get values to generate the queryId 355 | const size_t tx_size = transaction_size(); 356 | uint8_t tbh[sizeof(sender) + sizeof(nonce) + sizeof(tx_size)]; // calculate the hash of the previous values 357 | std::memcpy(tbh, &sender, sizeof(sender)); 358 | std::memcpy(tbh + sizeof(sender), &nonce, sizeof(nonce)); 359 | std::memcpy(tbh + sizeof(sender) + sizeof(nonce), &tx_size, sizeof(tx_size)); 360 | eosio::checksum256 calc_hash = sha256((char *)tbh, sizeof(tbh)); 361 | return calc_hash; 362 | } 363 | 364 | // Check that the queryId being passed matches with the one in the customer local table, return true/false accordingly 365 | bool __provable_queryId_match(const eosio::checksum256 queryId, const name sender) 366 | { 367 | name myQueryId_short; 368 | std::memcpy(&myQueryId_short, &queryId, sizeof(myQueryId_short)); 369 | // Access the local query table and find the right row 370 | ds_queryid queryids(sender, sender.value); 371 | auto itr = queryids.find(myQueryId_short.value); 372 | // Get the local queryId value 373 | eosio::checksum256 queryId_expected; 374 | std::memcpy(&queryId_expected, 0, sizeof(queryId_expected)); 375 | // Get the queryId from the table 376 | if (itr != queryids.end()) 377 | queryId_expected = itr->qid; 378 | else 379 | return false; 380 | // Check if the value retrieved is only 0 381 | if(checksum256_is_empty(queryId_expected)) 382 | return false; 383 | // Convert queryId and queryId_expected to string, to compare them 384 | const std::string queryId_str__expected = checksum256_to_string(queryId_expected); 385 | const std::string queryId_str = checksum256_to_string(queryId); 386 | // Compare the queryids and check if string values are empty 387 | if (queryId_str != queryId_str__expected || queryId_str.empty() || queryId_str__expected.empty()) 388 | return false; 389 | else 390 | return true; 391 | } 392 | 393 | void __provable_queryId_localEmplace(const eosio::checksum256 myQueryId, const name sender) 394 | { 395 | // Retreive the short queryId to use it as an index 396 | name myQueryId_short; 397 | std::memcpy(&myQueryId_short, &myQueryId, sizeof(myQueryId_short)); 398 | // Save the queryId in the local table 399 | ds_queryid queryids(sender, sender.value); 400 | queryids.emplace(sender, [&](auto& o) { 401 | o.key = myQueryId_short; 402 | o.qid = myQueryId; 403 | o.active = true; 404 | }); 405 | } 406 | 407 | 408 | /************************************************** 409 | * Provable Query * 410 | * Strings * 411 | **************************************************/ 412 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 413 | { 414 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 415 | action(permission_level{user, "active"_n}, 416 | "provableconn"_n, 417 | "querystr"_n, 418 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 419 | ).send(); 420 | return queryId; 421 | } 422 | 423 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const name sender) 424 | { 425 | return __provable_query(user, 0, datasource, query, 0, sender); 426 | } 427 | 428 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const std::string query, const name sender) 429 | { 430 | return __provable_query(user, timestamp, datasource, query, 0, sender); 431 | } 432 | 433 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const std::string query, const uint8_t prooftype, const name sender) 434 | { 435 | return __provable_query(user, 0, datasource, query, prooftype, sender); 436 | } 437 | 438 | 439 | /************************************************** 440 | * Provable Query * 441 | * Bytearrays * 442 | **************************************************/ 443 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 444 | { 445 | const eosio::checksum256 queryId = __provable_getNextQueryId(sender); 446 | printhex(query.data(), query.size()); 447 | auto n = name{user}; 448 | const std::string str = n.to_string(); 449 | action(permission_level{user, "active"_n}, 450 | "provableconn"_n, 451 | "queryba"_n, 452 | std::make_tuple(sender, (int8_t)1, (uint32_t)timestamp, queryId, datasource, query, prooftype) 453 | ).send(); 454 | return queryId; 455 | } 456 | 457 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const name sender) 458 | { 459 | return __provable_query(user, 0, datasource, query, 0, sender); 460 | } 461 | 462 | eosio::checksum256 __provable_query(const name user, const unsigned int timestamp, const std::string datasource, const vector query, const name sender) 463 | { 464 | return __provable_query(user, timestamp, datasource, query, 0, sender); 465 | } 466 | 467 | eosio::checksum256 __provable_query(const name user, const std::string datasource, const vector query, const uint8_t prooftype, const name sender) 468 | { 469 | return __provable_query(user, 0, datasource, query, prooftype, sender); 470 | } 471 | 472 | 473 | /************************************************** 474 | * Provable Query * 475 | * Random DS * 476 | **************************************************/ 477 | void __provable_randomDS_setCommitment(const eosio::checksum256 queryId, const eosio::checksum256 commitment, const name payer) 478 | { 479 | name myQueryId_short; // Calculate the short queryId, to use it as a key of the table 480 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 481 | ds_scommitment last_commitments(payer, payer.value); // Set the commitment in the eos table of the caller 482 | last_commitments.emplace(payer, [&](auto &o) { // The key must be a uint64_t so a short query Id is used 483 | o.shortqueryid = myQueryId_short; 484 | o.queryid = queryId; 485 | o.commitment = commitment; 486 | }); 487 | } 488 | 489 | eosio::checksum256 __provable_newRandomDSQuery(const name user, const uint32_t _delay, const uint8_t _nbytes, const name sender) 490 | { 491 | // 1. NBYTES - Convert nbytes to bytearray 492 | std::vector nbytesBa(1); 493 | nbytesBa[0] = _nbytes; 494 | 495 | // 2. SESSIONKEYHASH - Get the sessionKeyHash from the ledger public key. 496 | const eosio::checksum256 sessionPubkeyHash = __provable_randomDS_getSessionPubkeyHash(); 497 | std::vector sessionPubkeyHashBa(32); 498 | std::vector sessionPubkeyHashBa2(32); 499 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 500 | 501 | // 3. UNONCE - Need something block dependent so we decided to perform the hash of those 4 block dependent fields. This value have to be unpredictable from Provable 502 | const size_t tx_size = transaction_size(); 503 | const int tapos_block_num_ = tapos_block_num(); 504 | const int tapos_block_prefix_ = tapos_block_prefix(); 505 | uint8_t unonce[sizeof(tx_size) + sizeof(tapos_block_num_) + sizeof(tapos_block_prefix_)]; // Fill the unonce array: now() + transaction_size + tapos_block_num + tapos_block_prefix 506 | std::memcpy(unonce, &tx_size, sizeof(tx_size)); 507 | std::memcpy(unonce + sizeof(tx_size), &tapos_block_num_, sizeof(tapos_block_num_)); 508 | std::memcpy(unonce + sizeof(tx_size) + sizeof(tapos_block_num_), &tapos_block_prefix_, sizeof(tapos_block_prefix_)); 509 | eosio::checksum256 unonceHash = sha256((char *)unonce, sizeof(unonce)); // Container for the unonce hash 510 | std::vector unonceHashBa(32); // Convert the unonce hash in bytearray 511 | unonceHashBa = checksum256_to_vector32(unonceHash); 512 | 513 | // 4. DELAY - Delay converted in a big endian bytearray 514 | const uint32_t delayLedgerTime = _delay * 10; // Convert from seconds to ledger timer ticks 515 | std::vector delayBaBigEndian(32); 516 | delayBaBigEndian = uint32_to_vector32_bigendian(delayLedgerTime); 517 | 518 | // Set args2 to be passed as params of the provable "random" query 519 | std::vector> args; 520 | args.push_back(unonceHashBa); 521 | args.push_back(nbytesBa); 522 | args.push_back(sessionPubkeyHashBa); 523 | args.push_back(delayBaBigEndian); 524 | std::vector args2; 525 | for(auto && a : args) 526 | args2.insert(args2.end(), a.begin(), a.end()); 527 | 528 | // Call the provable_query and get the queryId 529 | const eosio::checksum256 queryId = __provable_query(user,"random", args2, proofType_Ledger, sender); // proofType and datasource are always fixed in this function 530 | 531 | // Calculate the commitment and call a function to set it 532 | std::vector delayBa(8); // delay converted to 8 byte 533 | delayBa = uint32_to_vector8(delayLedgerTime); 534 | uint8_t* charArray = &unonceHashBa[0]; 535 | eosio::checksum256 unonceHashBaHash = invert_checksum256(sha256((char *) charArray, unonceHashBa.size())); // unonce has to be passed hashed 536 | uint8_t commitmentTbh[8 + 1 + 32 + args[2].size()]; // Calculate the commitment to be hashed with the size of: 8 + 1 + 32 + 32 537 | std::memcpy(commitmentTbh, &delayBa[0], 8); // 8 538 | std::memcpy(commitmentTbh + 8, &args[1][0], 1); // 8 + 1 539 | std::memcpy(commitmentTbh + 8 + 1, &unonceHashBaHash.get_array()[0], 16); // 8 + 1 + 16 540 | std::memcpy(commitmentTbh + 8 + 1 + 16, &unonceHashBaHash.get_array()[1], 16); // 8 + 1 + 32 == commitmentSlice1 541 | std::memcpy(commitmentTbh + delayBa.size() + args[1].size() + 32, &args[2][0], args[2].size()); // 8 + 1 + 32 + 32 (commitmentSlice1 + sessionPubkeyHashBa) 542 | eosio::checksum256 commitment = sha256((char *)commitmentTbh, sizeof(commitmentTbh)); // Container for the commitment hash 543 | const name payer = user; // Payer for setting the commitment 544 | __provable_randomDS_setCommitment(queryId, commitment, payer); // Call the function to set query Id and commitment in the table 545 | 546 | return queryId; 547 | } 548 | 549 | void __provable_randomDS_get_signature_component(uint8_t component[32], const uint8_t signature[], const uint8_t signature_len, const uint8_t length_idx) 550 | { 551 | eosio::internal_use_do_not_use::eosio_assert(signature_len > length_idx, "Invalid index"); 552 | uint8_t component_len = signature[length_idx]; 553 | uint8_t byte_to_jump = component_len % 32; 554 | std::memcpy(component, &signature[length_idx + 1 + byte_to_jump], component_len - byte_to_jump); 555 | } 556 | 557 | bool __provable_randomDS_matchBytes32Prefix(const eosio::checksum256 content, const uint8_t prefix[], const uint8_t prefix_len, const uint8_t n_random_bytes) 558 | { 559 | eosio::internal_use_do_not_use::eosio_assert(prefix_len == n_random_bytes, "Prefix length and random bytes number should match."); 560 | const eosio::checksum256 content_inverted = invert_checksum256(content); 561 | uint8_t *first_word = (uint8_t *)&content_inverted.get_array()[0]; 562 | uint8_t *second_word = (uint8_t *)&content_inverted.get_array()[1]; 563 | for (int i = 0; i < n_random_bytes; ++i) 564 | { 565 | if (i < 16) 566 | { 567 | if (first_word[i] != prefix[i]) 568 | return false; 569 | } 570 | else 571 | { 572 | if (second_word[i] != prefix[i]) 573 | return false; 574 | } 575 | } 576 | return true; 577 | } 578 | 579 | bool __provable_randomDS_test_pubkey_signature(const uint8_t whatever, const uint8_t v, const uint8_t r[32], const uint8_t s[32], const eosio::checksum256 digest, const uint8_t pubkey[64]) 580 | { 581 | eosio::webauthn_signature sig; 582 | sig.auth_data[0] = v; 583 | for (int i = 0; i < 32; i++) 584 | sig.auth_data[i + 1] = r[i]; 585 | for (int i = 0; i < 32; i++) 586 | sig.auth_data[i + 1 + 32] = s[i]; 587 | 588 | const eosio::webauthn_public_key pubkey_recovered = get(recover_key(digest, sig)); 589 | 590 | if (pubkey_recovered.key.size() != 33) 591 | return false; 592 | if (pubkey_recovered.key[0] != 0x02 && pubkey_recovered.key[0] != 0x03) 593 | return false; 594 | // Discard the first (0x00) and the second byte (0x02 or 0x03) 595 | for (int i = 0; i < 32; i++) 596 | if ((uint8_t)pubkey_recovered.key[i + 1] != pubkey[i]) 597 | return false; 598 | return true; 599 | } 600 | 601 | bool __provable_randomDS_verifySig(const eosio::checksum256 digest, const uint8_t der_signature[], const uint8_t der_signature_len, const uint8_t pubkey[64]) 602 | { 603 | uint8_t r[32]; 604 | uint8_t s[32]; 605 | __provable_randomDS_get_signature_component(r, der_signature, der_signature_len, 3); 606 | __provable_randomDS_get_signature_component(s, der_signature, der_signature_len, 4 + der_signature[3] + 1); 607 | // We try either with v=27 or with v=28 608 | bool test_v27 = __provable_randomDS_test_pubkey_signature(0, 27, r, s, digest, pubkey); 609 | bool test_v28 = __provable_randomDS_test_pubkey_signature(0, 28, r, s, digest, pubkey); 610 | return test_v27 || test_v28; 611 | } 612 | 613 | uint8_t provable_randomDS_proofVerify(const eosio::checksum256 queryId, const std::vector result, const std::vector proof, const name payer) 614 | { 615 | /******************************************************************************************* 616 | * * 617 | * Step 1: the prefix has to match 'LP\x01' (Ledger Proof version 1) * 618 | * * 619 | *******************************************************************************************/ 620 | if (proof[0] != 'L' || proof[1] != 'P' || proof[2] != 1) 621 | return 1; 622 | 623 | 624 | /******************************************************************************************** 625 | * * 626 | * Step 2: the unique keyhash has to match with the sha256 of (context name + queryId) * 627 | * * 628 | ********************************************************************************************/ 629 | const uint8_t ledgerProofLength = 3 + 65 + (proof[3 + 65 + 1] + 2) + 32; 630 | uint8_t keyhash[32]; 631 | std::memcpy(keyhash, &proof.data()[ledgerProofLength], 32); 632 | eosio::checksum256 keyhash_sha = sha256((char *)keyhash, 32); 633 | std::string keyhash_sha_str = checksum256_to_string(keyhash_sha); 634 | uint8_t *first_word = (uint8_t *) &keyhash_sha.get_array()[0]; 635 | uint8_t *second_word = (uint8_t *) &keyhash_sha.get_array()[1]; 636 | std::string context_name_str = PROVABLE_NETWORK_NAME; 637 | char context_name[context_name_str.size()]; 638 | context_name_str.copy(context_name, context_name_str.size()); 639 | uint8_t tbh2[sizeof(context_name) + 32]; 640 | std::memcpy(tbh2, &context_name, sizeof(context_name)); 641 | std::memcpy(tbh2 + sizeof(context_name), &queryId.get_array()[0], 16); 642 | std::memcpy(tbh2 + sizeof(context_name) + 16, &queryId.get_array()[1], 16); 643 | eosio::checksum256 calc_hash = invert_checksum256(sha256((char *)tbh2, sizeof(context_name) + 644 | 16 + 16)); 645 | uint8_t tbh3[32]; 646 | std::memcpy(tbh3, &calc_hash.get_array()[0], 16); 647 | std::memcpy(tbh3 + 16, &calc_hash.get_array()[1], 16); 648 | eosio::checksum256 calc_hash_2 = sha256((char *)tbh3, 16 + 16); 649 | uint8_t *first_word3 = (uint8_t *) &calc_hash_2.get_array()[0]; 650 | uint8_t *second_word3 = (uint8_t *) &calc_hash_2.get_array()[1]; 651 | if(checksum256_to_string(keyhash_sha) != checksum256_to_string(calc_hash_2)) 652 | return 2; 653 | 654 | 655 | /******************************************************************************************** 656 | * * 657 | * Step 3: we assume sig1 is valid (it will be verified during step 5) * 658 | * and we verify if 'result' is the prefix of sha256(sig1) * 659 | * * 660 | ********************************************************************************************/ 661 | const uint8_t sig1_len = proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1] + 2; 662 | uint8_t sig1[sig1_len]; 663 | std::memcpy(sig1, &proof.data()[ledgerProofLength + (32 + 8 + 1 + 32)], sig1_len); 664 | eosio::checksum256 sig1_hash = sha256((char *)sig1, sizeof(sig1)); 665 | if (!__provable_randomDS_matchBytes32Prefix(sig1_hash, result.data(), result.size(), proof[ledgerProofLength + 32 + 8])) 666 | return 3; 667 | 668 | 669 | /******************************************************************************************** 670 | * * 671 | * Step 4: commitment match verification, * 672 | * sha256(delay, nbytes, unonce, sessionKeyHash) == commitment in table. * 673 | * * 674 | ********************************************************************************************/ 675 | const uint8_t slice_offset = 8 + 1 + 32; // delay + nbytes + unonceHashBa 676 | uint8_t commitmentSlice1[slice_offset]; 677 | std::memcpy(commitmentSlice1, &proof.data()[ledgerProofLength + 32], sizeof(commitmentSlice1)); 678 | // Extract the session public key and calculate the session publick key hash 679 | uint8_t sessionPubKey[64]; 680 | const uint16_t sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1_len + 65; // ledgerProofLength+32+(8+1+32)+sig1.len+65 681 | std::memcpy(sessionPubKey, &proof.data()[sig2offset - 64], sizeof(sessionPubKey)); 682 | eosio::checksum256 sessionPubkeyHash = invert_checksum256(sha256((char *)sessionPubKey, 683 | sizeof(sessionPubKey))); // Calculate the key hash 684 | vector sessionPubkeyHashBa(32); // Convert to bytearray the public key hash 685 | sessionPubkeyHashBa = checksum256_to_vector32(sessionPubkeyHash); 686 | // Recreate the lastCommitment to compare with the table one 687 | uint8_t tbh[slice_offset + 32]; 688 | std::memcpy(tbh, &commitmentSlice1, slice_offset); 689 | std::memcpy(tbh + slice_offset, &sessionPubkeyHashBa[0], 32); 690 | eosio::checksum256 lastCommitment = sha256((char *)tbh, sizeof(tbh)); 691 | // Retrieve the table commitment 692 | ds_scommitment last_commitments(payer, payer.value); 693 | name myQueryId_short; 694 | std::memcpy(&myQueryId_short, &queryId.get_array()[0], sizeof(myQueryId_short)); 695 | // Check if the key value exists 696 | auto itr = last_commitments.find(myQueryId_short.value); 697 | if (itr == last_commitments.end()) 698 | return 4; 699 | // Check the query id with the one in the table 700 | const std::string queryId_str__expected = checksum256_to_string(itr->queryid); 701 | const std::string queryId_str = checksum256_to_string(queryId); 702 | if (queryId_str != queryId_str__expected) 703 | return 4; 704 | // Check the commitment with the one in the table 705 | const std::string lastCommitment_str__expected = checksum256_to_string(itr->commitment); 706 | const std::string lastCommitment_str = checksum256_to_string(lastCommitment); 707 | if (lastCommitment_str != lastCommitment_str__expected) 708 | return 4; 709 | 710 | 711 | /******************************************************************************************** 712 | * * 713 | * Step 5: validity verification for sig1 (keyhash and args signed with the sessionKey) * 714 | * * 715 | ********************************************************************************************/ 716 | uint8_t toSign1[32 + 8 + 1 + 32]; 717 | std::memcpy(toSign1, &proof.data()[ledgerProofLength], sizeof(toSign1)); 718 | eosio::checksum256 toSign1_hash = sha256((char *)toSign1, sizeof(toSign1)); 719 | if (!__provable_randomDS_verifySig(toSign1_hash, sig1, sizeof(sig1), sessionPubKey)) 720 | return 5; 721 | 722 | 723 | /******************************************************************************************** 724 | * * 725 | * Step 6: verify the attestation signature, * 726 | * APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) * 727 | * * 728 | ********************************************************************************************/ 729 | uint8_t sig2[proof[sig2offset + 1] + 2]; 730 | std::memcpy(sig2, &proof.data()[sig2offset], sizeof(sig2)); 731 | uint8_t appkey_pubkey[64]; 732 | std::memcpy(appkey_pubkey, &proof.data()[3 + 1], sizeof(appkey_pubkey)); 733 | uint8_t toSign2[1 + 65 + 32]; 734 | toSign2[0] = 1; // role 735 | std::memcpy(toSign2 + 1, &proof.data()[sig2offset - 65], 65); 736 | std::memcpy(toSign2 + 65 + 1, CODE_HASH_RANDOMDS, 32); 737 | eosio::checksum256 toSign2_hash = sha256((char *)toSign2, sizeof(toSign2)); 738 | if (!__provable_randomDS_verifySig(toSign2_hash, sig2, sizeof(sig2), appkey_pubkey)) 739 | return 6; 740 | 741 | 742 | /******************************************************************************************** 743 | * * 744 | * Step 7: verify the APPKEY1 provenance (must be signed by Ledger) * 745 | * * 746 | ********************************************************************************************/ 747 | uint8_t toSign3[1 + 65]; 748 | toSign3[0] = 0xfe; 749 | std::memcpy(toSign3 + 1, &proof.data()[3], 65); 750 | uint8_t sig3[proof[3 + 65 + 1] + 2]; 751 | std::memcpy(sig3, &proof.data()[3 + 65], sizeof(sig3)); 752 | eosio::checksum256 toSign3_hash = sha256((char *)toSign3, sizeof(toSign3)); 753 | if (!__provable_randomDS_verifySig(toSign3_hash, sig3, sizeof(sig3), LEDGERKEY)) 754 | return 7; 755 | 756 | 757 | // Erase the commitment after the proof is verified 758 | auto itr2 = last_commitments.find(myQueryId_short.value); 759 | last_commitments.erase(itr2); 760 | return 0; 761 | } 762 | 763 | #endif 764 | -------------------------------------------------------------------------------- /wolframrand/wolframrand.cpp: -------------------------------------------------------------------------------- 1 | #include "provable/eos_api.hpp" 2 | 3 | class wolframrand : public eosio::contract 4 | { 5 | public: 6 | using contract::contract; 7 | 8 | wolframrand(name receiver, name code, datastream ds) : contract(receiver, code, ds) {} 9 | 10 | [[eosio::action]] 11 | void getrandomnum() 12 | { 13 | provable_query(10, "WolframAlpha", "random number between 1 and 6"); 14 | print(" Provable query was sent, standing by for the answer..."); 15 | } 16 | 17 | [[eosio::action]] 18 | void callback( 19 | const eosio::checksum256 queryId, 20 | const std::vector result, 21 | const std::vector proof 22 | ) 23 | { 24 | require_auth(provable_cbAddress()); 25 | const std::string result_str = vector_to_string(result); 26 | print("Result: ", result_str); 27 | if (result_str != "6") 28 | getrandomnum(); 29 | } 30 | }; 31 | --------------------------------------------------------------------------------