Add second block source check

This commit is contained in:
Djuri Baars 2024-03-30 11:40:58 +01:00
parent f84ae969d4
commit 91fc474a1f
3 changed files with 177 additions and 84 deletions

View File

@ -5,6 +5,7 @@ esp_websocket_client_handle_t blockNotifyClient = NULL;
uint currentBlockHeight = 816000; uint currentBlockHeight = 816000;
uint blockMedianFee = 1; uint blockMedianFee = 1;
bool blockNotifyInit = false; bool blockNotifyInit = false;
unsigned long int lastBlockUpdate;
// const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE----- // const char *mempoolWsCert = R"(-----BEGIN CERTIFICATE-----
// MIIHfTCCBmWgAwIBAgIRANFX3mhqRYDt1NFuENoSyaAwDQYJKoZIhvcNAQELBQAw // MIIHfTCCBmWgAwIBAgIRANFX3mhqRYDt1NFuENoSyaAwDQYJKoZIhvcNAQELBQAw
@ -50,19 +51,20 @@ bool blockNotifyInit = false;
// ew== // ew==
// -----END CERTIFICATE-----)"; // -----END CERTIFICATE-----)";
void setupBlockNotify() { void setupBlockNotify()
// currentBlockHeight = preferences.getUInt("blockHeight", 816000); {
IPAddress result; IPAddress result;
int dnsErr = -1; int dnsErr = -1;
String mempoolInstance = String mempoolInstance =
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
while (dnsErr != 1) { while (dnsErr != 1)
{
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result); dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
if (dnsErr != 1) { if (dnsErr != 1)
{
Serial.print(mempoolInstance); Serial.print(mempoolInstance);
Serial.println(F("mempool DNS could not be resolved")); Serial.println(F("mempool DNS could not be resolved"));
WiFi.reconnect(); WiFi.reconnect();
@ -71,18 +73,18 @@ void setupBlockNotify() {
} }
// Get current block height through regular API // Get current block height through regular API
HTTPClient *http = new HTTPClient();
http->begin("https://" + mempoolInstance + "/api/blocks/tip/height");
int httpCode = http->GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK) { currentBlockHeight = getBlockFetch();
String blockHeightStr = http->getString();
currentBlockHeight = blockHeightStr.toInt(); if (currentBlockHeight != -1)
// xTaskNotifyGive(blockUpdateTaskHandle); {
if (workQueue != nullptr) { lastBlockUpdate = esp_timer_get_time() / 1000000;
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; }
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
} if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
} }
// std::strcpy(wsServer, String("wss://" + mempoolInstance + // std::strcpy(wsServer, String("wss://" + mempoolInstance +
@ -102,38 +104,41 @@ void setupBlockNotify() {
} }
void onWebsocketEvent(void *handler_args, esp_event_base_t base, void onWebsocketEvent(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data) { int32_t event_id, void *event_data)
{
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}"; const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}";
switch (event_id) { switch (event_id)
case WEBSOCKET_EVENT_CONNECTED: {
blockNotifyInit = true; case WEBSOCKET_EVENT_CONNECTED:
blockNotifyInit = true;
Serial.println(F("Connected to Mempool.space WebSocket")); Serial.println(F("Connected to Mempool.space WebSocket"));
Serial.println(sub); Serial.println(sub);
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(), if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(),
sub.length(), portMAX_DELAY) == -1) { sub.length(), portMAX_DELAY) == -1)
Serial.println(F("Mempool.space WS Block Subscribe Error")); {
} Serial.println(F("Mempool.space WS Block Subscribe Error"));
}
break; break;
case WEBSOCKET_EVENT_DATA: case WEBSOCKET_EVENT_DATA:
onWebsocketMessage(data); onWebsocketMessage(data);
break; break;
case WEBSOCKET_EVENT_ERROR: case WEBSOCKET_EVENT_ERROR:
Serial.println(F("Mempool.space WS Connnection error")); Serial.println(F("Mempool.space WS Connnection error"));
break; break;
case WEBSOCKET_EVENT_DISCONNECTED: case WEBSOCKET_EVENT_DISCONNECTED:
Serial.println(F("Mempool.space WS Connnection Closed")); Serial.println(F("Mempool.space WS Connnection Closed"));
break; break;
} }
} }
void onWebsocketMessage(esp_websocket_event_data_t *event_data) { void onWebsocketMessage(esp_websocket_event_data_t *event_data)
{
JsonDocument doc; JsonDocument doc;
JsonDocument filter; JsonDocument filter;
filter["block"]["height"] = true; filter["block"]["height"] = true;
filter["mempool-blocks"][0]["medianFee"] = true; filter["mempool-blocks"][0]["medianFee"] = true;
@ -146,56 +151,67 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data) {
// return; // return;
// } // }
if (doc.containsKey("block")) { if (doc.containsKey("block"))
{
JsonObject block = doc["block"]; JsonObject block = doc["block"];
currentBlockHeight = block["height"].as<uint>(); currentBlockHeight = block["height"].as<uint>();
//Serial.printf("New block found: %d\r\n", block["height"].as<uint>()); // Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
preferences.putUInt("blockHeight", currentBlockHeight); preferences.putUInt("blockHeight", currentBlockHeight);
lastBlockUpdate = esp_timer_get_time() / 1000000;
if (workQueue != nullptr) { if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle); // xTaskNotifyGive(blockUpdateTaskHandle);
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT && if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
preferences.getBool("stealFocus", true)) { preferences.getBool("stealFocus", true))
{
uint64_t timerPeriod = 0; uint64_t timerPeriod = 0;
if (isTimerActive()) { if (isTimerActive())
{
// store timer periode before making inactive to prevent artifacts // store timer periode before making inactive to prevent artifacts
timerPeriod = getTimerSeconds(); timerPeriod = getTimerSeconds();
esp_timer_stop(screenRotateTimer); esp_timer_stop(screenRotateTimer);
} }
setCurrentScreen(SCREEN_BLOCK_HEIGHT); setCurrentScreen(SCREEN_BLOCK_HEIGHT);
if (timerPeriod > 0) { if (timerPeriod > 0)
{
esp_timer_start_periodic(screenRotateTimer, esp_timer_start_periodic(screenRotateTimer,
timerPeriod * usPerSecond); timerPeriod * usPerSecond);
} }
} }
if (preferences.getBool("ledFlashOnUpd", false)) { if (preferences.getBool("ledFlashOnUpd", false))
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated {
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
queueLedEffect(LED_FLASH_BLOCK_NOTIFY); queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
} }
} }
} else if (doc.containsKey("mempool-blocks")) { }
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>(); else if (doc.containsKey("mempool-blocks"))
{
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>();
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>()); uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
if (blockMedianFee == medianFee) { if (blockMedianFee == medianFee)
doc.clear(); {
return; doc.clear();
} return;
}
// Serial.printf("New median fee: %d\r\n", medianFee); // Serial.printf("New median fee: %d\r\n", medianFee);
blockMedianFee = medianFee; blockMedianFee = medianFee;
if (workQueue != nullptr) { if (workQueue != nullptr)
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0}; {
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
} xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
}
} }
doc.clear(); doc.clear();
@ -203,31 +219,67 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data) {
uint getBlockHeight() { return currentBlockHeight; } uint getBlockHeight() { return currentBlockHeight; }
void setBlockHeight(uint newBlockHeight) { void setBlockHeight(uint newBlockHeight)
{
currentBlockHeight = newBlockHeight; currentBlockHeight = newBlockHeight;
} }
uint getBlockMedianFee() { return blockMedianFee; } uint getBlockMedianFee() { return blockMedianFee; }
void setBlockMedianFee(uint newBlockMedianFee) { void setBlockMedianFee(uint newBlockMedianFee)
{
blockMedianFee = newBlockMedianFee; blockMedianFee = newBlockMedianFee;
} }
bool isBlockNotifyConnected() { bool isBlockNotifyConnected()
if (blockNotifyClient == NULL) return false; {
if (blockNotifyClient == NULL)
return false;
return esp_websocket_client_is_connected(blockNotifyClient); return esp_websocket_client_is_connected(blockNotifyClient);
} }
bool getBlockNotifyInit() { bool getBlockNotifyInit()
{
return blockNotifyInit; return blockNotifyInit;
} }
void stopBlockNotify() { void stopBlockNotify()
if (blockNotifyClient == NULL) return; {
if (blockNotifyClient == NULL)
return;
esp_websocket_client_close(blockNotifyClient, portMAX_DELAY); esp_websocket_client_close(blockNotifyClient, portMAX_DELAY);
esp_websocket_client_stop(blockNotifyClient); esp_websocket_client_stop(blockNotifyClient);
esp_websocket_client_destroy(blockNotifyClient); esp_websocket_client_destroy(blockNotifyClient);
blockNotifyClient = NULL; blockNotifyClient = NULL;
}
int getBlockFetch()
{
String mempoolInstance =
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
// Get current block height through regular API
HTTPClient *http = new HTTPClient();
http->begin("https://" + mempoolInstance + "/api/blocks/tip/height");
int httpCode = http->GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK)
{
String blockHeightStr = http->getString();
return blockHeightStr.toInt();
}
return -1;
}
uint getLastBlockUpdate()
{
return lastBlockUpdate;
}
void setLastBlockUpdate(uint lastUpdate)
{
lastBlockUpdate = lastUpdate;
} }

