Skip to content

Commit

Permalink
feat: accountManager
Browse files Browse the repository at this point in the history
  • Loading branch information
as-iotex committed Aug 20, 2022
1 parent cc6ebd7 commit c4ab8ae
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 1 deletion.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ add_library(iotex-client STATIC
src/random/os/random.cpp
src/random/platformRand32.cpp
src/storage/os/storage.cpp
src/account/accountManager.cpp
src/account/account.cpp
src/account/address.cpp
src/abi/abi.cpp
Expand Down
67 changes: 67 additions & 0 deletions examples/AccountManager/AccountManager.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @file AccountManager.ino
* @brief Demonstrates the usage of the AccountManager module.
*
*/

#include <Arduino.h>

#ifdef ESP32
#include <WiFi.h>
#endif
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#endif
#ifdef __SAMD21G18A__
#include <WiFiNINA.h>
#endif

#include <map>
#include "IoTeX-blockchain-client.h"

void setup() {
Serial.begin(115200);
#if defined(__SAMD21G18A__)
delay(5000); // Delay for 5000 seconds to allow a serial connection to be established
#endif
Serial.println("Starting");

// First erase all accounts in the NVM, to ensure that we start from a clean state.
for(int i=0; i<IOTEX_ACCOUNT_MANAGER_MAX_ACCOUNTS; i++)
{
accountManager.DeleteAccount(i);
}

// Create an account from a random private key.
AccountId id = accountManager.CreateNewAccount();
Serial.print("Created account with id ");
Serial.println(id);

// Get the account object.
Account account = accountManager.GetAccount(id);

// Delete the account.
accountManager.DeleteAccount(id);
Serial.print("Deleted account with id ");
Serial.println(id);

// Create an account from a private key.
uint8_t pkBytes[IOTEX_PRIVATE_KEY_SIZE];
randomGenerator.fillRandom(pkBytes, sizeof(pkBytes));
id = accountManager.CreateAccountFromPrivateKey(pkBytes);
Serial.print("Created account with id ");
Serial.println(id);

// Delete the account to get back to a clean state.
accountManager.DeleteAccount(id);
Serial.print("Deleted account with id ");
Serial.println(id);

Serial.println("Program finished");
}

void loop()
{
}
84 changes: 84 additions & 0 deletions examples/AccountManager/platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html

[platformio]
src_dir= .

[env]
src_filter =
lib_ldf_mode = deep+


; ------------------------------------------------------------------------------------------------------------------------------------------------------------
; ESP8266
; ------------------------------------------------------------------------------------------------------------------------------------------------------------

[env:esp8266]
platform = espressif8266
board = esp_wroom_02
framework = arduino
upload_speed = 921600
monitor_speed = 115200
lib_deps = IoTeX-blockchain-client


; ------------------------------------------------------------------------------------------------------------------------------------------------------------
; ESP32
; ------------------------------------------------------------------------------------------------------------------------------------------------------------

[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino
upload_speed = 921600
monitor_speed = 115200
lib_deps = IoTeX-blockchain-client

[env:esp32_debug]
platform = espressif32
board = esp32dev
framework = arduino
upload_speed = 921600
monitor_speed = 115200
build_type = debug
debug_tool = esp-prog
debug_init_break = tbreak setup
upload_protocol = esp-prog
lib_deps = IoTeX-blockchain-client

; ------------------------------------------------------------------------------------------------------------------------------------------------------------
; Nano 33 IoT
; ------------------------------------------------------------------------------------------------------------------------------------------------------------

[env:nano_33_iot]
platform = atmelsam
board = nano_33_iot
framework = arduino
monitor_speed = 115200
build_flags = -D __SAMD21G18A__
lib_deps =
IoTeX-blockchain-client
WiFiNINA
FlashStorage

[env:nano_33_iot_debug]
platform = atmelsam
board = nano_33_iot
build_type = debug
framework = arduino
upload_protocol = jlink
debug_tool = jlink
monitor_speed = 115200
debug_init_break = tbreak loop
build_flags = -D __SAMD21G18A__
lib_deps =
IoTeX-blockchain-client
WiFiNINA
FlashStorage
1 change: 1 addition & 0 deletions src/IoTeX-blockchain-client.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "abi/abiDecode.h"
#include "account/account.h"
#include "account/accountManager.h"
#include "account/address.h"
#include "api/wallet/wallets.h"
#include "connection/connection.h"
Expand Down
111 changes: 111 additions & 0 deletions src/account/accountManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Support for Linux will be added in the future
#ifdef ARDUINO

#include "account/accountManager.h"
#include "random/random.h"
#include "storage/storage.h"

using namespace iotex;
using namespace std;

static const auto& logModule = logModuleNamesLookupTable[LogModules::ACCOUNT];

namespace
{
/**
* @brief Checks if the private key is a valid private key or empty storage.
* If the NVM is empty or uninitialized, the value is either 0xFF or 0x00.
*/
bool isPrivateKeyValid(uint8_t pK[IOTEX_PRIVATE_KEY_SIZE])
{
uint8_t privateKeyAll0[IOTEX_PRIVATE_KEY_SIZE];
uint8_t privateKeyAll1[IOTEX_PRIVATE_KEY_SIZE];
memset(privateKeyAll0, 0x00, IOTEX_PRIVATE_KEY_SIZE);
memset(privateKeyAll1, 0xFF, IOTEX_PRIVATE_KEY_SIZE);

if
(
!memcmp(pK, privateKeyAll0, IOTEX_PRIVATE_KEY_SIZE) ||
!memcmp(pK, privateKeyAll1, IOTEX_PRIVATE_KEY_SIZE)
)
{
return false;
}
else return true;
}
}

AccountManager& iotex::accountManager = AccountManager::getInstance();

AccountManager::AccountManager(uint8_t maxAccounts) :
maxAccounts(maxAccounts)
{
storage.Initialize(maxAccounts * IOTEX_PRIVATE_KEY_SIZE);
availableIds.reserve(maxAccounts);
for(uint8_t i=0; i<maxAccounts; i++)
{
availableIds.push_back(i);
}

// Load all existent private keys from storage.
for(int i=0; i<maxAccounts; i++)
{
uint8_t privateKey[IOTEX_PRIVATE_KEY_SIZE];
uint32_t eepromAddress = i*IOTEX_PRIVATE_KEY_SIZE;
storage.readPrivateKey(eepromAddress, privateKey);
if (isPrivateKeyValid(privateKey))
{
Account account(privateKey);
accounts.insert(std::pair<AccountId, Account>(availableIds.back(), account));
availableIds.pop_back();
}
}
}

AccountId AccountManager::CreateNewAccount()
{
if (accounts.size() == maxAccounts)
{
return -1;
}

uint8_t privateKey[IOTEX_PRIVATE_KEY_SIZE];
randomGenerator.fillRandom(privateKey, sizeof(privateKey));
return CreateAccountFromPrivateKey(privateKey);
}

AccountId AccountManager::CreateAccountFromPrivateKey(const uint8_t privateKey[IOTEX_PRIVATE_KEY_SIZE])
{
if (accounts.size() == maxAccounts)
{
return -1;
}

Account account(privateKey);
uint32_t id = availableIds.back();
uint32_t eepromAddress = id*IOTEX_PRIVATE_KEY_SIZE;
availableIds.pop_back();
storage.savePrivateKey(eepromAddress, privateKey);
accounts.insert(std::pair<AccountId, Account>(id, account));
return id;
}

const Account& AccountManager::GetAccount(AccountId id)
{
return accounts.at(id);
}

void AccountManager::DeleteAccount(AccountId id)
{
if (accounts.find(id) == accounts.end())
{
return;
}
accounts.erase(id);
availableIds.push_back(id);
uint8_t privateKeyAll1[IOTEX_PRIVATE_KEY_SIZE];
memset(privateKeyAll1, 0xFF, IOTEX_PRIVATE_KEY_SIZE);
storage.savePrivateKey(id*IOTEX_PRIVATE_KEY_SIZE, privateKeyAll1);
}

#endif
64 changes: 64 additions & 0 deletions src/account/accountManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef IOTEX_ACCOUNT_MANAGER_H
#define IOTEX_ACCOUNT_MANAGER_H

// Support for Linux will be added in the future
#ifdef ARDUINO

#include "account/account.h"
#include <map>

#define IOTEX_ACCOUNT_MANAGER_MAX_ACCOUNTS 10

namespace iotex
{
using AccountId = int32_t;

/**
* @brief A singleton that abstracts account creation and private key storage
*/
class AccountManager
{
public:
static AccountManager& getInstance()
{
static AccountManager instance;
return instance;
}

/**
* @brief Creates an account from a random private key. Also stores it in NVM.
*/
AccountId CreateNewAccount();

/**
* @brief Creates an account from a supplied private key. Also stores it in NVM.
*
*/
AccountId CreateAccountFromPrivateKey(const uint8_t privateKey[IOTEX_PRIVATE_KEY_SIZE]);

/**
* @brief Gets the account object
*/
const Account& GetAccount(AccountId id);

/**
* @brief Deletes the account. Also erases it from NVM.
*/
void DeleteAccount(AccountId id);

private:
AccountManager(uint8_t maxAccounts = IOTEX_ACCOUNT_MANAGER_MAX_ACCOUNTS);
const uint8_t maxAccounts;
std::vector<AccountId> availableIds;
std::map<AccountId, Account> accounts;

public:
AccountManager(AccountManager const&) = delete;
void operator=(AccountManager const&) = delete;
};

extern AccountManager& accountManager;
} // namespace iotex
#endif

#endif
3 changes: 2 additions & 1 deletion src/helpers/client_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ const std::string iotex::logModuleNamesLookupTable[(int)LogModules::VALUES_COUNT
"USER",
"GENERAL",
"HTTP",
"CONTRACT"
"CONTRACT",
"ACCOUNT"
};

const std::string& iotex::userLogModule = iotex::logModuleNamesLookupTable[0];
Expand Down
1 change: 1 addition & 0 deletions src/helpers/client_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum LogModules
GENERAL,
HTTP,
CONTRACT,
ACCOUNT,
VALUES_COUNT
};

Expand Down
2 changes: 2 additions & 0 deletions src/storage/nano33iot/storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ ResultCode Storage::readPrivateKey(uint32_t eepromAddress, uint8_t privateKey[IO
if(!EEPROM.isValid())
{
IOTEX_ERROR(logModule, "Failed to read PK. No private keys are stored");
// Set private key to all zeros
memset(privateKey, 0, IOTEX_PRIVATE_KEY_SIZE);
return ResultCode::ERROR_STORAGE_EMPTY;
}

Expand Down

0 comments on commit c4ab8ae

Please sign in to comment.