Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,7 @@ devcontainer.json

# Terminalizer
/demo.yml
/config.yml
/config.yml

# Runtime data
/data/
23 changes: 23 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/core/miner.cpp)
list(APPEND ASTRO_CORE_SOURCES src/core/miner.cpp)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/core/block_store.cpp)
list(APPEND ASTRO_CORE_SOURCES src/core/block_store.cpp)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/net/p2p.cpp)
list(APPEND ASTRO_CORE_SOURCES src/net/p2p.cpp)
endif()
Expand Down Expand Up @@ -209,6 +212,26 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/cli/mine.cpp)
install(TARGETS astro-mine RUNTIME DESTINATION bin)
endif()

if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/cli/store.cpp)
add_executable(astro-store src/cli/store.cpp)
target_include_directories(astro-store PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/include
)
if(TARGET astro_core)
target_link_libraries(astro-store PRIVATE astro_core)
endif()
if(OpenSSL_FOUND)
if(TARGET OpenSSL::Crypto)
target_link_libraries(astro-store PRIVATE OpenSSL::Crypto)
else()
target_link_libraries(astro-store PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
set_target_properties(astro-store PROPERTIES OUTPUT_NAME "astro-store")
install(TARGETS astro-store RUNTIME DESTINATION bin)
endif()

if(ASTRO_BUILD_TESTS)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests)
enable_testing()
Expand Down
1 change: 0 additions & 1 deletion include/astro/core/block.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once
#include <cstdint>
#include <vector>
#include <span>
#include <string>
#include "astro/core/hash.hpp"
#include "astro/core/transaction.hpp"
Expand Down
12 changes: 11 additions & 1 deletion include/astro/core/chain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
#include <optional>
#include "astro/core/block.hpp"
#include "astro/core/transaction.hpp"
#include "astro/core/pow.hpp"

namespace astro { namespace storage { class BlockStore; } }

namespace astro::core {

Expand Down Expand Up @@ -50,6 +51,15 @@ namespace astro::core {

Block build_block_from_transactions(std::vector<Transaction> transactions, uint64_t timestamp) const;

// Load blocks from the block store (verifies each via validate_block).
// If the chain is empty, the first valid block becomes genesis.
void restore_from_store(astro::storage::BlockStore& store);

// Validate then append AND persist atomically.
ValidationResult append_and_store(const Block& block, astro::storage::BlockStore& store);

const std::vector<Block>& blocks() const { return blocks_; }

private:
ChainConfig config_{};
std::vector<Block> blocks_;
Expand Down
35 changes: 35 additions & 0 deletions include/astro/storage/block_store.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once
#include <cstdint>
#include <vector>
#include <filesystem>
#include <astro/core/block.hpp>

namespace astro::storage {
struct RecordHeader {
uint32_t magic;
uint64_t version;
uint16_t kind;
uint64_t length;
};

class BlockStore {
public:
explicit BlockStore(std::filesystem::path root_path);
~BlockStore();

void append_block(const astro::core::Block& block);

std::vector<astro::core::Block> load_all_blocks();

const std::filesystem::path& directory() const { return root_path_; }
const std::filesystem::path& log_path() const { return log_path_; }

private:
void open_write_log();
void close_write_log();
void fsync_fd();
std::filesystem::path root_path_;
std::filesystem::path log_path_;
int log_fd = -1;
};
}
41 changes: 41 additions & 0 deletions src/cli/store.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <iostream>
#include <filesystem>
#include <span>

#include "astro/storage/block_store.hpp"
#include "astro/core/chain.hpp"
#include "astro/core/keys.hpp"
#include "astro/core/hash.hpp"

using namespace astro::core;
namespace fs = std::filesystem;

int main() {
if (!crypto_init()) { std::cerr << "OpenSSL init failed\n"; return 1; }

fs::path data = "./data";
astro::storage::BlockStore store(data);

Chain chain(ChainConfig{.difficulty_bits=0});
chain.restore_from_store(store);
std::cout << "[💾] restored height: " << chain.height() << "\n";

if (chain.height() == 0) {
auto genesis_block = make_genesis_block("Astro: Persisted.", 1700000000ULL);
auto validation_result = chain.append_and_store(genesis_block, store);
std::cout << (validation_result.is_valid ? "[+] wrote genesis\n" : "[x] failed genesis\n");
} else {
auto key_pair = generate_ec_keypair();
Transaction transaction; transaction.version=1; transaction.nonce=chain.height(); transaction.amount=1;
transaction.from_pub_pem=key_pair.pubkey_pem; transaction.to_label="demo"; transaction.sign(key_pair.privkey_pem);
auto new_block = chain.build_block_from_transactions({transaction}, 1700000000ULL + chain.height());
auto validation_result = chain.append_and_store(new_block, store);
std::cout << (validation_result.is_valid ? "[+] appended block\n" : "[x] append failed\n");
}

auto tip_hash = chain.tip_hash();
if (tip_hash) {
std::cout << "tip: " << to_hex(std::span<const uint8_t>(tip_hash->data(), tip_hash->size())).substr(0,16) << "...\n";
}
return 0;
}
27 changes: 22 additions & 5 deletions src/cli/tui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <filesystem>

#include "astro/core/chain.hpp"
#include "astro/core/keys.hpp"
#include "astro/core/hash.hpp"
#include "astro/core/block.hpp"
#include "astro/storage/block_store.hpp"

using namespace astro::core;

Expand Down Expand Up @@ -121,7 +123,7 @@ struct KeyDebounce {
}
};

} // namespace tui
}