View File

@ -29,4 +29,7 @@ uint getBlockMedianFee();
bool isBlockNotifyConnected(); bool isBlockNotifyConnected();
void stopBlockNotify(); void stopBlockNotify();
bool getBlockNotifyInit(); bool getBlockNotifyInit();
uint getLastBlockUpdate();
int getBlockFetch();
void setLastBlockUpdate(uint lastUpdate);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2023 Djuri Baars * Copyright 2023-2024 Djuri Baars
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,13 +23,15 @@ uint wifiLostConnection;
uint priceNotifyLostConnection = 0; uint priceNotifyLostConnection = 0;
uint blockNotifyLostConnection = 0; uint blockNotifyLostConnection = 0;
extern "C" void app_main() { extern "C" void app_main()
{
initArduino(); initArduino();
Serial.begin(115200); Serial.begin(115200);
setup(); setup();
while (true) { while (true)
{
// vTaskList(ptrTaskList); // vTaskList(ptrTaskList);
// Serial.println(F("**********************************")); // Serial.println(F("**********************************"));
// Serial.println(F("Task State Prio Stack Num")); // Serial.println(F("Task State Prio Stack Num"));
@ -39,60 +41,96 @@ extern "C" void app_main() {
if (eventSourceTaskHandle != NULL) if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle); xTaskNotifyGive(eventSourceTaskHandle);
int64_t currentUptime = esp_timer_get_time() / 1000000;; int64_t currentUptime = esp_timer_get_time() / 1000000;
;
if (!WiFi.isConnected()) { if (!WiFi.isConnected())
if (!wifiLostConnection) { {
if (!wifiLostConnection)
{
wifiLostConnection = currentUptime; wifiLostConnection = currentUptime;
Serial.println("Lost WiFi connection, trying to reconnect..."); Serial.println("Lost WiFi connection, trying to reconnect...");
} }
if ((currentUptime - wifiLostConnection) > 600) { if ((currentUptime - wifiLostConnection) > 600)
{
Serial.println("Still no connection after 10 minutes, restarting..."); Serial.println("Still no connection after 10 minutes, restarting...");
delay(2000); delay(2000);
ESP.restart(); ESP.restart();
} }
WiFi.begin(); WiFi.begin();
} else if (wifiLostConnection) { }
else if (wifiLostConnection)
{
wifiLostConnection = 0; wifiLostConnection = 0;
Serial.println("Connection restored, reset timer."); Serial.println("Connection restored, reset timer.");
} else if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", false) && !isPriceNotifyConnected()) { }
else if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", false) && !isPriceNotifyConnected())
{
priceNotifyLostConnection++; priceNotifyLostConnection++;
Serial.println("Lost price data connection..."); Serial.println("Lost price data connection...");
queueLedEffect(LED_DATA_PRICE_ERROR); queueLedEffect(LED_DATA_PRICE_ERROR);
// if price WS connection does not come back after 6*5 seconds, destroy and recreate // if price WS connection does not come back after 6*5 seconds, destroy and recreate
if (priceNotifyLostConnection > 6) { if (priceNotifyLostConnection > 6)
{
Serial.println("Restarting price handler..."); Serial.println("Restarting price handler...");
stopPriceNotify(); stopPriceNotify();
setupPriceNotify(); setupPriceNotify();
priceNotifyLostConnection = 0; priceNotifyLostConnection = 0;
} }
} else if (getBlockNotifyInit() && !isBlockNotifyConnected()) { }
else if (getBlockNotifyInit() && !isBlockNotifyConnected())
{
blockNotifyLostConnection++; blockNotifyLostConnection++;
Serial.println("Lost block data connection..."); Serial.println("Lost block data connection...");
queueLedEffect(LED_DATA_BLOCK_ERROR); queueLedEffect(LED_DATA_BLOCK_ERROR);
// if mempool WS connection does not come back after 6*5 seconds, destroy and recreate // if mempool WS connection does not come back after 6*5 seconds, destroy and recreate
if (blockNotifyLostConnection > 6) { if (blockNotifyLostConnection > 6)
{
Serial.println("Restarting block handler..."); Serial.println("Restarting block handler...");
stopBlockNotify(); stopBlockNotify();
setupBlockNotify(); setupBlockNotify();
blockNotifyLostConnection = 0; blockNotifyLostConnection = 0;
} }
} else if (blockNotifyLostConnection > 0 || priceNotifyLostConnection > 0) { }
else if (blockNotifyLostConnection > 0 || priceNotifyLostConnection > 0)
{
blockNotifyLostConnection = 0; blockNotifyLostConnection = 0;
priceNotifyLostConnection = 0; priceNotifyLostConnection = 0;
} }
// if more than 5 price updates are missed, there is probably something wrong, reconnect // if more than 5 price updates are missed, there is probably something wrong, reconnect
if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE)*5)) { if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5))
stopPriceNotify(); {
setupPriceNotify(); Serial.println("Detected 5 missed price updates... restarting price handler.");
priceNotifyLostConnection = 0; stopPriceNotify();
setupPriceNotify();
priceNotifyLostConnection = 0;
}
// If after 45 minutes no mempool blocks, check the rest API
if ((getLastBlockUpdate() - currentUptime) > 45 * 60)
{
Serial.println("Long time (45 min) since last block, checking if I missed anything...");
int currentBlock = getBlockFetch();
if (currentBlock != -1)
{
if (currentBlock != getBlockHeight())
{
Serial.println("Detected stuck block height... restarting block handler.");
// Mempool source stuck, restart
stopBlockNotify();
setupBlockNotify();
}
// set last block update so it doesn't fetch for 45 minutes
setLastBlockUpdate(currentUptime);
}
} }
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));