diff --git a/app/core/updater.c b/app/core/updater.c index 809d517..3c04fb6 100644 --- a/app/core/updater.c +++ b/app/core/updater.c @@ -32,7 +32,7 @@ static app_err_t updater_database_update(uint8_t* data, size_t len) { return ERR_DATA; } - if (eth_db_update((fs_entry_t *) data, len - SIG_LEN) != ERR_OK) { + if (eth_db_update(data, len - SIG_LEN) != ERR_OK) { ui_info(LSTR(INFO_ERROR_TITLE), LSTR(INFO_DB_UPDATE_ERROR), 1); return ERR_DATA; } else { diff --git a/app/error.h b/app/error.h index db5dfcd..e81a790 100644 --- a/app/error.h +++ b/app/error.h @@ -11,7 +11,8 @@ typedef enum { ERR_HW, ERR_UNSUPPORTED, ERR_NEED_MORE_DATA, - ERR_FULL + ERR_FULL, + ERR_VERSION } app_err_t; #endif diff --git a/app/ethereum/eth_db.c b/app/ethereum/eth_db.c index 4bfd474..2d44b18 100644 --- a/app/ethereum/eth_db.c +++ b/app/ethereum/eth_db.c @@ -4,6 +4,7 @@ #define FS_CHAIN_MAGIC 0x4348 #define FS_ERC20_MAGIC 0x3020 #define FS_VERSION_MAGIC 0x4532 +#define FS_DELTA_MAGIC 0x444c #define ERC20_NET_LEN 24 @@ -24,6 +25,20 @@ struct __attribute__((packed)) version_desc { uint32_t version; }; +struct __attribute__((packed)) delta_desc { + uint16_t magic; + uint32_t old_version; + uint16_t erase_chain_len; + uint16_t erase_token_len; + uint8_t data[]; +}; + +struct delta_erase_ctx { + uint8_t* erase_chain; + uint8_t* erase_token; + uint16_t erase_chain_len; + uint16_t erase_token_len; +}; fs_action_t _eth_db_match_chain(void* ctx, fs_entry_t* entry) { if (entry->magic != FS_CHAIN_MAGIC) { @@ -66,6 +81,57 @@ fs_action_t _eth_db_match_all(void* ctx, fs_entry_t* entry) { return ((entry->magic == FS_CHAIN_MAGIC) || (entry->magic == FS_ERC20_MAGIC) || (entry->magic == FS_VERSION_MAGIC)) ? FS_REJECT : FS_ACCEPT; } +static inline fs_action_t _eth_db_match_erase_chain(struct delta_erase_ctx* ctx, struct chain_raw_desc* entry) { + for (int i = 0; i < ctx->erase_chain_len; i += 4) { + if (!memcmp(&entry->chain_id, &ctx->erase_chain[i], 4)) { + return FS_REJECT; + } + } + + return FS_ACCEPT; +} + +static inline fs_action_t _eth_db_match_erase_token(struct delta_erase_ctx* ctx, struct erc20_raw_desc* entry) { + uint8_t* ticker = entry->data + (entry->net_count * ERC20_NET_LEN) + 1; + size_t off = 0; + + while (off < ctx->erase_token_len) { + size_t ticker_off = 0; + + while(1) { + if (ticker[ticker_off] != ctx->erase_token[off]) { + while(ctx->erase_token[off++] != '\0') { + continue; + } + + break; + } + + if (ticker[ticker_off] == '\0') { + return FS_REJECT; + } + + ticker_off++; + off++; + } + } + + return FS_ACCEPT; +} + +fs_action_t _eth_db_match_delta(void* ctx, fs_entry_t* entry) { + switch(entry->magic) { + case FS_VERSION_MAGIC: + return FS_REJECT; + case FS_CHAIN_MAGIC: + return _eth_db_match_erase_chain((struct delta_erase_ctx*) ctx, (struct chain_raw_desc*) entry); + case FS_ERC20_MAGIC: + return _eth_db_match_erase_token((struct delta_erase_ctx*) ctx, (struct erc20_raw_desc*) entry); + default: + return FS_ACCEPT; + } +} + app_err_t eth_db_lookup_chain(chain_desc_t* chain) { struct chain_raw_desc* chain_data = (struct chain_raw_desc*) fs_find(_eth_db_match_chain, chain); @@ -106,11 +172,10 @@ app_err_t eth_db_lookup_version(uint32_t* version) { return ERR_OK; } -app_err_t eth_db_update(fs_entry_t* entries, size_t len) { +static app_err_t eth_full_db_rewrite(fs_entry_t* entries, size_t len) { app_err_t err = fs_erase_all(_eth_db_match_all, NULL); - // since our matcher doesn't know when it has reached - // completion, if everything went OK the error code + // since our matcher doesn't know when it has reached completion, if everything went OK the error code // will be ERR_DATA on success. if (err != ERR_DATA) { return err; @@ -118,3 +183,56 @@ app_err_t eth_db_update(fs_entry_t* entries, size_t len) { return fs_write(entries, len); } + +static app_err_t eth_delta_db_update(struct delta_desc* delta, size_t len) { + uint32_t version; + if (eth_db_lookup_version(&version) != ERR_OK) { + return ERR_HW; + } + + if (delta->old_version != version) { + return ERR_VERSION; + } + + struct delta_erase_ctx erase_ctx = { + .erase_chain = &delta->data[0], + .erase_token = &delta->data[delta->erase_chain_len], + .erase_chain_len = delta->erase_chain_len, + .erase_token_len = delta->erase_token_len + }; + + fs_entry_t* entries = (fs_entry_t*) &delta->data[delta->erase_chain_len + delta->erase_token_len]; + + size_t off = sizeof(struct delta_desc) + delta->erase_chain_len + delta->erase_token_len; + + if (off > len) { + return ERR_DATA; + } + + app_err_t err = fs_erase_all(_eth_db_match_delta, &erase_ctx); + + // since our matcher doesn't know when it has reached completion, if everything went OK the error code + // will be ERR_DATA on success. This could be optimized if noticeable delays are observed. + if (err != ERR_DATA) { + return err; + } + + return fs_write(entries, (len - off)); +} + +app_err_t eth_db_update(uint8_t* data, size_t len) { + if (len < 1) { + return ERR_DATA; + } + + uint16_t magic = data[0] | (data[1] << 8); + + switch(magic) { + case FS_VERSION_MAGIC: + return eth_full_db_rewrite((fs_entry_t*) data, len); + case FS_DELTA_MAGIC: + return eth_delta_db_update((struct delta_desc*) data, len); + default: + return ERR_UNSUPPORTED; + } +} diff --git a/app/ethereum/eth_db.h b/app/ethereum/eth_db.h index bcda85b..1464a3d 100644 --- a/app/ethereum/eth_db.h +++ b/app/ethereum/eth_db.h @@ -21,6 +21,6 @@ typedef struct { app_err_t eth_db_lookup_chain(chain_desc_t* chain); app_err_t eth_db_lookup_erc20(erc20_desc_t* erc20); app_err_t eth_db_lookup_version(uint32_t* version); -app_err_t eth_db_update(fs_entry_t* entries, size_t len); +app_err_t eth_db_update(uint8_t* data, size_t len); #endif