/*** * This example of ESP32 (Pycom LoPy4) firmware was developed by Javier Arcenegui at "Instituto de * Microelectrónica de Sevilla IMSE-CNM (Universidad de Sevilla-CSIC)" for the EIP4519. This firmware makes the device generate its own Ethereum account and save in the EEPROM the data required in the future to regenerate * the same account. In the newest versions of ESP32 ("ESP32 S2" or * "ESP32 S3") these data can be saved in a protected area. * * The Smart Contract is published on "0x6Dba58fF5AA2d8447C4460d2527033A81646Ae97". * It was proved in the Ethereum Kovan testnet. * * This code was developed with the infuraIO extension for Visual Studio Code and the Arduino Framework. * The alphawallet/Web3E@^1.22 library must be added in the configuration file (platformio.ini) * * Helper data are employed to regenerate the same Ethereum account based on CTR-DRBG PRNG. * The helper data format is the following: * * ------------------------- Helper data Format ----------------------- * |---------------|----------------|----------------|----|----|----| * | key_ctr |Personalization | contex_counter | rc | el | ri | * |---------------|----------------|----------------|----|----|----| * 51 35 19 3 2 1 0 (Byte) * ***/ #include #include #include #include #include "mbedtls/ctr_drbg.h" #include "mbedtls/entropy.h" #include #include #include "esp_wifi.h" #include #include #include #include #include "Trezor/secp256k1.h" #include "Trezor/ecdsa.h" #include "Trezor/sha3.h" #define EEPROM_SIZE 52 //The size required by the 51 bytes of helper data and the byte to know if the token is registered on the blockchain #define EIP4519CONTRACT "0x6Dba58fF5AA2d8447C4460d2527033A81646Ae97" //This is the reference for the EIP4519 Smart Non-Fungible Token on Kovan (IT MUST BE CHANGED) //These are the states of the EIP4519 enum STATES{ waitingForOwner = 0, engagedWithOwner = 1, waitingForUser = 2, engagedWithUser = 3 }; void setupWifi(); //This function is the same as the wifi example of the ESP32 Arduino Wifi uint8_t HexTo4Bits(uint8_t Hex); //This function is used to change an hexadecimal defined in ASCII character to a uint8_t STATES queryTokenStatus(); //This function is used to recover the information of the SmartNFT associated to this device void hex2Char(unsigned char *inHex, unsigned char *charH, unsigned char *charL); //This function is needed to convert an uint8_t to two ASCII characters void sendEngage(); //This function sends the hash of the shared key to complete the transaction in Ethereum //Declaration of the variable to save the state of the SmartNFT STATES tokenState; //Declaration of the variables for the private/public Keys and the Address associated unsigned char privateKey_ethAccount[32]; unsigned char publicKey_ethAccount[64]; unsigned char ethAddress[20]; //Declaration of the variable to save the helper data generated/recovered, which are employed to obtain the private Key of Ethereum account uint8_t helperData[51]; //These variables are used to save the addresses in ASCII associated to the SmartNFT and the SmartNFT ID unsigned char OwnerAddress[42]; unsigned char UserAddress[42]; unsigned char deviceAddress[43]; uint32_t tokenID; //Wifi setup parameters: IT MUST BE CHANGED int wificounter = 0; const char *ssid = ""; const char *password = "= 10) { printf("Restarting ...\n"); ESP.restart(); //Targeting 8266 & ESP32. You may need to replace this } delay(10); printf("\n"); printf("WiFi connected.\n"); printf("IP address: %d.%d.%d.%d\n",WiFi.localIP()[0],WiFi.localIP()[1],WiFi.localIP()[2],WiFi.localIP()[3]); } STATES queryTokenStatus(){ Contract contract(&web3, EIP4519CONTRACT); //Generate the JSON to check the status deviceAddress[0] = '0'; deviceAddress[1] = 'x'; for(int i = 0 ; i < 20 ; i++){ hex2Char(ðAddress[i], &deviceAddress[2*i+2], &deviceAddress[2*i+3]); } String tokenAddress; printf("Query Status from token : "); for(int i = 0 ; i < 40 ; i++){ printf("%c",deviceAddress[i+2]); tokenDevice[i+2] = deviceAddress[i+2]; } printf("\n"); //Call the function to get SmartNFT information String func = "getInfoTokenFromBCA(address)"; string param = contract.SetupContractData(func.c_str(), &tokenDevice); string result = contract.ViewCall(¶m); //When data from blockchain are received, the SmartNFT information is saved printf("%s\n",result.c_str()); int i = 0; while(result[i] != 'x'){ i++; } i++; OwnerAddress[0] = UserAddress[0] = '0'; OwnerAddress[1] = UserAddress[1] = 'x'; //Owner address for(int j =24;j<64;j++){ OwnerAddress[j-22] = result[i+j]; } i +=64; //User address for(int j =24;j<64;j++){ UserAddress[j-22] = result[i+j]; } printf("\nOwner of token : 0x"); for(int j = 0 ; j < 40 ; j++){ printf("%c",OwnerAddress[j+2]); } printf("\nUser of token : 0x"); for(int j = 0 ; j < 40 ; j++){ printf("%c",UserAddress[j+2]); } i +=64; //tokenID tokenID = HexTo4Bits(result[i+56])*268435456+HexTo4Bits(result[i+57])*16777216+HexTo4Bits(result[i+58])*1048576+HexTo4Bits(result[i+59])*65536+HexTo4Bits(result[i+60])*4096+HexTo4Bits(result[i+61])*256+HexTo4Bits(result[i+62])*16+HexTo4Bits(result[i+63]); printf("\nToken ID = %d\n",tokenID); //SmartNFT state switch ((result[i+63]) { case '1': return engagedWithOwner; break; case '2': return waitingForUser; break; case '3': return engagedWithUser; break; default: return waitingForOwner break; } } uint8_t HexTo4Bits(uint8_t Hex){ if (Hex>='0'&&Hex<='9'){ return (Hex-'0'); }else if(Hex>='a'&&Hex<='f'){ return Hex-'a'+10; }else if(Hex>='A'&&Hex<='F'){ return Hex-'A'+10; } } void hex2Char(unsigned char *inHex, unsigned char *charH, unsigned char *charL){ uint8_t charIN[2]; charIN[0] = (inHex[0] / 16); charIN[1] = (inHex[0] % 16); if (charIN[0] < 10){ charH[0] = charIN[0] + '0'; }else{ charH[0] = charIN[0] + 'a' - 10; } if (charIN[1] < 10){ charL[0] = charIN[1] + '0'; }else{ charL[0] = charIN[1] + 'a' - 10; } } void sendEngage(){ Contract contract(&web3, EIP4519CONTRACT); //Define the function to call the blockchain. This function depends on the SmartNFT state string func; if(tokenState == waitingForOwner){ func = "ownerEngagement(uint256)"; }else if(tokenState == waitingForUser){ func = "userEngagement(uint256)"; } //Generate the hash of the shared key keccak_256(K_XD,32,hash_K_XD); //Put this hash as a uint256 variable uint256_t hash_K_XD_uin256 = 0; for(int i =0 ; i < 32 ; i++){ hash_K_XD_uin256 += (uint256_t)hash_K_XD[i]<<8*(7-i); } //Call the function string param = contract.SetupContractData(func.c_str(), &hash_K_XD_uin256); string result = contract.Call(¶m); }