static std::string short_hash(const Hash256& h, size_t keep = 10) {
auto hex = to_hex(std::span<const uint8_t>(h.data(), h.size()));
Expand All @@ -133,14 +135,17 @@ struct LogLine { std::string text; int color = 37; };

struct App {
Chain chain;
astro::storage::BlockStore store{std::filesystem::path("./data")};
std::vector<LogLine> log;
size_t max_log = 200;

size_t chain_scroll = 0;
bool dirty = true;

void push_log(std::string s, int color=37) {
log.push_back({std::move(s), color});
if (log.size() > max_log) log.erase(log.begin(), log.begin()+ (log.size()-max_log));
dirty = true;
}
};

Expand All @@ -154,9 +159,10 @@ static bool do_genesis(App& app) {
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count());
Block genesis_block = make_genesis_block("Astro: Born from bytes.", unix_time);
auto validation_result = app.chain.append_block(genesis_block);
auto validation_result = app.chain.append_and_store(genesis_block, app.store);
if (validation_result.is_valid) {
app.push_log("genesis appended ✓", 32);
app.dirty = true;
return true;
} else {
app.push_log("genesis append failed", 31);
Expand All @@ -181,7 +187,7 @@ static bool do_append_signed_block(App& app) {
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count());
Block new_block = app.chain.build_block_from_transactions({transaction}, unix_time);
auto validation_result = app.chain.append_block(new_block);
auto validation_result = app.chain.append_and_store(new_block, app.store);
if (validation_result.is_valid) { app.push_log("block appended ✓", 32); return true; }
app.push_log("append failed (validation error)", 31);
return false;
Expand Down Expand Up @@ -300,6 +306,10 @@ int main() {

App app;
tui::FPS fps;
app.chain.restore_from_store(app.store);
if (app.chain.height() > 0) {
app.push_log("restored chain from ./data", 36);
}
app.push_log("TUI started", 36);
app.push_log("Press G to create genesis", 33);
int rows = 36, cols = 120;
Expand All @@ -313,6 +323,8 @@ int main() {
using clock = std::chrono::steady_clock;
auto next = clock::now();
tui::KeyDebounce debounce;
auto last_draw = clock::now();
const auto min_draw_interval = std::chrono::milliseconds(120);

while (tui::g_running) {
for (int k; (k = tui::read_key()) != -1; ) {
Expand All @@ -326,8 +338,13 @@ int main() {
}
}

draw(app, rows, cols, fps);
fps.tick();
auto now = clock::now();
if (app.dirty || (now - last_draw) >= min_draw_interval) {
draw(app, rows, cols, fps);
fps.tick();
app.dirty = false;
last_draw = now;
}

next += std::chrono::milliseconds(33);
std::this_thread::sleep_until(next);
Expand Down
Loading
Loading