diff --git a/docs/setup/download_and_build_on_windows.md b/docs/setup/download_and_build_on_windows.md index deb015e..20d4906 100644 --- a/docs/setup/download_and_build_on_windows.md +++ b/docs/setup/download_and_build_on_windows.md @@ -8,7 +8,7 @@ In a Git bash command prompt, `cd` to the directory where you intend to install the source code. ```bash -git clone --recursive git@cpal.pw:~/wallet +git clone --recursive git@rho.la:~/wallet ``` Then launch the visual studio X64 native tools command prompt. `cd` to the diff --git a/src/ILog.h b/src/ILog.h index 2a55c96..bee8b24 100644 --- a/src/ILog.h +++ b/src/ILog.h @@ -10,17 +10,25 @@ void ILogDebug(const char*); void queue_error_message(const char*); //Used for error conditions within a destructor because you cannot throw within a destructor void queue_fatal_error(const char*); //Used for fatal error conditions within a destructor in place of FatalException because you cannot throw within a destructor +struct sqlite3; + class MyException: public std::exception { private: std::string err; + int err_number; public: virtual ~MyException() override = default; MyException() = delete; - explicit MyException(const std::string &m) noexcept :err(m){} - explicit MyException(const char* sz) noexcept :err(sz) {} + explicit MyException(const std::string &m) noexcept :err(m.c_str()),err_number(-1){} + explicit MyException(const char* sz) noexcept :err(sz),err_number(-1) {} + explicit MyException(const char* sz, int i) noexcept :err(sz), err_number(i) {} + explicit MyException(int, sqlite3*) noexcept; virtual const char* what() const override { return err.c_str(); } + virtual const int what_num() const { + return err_number; + } }; class FatalException : public MyException { @@ -35,12 +43,6 @@ public: HashReuseException() noexcept; }; -class SQLexception : public MyException { -public: - using MyException::MyException; - SQLexception() noexcept; -}; - class NonUtf8DataInDatabase : public MyException { public: using MyException::MyException; diff --git a/src/ISqlit3Impl.cpp b/src/ISqlit3Impl.cpp index cebd543..20ec69f 100644 --- a/src/ISqlit3Impl.cpp +++ b/src/ISqlit3Impl.cpp @@ -24,7 +24,7 @@ void sqlite3_init() { if (sqlite3_initialize() != SQLITE_OK) { errorCode = 7; szError = "Fatal Error: Sqlite library did not init."; -// Cannot log the error, because logging not set up yet, so logging itself causes an exception + // Cannot log the error, because logging not set up yet, so logging itself causes an exception throw FatalException(szError.c_str()); } } @@ -35,7 +35,7 @@ static int callback(void* NotUsed, int argc, char** argv, char** azColName) { std::string str; str.reserve(256); for (int i = 0; i < argc; i++) { - str =str + "\t\"" + azColName[i]+ R"|("=)|" + (argv[i]!=nullptr ? argv[i] : "NULL"); + str = str + "\t\"" + azColName[i] + R"|("=)|" + (argv[i] != nullptr ? argv[i] : "NULL"); } ILogMessage(str.c_str()); return 0; @@ -52,7 +52,7 @@ public: pdb = nullptr; #endif int rc =sqlite3_open_v2(dbName, &pdb, flags, nullptr); - if (rc != SQLITE_OK) throw SQLexception(error_message(rc, pdb)); + if (rc != SQLITE_OK) throw MyException(rc, pdb); assert(pdb != nullptr); // pdb can never be nullptr, since the sqlite3_open_v2 command always initializes // it even if open fails @@ -62,7 +62,7 @@ public: char* zErrMsg = nullptr; int rc = sqlite3_exec(pdb, szsql, callback, nullptr, &zErrMsg); if (rc != SQLITE_OK) { - SQLexception e(std::string("SQL Exec Error: ") + zErrMsg); + auto e = MyException(zErrMsg, rc); sqlite3_free(zErrMsg); throw e; } @@ -90,6 +90,11 @@ ISqlite3* Sqlite3_open(const char * db_name) { return new ISqlite3Impl(db_name, SQLITE_OPEN_READWRITE); } +MyException::MyException(int i, sqlite3* pdb) noexcept: + err_number(i), + err(error_message(i, pdb)) +{} + // Factory method to create database. ISqlite3 * Sqlite3_create(const char* db_name) { return new ISqlite3Impl(db_name, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); @@ -101,10 +106,10 @@ class IcompiledImpl_sql : friend class ISqlite3Impl; private: sqlite3_stmt *pStmt; - ISqlite3Impl *pdbImplOwn; + ISqlite3Impl*pdbImplOwn; auto e(int rc) { assert(rc != SQLITE_OK); - return SQLexception(error_message(rc, pdbImplOwn->pdb)); + return MyException(rc, pdbImplOwn->pdb); } public: IcompiledImpl_sql() = delete; @@ -192,7 +197,7 @@ public: // from n blocks to n+1 blocks when a single transaction updates the root and anciliary data. // We will build the blockchain hash table in postfix format, with patricia tree nodes that // have skiplink format stored only in memory and rebuilt each startup so that it grows append only, - throw SQLexception("Abnormal busy database"); + throw MyException("Abnormal busy database", 2^14+2); break; case SQLITE_MISUSE: //ret = MISUSE; diff --git a/src/db_accessors.h b/src/db_accessors.h index c065d62..72c037c 100644 --- a/src/db_accessors.h +++ b/src/db_accessors.h @@ -114,8 +114,9 @@ namespace ro { result step() { return (*this)->Isqlite3_step(); } + void final_step() { - if (step() != result::DONE) throw SQLexception("SQL: Unexpected rows remaining"); + if (step() != result::DONE) throw MyException("SQL: Unexpected rows remaining", 16384); } void reset() { (*this)->Isqlite3_reset(); @@ -183,12 +184,15 @@ class sql_insert_name { ro::sql csql_into_names; ro::sql csql_namekey_into_keys; ro::sql csql_commit; + ro::sql csql_rollback; public: sql_insert_name(ISqlite3* p) : - csql_begin(p, R"|(BEGIN;)|"), - csql_into_names(p, R"|(INSERT OR ROLLBACK INTO "Names" VALUES(?1);)|"), - csql_namekey_into_keys(p, R"|(INSERT OR ROLLBACK INTO "Keys" VALUES(?1, last_insert_rowid(), 1);)|"), - csql_commit(p, R"|(COMMIT;)|") {} + csql_begin(p, R"|(BEGIN IMMEDIATE;)|"), + csql_into_names(p, R"|(INSERT OR FAIL INTO "Names" VALUES(?1);)|"), + csql_namekey_into_keys(p, R"|(INSERT OR FAIL INTO "Keys" VALUES(?1, last_insert_rowid(), 1);)|"), + csql_commit(p, R"|(COMMIT;)|"), + csql_rollback(p, R"|(ROLLBACK;)|") + {} sql_insert_name(const std::unique_ptr& p) : sql_insert_name(p.get()) {} void operator()(const char* psz, const ristretto255::point& pt) { csql_begin.do_one(); @@ -196,9 +200,18 @@ public: csql_into_names.do_one(psz); csql_namekey_into_keys.do_one(pt); } - catch (const std::exception & e) { - csql_commit.do_one(); - throw e; + catch (const MyException& e) { + csql_rollback.do_one(); + if (e.what_num() == 19) { + throw MyException("Name already in database"); + } + else { + throw; + } + } + catch (const std::exception &) { + csql_rollback.do_one(); + throw; } csql_commit.do_one(); } diff --git a/src/display_wallet.cpp b/src/display_wallet.cpp index 133f485..9430788 100644 --- a/src/display_wallet.cpp +++ b/src/display_wallet.cpp @@ -79,10 +79,10 @@ void display_wallet::close_menu_event_handler(wxCommandEvent& event) { void display_wallet::add_name_event_handler(wxCommandEvent& event) { wxTextEntryDialog dialog(this, - "This is a small sample\n" - "A long, long string to test out the text entrybox", - "Please enter a string", - "Default value", + R"("A Zooko name has a human readable name, +and a public key defined by the name and the wallet master secret)", + "Create a Zooko name", + "", wxOK | wxCANCEL); if (dialog.ShowModal() == wxID_OK) { diff --git a/src/frame.cpp b/src/frame.cpp index 221b8fc..7483213 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -65,7 +65,7 @@ Frame::Frame(wxString wxs) m_panel(), m_LastUsedSqlite() { -try { + try { assert(singletonFrame == nullptr); singletonFrame = this; SetIcon(wxICON(AAArho)); @@ -150,14 +150,29 @@ try { menuBar->EnableTop(1, false); //disable edit menu. // child controls m_LastUsedSqlite.Assign(singletonApp->pConfig->Read(wxT("/Wallet/LastUsed"), wxT(""))); - if (!m_LastUsedSqlite.IsOk() || !m_LastUsedSqlite.HasName() || !m_LastUsedSqlite.HasExt()) { - m_panel = new welcome_to_rhocoin(this); //Owner is "this", via the base class wxFrame. m_panel is a - // non owning pointer in the derived class that duplicates the owning pointer in the base class. + wxPanel* panel{ nullptr }; + try { + if (m_LastUsedSqlite.IsOk()) + { //Try to load an existing file. + panel = new display_wallet(this, m_LastUsedSqlite); + } + else { + panel = new welcome_to_rhocoin(this); + } } - else { - display_wallet* panel = new display_wallet(this, m_LastUsedSqlite); - m_panel = panel; + catch (const std::exception& e) { + // if the attempt to load an existing wallet file fails, + // we have to complete startup somehow. + + queue_error_message(e.what()); + panel = new welcome_to_rhocoin(this); //Owner is "this", via the base class wxFrame. } + // m_panel is a non owning pointer in the derived class that duplicates the + // owning pointer in the base class. This looks like a violation of DIY. + // but I have the concept of the primary child of the frame window, while + // wxWidgets lacks that concept. + // m_panel signifies the child window of the frame that currently matters. + m_panel = panel; this->RestorePositionFromConfig(ClientToWindowSize(m_panel->GetBestSize())); SetClientSize(GetClientSize()); } @@ -230,7 +245,7 @@ void Frame::NewWallet(wxFileName& filename, ristretto255::hash<256>& secret) { db->exec(R"|( PRAGMA journal_mode = WAL; PRAGMA synchronous = 1; -BEGIN TRANSACTION; +BEGIN IMMEDIATE TRANSACTION; CREATE TABLE "Keys"( "pubkey" BLOB NOT NULL UNIQUE PRIMARY KEY, "id" integer NOT NULL, diff --git a/src/localization.cpp b/src/localization.cpp index 10aca9a..03c35ed 100644 --- a/src/localization.cpp +++ b/src/localization.cpp @@ -129,8 +129,6 @@ FatalException::FatalException() noexcept : MyException(R"|(unspecified fatal exception)|") {}; HashReuseException::HashReuseException() noexcept : MyException(R"|(finalized the same hash constructor twice or more)|") {}; -SQLexception::SQLexception() noexcept : - MyException(R"|(SQL Exception)|") {}; BadDataException::BadDataException() noexcept : MyException(R"|(Bad data exception)|") {}; NonUtf8DataInDatabase::NonUtf8DataInDatabase() noexcept : diff --git a/src/stdafx.h b/src/stdafx.h index d32b46b..b578fed 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -62,7 +62,7 @@ constexpr bool b_WINDOWS = false; static_assert(wxUSE_UNSAFE_WXSTRING_CONV == 1, R"(In fully utf environment, (wallet.manifest plus /utf-8 compile option) all string conversions are safe.)"); -static_assert(wxMAJOR_VERSION == 3 && wxMINOR_VERSION == 2 && wxRELEASE_NUMBER == 0 && wxSUBRELEASE_NUMBER == 1, "expecting wxWidgets 3.1.7"); +static_assert(wxMAJOR_VERSION == 3 && wxMINOR_VERSION == 2 && wxRELEASE_NUMBER == 2 && wxSUBRELEASE_NUMBER == 1 && wxVERSION_STRING == wxT("wxWidgets 3.2.2.1"), "expecting wxWidgets 3.2.2.1"); static_assert(wxUSE_IPV6 == 1, "IP6 unavailable in wxWidgets"); static_assert(WXWIN_COMPATIBILITY_3_0 == 0, "wxWidgets api out of date"); static_assert(wxUSE_COMPILER_TLS == (b_WINDOWS ? 2 : 1), "out of date workarounds in wxWidgets for windows bugs"); diff --git a/src/unit_test.cpp b/src/unit_test.cpp index 78b045b..40f6faa 100644 --- a/src/unit_test.cpp +++ b/src/unit_test.cpp @@ -424,7 +424,7 @@ static bool OpenWallet(void) { db->exec(R"|( PRAGMA journal_mode = WAL; PRAGMA synchronous = 1; -BEGIN TRANSACTION; +BEGIN IMMEDIATE TRANSACTION; CREATE TABLE "Keys"( "pubkey" BLOB NOT NULL UNIQUE PRIMARY KEY, "id" integer NOT NULL, diff --git a/wxWidgets b/wxWidgets index 2648eb4..02e885c 160000 --- a/wxWidgets +++ b/wxWidgets @@ -1 +1 @@ -Subproject commit 2648eb4da156a751a377cfe96b91faa03e535c10 +Subproject commit 02e885c6f079c6e12a632f92cd7cfcceecf0c39b