From 51d617e70f90a972185917ea0096c1ecef2bfa8b Mon Sep 17 00:00:00 2001 From: Bitflash-sh Date: Fri, 24 Jul 2026 11:18:11 -0300 Subject: Bitflash: fair-launch release Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap. --- src/base58.h | 204 + src/bignum.h | 525 ++ src/bitflashd.cpp | 45 + src/btfaddr.cpp | 248 + src/btfaddr.h | 72 + src/btfchan.cpp | 94 + src/btfchan.h | 59 + src/btfrv.cpp | 250 + src/btfrv.h | 59 + src/btftunnel.cpp | 161 + src/btftunnel.h | 49 + src/compat.h | 193 + src/db.cpp | 604 ++ src/db.h | 420 ++ src/headers.h | 111 + src/headless.cpp | 177 + src/irc.cpp | 271 + src/irc.h | 7 + src/key.h | 156 + src/license.txt | 19 + src/main.cpp | 2696 +++++++++ src/main.h | 1361 +++++ src/makefile | 83 + src/makefile.linux | 48 + src/makefile.mingw | 44 + src/makefile.vc | 77 + src/market.cpp | 264 + src/market.h | 182 + src/net.cpp | 1207 ++++ src/net.h | 863 +++ src/nostr.cpp | 838 +++ src/nostr.h | 39 + src/randomx_pow.cpp | 143 + src/randomx_pow.h | 44 + src/rc/addressbook16.bmp | Bin 0 -> 1334 bytes src/rc/addressbook16mask.bmp | Bin 0 -> 126 bytes src/rc/addressbook20.bmp | Bin 0 -> 1478 bytes src/rc/addressbook20mask.bmp | Bin 0 -> 142 bytes src/rc/bitcoin.ico | Bin 0 -> 372526 bytes src/rc/bitcoin_bc_original.ico | Bin 0 -> 22486 bytes src/rc/check.ico | Bin 0 -> 766 bytes src/rc/send16.bmp | Bin 0 -> 1334 bytes src/rc/send16mask.bmp | Bin 0 -> 126 bytes src/rc/send16masknoshadow.bmp | Bin 0 -> 126 bytes src/rc/send20.bmp | Bin 0 -> 1478 bytes src/rc/send20mask.bmp | Bin 0 -> 142 bytes src/readme.txt | 54 + src/script.cpp | 1127 ++++ src/script.h | 597 ++ src/serialize.h | 1151 ++++ src/sha.cpp | 554 ++ src/sha.h | 177 + src/test_btfaddr.cpp | 142 + src/test_btfchan.cpp | 83 + src/test_btfrv.cpp | 116 + src/test_btftunnel.cpp | 97 + src/test_relayproc.cpp | 68 + src/ui.cpp | 3265 +++++++++++ src/ui.h | 417 ++ src/ui.rc | 14 + src/uibase.cpp | 1808 ++++++ src/uibase.h | 720 +++ src/uint256.h | 750 +++ src/uiproject.fbp | 11825 +++++++++++++++++++++++++++++++++++++++ src/util.cpp | 393 ++ src/util.h | 413 ++ 66 files changed, 35384 insertions(+) create mode 100644 src/base58.h create mode 100644 src/bignum.h create mode 100644 src/bitflashd.cpp create mode 100644 src/btfaddr.cpp create mode 100644 src/btfaddr.h create mode 100644 src/btfchan.cpp create mode 100644 src/btfchan.h create mode 100644 src/btfrv.cpp create mode 100644 src/btfrv.h create mode 100644 src/btftunnel.cpp create mode 100644 src/btftunnel.h create mode 100644 src/compat.h create mode 100644 src/db.cpp create mode 100644 src/db.h create mode 100644 src/headers.h create mode 100644 src/headless.cpp create mode 100644 src/irc.cpp create mode 100644 src/irc.h create mode 100644 src/key.h create mode 100644 src/license.txt create mode 100644 src/main.cpp create mode 100644 src/main.h create mode 100644 src/makefile create mode 100644 src/makefile.linux create mode 100644 src/makefile.mingw create mode 100644 src/makefile.vc create mode 100644 src/market.cpp create mode 100644 src/market.h create mode 100644 src/net.cpp create mode 100644 src/net.h create mode 100644 src/nostr.cpp create mode 100644 src/nostr.h create mode 100644 src/randomx_pow.cpp create mode 100644 src/randomx_pow.h create mode 100644 src/rc/addressbook16.bmp create mode 100644 src/rc/addressbook16mask.bmp create mode 100644 src/rc/addressbook20.bmp create mode 100644 src/rc/addressbook20mask.bmp create mode 100644 src/rc/bitcoin.ico create mode 100644 src/rc/bitcoin_bc_original.ico create mode 100644 src/rc/check.ico create mode 100644 src/rc/send16.bmp create mode 100644 src/rc/send16mask.bmp create mode 100644 src/rc/send16masknoshadow.bmp create mode 100644 src/rc/send20.bmp create mode 100644 src/rc/send20mask.bmp create mode 100644 src/readme.txt create mode 100644 src/script.cpp create mode 100644 src/script.h create mode 100644 src/serialize.h create mode 100644 src/sha.cpp create mode 100644 src/sha.h create mode 100644 src/test_btfaddr.cpp create mode 100644 src/test_btfchan.cpp create mode 100644 src/test_btfrv.cpp create mode 100644 src/test_btftunnel.cpp create mode 100644 src/test_relayproc.cpp create mode 100644 src/ui.cpp create mode 100644 src/ui.h create mode 100644 src/ui.rc create mode 100644 src/uibase.cpp create mode 100644 src/uibase.h create mode 100644 src/uint256.h create mode 100644 src/uiproject.fbp create mode 100644 src/util.cpp create mode 100644 src/util.h (limited to 'src') diff --git a/src/base58.h b/src/base58.h new file mode 100644 index 0000000..c4f4526 --- /dev/null +++ b/src/base58.h @@ -0,0 +1,204 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +// +// Why base-58 instead of standard base-64 encoding? +// - Don't want 0OIl characters that look the same in some fonts and +// could be used to create visually identical looking account numbers. +// - A string with non-alphanumeric characters is not as easily accepted as an account number. +// - E-mail usually won't line-break if there's no punctuation to break at. +// - Doubleclicking selects the whole number as one word if it's all alphanumeric. +// + + +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + +inline string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) +{ + CAutoBN_CTX pctx; + CBigNum bn58 = 58; + CBigNum bn0 = 0; + + // Convert big endian data to little endian + // Extra zero at the end make sure bignum will interpret as a positive number + vector vchTmp(pend-pbegin+1, 0); + reverse_copy(pbegin, pend, vchTmp.begin()); + + // Convert little endian data to bignum + CBigNum bn; + bn.setvch(vchTmp); + + // Convert bignum to string + string str; + str.reserve((pend - pbegin) * 138 / 100 + 1); + CBigNum dv; + CBigNum rem; + while (bn > bn0) + { + if (!BN_div(dv.get(), rem.get(), bn.get(), bn58.get(), pctx)) + throw bignum_error("EncodeBase58 : BN_div failed"); + bn = dv; + unsigned int c = rem.getulong(); + str += pszBase58[c]; + } + + // Leading zeroes encoded as base58 zeros + for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) + str += pszBase58[0]; + + // Convert little endian string to big endian + reverse(str.begin(), str.end()); + return str; +} + +inline string EncodeBase58(const vector& vch) +{ + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +inline bool DecodeBase58(const char* psz, vector& vchRet) +{ + CAutoBN_CTX pctx; + vchRet.clear(); + CBigNum bn58 = 58; + CBigNum bn = 0; + CBigNum bnChar; + while (isspace(*psz)) + psz++; + + // Convert big endian string to bignum + for (const char* p = psz; *p; p++) + { + const char* p1 = strchr(pszBase58, *p); + if (p1 == NULL) + { + while (isspace(*p)) + p++; + if (*p != '\0') + return false; + break; + } + bnChar.setulong(p1 - pszBase58); + if (!BN_mul(bn.get(), bn.get(), bn58.get(), pctx)) + throw bignum_error("DecodeBase58 : BN_mul failed"); + bn += bnChar; + } + + // Get bignum as little endian data + vector vchTmp = bn.getvch(); + + // Trim off sign byte if present + if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) + vchTmp.erase(vchTmp.end()-1); + + // Restore leading zeros + int nLeadingZeros = 0; + for (const char* p = psz; *p == pszBase58[0]; p++) + nLeadingZeros++; + vchRet.assign(nLeadingZeros + vchTmp.size(), 0); + + // Convert little endian data to big endian + reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); + return true; +} + +inline bool DecodeBase58(const string& str, vector& vchRet) +{ + return DecodeBase58(str.c_str(), vchRet); +} + + + + + +inline string EncodeBase58Check(const vector& vchIn) +{ + // add 4-byte hash check to the end + vector vch(vchIn); + uint256 hash = Hash(vch.begin(), vch.end()); + vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); + return EncodeBase58(vch); +} + +inline bool DecodeBase58Check(const char* psz, vector& vchRet) +{ + if (!DecodeBase58(psz, vchRet)) + return false; + if (vchRet.size() < 4) + { + vchRet.clear(); + return false; + } + uint256 hash = Hash(vchRet.begin(), vchRet.end()-4); + if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) + { + vchRet.clear(); + return false; + } + vchRet.resize(vchRet.size()-4); + return true; +} + +inline bool DecodeBase58Check(const string& str, vector& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + + + + + + +// Version 25 (0x19): Bitflash addresses start with "B" +static const unsigned char ADDRESSVERSION = 25; + +inline string Hash160ToAddress(uint160 hash160) +{ + // add 1-byte version number to the front + vector vch(1, ADDRESSVERSION); + vch.insert(vch.end(), UBEGIN(hash160), UEND(hash160)); + return EncodeBase58Check(vch); +} + +inline bool AddressToHash160(const char* psz, uint160& hash160Ret) +{ + vector vch; + if (!DecodeBase58Check(psz, vch)) + return false; + if (vch.empty()) + return false; + unsigned char nVersion = vch[0]; + if (vch.size() != sizeof(hash160Ret) + 1) + return false; + memcpy(&hash160Ret, &vch[1], sizeof(hash160Ret)); + // security fix: accept only the exact network version + // (the original "<=" would accept addresses of any lower version) + return (nVersion == ADDRESSVERSION); +} + +inline bool AddressToHash160(const string& str, uint160& hash160Ret) +{ + return AddressToHash160(str.c_str(), hash160Ret); +} + +inline bool IsValidBitcoinAddress(const char* psz) +{ + uint160 hash160; + return AddressToHash160(psz, hash160); +} + +inline bool IsValidBitcoinAddress(const string& str) +{ + return IsValidBitcoinAddress(str.c_str()); +} + + + + +inline string PubKeyToAddress(const vector& vchPubKey) +{ + return Hash160ToAddress(Hash160(vchPubKey)); +} diff --git a/src/bignum.h b/src/bignum.h new file mode 100644 index 0000000..9da104b --- /dev/null +++ b/src/bignum.h @@ -0,0 +1,525 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +// +// Ported to OpenSSL 1.1+/3.x: BIGNUM became an opaque type, so CBigNum now +// wraps a BIGNUM* instead of inheriting from BIGNUM. Identical semantics. + +#include +#include +#include + + + + + +class bignum_error : public std::runtime_error +{ +public: + explicit bignum_error(const std::string& str) : std::runtime_error(str) {} +}; + + + +class CAutoBN_CTX +{ +protected: + BN_CTX* pctx; + BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; } + +public: + CAutoBN_CTX() + { + pctx = BN_CTX_new(); + if (pctx == NULL) + throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL"); + } + + ~CAutoBN_CTX() + { + if (pctx != NULL) + BN_CTX_free(pctx); + } + + operator BN_CTX*() { return pctx; } + BN_CTX& operator*() { return *pctx; } + BN_CTX** operator&() { return &pctx; } + bool operator!() { return (pctx == NULL); } +}; + + + +class CBigNum +{ +protected: + BIGNUM* bn; + + void init() + { + bn = BN_new(); + if (bn == NULL) + throw bignum_error("CBigNum : BN_new failed"); + } + +public: + BIGNUM* get() { return bn; } + const BIGNUM* get() const { return bn; } + + CBigNum() + { + init(); + } + + CBigNum(const CBigNum& b) + { + init(); + if (!BN_copy(bn, b.bn)) + { + BN_clear_free(bn); + throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); + } + } + + explicit CBigNum(const std::string& str) + { + init(); + SetHex(str); + } + + CBigNum& operator=(const CBigNum& b) + { + if (!BN_copy(bn, b.bn)) + throw bignum_error("CBigNum::operator= : BN_copy failed"); + return (*this); + } + + ~CBigNum() + { + BN_clear_free(bn); + } + + CBigNum(char n) { init(); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { init(); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { init(); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long n) { init(); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64 n) { init(); setint64(n); } + CBigNum(unsigned char n) { init(); setulong(n); } + CBigNum(unsigned short n) { init(); setulong(n); } + CBigNum(unsigned int n) { init(); setulong(n); } + CBigNum(unsigned long n) { init(); setulong(n); } + CBigNum(uint64 n) { init(); setuint64(n); } + explicit CBigNum(uint256 n) { init(); setuint256(n); } + + explicit CBigNum(const std::vector& vch) + { + init(); + setvch(vch); + } + + void setulong(unsigned long n) + { + if (!BN_set_word(bn, n)) + throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); + } + + unsigned long getulong() const + { + return BN_get_word(bn); + } + + unsigned int getuint() const + { + return BN_get_word(bn); + } + + int getint() const + { + unsigned long n = BN_get_word(bn); + if (!BN_is_negative(bn)) + return (n > INT_MAX ? INT_MAX : n); + else + return (n > INT_MAX ? INT_MIN : -(int)n); + } + + void setint64(int64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fNegative = false; + if (n < (int64)0) + { + n = -n; + fNegative = true; + } + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = (fNegative ? 0x80 : 0); + else if (fNegative) + c |= 0x80; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, bn); + } + + void setuint64(uint64 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + for (int i = 0; i < 8; i++) + { + unsigned char c = (n >> 56) & 0xff; + n <<= 8; + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize) & 0xff; + BN_mpi2bn(pch, p - pch, bn); + } + + void setuint256(uint256 n) + { + unsigned char pch[sizeof(n) + 6]; + unsigned char* p = pch + 4; + bool fLeadingZeroes = true; + unsigned char* pbegin = (unsigned char*)&n; + unsigned char* psrc = pbegin + sizeof(n); + while (psrc != pbegin) + { + unsigned char c = *(--psrc); + if (fLeadingZeroes) + { + if (c == 0) + continue; + if (c & 0x80) + *p++ = 0; + fLeadingZeroes = false; + } + *p++ = c; + } + unsigned int nSize = p - (pch + 4); + pch[0] = (nSize >> 24) & 0xff; + pch[1] = (nSize >> 16) & 0xff; + pch[2] = (nSize >> 8) & 0xff; + pch[3] = (nSize >> 0) & 0xff; + BN_mpi2bn(pch, p - pch, bn); + } + + uint256 getuint256() + { + unsigned int nSize = BN_bn2mpi(bn, NULL); + if (nSize < 4) + return 0; + std::vector vch(nSize); + BN_bn2mpi(bn, &vch[0]); + if (vch.size() > 4) + vch[4] &= 0x7f; + uint256 n = 0; + for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) + ((unsigned char*)&n)[i] = vch[j]; + return n; + } + + void setvch(const std::vector& vch) + { + std::vector vch2(vch.size() + 4); + unsigned int nSize = vch.size(); + vch2[0] = (nSize >> 24) & 0xff; + vch2[1] = (nSize >> 16) & 0xff; + vch2[2] = (nSize >> 8) & 0xff; + vch2[3] = (nSize >> 0) & 0xff; + reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); + BN_mpi2bn(&vch2[0], vch2.size(), bn); + } + + std::vector getvch() const + { + unsigned int nSize = BN_bn2mpi(bn, NULL); + if (nSize < 4) + return std::vector(); + std::vector vch(nSize); + BN_bn2mpi(bn, &vch[0]); + vch.erase(vch.begin(), vch.begin() + 4); + reverse(vch.begin(), vch.end()); + return vch; + } + + CBigNum& SetCompact(unsigned int nCompact) + { + unsigned int nSize = nCompact >> 24; + std::vector vch(4 + nSize); + vch[3] = nSize; + if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff; + if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff; + if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff; + BN_mpi2bn(&vch[0], vch.size(), bn); + return *this; + } + + unsigned int GetCompact() const + { + unsigned int nSize = BN_bn2mpi(bn, NULL); + std::vector vch(nSize); + nSize -= 4; + BN_bn2mpi(bn, &vch[0]); + unsigned int nCompact = nSize << 24; + if (nSize >= 1) nCompact |= (vch[4] << 16); + if (nSize >= 2) nCompact |= (vch[5] << 8); + if (nSize >= 3) nCompact |= (vch[6] << 0); + return nCompact; + } + + void SetHex(const std::string& str) + { + // skip 0x + const char* psz = str.c_str(); + while (isspace(*psz)) + psz++; + bool fNegative = false; + if (*psz == '-') + { + fNegative = true; + psz++; + } + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + while (isspace(*psz)) + psz++; + + // hex string to bignum + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + *this = 0; + while (isxdigit(*psz)) + { + *this <<= 4; + int n = phexdigit[(unsigned char)*psz++]; + *this += n; + } + if (fNegative) + *this = 0 - *this; + } + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return ::GetSerializeSize(getvch(), nType, nVersion); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + ::Serialize(s, getvch(), nType, nVersion); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + vector vch; + ::Unserialize(s, vch, nType, nVersion); + setvch(vch); + } + + + bool operator!() const + { + return BN_is_zero(bn); + } + + CBigNum& operator+=(const CBigNum& b) + { + if (!BN_add(bn, bn, b.bn)) + throw bignum_error("CBigNum::operator+= : BN_add failed"); + return *this; + } + + CBigNum& operator-=(const CBigNum& b) + { + *this = *this - b; + return *this; + } + + CBigNum& operator*=(const CBigNum& b) + { + CAutoBN_CTX pctx; + if (!BN_mul(bn, bn, b.bn, pctx)) + throw bignum_error("CBigNum::operator*= : BN_mul failed"); + return *this; + } + + CBigNum& operator/=(const CBigNum& b) + { + *this = *this / b; + return *this; + } + + CBigNum& operator%=(const CBigNum& b) + { + *this = *this % b; + return *this; + } + + CBigNum& operator<<=(unsigned int shift) + { + if (!BN_lshift(bn, bn, shift)) + throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); + return *this; + } + + CBigNum& operator>>=(unsigned int shift) + { + if (!BN_rshift(bn, bn, shift)) + throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); + return *this; + } + + + CBigNum& operator++() + { + // prefix operator + if (!BN_add(bn, bn, BN_value_one())) + throw bignum_error("CBigNum::operator++ : BN_add failed"); + return *this; + } + + const CBigNum operator++(int) + { + // postfix operator + const CBigNum ret = *this; + ++(*this); + return ret; + } + + CBigNum& operator--() + { + // prefix operator + CBigNum r; + if (!BN_sub(r.bn, bn, BN_value_one())) + throw bignum_error("CBigNum::operator-- : BN_sub failed"); + *this = r; + return *this; + } + + const CBigNum operator--(int) + { + // postfix operator + const CBigNum ret = *this; + --(*this); + return ret; + } + + + friend inline const CBigNum operator+(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator-(const CBigNum& a); + friend inline const CBigNum operator*(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); + friend inline const CBigNum operator<<(const CBigNum& a, unsigned int shift); + friend inline const CBigNum operator>>(const CBigNum& a, unsigned int shift); + friend inline bool operator==(const CBigNum& a, const CBigNum& b); + friend inline bool operator!=(const CBigNum& a, const CBigNum& b); + friend inline bool operator<=(const CBigNum& a, const CBigNum& b); + friend inline bool operator>=(const CBigNum& a, const CBigNum& b); + friend inline bool operator<(const CBigNum& a, const CBigNum& b); + friend inline bool operator>(const CBigNum& a, const CBigNum& b); +}; + + + +inline const CBigNum operator+(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_add(r.bn, a.bn, b.bn)) + throw bignum_error("CBigNum::operator+ : BN_add failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a, const CBigNum& b) +{ + CBigNum r; + if (!BN_sub(r.bn, a.bn, b.bn)) + throw bignum_error("CBigNum::operator- : BN_sub failed"); + return r; +} + +inline const CBigNum operator-(const CBigNum& a) +{ + CBigNum r(a); + BN_set_negative(r.bn, !BN_is_negative(r.bn)); + return r; +} + +inline const CBigNum operator*(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mul(r.bn, a.bn, b.bn, pctx)) + throw bignum_error("CBigNum::operator* : BN_mul failed"); + return r; +} + +inline const CBigNum operator/(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_div(r.bn, NULL, a.bn, b.bn, pctx)) + throw bignum_error("CBigNum::operator/ : BN_div failed"); + return r; +} + +inline const CBigNum operator%(const CBigNum& a, const CBigNum& b) +{ + CAutoBN_CTX pctx; + CBigNum r; + if (!BN_mod(r.bn, a.bn, b.bn, pctx)) + throw bignum_error("CBigNum::operator% : BN_div failed"); + return r; +} + +inline const CBigNum operator<<(const CBigNum& a, unsigned int shift) +{ + CBigNum r; + if (!BN_lshift(r.bn, a.bn, shift)) + throw bignum_error("CBigNum:operator<< : BN_lshift failed"); + return r; +} + +inline const CBigNum operator>>(const CBigNum& a, unsigned int shift) +{ + CBigNum r; + if (!BN_rshift(r.bn, a.bn, shift)) + throw bignum_error("CBigNum:operator>> : BN_rshift failed"); + return r; +} + +inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.bn, b.bn) == 0); } +inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.bn, b.bn) != 0); } +inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.bn, b.bn) <= 0); } +inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.bn, b.bn) >= 0); } +inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.bn, b.bn) < 0); } +inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(a.bn, b.bn) > 0); } diff --git a/src/bitflashd.cpp b/src/bitflashd.cpp new file mode 100644 index 0000000..d4ced77 --- /dev/null +++ b/src/bitflashd.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// bitflashd -- the headless Bitflash rendezvous relay (the "meeting node"). +// Runs on a machine with a public IP (a cheap VPS) and lets nodes behind CGNAT +// reach each other: both sides connect OUT to this relay, which pairs them by +// `.btf` pubkey and forwards ciphertext (it holds no keys and reads nothing). +// +// Build on the VPS (Linux): +// g++ -std=gnu++14 -O2 -pthread bitflashd.cpp btfrv.cpp -o bitflashd +// Run: +// ./bitflashd 8434 # or any port you open in the firewall +// +// Cross-builds unchanged on Windows (for local testing): +// g++ -std=gnu++14 -O2 -pthread bitflashd.cpp btfrv.cpp -lws2_32 -o bitflashd.exe + +#include "btfrv.h" +#include +#include +#include + +int main(int argc, char* argv[]) +{ + unsigned short port = 8434; + if (argc >= 2) + port = (unsigned short)atoi(argv[1]); + + printf("bitflashd -- Bitflash rendezvous relay\n"); + printf("listening on 0.0.0.0:%u (open this port in your firewall)\n", port); + fflush(stdout); + + if (!btf::RvInit()) + { + fprintf(stderr, "bitflashd: socket init failed\n"); + return 1; + } + + // Blocks forever, forwarding between paired peers. + if (!btf::RvRelayRun(port)) + { + fprintf(stderr, "bitflashd: could not bind/listen on port %u\n", port); + return 1; + } + return 0; +} diff --git a/src/btfaddr.cpp b/src/btfaddr.cpp new file mode 100644 index 0000000..5f983bc --- /dev/null +++ b/src/btfaddr.cpp @@ -0,0 +1,248 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Bitflash `.btf` self-certifying addresses + service descriptors. +// Ported from the Itzal hidden.rs design. See btfaddr.h. + +#include "btfaddr.h" + +#include +#define SECP256K1_STATIC +#include +#include +#include +#include + +#include +#include +#include + +using json = nlohmann::json; + +namespace btf +{ + +const char* TLD = ".btf"; + +static const char* B32 = "abcdefghijklmnopqrstuvwxyz234567"; + +std::string Base32Encode(const unsigned char* data, size_t n) +{ + std::string out; + out.reserve(n * 8 / 5 + 1); + uint64_t buf = 0; + uint32_t bits = 0; + for (size_t i = 0; i < n; i++) + { + buf = (buf << 8) | data[i]; + bits += 8; + while (bits >= 5) + { + bits -= 5; + out.push_back(B32[(buf >> bits) & 0x1f]); + } + buf &= (bits == 0) ? 0 : ((1ULL << bits) - 1); + } + if (bits > 0) + out.push_back(B32[(buf << (5 - bits)) & 0x1f]); + return out; +} + +bool Base32Decode(const std::string& s, std::vector& out) +{ + out.clear(); + out.reserve(s.size() * 5 / 8); + uint64_t buf = 0; + uint32_t bits = 0; + for (unsigned char c : s) + { + uint64_t val; + if (c >= 'a' && c <= 'z') val = c - 'a'; + else if (c >= 'A' && c <= 'Z') val = c - 'A'; + else if (c >= '2' && c <= '7') val = c - '2' + 26; + else return false; + buf = (buf << 5) | val; + bits += 5; + if (bits >= 8) + { + bits -= 8; + out.push_back((unsigned char)((buf >> bits) & 0xff)); + } + buf &= (bits == 0) ? 0 : ((1ULL << bits) - 1); + } + return true; +} + +static std::string ToLowerTrim(const std::string& in) +{ + std::string s = in; + // trim whitespace + size_t a = s.find_first_not_of(" \t\r\n"); + size_t b = s.find_last_not_of(" \t\r\n"); + if (a == std::string::npos) return std::string(); + s = s.substr(a, b - a + 1); + // strip trailing dots (fully-qualified hostname form) + while (!s.empty() && s.back() == '.') s.pop_back(); + for (char& c : s) c = (char)std::tolower((unsigned char)c); + return s; +} + +// Two-byte checksum, domain-separated so it can't collide with any other hash use. +static void Checksum(const unsigned char pubkey[32], unsigned char out[2]) +{ + static const char* DS = ".btf-checksum-v1"; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (const unsigned char*)DS, strlen(DS)); + SHA256_Update(&ctx, pubkey, 32); + unsigned char d[32]; + SHA256_Final(d, &ctx); + out[0] = d[0]; + out[1] = d[1]; +} + +std::string Address(const unsigned char pubkey[32]) +{ + unsigned char raw[34]; + memcpy(raw, pubkey, 32); + Checksum(pubkey, raw + 32); + return Base32Encode(raw, 34) + TLD; +} + +bool IsBtf(const std::string& host) +{ + std::string s = ToLowerTrim(host); + std::string tld = TLD; + if (s.size() < tld.size()) return false; + return s.compare(s.size() - tld.size(), tld.size(), tld) == 0; +} + +bool ParseAddress(const std::string& addr, unsigned char pubkeyOut[32]) +{ + std::string s = ToLowerTrim(addr); + std::string tld = TLD; + if (s.size() < tld.size()) return false; + if (s.compare(s.size() - tld.size(), tld.size(), tld) != 0) return false; + std::string label = s.substr(0, s.size() - tld.size()); + + std::vector raw; + if (!Base32Decode(label, raw)) return false; + if (raw.size() < 34) return false; + + unsigned char pk[32]; + memcpy(pk, &raw[0], 32); + unsigned char sum[2]; + Checksum(pk, sum); + if (raw[32] != sum[0] || raw[33] != sum[1]) return false; + + memcpy(pubkeyOut, pk, 32); + return true; +} + + +// ---- descriptor ------------------------------------------------------------ + +static std::string HexEncode(const unsigned char* p, size_t n) +{ + static const char* h = "0123456789abcdef"; + std::string s; + s.reserve(n * 2); + for (size_t i = 0; i < n; i++) { s += h[p[i] >> 4]; s += h[p[i] & 0xf]; } + return s; +} + +static bool HexDecode(const std::string& s, unsigned char* out, size_t n) +{ + if (s.size() != n * 2) return false; + for (size_t i = 0; i < n; i++) + { + unsigned int b; + if (sscanf(s.c_str() + i * 2, "%2x", &b) != 1) return false; + out[i] = (unsigned char)b; + } + return true; +} + +// Digest signed over the descriptor fields (domain-separated). +static void DescriptorDigest(const unsigned char pubkey[32], const std::string& enc, + const std::string& meeting_node, uint64_t created, + unsigned char out[32]) +{ + static const char* DS = ".btf-descriptor-v2"; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (const unsigned char*)DS, strlen(DS)); + SHA256_Update(&ctx, pubkey, 32); + SHA256_Update(&ctx, (const unsigned char*)enc.data(), enc.size()); + SHA256_Update(&ctx, (const unsigned char*)meeting_node.data(), meeting_node.size()); + unsigned char be[8]; + for (int i = 0; i < 8; i++) be[i] = (unsigned char)((created >> (8 * (7 - i))) & 0xff); + SHA256_Update(&ctx, be, 8); + SHA256_Final(out, &ctx); +} + +std::string SignDescriptor(void* ctxv, const unsigned char seckey[32], + const std::string& enc, const std::string& meeting_node, + uint64_t created) +{ + secp256k1_context* ctx = (secp256k1_context*)ctxv; + secp256k1_keypair kp; + if (!secp256k1_keypair_create(ctx, &kp, seckey)) return std::string(); + secp256k1_xonly_pubkey xpub; + if (!secp256k1_keypair_xonly_pub(ctx, &xpub, NULL, &kp)) return std::string(); + unsigned char pubkey[32]; + secp256k1_xonly_pubkey_serialize(ctx, pubkey, &xpub); + + unsigned char digest[32]; + DescriptorDigest(pubkey, enc, meeting_node, created, digest); + + unsigned char sig[64]; + // Deterministic signature (NULL aux_rand), matching Itzal's no-aux-rand signing. + if (!secp256k1_schnorrsig_sign32(ctx, sig, digest, &kp, NULL)) return std::string(); + + json v = json::object(); + v["v"] = 2; + v["pubkey"] = HexEncode(pubkey, 32); + v["enc"] = enc; + v["meeting_node"] = meeting_node; + v["created"] = created; + v["sig"] = HexEncode(sig, 64); + return v.dump(); +} + +bool VerifyDescriptor(void* ctxv, const std::string& jsonStr, + const unsigned char expect_pubkey[32], Descriptor& out) +{ + secp256k1_context* ctx = (secp256k1_context*)ctxv; + json v; + try { v = json::parse(jsonStr); } catch (...) { return false; } + if (!v.is_object()) return false; + if (!v.contains("pubkey") || !v.contains("enc") || !v.contains("meeting_node") + || !v.contains("created") || !v.contains("sig")) return false; + + unsigned char pubkey[32]; + if (!HexDecode(v["pubkey"].get(), pubkey, 32)) return false; + if (memcmp(pubkey, expect_pubkey, 32) != 0) return false; // descriptor is for a different key + + std::string enc = v["enc"].get(); + std::string meeting_node = v["meeting_node"].get(); + uint64_t created = v["created"].get(); + + unsigned char sig[64]; + if (!HexDecode(v["sig"].get(), sig, 64)) return false; + + unsigned char digest[32]; + DescriptorDigest(expect_pubkey, enc, meeting_node, created, digest); + + secp256k1_xonly_pubkey xpub; + if (!secp256k1_xonly_pubkey_parse(ctx, &xpub, expect_pubkey)) return false; + if (!secp256k1_schnorrsig_verify(ctx, sig, digest, 32, &xpub)) return false; + + memcpy(out.pubkey, expect_pubkey, 32); + out.enc = enc; + out.meeting_node = meeting_node; + out.created = created; + return true; +} + +} // namespace btf diff --git a/src/btfaddr.h b/src/btfaddr.h new file mode 100644 index 0000000..1addf8f --- /dev/null +++ b/src/btfaddr.h @@ -0,0 +1,72 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Bitflash hidden services -- self-certifying `.btf` addresses (a Tor `.onion` +// analogue, ported from the Itzal design). An address IS the service's public +// key: base32(pubkey[32] || checksum[2]).btf, derived from the owner's +// secp256k1 x-only key, so it cannot be forged (no CA, no DNS). Resolving it +// means proving a descriptor is signed by exactly this key. +// +// This is the ADDRESS + DESCRIPTOR layer. Reachability (rendezvous over the +// embedded Nostr relays so the origin IP is never exposed) builds on top. + +#ifndef BITFLASH_BTFADDR_H +#define BITFLASH_BTFADDR_H + +#include +#include +#include + +namespace btf +{ + +// Pseudo-TLD, resolved inside the Bitflash client, never by real DNS. +extern const char* TLD; // ".btf" + +// RFC 4648 base32, lowercase, no padding (case-insensitive hostname label). +std::string Base32Encode(const unsigned char* data, size_t n); +bool Base32Decode(const std::string& s, std::vector& out); + +// Derive the `.btf` address for a 32-byte x-only public key. +std::string Address(const unsigned char pubkey[32]); + +// True if a hostname is a `.btf` address (case-insensitive, optional trailing dot). +bool IsBtf(const std::string& host); + +// Parse a `.btf` address back to its 32-byte public key, verifying the checksum. +// Returns false if the label isn't valid base32, is too short, or checksum fails. +bool ParseAddress(const std::string& addr, unsigned char pubkeyOut[32]); + + +// ---- Service descriptor (published later on Nostr) ------------------------- +// +// A descriptor tells a client how to REACH a service after resolving its +// address: "service is reachable via , encrypt end-to-end +// to my x25519 key ". It is signed by the service key and self-certifying: +// the client requires the signature to verify under the exact pubkey the +// address decodes to, so nobody can publish a descriptor for an address they +// don't own (no hijacking, even via a malicious relay). + +struct Descriptor +{ + unsigned char pubkey[32]; + std::string enc; // service x25519 public key (hex) for the E2E channel + std::string meeting_node; // rendezvous node the service is registered at + uint64_t created; +}; + +// Sign a descriptor. `ctx` is a secp256k1_context* (passed as void* to keep this +// header free of secp headers); `seckey` is the 32-byte service secret key. +// Returns the descriptor JSON as a string, or "" on failure. +std::string SignDescriptor(void* ctx, const unsigned char seckey[32], + const std::string& enc, const std::string& meeting_node, + uint64_t created); + +// Verify a descriptor is validly signed by `expect_pubkey` (the key the resolved +// `.btf` address encodes). Returns true and fills `out` on success. +bool VerifyDescriptor(void* ctx, const std::string& jsonStr, + const unsigned char expect_pubkey[32], Descriptor& out); + +} // namespace btf + +#endif diff --git a/src/btfchan.cpp b/src/btfchan.cpp new file mode 100644 index 0000000..09aaaad --- /dev/null +++ b/src/btfchan.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Bitflash end-to-end encrypted channel. Ported from Itzal echan.rs. +// Uses libsodium crypto_box_curve25519xchacha20poly1305 (X25519 + XChaCha20-Poly1305), +// the same primitive as Rust's crypto_box ChaChaBox. + +#include "btfchan.h" +#include +#include + +namespace btf +{ + +// Compile-time sanity: our constants match libsodium's. +static_assert(CHAN_NONCEBYTES == crypto_box_curve25519xchacha20poly1305_NONCEBYTES, "nonce"); +static_assert(CHAN_MACBYTES == crypto_box_curve25519xchacha20poly1305_MACBYTES, "mac"); +static_assert(CHAN_KEYBYTES == crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES, "key"); + +bool ChanInit() +{ + return sodium_init() >= 0; +} + +void ChanKeypair(unsigned char pk[32], unsigned char sk[32]) +{ + crypto_box_curve25519xchacha20poly1305_keypair(pk, sk); +} + +void ChanPublicFromSecret(unsigned char pk[32], const unsigned char sk[32]) +{ + // X25519 public = scalarmult of the secret with the curve25519 base point. + crypto_scalarmult_base(pk, sk); +} + +bool ChanClientHandshake(const unsigned char endpoint_static_pub[32], + unsigned char eph_pub_out[32], unsigned char shared_out[32]) +{ + unsigned char eph_sk[32]; + crypto_box_curve25519xchacha20poly1305_keypair(eph_pub_out, eph_sk); + // shared = X25519(eph_sk, endpoint_static_pub) -> hashed to a box key + int r = crypto_box_curve25519xchacha20poly1305_beforenm(shared_out, endpoint_static_pub, eph_sk); + sodium_memzero(eph_sk, sizeof(eph_sk)); + return r == 0; +} + +bool ChanEndpointHandshake(const unsigned char client_eph_pub[32], + const unsigned char my_static_sk[32], unsigned char shared_out[32]) +{ + // shared = X25519(my_static_sk, client_eph_pub) -> same box key as the client computed + return crypto_box_curve25519xchacha20poly1305_beforenm(shared_out, client_eph_pub, my_static_sk) == 0; +} + +std::vector ChanEncryptFrame(const unsigned char shared[32], + const unsigned char* pt, size_t n) +{ + unsigned char nonce[CHAN_NONCEBYTES]; + randombytes_buf(nonce, sizeof(nonce)); + + size_t ctlen = n + CHAN_MACBYTES; + size_t bodylen = CHAN_NONCEBYTES + ctlen; + + std::vector out(4 + bodylen); + out[0] = (unsigned char)((bodylen >> 24) & 0xff); + out[1] = (unsigned char)((bodylen >> 16) & 0xff); + out[2] = (unsigned char)((bodylen >> 8) & 0xff); + out[3] = (unsigned char)(bodylen & 0xff); + memcpy(&out[4], nonce, CHAN_NONCEBYTES); + + crypto_box_curve25519xchacha20poly1305_easy_afternm( + &out[4 + CHAN_NONCEBYTES], pt, n, nonce, shared); + return out; +} + +bool ChanDecryptBody(const unsigned char shared[32], + const unsigned char* body, size_t n, std::vector& out) +{ + if (n < (size_t)(CHAN_NONCEBYTES + CHAN_MACBYTES)) + return false; + const unsigned char* nonce = body; + const unsigned char* ct = body + CHAN_NONCEBYTES; + size_t ctlen = n - CHAN_NONCEBYTES; + out.resize(ctlen - CHAN_MACBYTES); + int r = crypto_box_curve25519xchacha20poly1305_open_easy_afternm( + out.empty() ? NULL : &out[0], ct, ctlen, nonce, shared); + if (r != 0) + { + out.clear(); + return false; + } + return true; +} + +} // namespace btf diff --git a/src/btfchan.h b/src/btfchan.h new file mode 100644 index 0000000..7963f9d --- /dev/null +++ b/src/btfchan.h @@ -0,0 +1,59 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// End-to-end encrypted channel for Bitflash (ported from the Itzal echan.rs). +// Wraps a raw pipe in an authenticated stream so relays/rendezvous nodes see +// only ciphertext and cannot MITM: only the endpoint holding the static secret +// can derive the key. +// +// Handshake: the client makes an ephemeral X25519 key and sends its public half +// to the endpoint. Both sides compute the same box (X25519 ECDH -> shared key +// for XChaCha20-Poly1305, i.e. libsodium crypto_box). Wire frame: +// [len:4 BE][nonce:24][ciphertext || 16-byte tag] +// with a fresh random 24-byte nonce per frame (safe to pick at random). + +#ifndef BITFLASH_BTFCHAN_H +#define BITFLASH_BTFCHAN_H + +#include +#include +#include + +namespace btf +{ + +static const int CHAN_KEYBYTES = 32; // X25519 public/secret and shared key +static const int CHAN_NONCEBYTES = 24; // XChaCha20-Poly1305 nonce +static const int CHAN_MACBYTES = 16; // Poly1305 tag + +// Must be called once before any channel use (initializes libsodium). Idempotent. +bool ChanInit(); + +// Generate a static X25519 keypair (e.g. the endpoint's long-term key). +void ChanKeypair(unsigned char pk[32], unsigned char sk[32]); + +// Derive the X25519 public key from a stored secret key (X25519 base mult). +void ChanPublicFromSecret(unsigned char pk[32], const unsigned char sk[32]); + +// Client side: given the endpoint's static public key, produce an ephemeral +// public key (to hand the endpoint) and the shared key. Returns false on error. +bool ChanClientHandshake(const unsigned char endpoint_static_pub[32], + unsigned char eph_pub_out[32], unsigned char shared_out[32]); + +// Endpoint side: rebuild the same shared key from the client's ephemeral public +// key and my static secret. Only the static-secret holder can do this. +bool ChanEndpointHandshake(const unsigned char client_eph_pub[32], + const unsigned char my_static_sk[32], unsigned char shared_out[32]); + +// Encrypt a plaintext chunk into a full wire frame ([len][nonce][ct||tag]). +std::vector ChanEncryptFrame(const unsigned char shared[32], + const unsigned char* pt, size_t n); + +// Decrypt a frame BODY (nonce||ciphertext||tag, i.e. without the 4-byte length +// prefix). Returns true and fills `out` on success; false on auth failure. +bool ChanDecryptBody(const unsigned char shared[32], + const unsigned char* body, size_t n, std::vector& out); + +} // namespace btf + +#endif diff --git a/src/btfrv.cpp b/src/btfrv.cpp new file mode 100644 index 0000000..ffda08c --- /dev/null +++ b/src/btfrv.cpp @@ -0,0 +1,250 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Bitflash rendezvous transport. See btfrv.h. +// Windows/Winsock implementation (Layer 6 will #ifdef this for Linux/BSD sockets). + +#include "btfrv.h" + +// Portable sockets: Winsock on Windows, BSD sockets on Linux/POSIX (for the +// headless relay `bitflashd` that runs on the VPS). +#ifdef _WIN32 +#include +#include +#define CLOSESOCK closesocket +#else +#include +#include +#include +#include +#include +#include +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +#define CLOSESOCK ::close +#ifndef SD_BOTH +#define SD_BOTH SHUT_RDWR +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace btf +{ + +static std::atomic g_stop(false); +static SOCKET g_listen = INVALID_SOCKET; + +// Services that have registered and are waiting for a client, keyed by pubkey. +static std::map g_waiting; +static std::mutex g_mtx; + +bool RvInit() +{ + static bool done = false; + if (done) return true; +#ifdef _WIN32 + WSADATA wsa; + if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return false; +#else + signal(SIGPIPE, SIG_IGN); // don't die when writing to a closed peer +#endif + done = true; + return true; +} + +// ---- low-level helpers ---- + +static bool ReadN(SOCKET s, void* buf, int n) +{ + char* p = (char*)buf; + int off = 0; + while (off < n) + { + int r = recv(s, p + off, n - off, 0); + if (r <= 0) return false; + off += r; + } + return true; +} + +static bool WriteN(SOCKET s, const void* buf, int n) +{ + const char* p = (const char*)buf; + int off = 0; + while (off < n) + { + int r = send(s, p + off, n - off, 0); + if (r <= 0) return false; + off += r; + } + return true; +} + +static SOCKET ConnectTo(const char* host, unsigned short port) +{ + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + char portstr[16]; sprintf(portstr, "%u", (unsigned)port); + if (getaddrinfo(host, portstr, &hints, &res) != 0 || !res) return INVALID_SOCKET; + SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s == INVALID_SOCKET) { freeaddrinfo(res); return INVALID_SOCKET; } + if (connect(s, res->ai_addr, (int)res->ai_addrlen) != 0) + { + CLOSESOCK(s); freeaddrinfo(res); return INVALID_SOCKET; + } + freeaddrinfo(res); + return s; +} + +// ---- relay side ---- + +// Pump bytes from `from` to `to`. Owns closing `from`; shuts down `to` on exit +// so the peer-direction thread unblocks and closes its own socket exactly once. +static void Forward(SOCKET from, SOCKET to) +{ + char buf[32 * 1024]; + for (;;) + { + int r = recv(from, buf, sizeof(buf), 0); + if (r <= 0) break; + if (!WriteN(to, buf, r)) break; + } + shutdown(to, SD_BOTH); + CLOSESOCK(from); +} + +static void HandleIncoming(SOCKET s) +{ + unsigned char hdr[33]; + if (!ReadN(s, hdr, 33)) { CLOSESOCK(s); return; } + char role = (char)hdr[0]; + std::string pubkey((char*)hdr + 1, 32); + + if (role == 'S') + { + // Register and wait; a future client will pair us. + std::lock_guard lk(g_mtx); + // If a stale service for this key exists, drop it. + std::map::iterator it = g_waiting.find(pubkey); + if (it != g_waiting.end()) CLOSESOCK(it->second); + g_waiting[pubkey] = s; + // leave the socket open in the map; do not close here. + } + else if (role == 'C') + { + SOCKET svc = INVALID_SOCKET; + { + std::lock_guard lk(g_mtx); + std::map::iterator it = g_waiting.find(pubkey); + if (it != g_waiting.end()) { svc = it->second; g_waiting.erase(it); } + } + if (svc == INVALID_SOCKET) + { + unsigned char no = 0x00; + WriteN(s, &no, 1); + CLOSESOCK(s); + return; + } + // Pair: tell both sides, then forward raw bytes bidirectionally. + unsigned char ok = 0x01; + bool a = WriteN(svc, &ok, 1); + bool b = WriteN(s, &ok, 1); + if (!a || !b) { CLOSESOCK(svc); CLOSESOCK(s); return; } + std::thread(Forward, s, svc).detach(); + std::thread(Forward, svc, s).detach(); + } + else + { + CLOSESOCK(s); + } +} + +bool RvRelayRun(unsigned short port) +{ + if (!RvInit()) return false; + g_stop = false; + + g_listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (g_listen == INVALID_SOCKET) return false; + int yes = 1; + setsockopt(g_listen, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + if (bind(g_listen, (struct sockaddr*)&addr, sizeof(addr)) != 0) { CLOSESOCK(g_listen); return false; } + if (listen(g_listen, 16) != 0) { CLOSESOCK(g_listen); return false; } + + while (!g_stop) + { + struct sockaddr_in cli; + socklen_t len = sizeof(cli); + SOCKET s = accept(g_listen, (struct sockaddr*)&cli, &len); + if (s == INVALID_SOCKET) + { + if (g_stop) break; + continue; + } + std::thread(HandleIncoming, s).detach(); + } + CLOSESOCK(g_listen); + g_listen = INVALID_SOCKET; + return true; +} + +void RvRelayStop() +{ + g_stop = true; + if (g_listen != INVALID_SOCKET) + CLOSESOCK(g_listen); // unblock accept() +} + +// ---- service / client helpers ---- + +RvSocket RvServiceRegister(const char* relay_host, unsigned short port, + const unsigned char my_pubkey[32]) +{ + if (!RvInit()) return RV_INVALID; + SOCKET s = ConnectTo(relay_host, port); + if (s == INVALID_SOCKET) return RV_INVALID; + unsigned char hdr[33]; + hdr[0] = (unsigned char)'S'; + memcpy(hdr + 1, my_pubkey, 32); + if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } + unsigned char paired = 0; + if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; } + return (RvSocket)s; +} + +RvSocket RvClientConnect(const char* relay_host, unsigned short port, + const unsigned char target_pubkey[32]) +{ + if (!RvInit()) return RV_INVALID; + SOCKET s = ConnectTo(relay_host, port); + if (s == INVALID_SOCKET) return RV_INVALID; + unsigned char hdr[33]; + hdr[0] = (unsigned char)'C'; + memcpy(hdr + 1, target_pubkey, 32); + if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } + unsigned char paired = 0; + if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; } + return (RvSocket)s; +} + +bool RvReadN(RvSocket s, void* buf, int n) { return ReadN((SOCKET)s, buf, n); } +bool RvWriteN(RvSocket s, const void* buf, int n) { return WriteN((SOCKET)s, buf, n); } +void RvClose(RvSocket s) { if (s != RV_INVALID) CLOSESOCK((SOCKET)s); } + +} // namespace btf diff --git a/src/btfrv.h b/src/btfrv.h new file mode 100644 index 0000000..c776201 --- /dev/null +++ b/src/btfrv.h @@ -0,0 +1,59 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Bitflash rendezvous transport -- the "meeting node" that lets two nodes behind +// CGNAT connect without a public IP or port forwarding. Both sides connect OUT +// to the relay (which HAS a public IP, e.g. the VPS); the relay pairs them by +// `.btf` pubkey and forwards raw bytes between them. The endpoints then run the +// echan end-to-end channel on top, so the relay sees only ciphertext and cannot +// MITM (the client boxes to the service's STATIC key from its self-certified +// descriptor -- a substituting relay just breaks the channel, never reads it). +// +// This is the server that runs on the VPS (headless, Layer 6 ports it to Linux) +// plus the client/service helpers the desktop node uses. + +#ifndef BITFLASH_BTFRV_H +#define BITFLASH_BTFRV_H + +#include + +namespace btf +{ + +// A connected, paired rendezvous socket (opaque). Read/write raw bytes; the +// caller layers echan on top. Close with RvClose. +typedef intptr_t RvSocket; +static const RvSocket RV_INVALID = (RvSocket)-1; + +// Initialize sockets (Winsock on Windows). Idempotent. +bool RvInit(); + +// ---- relay side (runs on the VPS) ---- +// Run the rendezvous relay forever on `port`, forwarding between paired peers. +// Blocks; run it in a thread. Returns false if it can't bind/listen. +bool RvRelayRun(unsigned short port); + +// Stop a running relay (signals RvRelayRun to return). +void RvRelayStop(); + +// ---- service side (the node being reached by its .btf address) ---- +// Register at the relay under `my_pubkey` (32 bytes) and block until a client is +// paired to us. Returns the paired socket, or RV_INVALID on error. +RvSocket RvServiceRegister(const char* relay_host, unsigned short port, + const unsigned char my_pubkey[32]); + +// ---- client side (the node dialing a .btf address) ---- +// Connect through the relay to the service registered under `target_pubkey`. +// Returns the paired socket, or RV_INVALID on error. +RvSocket RvClientConnect(const char* relay_host, unsigned short port, + const unsigned char target_pubkey[32]); + +// Blocking read/write of exactly n bytes over a rendezvous socket. Return false +// on error/EOF. +bool RvReadN(RvSocket s, void* buf, int n); +bool RvWriteN(RvSocket s, const void* buf, int n); +void RvClose(RvSocket s); + +} // namespace btf + +#endif diff --git a/src/btftunnel.cpp b/src/btftunnel.cpp new file mode 100644 index 0000000..65c4b89 --- /dev/null +++ b/src/btftunnel.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Transparent anonymous tunnel. See btftunnel.h. + +#include "btftunnel.h" +#include "btfchan.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +typedef int SOCKET; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#define closesocket(s) ::close(s) +#ifndef SD_BOTH +#define SD_BOTH SHUT_RDWR +#endif +#endif +#include +#include +#include + +namespace btf +{ + +bool TunnelInit() +{ + return RvInit() && ChanInit(); +} + +// Create a connected loopback socket pair (no socketpair() on Windows). +// Returns app end and pump end; both are ordinary full-duplex TCP sockets. +static bool MakeLocalPair(btf_socket_t& appEnd, btf_socket_t& pumpEnd) +{ + SOCKET listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == INVALID_SOCKET) return false; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; // ephemeral + if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) != 0) { closesocket(listener); return false; } + if (listen(listener, 1) != 0) { closesocket(listener); return false; } + +#ifdef _WIN32 + int len = sizeof(addr); +#else + socklen_t len = sizeof(addr); +#endif + if (getsockname(listener, (struct sockaddr*)&addr, &len) != 0) { closesocket(listener); return false; } + + SOCKET a = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (a == INVALID_SOCKET) { closesocket(listener); return false; } + if (connect(a, (struct sockaddr*)&addr, sizeof(addr)) != 0) { closesocket(a); closesocket(listener); return false; } + + SOCKET b = accept(listener, NULL, NULL); + closesocket(listener); + if (b == INVALID_SOCKET) { closesocket(a); return false; } + + appEnd = a; + pumpEnd = b; + return true; +} + +static bool SockReadN(SOCKET s, void* buf, int n) +{ + char* p = (char*)buf; int off = 0; + while (off < n) { int r = recv(s, p + off, n - off, 0); if (r <= 0) return false; off += r; } + return true; +} +static bool SockWriteN(SOCKET s, const void* buf, int n) +{ + const char* p = (const char*)buf; int off = 0; + while (off < n) { int r = send(s, p + off, n - off, 0); if (r <= 0) return false; off += r; } + return true; +} + +// Outbound pump: plaintext from the app -> echan frame -> rendezvous socket. +static void PumpOut(SOCKET pumpEnd, RvSocket rv, std::vector key) +{ + char buf[16 * 1024]; + for (;;) + { + int r = recv(pumpEnd, buf, sizeof(buf), 0); + if (r <= 0) break; + std::vector frame = ChanEncryptFrame(key.data(), (const unsigned char*)buf, r); + if (!RvWriteN(rv, frame.data(), (int)frame.size())) break; + } + // Tear down: unblock the inbound pump and the app. + shutdown((SOCKET)rv, SD_BOTH); + shutdown(pumpEnd, SD_BOTH); +} + +// Inbound pump: echan frames from the rendezvous socket -> plaintext to the app. +static void PumpIn(SOCKET pumpEnd, RvSocket rv, std::vector key) +{ + for (;;) + { + unsigned char len[4]; + if (!RvReadN(rv, len, 4)) break; + size_t n = ((size_t)len[0]<<24)|((size_t)len[1]<<16)|((size_t)len[2]<<8)|len[3]; + if (n < (size_t)(CHAN_NONCEBYTES + CHAN_MACBYTES) || n > (1u<<20)) break; + std::vector body(n); + if (!RvReadN(rv, body.data(), (int)n)) break; + std::vector pt; + if (!ChanDecryptBody(key.data(), body.data(), (int)n, pt)) break; // auth fail -> drop + if (!pt.empty() && !SockWriteN(pumpEnd, pt.data(), (int)pt.size())) break; + } + shutdown(pumpEnd, SD_BOTH); + shutdown((SOCKET)rv, SD_BOTH); +} + +// Start the two pumps and hand back the app-facing socket. When both pumps +// finish, close the pump end and the rendezvous socket. +static btf_socket_t StartTunnel(RvSocket rv, const unsigned char shared[32]) +{ + btf_socket_t appEnd, pumpEnd; + if (!MakeLocalPair(appEnd, pumpEnd)) { RvClose(rv); return INVALID_SOCKET; } + + std::vector key(shared, shared + 32); + std::thread(PumpOut, pumpEnd, rv, key).detach(); + std::thread([pumpEnd, rv, key]() { + PumpIn(pumpEnd, rv, key); + closesocket(pumpEnd); + RvClose(rv); + }).detach(); + return appEnd; +} + +btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port, + const unsigned char target_pubkey[32], + const unsigned char service_enc_pub[32]) +{ + if (!TunnelInit()) return INVALID_SOCKET; + RvSocket rv = RvClientConnect(meeting_host, port, target_pubkey); + if (rv == RV_INVALID) return INVALID_SOCKET; + + unsigned char eph_pub[32], shared[32]; + if (!ChanClientHandshake(service_enc_pub, eph_pub, shared)) { RvClose(rv); return INVALID_SOCKET; } + if (!RvWriteN(rv, eph_pub, 32)) { RvClose(rv); return INVALID_SOCKET; } + return StartTunnel(rv, shared); +} + +btf_socket_t BtfServiceWrap(RvSocket rv, const unsigned char my_enc_sk[32]) +{ + if (!TunnelInit()) { RvClose(rv); return INVALID_SOCKET; } + unsigned char eph_pub[32], shared[32]; + if (!RvReadN(rv, eph_pub, 32)) { RvClose(rv); return INVALID_SOCKET; } + if (!ChanEndpointHandshake(eph_pub, my_enc_sk, shared)) { RvClose(rv); return INVALID_SOCKET; } + return StartTunnel(rv, shared); +} + +} // namespace btf diff --git a/src/btftunnel.h b/src/btftunnel.h new file mode 100644 index 0000000..1fa4f01 --- /dev/null +++ b/src/btftunnel.h @@ -0,0 +1,49 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Transparent anonymous tunnel. Gives the existing CNode a plain local socket +// while, behind it, a pair of pump threads bridge plaintext <-> echan <-> +// rendezvous. CNode reads/writes normally and never knows its bytes travel +// end-to-end encrypted through a meeting node (the Itzal `wrap()` pattern). +// +// Client side: resolve a `.btf` -> meeting node + service enc key (done by the +// caller), then BtfClientTunnel connects through the relay, runs the echan +// handshake, and returns a local SOCKET to hand a new outbound CNode. +// +// Service side: after RvServiceRegister pairs us with a client, BtfServiceWrap +// completes the echan handshake and returns a local SOCKET for an inbound CNode. + +#ifndef BITFLASH_BTFTUNNEL_H +#define BITFLASH_BTFTUNNEL_H + +#include "btfrv.h" + +#ifdef _WIN32 +#include +typedef SOCKET btf_socket_t; +#else +typedef int btf_socket_t; +#define INVALID_SOCKET (-1) +#endif + +namespace btf +{ + +// Initialize (sockets + libsodium). Idempotent. +bool TunnelInit(); + +// Client: dial the service registered under `target_pubkey` at the meeting node +// (host:port), box to its static x25519 `service_enc_pub`, and return a local +// plaintext SOCKET tunneled end-to-end to the service. INVALID_SOCKET on error. +btf_socket_t BtfClientTunnel(const char* meeting_host, unsigned short port, + const unsigned char target_pubkey[32], + const unsigned char service_enc_pub[32]); + +// Service: wrap an already-paired rendezvous socket (from RvServiceRegister) +// with echan using my static x25519 secret, returning a local plaintext SOCKET +// for an inbound CNode. Consumes `rv` (closed when the tunnel tears down). +btf_socket_t BtfServiceWrap(RvSocket rv, const unsigned char my_enc_sk[32]); + +} // namespace btf + +#endif diff --git a/src/compat.h b/src/compat.h new file mode 100644 index 0000000..e3f76a1 --- /dev/null +++ b/src/compat.h @@ -0,0 +1,193 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// POSIX/Linux compatibility shim. Lets the Windows-flavored core (Winsock, +// _beginthread, CRITICAL_SECTION, Sleep, ...) compile largely unchanged on +// Linux for the headless node/miner `bitflash-node`. Included by headers.h on +// non-Windows platforms only; the Windows build never sees this file. + +#ifndef BITFLASH_COMPAT_H +#define BITFLASH_COMPAT_H +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ---- Windows integer/type aliases used across the core ---- +typedef unsigned int DWORD; +typedef void* HANDLE; +typedef int64_t LARGE_INTEGER; +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +#ifndef _I64_MAX +#define _I64_MAX 0x7fffffffffffffffLL +#define _I64_MIN (-0x7fffffffffffffffLL - 1) +#endif +#ifndef _UI64_MAX +#define _UI64_MAX 0xffffffffffffffffULL +#endif + +// High-resolution counter (used only for optional timing in the core). +inline void QueryPerformanceCounter(LARGE_INTEGER* p) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + *p = (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec; +} + +// ---- socket layer (Winsock -> BSD sockets) ---- +typedef int SOCKET; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#ifndef SOCKET_ERROR +#define SOCKET_ERROR (-1) +#endif +#ifndef NO_ERROR +#define NO_ERROR 0 +#endif +#define closesocket(s) ::close(s) +inline int WSAGetLastError() { return errno; } + +// Winsock error codes mapped to POSIX errno values +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAEALREADY EALREADY +#define WSAECONNREFUSED ECONNREFUSED +#define WSAENOTSOCK EBADF + +// Only FIONBIO (non-blocking toggle) is used; implement it robustly via fcntl. +inline int ioctlsocket(SOCKET s, long cmd, u_long* argp) +{ + if (cmd == (long)FIONBIO) + { + int flags = fcntl(s, F_GETFL, 0); + if (flags < 0) return -1; + if (*argp) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; + return fcntl(s, F_SETFL, flags); + } + return ioctl(s, cmd, argp); +} + +// WSAStartup/WSACleanup are no-ops outside Windows. +typedef int WSADATA; +inline int WSAStartup(unsigned short, WSADATA*) { return 0; } +inline int WSACleanup() { return 0; } +#ifndef MAKEWORD +#define MAKEWORD(a, b) 0 +#endif + +// ---- Sleep(ms) ---- +inline void Sleep(unsigned int ms) +{ + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (long)(ms % 1000) * 1000000L; + nanosleep(&ts, NULL); +} + +// ---- threads: _beginthread / _endthread over pthreads ---- +struct _bf_thread_ctx { void (*fn)(void*); void* arg; }; +inline void* _bf_thread_trampoline(void* p) +{ + _bf_thread_ctx* c = (_bf_thread_ctx*)p; + void (*fn)(void*) = c->fn; + void* arg = c->arg; + delete c; + fn(arg); + return NULL; +} +inline uintptr_t _beginthread(void (*start)(void*), unsigned, void* arg) +{ + _bf_thread_ctx* c = new _bf_thread_ctx; + c->fn = start; c->arg = arg; + pthread_t th; + if (pthread_create(&th, NULL, _bf_thread_trampoline, c) != 0) + { + delete c; + return (uintptr_t)-1; + } + pthread_detach(th); + return (uintptr_t)th; +} +inline void _endthread() { pthread_exit(NULL); } + +// ---- CRITICAL_SECTION over a recursive mutex (util.h's CCriticalSection +// compiles unchanged on top of these) ---- +typedef pthread_mutex_t CRITICAL_SECTION; +inline void InitializeCriticalSection(CRITICAL_SECTION* cs) +{ + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(cs, &a); + pthread_mutexattr_destroy(&a); +} +inline void DeleteCriticalSection(CRITICAL_SECTION* cs) { pthread_mutex_destroy(cs); } +inline void EnterCriticalSection(CRITICAL_SECTION* cs) { pthread_mutex_lock(cs); } +inline void LeaveCriticalSection(CRITICAL_SECTION* cs) { pthread_mutex_unlock(cs); } +inline bool TryEnterCriticalSection(CRITICAL_SECTION* cs) { return pthread_mutex_trylock(cs) == 0; } + +// ---- misc Windows CRT/debug helpers ---- +inline void OutputDebugStringA(const char*) {} +inline void DebugBreak() {} +#ifndef _HEAPOK +#define _HEAPOK 0 +#endif +inline int _heapchk() { return _HEAPOK; } +#define _vsnprintf vsnprintf +inline char* strlwr(char* s) { for (char* p = s; *p; ++p) *p = (char)tolower((unsigned char)*p); return s; } +#define _strlwr strlwr +inline int _mkdir(const char* p) { return ::mkdir(p, 0755); } + +// Thread priority is a Windows nicety; on Linux the message-handler thread just +// runs at normal priority (harmless no-op). +inline void* GetCurrentThread() { return (void*)0; } +inline void SetThreadPriority(void*, int) {} +#ifndef THREAD_PRIORITY_BELOW_NORMAL +#define THREAD_PRIORITY_BELOW_NORMAL 0 +#define THREAD_PRIORITY_LOWEST 0 +#define THREAD_PRIORITY_NORMAL 0 +#endif + +// ---- wxWidgets shims (the headless build has no GUI; the few core call sites +// degrade to console/no-op) ---- +#define wxTheApp ((void*)0) +#ifndef wxOK +#define wxOK 0 +#define wxICON_ERROR 0 +#define wxICON_EXCLAMATION 0 +#define wxICON_INFORMATION 0 +#endif +inline void wxMessageBox(const std::string& msg, const std::string& caption = "", long = 0) +{ + fprintf(stderr, "%s%s%s\n", caption.c_str(), caption.empty() ? "" : ": ", msg.c_str()); +} + +#endif // !_WIN32 +#endif // BITFLASH_COMPAT_H diff --git a/src/db.cpp b/src/db.cpp new file mode 100644 index 0000000..0df68fc --- /dev/null +++ b/src/db.cpp @@ -0,0 +1,604 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + + + + + + +// +// CDB +// + +static CCriticalSection cs_db; +static bool fDbEnvInit = false; +DbEnv dbenv(0u); +static map mapFileUseCount; + +class CDBInit +{ +public: + CDBInit() + { + } + ~CDBInit() + { + if (fDbEnvInit) + { + dbenv.close(0); + fDbEnvInit = false; + } + } +} +instance_of_cdbinit; + + +CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) +{ + int ret; + if (pszFile == NULL) + return; + + bool fCreate = strchr(pszMode, 'c'); + bool fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + unsigned int nFlags = DB_THREAD; + if (fCreate) + nFlags |= DB_CREATE; + else if (fReadOnly) + nFlags |= DB_RDONLY; + if (!fReadOnly || fTxn) + nFlags |= DB_AUTO_COMMIT; + + CRITICAL_BLOCK(cs_db) + { + if (!fDbEnvInit) + { + string strAppDir = GetAppDir(); + string strLogDir = strAppDir + "/database"; + _mkdir(strLogDir.c_str()); + printf("dbenv.open strAppDir=%s\n", strAppDir.c_str()); + + dbenv.set_lg_dir(strLogDir.c_str()); + dbenv.set_lg_max(10000000); + dbenv.set_lk_max_locks(10000); + dbenv.set_lk_max_objects(10000); + dbenv.set_errfile(fopen("db.log", "a")); /// debug + ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption + ret = dbenv.open(strAppDir.c_str(), + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_PRIVATE | + DB_RECOVER, + 0); + if (ret > 0) + throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret)); + fDbEnvInit = true; + } + + strFile = pszFile; + ++mapFileUseCount[strFile]; + } + + pdb = new Db(&dbenv, 0); + + ret = pdb->open(NULL, // Txn pointer + pszFile, // Filename + "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags + 0); + + if (ret > 0) + { + delete pdb; + pdb = NULL; + CRITICAL_BLOCK(cs_db) + --mapFileUseCount[strFile]; + strFile = ""; + throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret)); + } + + if (fCreate && !Exists(string("version"))) + WriteVersion(VERSION); + + RandAddSeed(); +} + +void CDB::Close() +{ + if (!pdb) + return; + if (!vTxn.empty()) + vTxn.front()->abort(); + vTxn.clear(); + pdb->close(0); + delete pdb; + pdb = NULL; + dbenv.txn_checkpoint(0, 0, 0); + + CRITICAL_BLOCK(cs_db) + --mapFileUseCount[strFile]; + + RandAddSeed(); +} + +void DBFlush(bool fShutdown) +{ + // Flush log data to the actual data file + // on all files that are not in use + printf("DBFlush(%s)\n", fShutdown ? "true" : "false"); + CRITICAL_BLOCK(cs_db) + { + dbenv.txn_checkpoint(0, 0, 0); + map::iterator mi = mapFileUseCount.begin(); + while (mi != mapFileUseCount.end()) + { + string strFile = (*mi).first; + int nRefCount = (*mi).second; + if (nRefCount == 0) + { + dbenv.lsn_reset(strFile.c_str(), 0); + mapFileUseCount.erase(mi++); + } + else + mi++; + } + if (fShutdown) + { + char** listp; + if (mapFileUseCount.empty()) + dbenv.log_archive(&listp, DB_ARCH_REMOVE); + dbenv.close(0); + fDbEnvInit = false; + } + } +} + + + + + + +// +// CTxDB +// + +bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex) +{ + assert(!fClient); + txindex.SetNull(); + return Read(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex) +{ + assert(!fClient); + return Write(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight) +{ + assert(!fClient); + + // Add to tx index + uint256 hash = tx.GetHash(); + CTxIndex txindex(pos, tx.vout.size()); + return Write(make_pair(string("tx"), hash), txindex); +} + +bool CTxDB::EraseTxIndex(const CTransaction& tx) +{ + assert(!fClient); + uint256 hash = tx.GetHash(); + + return Erase(make_pair(string("tx"), hash)); +} + +bool CTxDB::ContainsTx(uint256 hash) +{ + assert(!fClient); + return Exists(make_pair(string("tx"), hash)); +} + +bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector& vtx) +{ + assert(!fClient); + vtx.clear(); + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + string strType; + uint160 hashItem; + CDiskTxPos pos; + ssKey >> strType >> hashItem >> pos; + int nItemHeight; + ssValue >> nItemHeight; + + // Read transaction + if (strType != "owner" || hashItem != hash160) + break; + if (nItemHeight >= nMinHeight) + { + vtx.resize(vtx.size()+1); + if (!vtx.back().ReadFromDisk(pos)) + return false; + } + } + return true; +} + +bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) +{ + assert(!fClient); + tx.SetNull(); + if (!ReadTxIndex(hash, txindex)) + return false; + return (tx.ReadFromDisk(txindex.pos)); +} + +bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) +{ + CTxIndex txindex; + return ReadDiskTx(hash, tx, txindex); +} + +bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex) +{ + return ReadDiskTx(outpoint.hash, tx, txindex); +} + +bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx) +{ + CTxIndex txindex; + return ReadDiskTx(outpoint.hash, tx, txindex); +} + +bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) +{ + return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); +} + +bool CTxDB::EraseBlockIndex(uint256 hash) +{ + return Erase(make_pair(string("blockindex"), hash)); +} + +bool CTxDB::ReadHashBestChain(uint256& hashBestChain) +{ + return Read(string("hashBestChain"), hashBestChain); +} + +bool CTxDB::WriteHashBestChain(uint256 hashBestChain) +{ + return Write(string("hashBestChain"), hashBestChain); +} + +CBlockIndex* InsertBlockIndex(uint256 hash) +{ + if (hash == 0) + return NULL; + + // Return existing + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + return (*mi).second; + + // Create new + CBlockIndex* pindexNew = new CBlockIndex(); + if (!pindexNew) + throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); + mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + + return pindexNew; +} + +bool CTxDB::LoadBlockIndex() +{ + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + unsigned int fFlags = DB_SET_RANGE; + loop + { + // Read next record + CDataStream ssKey; + if (fFlags == DB_SET_RANGE) + ssKey << make_pair(string("blockindex"), uint256(0)); + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + string strType; + ssKey >> strType; + if (strType == "blockindex") + { + CDiskBlockIndex diskindex; + ssValue >> diskindex; + + // Construct block index object + CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); + pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); + pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); + pindexNew->nFile = diskindex.nFile; + pindexNew->nBlockPos = diskindex.nBlockPos; + pindexNew->nHeight = diskindex.nHeight; + pindexNew->nVersion = diskindex.nVersion; + pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; + pindexNew->nTime = diskindex.nTime; + pindexNew->nBits = diskindex.nBits; + pindexNew->nNonce = diskindex.nNonce; + + // Watch for genesis block and best block + if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) + pindexGenesisBlock = pindexNew; + } + else + { + break; + } + } + + if (!ReadHashBestChain(hashBestChain)) + { + if (pindexGenesisBlock == NULL) + return true; + return error("CTxDB::LoadBlockIndex() : hashBestChain not found\n"); + } + + if (!mapBlockIndex.count(hashBestChain)) + return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found\n"); + pindexBest = mapBlockIndex[hashBestChain]; + nBestHeight = pindexBest->nHeight; + printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight); + + return true; +} + + + + + +// +// CAddrDB +// + +bool CAddrDB::WriteAddress(const CAddress& addr) +{ + return Write(make_pair(string("addr"), addr.GetKey()), addr); +} + +bool CAddrDB::LoadAddresses() +{ + CRITICAL_BLOCK(cs_mapAddresses) + { + // Load user provided addresses + CAutoFile filein = fopen("addr.txt", "rt"); + if (filein) + { + try + { + char psz[1000]; + while (fgets(psz, sizeof(psz), filein)) + { + CAddress addr(psz, NODE_NETWORK); + if (addr.ip != 0) + AddAddress(*this, addr); + } + } + catch (...) { } + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + loop + { + // Read next record + CDataStream ssKey; + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + string strType; + ssKey >> strType; + if (strType == "addr") + { + CAddress addr; + ssValue >> addr; + mapAddresses.insert(make_pair(addr.GetKey(), addr)); + } + } + + //// debug print + printf("mapAddresses:\n"); + foreach(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + item.second.print(); + printf("-----\n"); + } + + return true; +} + +bool LoadAddresses() +{ + return CAddrDB("cr+").LoadAddresses(); +} + + + + +// +// CReviewDB +// + +bool CReviewDB::ReadReviews(uint256 hash, vector& vReviews) +{ + vReviews.size(); // msvc workaround, just need to do anything with vReviews + return Read(make_pair(string("reviews"), hash), vReviews); +} + +bool CReviewDB::WriteReviews(uint256 hash, const vector& vReviews) +{ + return Write(make_pair(string("reviews"), hash), vReviews); +} + + + + + + + +// +// CWalletDB +// + +bool CWalletDB::LoadWallet(vector& vchDefaultKeyRet) +{ + vchDefaultKeyRet.clear(); + + //// todo: shouldn't we catch exceptions and try to recover and continue? + CRITICAL_BLOCK(cs_mapKeys) + CRITICAL_BLOCK(cs_mapWallet) + { + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + loop + { + // Read next record + CDataStream ssKey; + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) + return false; + + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + string strType; + ssKey >> strType; + if (strType == "name") + { + string strAddress; + ssKey >> strAddress; + ssValue >> mapAddressBook[strAddress]; + } + else if (strType == "tx") + { + uint256 hash; + ssKey >> hash; + CWalletTx& wtx = mapWallet[hash]; + ssValue >> wtx; + + if (wtx.GetHash() != hash) + printf("Error in wallet.dat, hash mismatch\n"); + + //// debug print + //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //printf(" %12I64d %s %s %s\n", + // wtx.vout[0].nValue, + // DateTimeStr(wtx.nTime).c_str(), + // wtx.hashBlock.ToString().substr(0,14).c_str(), + // wtx.mapValue["message"].c_str()); + } + else if (strType == "key") + { + vector vchPubKey; + ssKey >> vchPubKey; + CPrivKey vchPrivKey; + ssValue >> vchPrivKey; + + mapKeys[vchPubKey] = vchPrivKey; + mapPubKeys[Hash160(vchPubKey)] = vchPubKey; + } + else if (strType == "defaultkey") + { + ssValue >> vchDefaultKeyRet; + } + else if (strType == "setting") /// or settings or option or options or config? + { + string strKey; + ssKey >> strKey; + if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; + if (strKey == "nTransactionFee") ssValue >> nTransactionFee; + if (strKey == "addrIncoming") ssValue >> addrIncoming; + } + } + } + + printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); + printf("nTransactionFee = %I64d\n", nTransactionFee); + printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); + + return true; +} + +bool LoadWallet() +{ + vector vchDefaultKey; + if (!CWalletDB("cr").LoadWallet(vchDefaultKey)) + return false; + + if (mapKeys.count(vchDefaultKey)) + { + // Set keyUser + keyUser.SetPubKey(vchDefaultKey); + keyUser.SetPrivKey(mapKeys[vchDefaultKey]); + } + else + { + // Create new keyUser and set as default key + keyUser.MakeNewKey(); + if (!AddKey(keyUser)) + return false; + if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address")) + return false; + CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); + } + + return true; +} diff --git a/src/db.h b/src/db.h new file mode 100644 index 0000000..feaa45e --- /dev/null +++ b/src/db.h @@ -0,0 +1,420 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include +class CTransaction; +class CTxIndex; +class CDiskBlockIndex; +class CDiskTxPos; +class COutPoint; +class CUser; +class CReview; +class CAddress; +class CWalletTx; + +extern map mapAddressBook; +extern bool fClient; + + +extern DbEnv dbenv; +extern void DBFlush(bool fShutdown); + + + + +class CDB +{ +protected: + Db* pdb; + string strFile; + vector vTxn; + + explicit CDB(const char* pszFile, const char* pszMode="r+", bool fTxn=false); + ~CDB() { Close(); } +public: + void Close(); +private: + CDB(const CDB&); + void operator=(const CDB&); + +protected: + template + bool Read(const K& key, T& value) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Read + Dbt datValue; + datValue.set_flags(DB_DBT_MALLOC); + int ret = pdb->get(GetTxn(), &datKey, &datValue, 0); + memset(datKey.get_data(), 0, datKey.get_size()); + if (datValue.get_data() == NULL) + return false; + + // Unserialize value + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK); + ssValue >> value; + + // Clear and free memory + memset(datValue.get_data(), 0, datValue.get_size()); + free(datValue.get_data()); + return (ret == 0); + } + + template + bool Write(const K& key, const T& value, bool fOverwrite=true) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Value + CDataStream ssValue(SER_DISK); + ssValue.reserve(10000); + ssValue << value; + Dbt datValue(&ssValue[0], ssValue.size()); + + // Write + int ret = pdb->put(GetTxn(), &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); + + // Clear memory in case it was a private key + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + return (ret == 0); + } + + template + bool Erase(const K& key) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Erase + int ret = pdb->del(GetTxn(), &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0 || ret == DB_NOTFOUND); + } + + template + bool Exists(const K& key) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Exists + int ret = pdb->exists(GetTxn(), &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0); + } + + Dbc* GetCursor() + { + if (!pdb) + return NULL; + Dbc* pcursor = NULL; + int ret = pdb->cursor(NULL, &pcursor, 0); + if (ret != 0) + return NULL; + return pcursor; + } + + int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) + { + // Read at cursor + Dbt datKey; + if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datKey.set_data(&ssKey[0]); + datKey.set_size(ssKey.size()); + } + Dbt datValue; + if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) + { + datValue.set_data(&ssValue[0]); + datValue.set_size(ssValue.size()); + } + datKey.set_flags(DB_DBT_MALLOC); + datValue.set_flags(DB_DBT_MALLOC); + int ret = pcursor->get(&datKey, &datValue, fFlags); + if (ret != 0) + return ret; + else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + return 99999; + + // Convert to streams + ssKey.SetType(SER_DISK); + ssKey.clear(); + ssKey.write((char*)datKey.get_data(), datKey.get_size()); + ssValue.SetType(SER_DISK); + ssValue.clear(); + ssValue.write((char*)datValue.get_data(), datValue.get_size()); + + // Clear and free memory + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + free(datKey.get_data()); + free(datValue.get_data()); + return 0; + } + + DbTxn* GetTxn() + { + if (!vTxn.empty()) + return vTxn.back(); + else + return NULL; + } + +public: + bool TxnBegin() + { + if (!pdb) + return false; + DbTxn* ptxn = NULL; + int ret = dbenv.txn_begin(GetTxn(), &ptxn, 0); + if (!ptxn || ret != 0) + return false; + vTxn.push_back(ptxn); + return true; + } + + bool TxnCommit() + { + if (!pdb) + return false; + if (vTxn.empty()) + return false; + int ret = vTxn.back()->commit(0); + vTxn.pop_back(); + return (ret == 0); + } + + bool TxnAbort() + { + if (!pdb) + return false; + if (vTxn.empty()) + return false; + int ret = vTxn.back()->abort(); + vTxn.pop_back(); + return (ret == 0); + } + + bool ReadVersion(int& nVersion) + { + nVersion = 0; + return Read(string("version"), nVersion); + } + + bool WriteVersion(int nVersion) + { + return Write(string("version"), nVersion); + } +}; + + + + + + + + +class CTxDB : public CDB +{ +public: + CTxDB(const char* pszMode="r+", bool fTxn=false) : CDB(!fClient ? "blkindex.dat" : NULL, pszMode, fTxn) { } +private: + CTxDB(const CTxDB&); + void operator=(const CTxDB&); +public: + bool ReadTxIndex(uint256 hash, CTxIndex& txindex); + bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex); + bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight); + bool EraseTxIndex(const CTransaction& tx); + bool ContainsTx(uint256 hash); + bool ReadOwnerTxes(uint160 hash160, int nHeight, vector& vtx); + bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex); + bool ReadDiskTx(uint256 hash, CTransaction& tx); + bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex); + bool ReadDiskTx(COutPoint outpoint, CTransaction& tx); + bool WriteBlockIndex(const CDiskBlockIndex& blockindex); + bool EraseBlockIndex(uint256 hash); + bool ReadHashBestChain(uint256& hashBestChain); + bool WriteHashBestChain(uint256 hashBestChain); + bool LoadBlockIndex(); +}; + + + + + +class CReviewDB : public CDB +{ +public: + CReviewDB(const char* pszMode="r+", bool fTxn=false) : CDB("reviews.dat", pszMode, fTxn) { } +private: + CReviewDB(const CReviewDB&); + void operator=(const CReviewDB&); +public: + bool ReadUser(uint256 hash, CUser& user) + { + return Read(make_pair(string("user"), hash), user); + } + + bool WriteUser(uint256 hash, const CUser& user) + { + return Write(make_pair(string("user"), hash), user); + } + + bool ReadReviews(uint256 hash, vector& vReviews); + bool WriteReviews(uint256 hash, const vector& vReviews); +}; + + + + + +class CMarketDB : public CDB +{ +public: + CMarketDB(const char* pszMode="r+", bool fTxn=false) : CDB("market.dat", pszMode, fTxn) { } +private: + CMarketDB(const CMarketDB&); + void operator=(const CMarketDB&); +}; + + + + + +class CAddrDB : public CDB +{ +public: + CAddrDB(const char* pszMode="r+", bool fTxn=false) : CDB("addr.dat", pszMode, fTxn) { } +private: + CAddrDB(const CAddrDB&); + void operator=(const CAddrDB&); +public: + bool WriteAddress(const CAddress& addr); + bool LoadAddresses(); +}; + +bool LoadAddresses(); + + + + + +class CWalletDB : public CDB +{ +public: + CWalletDB(const char* pszMode="r+", bool fTxn=false) : CDB("wallet.dat", pszMode, fTxn) { } +private: + CWalletDB(const CWalletDB&); + void operator=(const CWalletDB&); +public: + bool ReadName(const string& strAddress, string& strName) + { + strName = ""; + return Read(make_pair(string("name"), strAddress), strName); + } + + bool WriteName(const string& strAddress, const string& strName) + { + mapAddressBook[strAddress] = strName; + return Write(make_pair(string("name"), strAddress), strName); + } + + bool EraseName(const string& strAddress) + { + mapAddressBook.erase(strAddress); + return Erase(make_pair(string("name"), strAddress)); + } + + bool ReadTx(uint256 hash, CWalletTx& wtx) + { + return Read(make_pair(string("tx"), hash), wtx); + } + + bool WriteTx(uint256 hash, const CWalletTx& wtx) + { + return Write(make_pair(string("tx"), hash), wtx); + } + + bool EraseTx(uint256 hash) + { + return Erase(make_pair(string("tx"), hash)); + } + + bool ReadKey(const vector& vchPubKey, CPrivKey& vchPrivKey) + { + vchPrivKey.clear(); + return Read(make_pair(string("key"), vchPubKey), vchPrivKey); + } + + bool WriteKey(const vector& vchPubKey, const CPrivKey& vchPrivKey) + { + return Write(make_pair(string("key"), vchPubKey), vchPrivKey, false); + } + + bool ReadDefaultKey(vector& vchPubKey) + { + vchPubKey.clear(); + return Read(string("defaultkey"), vchPubKey); + } + + bool WriteDefaultKey(const vector& vchPubKey) + { + return Write(string("defaultkey"), vchPubKey); + } + + template + bool ReadSetting(const string& strKey, T& value) + { + return Read(make_pair(string("setting"), strKey), value); + } + + template + bool WriteSetting(const string& strKey, const T& value) + { + return Write(make_pair(string("setting"), strKey), value); + } + + bool LoadWallet(vector& vchDefaultKeyRet); +}; + +bool LoadWallet(); + +inline bool SetAddressBookName(const string& strAddress, const string& strName) +{ + return CWalletDB().WriteName(strAddress, strName); +} diff --git a/src/headers.h b/src/headers.h new file mode 100644 index 0000000..e253803 --- /dev/null +++ b/src/headers.h @@ -0,0 +1,111 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#ifdef _MSC_VER +#pragma warning(disable:4786) +#pragma warning(disable:4804) +#pragma warning(disable:4717) +#endif + +#ifdef _WIN32 +// ---- Windows build (GUI wallet + miner) ---- +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0601 +#define WIN32_LEAN_AND_MEAN 1 +// winsock2.h must come before windows.h/wx to avoid pulling in winsock 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +// ---- Linux/POSIX headless build (bitflash-node) ---- +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#define BOUNDSCHECK 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma hdrstop +using namespace std; + +// In 2009 windows.h provided min/max macros accepting mixed types; wxWidgets 3 +// defines NOMINMAX, so we restore mixed-type versions here. For matching types, +// std::min/std::max remain preferred during overload resolution. +template +inline typename std::common_type::type min(const T1& a, const T2& b) +{ + return (a < b) ? a : b; +} +template +inline typename std::common_type::type max(const T1& a, const T2& b) +{ + return (a > b) ? a : b; +} + + + +#include "serialize.h" +#include "uint256.h" +#include "util.h" +#include "key.h" +#include "bignum.h" +#include "base58.h" +#include "script.h" +#include "db.h" +#include "net.h" +#include "irc.h" +#include "nostr.h" +#include "randomx_pow.h" +#include "main.h" +#include "market.h" +#ifdef _WIN32 +#include "uibase.h" +#include "ui.h" +#else +// Headless build has no GUI; the core only calls a couple of UI helpers, +// provided by headless.cpp (a no-op repaint and a timestamp formatter). +void MainFrameRepaint(); +string DateTimeStr(int64 nTime); +#endif diff --git a/src/headless.cpp b/src/headless.cpp new file mode 100644 index 0000000..94597f1 --- /dev/null +++ b/src/headless.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Headless entry point for the Linux node/miner `bitflash-node`. Replaces the +// wxWidgets GUI (ui.cpp) with a command-line daemon: it loads the chain and +// wallet, starts the P2P/Nostr node (which auto-discovers .btf peers through +// the rendezvous relay), and optionally mines with RandomX. No GUI, no wx. +// +// ./bitflash-node -gen mine with the CPU, auto-connect peers +// ./bitflash-node -gen -datadir=/path use a specific data directory +// ./bitflash-node -rvrelay=host:port use a specific rendezvous relay +// ./bitflash-node -connectbtf=.btf pin a specific peer +// ./bitflash-node -solomine -gen mine without waiting for a peer (test) + +#include "headers.h" +#include + +// ---- UI hooks the core references, provided here as headless equivalents ---- + +// The core asks the GUI to repaint on new blocks/transactions; nothing to do. +void MainFrameRepaint() {} + +// Wallet address book -- defined in ui.cpp on Windows; the core (db.cpp) reads +// and writes it, so the headless build must provide the definition. +map mapAddressBook; + +// Format a unix timestamp for transaction listings (ui.cpp used wxDateTime). +string DateTimeStr(int64 nTime) +{ + time_t t = (time_t)nTime; + struct tm* ptm = localtime(&t); + if (!ptm) + return ""; + char buf[64]; + strftime(buf, sizeof(buf), "%m/%d/%y %H:%M", ptm); + return string(buf); +} + + +// ---- argument parsing (mirrors ui.cpp's ParseParameters) ---- +static map ParseParameters(int argc, char* argv[]) +{ + map mapArgs; + for (int i = 0; i < argc; i++) + { + string s = argv[i]; + string val; + size_t eq = s.find('='); + if (eq != string::npos) + { + val = s.substr(eq + 1); + s = s.substr(0, eq); + } + for (size_t j = 0; j < s.size(); j++) + s[j] = (char)tolower((unsigned char)s[j]); + if (!s.empty() && s[0] == '-') + s[0] = '/'; + mapArgs[s] = val; + } + return mapArgs; +} + + +static void HandleSignal(int) +{ + fShutdown = true; +} + + +int main(int argc, char* argv[]) +{ + map mapArgs = ParseParameters(argc, argv); + + if (mapArgs.count("/?") || mapArgs.count("/help") || mapArgs.count("/h")) + { + fprintf(stderr, + "Bitflash headless node\n" + "Usage: %s [options]\n" + " -gen generate coins (mine with RandomX)\n" + " -datadir= data directory (default ~/.bitflash)\n" + " -port= P2P listen port (default 8433)\n" + " -rvrelay=host:port rendezvous meeting relay (has a seed default)\n" + " -connectbtf=.btf keep a connection to a specific .btf peer\n" + " -solomine mine without waiting for a peer (testing)\n" + " -debug verbose logging\n", + argv[0]); + return 0; + } + + // Parameters (same set the Windows UI honors) + if (mapArgs.count("/datadir")) + strSetDataDir = mapArgs["/datadir"]; + if (mapArgs.count("/port")) + nListenPort = htons(atoi(mapArgs["/port"].c_str())); + if (mapArgs.count("/proxy")) + addrProxy = CAddress(mapArgs["/proxy"].c_str()); + if (mapArgs.count("/rvrelay")) + strBtfMeetingRelay = mapArgs["/rvrelay"]; + if (mapArgs.count("/connectbtf")) + strBtfConnect = mapArgs["/connectbtf"]; + if (mapArgs.count("/debug")) + fDebug = true; + if (mapArgs.count("/solomine")) + fSoloMineTest = true; + if (mapArgs.count("/gen")) + fGenerateBitcoins = mapArgs["/gen"].empty() ? true : atoi(mapArgs["/gen"].c_str()); + + printf("\nBitflash headless node starting\n"); + + // + // Load data files (mirror of the Windows OnInit2 startup) + // + printf("Loading addresses...\n"); + if (!LoadAddresses()) + printf("Error loading addr.dat\n"); + + printf("Loading block index...\n"); + if (!LoadBlockIndex()) + { + printf("Error loading blkindex.dat\n"); + return 1; + } + + printf("Loading wallet...\n"); + if (!LoadWallet()) + { + printf("Error loading wallet.dat\n"); + return 1; + } + + printf("Done loading. nBestHeight=%d, wallet keys=%d\n", + nBestHeight, (int)mapKeys.size()); + + // Rebroadcast any wallet transactions not yet in a block + ReacceptWalletTransactions(); + + // + // Start the node (P2P + Nostr discovery + .btf rendezvous) + // + string strErrors; + if (!StartNode(strErrors)) + { + printf("StartNode failed: %s\n", strErrors.c_str()); + return 1; + } + + // + // Start mining if requested + // + if (fGenerateBitcoins) + { + if (_beginthread(ThreadBitcoinMiner, 0, NULL) == (uintptr_t)-1) + printf("Error: _beginthread(ThreadBitcoinMiner) failed\n"); + else + printf("Mining enabled (RandomX, CPU)\n"); + } + else + { + printf("Mining disabled (pass -gen to mine)\n"); + } + + // + // Run until interrupted + // + signal(SIGINT, HandleSignal); + signal(SIGTERM, HandleSignal); + printf("Bitflash node running. Press Ctrl-C to stop.\n"); + + while (!fShutdown) + Sleep(500); + + printf("Shutting down...\n"); + StopNode(); + Sleep(500); + printf("Bitflash node stopped.\n"); + return 0; +} diff --git a/src/irc.cpp b/src/irc.cpp new file mode 100644 index 0000000..ff82c66 --- /dev/null +++ b/src/irc.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + + + + +#pragma pack(1) +struct ircaddr +{ + int ip; + short port; +}; + +string EncodeAddress(const CAddress& addr) +{ + struct ircaddr tmp; + tmp.ip = addr.ip; + tmp.port = addr.port; + + vector vch(UBEGIN(tmp), UEND(tmp)); + return string("u") + EncodeBase58Check(vch); +} + +bool DecodeAddress(string str, CAddress& addr) +{ + vector vch; + if (!DecodeBase58Check(str.substr(1), vch)) + return false; + + struct ircaddr tmp; + if (vch.size() != sizeof(tmp)) + return false; + memcpy(&tmp, &vch[0], sizeof(tmp)); + + addr = CAddress(tmp.ip, tmp.port); + return true; +} + + + + + + +static bool Send(SOCKET hSocket, const char* pszSend) +{ + if (strstr(pszSend, "PONG") != pszSend) + printf("SENDING: %s\n", pszSend); + const char* psz = pszSend; + const char* pszEnd = psz + strlen(psz); + while (psz < pszEnd) + { + int ret = send(hSocket, psz, pszEnd - psz, 0); + if (ret < 0) + return false; + psz += ret; + } + return true; +} + +bool RecvLine(SOCKET hSocket, string& strLine) +{ + strLine = ""; + loop + { + char c; + int nBytes = recv(hSocket, &c, 1, 0); + if (nBytes > 0) + { + if (c == '\n') + continue; + if (c == '\r') + return true; + strLine += c; + } + else if (nBytes <= 0) + { + if (!strLine.empty()) + return true; + // socket closed + printf("IRC socket closed\n"); + return false; + } + else + { + // socket error + int nErr = WSAGetLastError(); + if (nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + printf("IRC recv failed: %d\n", nErr); + return false; + } + } + } +} + +bool RecvLineIRC(SOCKET hSocket, string& strLine) +{ + loop + { + bool fRet = RecvLine(hSocket, strLine); + if (fRet) + { + if (fShutdown) + return false; + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords[0] == "PING") + { + strLine[1] = 'O'; + strLine += '\r'; + Send(hSocket, strLine.c_str()); + continue; + } + } + return fRet; + } +} + +bool RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL) +{ + loop + { + string strLine; + if (!RecvLineIRC(hSocket, strLine)) + return false; + printf("IRC %s\n", strLine.c_str()); + if (psz1 && strLine.find(psz1) != -1) + return true; + if (psz2 && strLine.find(psz2) != -1) + return true; + if (psz3 && strLine.find(psz3) != -1) + return true; + } +} + + + + +bool fRestartIRCSeed = false; + +void ThreadIRCSeed(void* parg) +{ + // IRC peer discovery disabled: the 2009 IRC network no longer exists (and + // what answers today crashes the parser). Replaced by Nostr relay + // discovery. + printf("ThreadIRCSeed: IRC peer discovery disabled (using Nostr)\n"); + return; + + loop + { + struct hostent* phostent = gethostbyname("chat.freenode.net"); + CAddress addrConnect(*(u_long*)phostent->h_addr_list[0], htons(6667)); + + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + { + printf("IRC connect failed\n"); + return; + } + + if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname")) + { + closesocket(hSocket); + return; + } + + string strMyName = EncodeAddress(addrLocalHost); + + if (!addrLocalHost.IsRoutable()) + strMyName = strprintf("x%u", GetRand(1000000000)); + + Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); + Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str()); + + if (!RecvUntil(hSocket, " 004 ")) + { + closesocket(hSocket); + return; + } + Sleep(500); + + Send(hSocket, "JOIN #bitcoin\r"); + Send(hSocket, "WHO #bitcoin\r"); + + while (!fRestartIRCSeed) + { + string strLine; + if (fShutdown || !RecvLineIRC(hSocket, strLine)) + { + closesocket(hSocket); + return; + } + if (strLine.empty() || strLine[0] != ':') + continue; + printf("IRC %s\n", strLine.c_str()); + + vector vWords; + ParseString(strLine, ' ', vWords); + if (vWords.size() < 2) + continue; + + char pszName[10000]; + pszName[0] = '\0'; + + if (vWords[1] == "352" && vWords.size() >= 8) + { + // index 7 is limited to 16 characters + // could get full length name at index 10, but would be different from join messages + strcpy(pszName, vWords[7].c_str()); + printf("GOT WHO: [%s] ", pszName); + } + + if (vWords[1] == "JOIN") + { + // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname + strcpy(pszName, vWords[0].c_str() + 1); + if (strchr(pszName, '!')) + *strchr(pszName, '!') = '\0'; + printf("GOT JOIN: [%s] ", pszName); + } + + if (pszName[0] == 'u') + { + CAddress addr; + if (DecodeAddress(pszName, addr)) + { + CAddrDB addrdb; + if (AddAddress(addrdb, addr)) + printf("new "); + addr.print(); + } + else + { + printf("decode failed\n"); + } + } + } + + fRestartIRCSeed = false; + closesocket(hSocket); + } +} + + + + + + + + + + +#ifdef TEST +int main(int argc, char *argv[]) +{ + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR) + { + printf("Error at WSAStartup()\n"); + return false; + } + + ThreadIRCSeed(NULL); + + WSACleanup(); + return 0; +} +#endif diff --git a/src/irc.h b/src/irc.h new file mode 100644 index 0000000..1ae4ffc --- /dev/null +++ b/src/irc.h @@ -0,0 +1,7 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +extern bool RecvLine(SOCKET hSocket, string& strLine); +extern void ThreadIRCSeed(void* parg); +extern bool fRestartIRCSeed; diff --git a/src/key.h b/src/key.h new file mode 100644 index 0000000..26e1ae2 --- /dev/null +++ b/src/key.h @@ -0,0 +1,156 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +// secp160k1 +// const unsigned int PRIVATE_KEY_SIZE = 192; +// const unsigned int PUBLIC_KEY_SIZE = 41; +// const unsigned int SIGNATURE_SIZE = 48; +// +// secp192k1 +// const unsigned int PRIVATE_KEY_SIZE = 222; +// const unsigned int PUBLIC_KEY_SIZE = 49; +// const unsigned int SIGNATURE_SIZE = 57; +// +// secp224k1 +// const unsigned int PRIVATE_KEY_SIZE = 250; +// const unsigned int PUBLIC_KEY_SIZE = 57; +// const unsigned int SIGNATURE_SIZE = 66; +// +// secp256k1: +// const unsigned int PRIVATE_KEY_SIZE = 279; +// const unsigned int PUBLIC_KEY_SIZE = 65; +// const unsigned int SIGNATURE_SIZE = 72; +// +// see www.keylength.com +// script supports up to 75 for single byte push + + + +class key_error : public std::runtime_error +{ +public: + explicit key_error(const std::string& str) : std::runtime_error(str) {} +}; + + +// secure_allocator is defined is serialize.h +typedef vector > CPrivKey; + + + +class CKey +{ +protected: + EC_KEY* pkey; + +public: + CKey() + { + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (pkey == NULL) + throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); + } + + CKey(const CKey& b) + { + pkey = EC_KEY_dup(b.pkey); + if (pkey == NULL) + throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); + } + + CKey& operator=(const CKey& b) + { + if (!EC_KEY_copy(pkey, b.pkey)) + throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed"); + return (*this); + } + + ~CKey() + { + EC_KEY_free(pkey); + } + + void MakeNewKey() + { + if (!EC_KEY_generate_key(pkey)) + throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); + } + + bool SetPrivKey(const CPrivKey& vchPrivKey) + { + const unsigned char* pbegin = &vchPrivKey[0]; + if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) + return false; + return true; + } + + CPrivKey GetPrivKey() const + { + unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); + if (!nSize) + throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); + CPrivKey vchPrivKey(nSize, 0); + unsigned char* pbegin = &vchPrivKey[0]; + if (i2d_ECPrivateKey(pkey, &pbegin) != nSize) + throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size"); + return vchPrivKey; + } + + bool SetPubKey(const vector& vchPubKey) + { + const unsigned char* pbegin = &vchPubKey[0]; + if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) + return false; + return true; + } + + vector GetPubKey() const + { + unsigned int nSize = i2o_ECPublicKey(pkey, NULL); + if (!nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); + vector vchPubKey(nSize, 0); + unsigned char* pbegin = &vchPubKey[0]; + if (i2o_ECPublicKey(pkey, &pbegin) != nSize) + throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); + return vchPubKey; + } + + bool Sign(uint256 hash, vector& vchSig) + { + vchSig.clear(); + unsigned char pchSig[10000]; + unsigned int nSize = 0; + if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), pchSig, &nSize, pkey)) + return false; + vchSig.resize(nSize); + memcpy(&vchSig[0], pchSig, nSize); + return true; + } + + bool Verify(uint256 hash, const vector& vchSig) + { + // -1 = error, 0 = bad sig, 1 = good + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + return false; + return true; + } + + static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, vector& vchSig) + { + CKey key; + if (!key.SetPrivKey(vchPrivKey)) + return false; + return key.Sign(hash, vchSig); + } + + static bool Verify(const vector& vchPubKey, uint256 hash, const vector& vchSig) + { + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + return key.Verify(hash, vchSig); + } +}; diff --git a/src/license.txt b/src/license.txt new file mode 100644 index 0000000..a456302 --- /dev/null +++ b/src/license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2009 Satoshi Nakamoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..1dbce58 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,2696 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#include "sha.h" + + + + + +// +// Global state +// + +CCriticalSection cs_main; + +map mapTransactions; +CCriticalSection cs_mapTransactions; +unsigned int nTransactionsUpdated = 0; +map mapNextTx; + +map mapBlockIndex; +// Bitflash genesis -- mined under RandomX PoW on 2026-07-22 +// (nTime 1753142400, nBits 0x1f0fffff, RandomX key "Bitflash/RandomX/v1/...") +// Bitflash 2026 stable relaunch genesis (2-min blocks, RandomX). Mined values. +unsigned int GENESIS_NONCE = 3141; +uint256 hashGenesisBlock("0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169"); +uint256 hashGenesisMerkleRoot("0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325"); +CBlockIndex* pindexGenesisBlock = NULL; +int nBestHeight = -1; +uint256 hashBestChain = 0; +CBlockIndex* pindexBest = NULL; + +map mapOrphanBlocks; +multimap mapOrphanBlocksByPrev; + +map mapOrphanTransactions; +multimap mapOrphanTransactionsByPrev; + +map mapWallet; +vector > vWalletUpdated; +CCriticalSection cs_mapWallet; + +map, CPrivKey> mapKeys; +map > mapPubKeys; +CCriticalSection cs_mapKeys; +CKey keyUser; + +string strSetDataDir; +int nDropMessagesTest = 0; +bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local test) + +// Settings +int fGenerateBitcoins; +int64 nTransactionFee = 0; +CAddress addrIncoming; + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapKeys +// + +bool AddKey(const CKey& key) +{ + CRITICAL_BLOCK(cs_mapKeys) + { + mapKeys[key.GetPubKey()] = key.GetPrivKey(); + mapPubKeys[Hash160(key.GetPubKey())] = key.GetPubKey(); + } + return CWalletDB().WriteKey(key.GetPubKey(), key.GetPrivKey()); +} + +vector GenerateNewKey() +{ + CKey key; + key.MakeNewKey(); + if (!AddKey(key)) + throw runtime_error("GenerateNewKey() : AddKey failed\n"); + return key.GetPubKey(); +} + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapWallet +// + +bool AddToWallet(const CWalletTx& wtxIn) +{ + uint256 hash = wtxIn.GetHash(); + CRITICAL_BLOCK(cs_mapWallet) + { + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + bool fInsertedNew = ret.second; + if (fInsertedNew) + wtx.nTimeReceived = GetAdjustedTime(); + + //// debug print + printf("AddToWallet %s %s\n", wtxIn.GetHash().ToString().substr(0,6).c_str(), fInsertedNew ? "new" : "update"); + + if (!fInsertedNew) + { + // Merge + bool fUpdated = false; + if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) + { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) + { + wtx.vMerkleBranch = wtxIn.vMerkleBranch; + wtx.nIndex = wtxIn.nIndex; + fUpdated = true; + } + if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) + { + wtx.fFromMe = wtxIn.fFromMe; + fUpdated = true; + } + if (wtxIn.fSpent && wtxIn.fSpent != wtx.fSpent) + { + wtx.fSpent = wtxIn.fSpent; + fUpdated = true; + } + if (!fUpdated) + return true; + } + + // Write to disk + if (!wtx.WriteToDisk()) + return false; + + // Notify UI + vWalletUpdated.push_back(make_pair(hash, fInsertedNew)); + } + + // Refresh UI + MainFrameRepaint(); + return true; +} + +bool AddToWalletIfMine(const CTransaction& tx, const CBlock* pblock) +{ + if (tx.IsMine() || mapWallet.count(tx.GetHash())) + { + CWalletTx wtx(tx); + // Get merkle branch if transaction was found in a block + if (pblock) + wtx.SetMerkleBranch(pblock); + return AddToWallet(wtx); + } + return true; +} + +bool EraseFromWallet(uint256 hash) +{ + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.erase(hash)) + CWalletDB().EraseTx(hash); + } + return true; +} + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// mapOrphanTransactions +// + +void AddOrphanTx(const CDataStream& vMsg) +{ + CTransaction tx; + CDataStream(vMsg) >> tx; + uint256 hash = tx.GetHash(); + if (mapOrphanTransactions.count(hash)) + return; + CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); + foreach(const CTxIn& txin, tx.vin) + mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); +} + +void EraseOrphanTx(uint256 hash) +{ + if (!mapOrphanTransactions.count(hash)) + return; + const CDataStream* pvMsg = mapOrphanTransactions[hash]; + CTransaction tx; + CDataStream(*pvMsg) >> tx; + foreach(const CTxIn& txin, tx.vin) + { + for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash); + mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);) + { + if ((*mi).second == pvMsg) + mapOrphanTransactionsByPrev.erase(mi++); + else + mi++; + } + } + delete pvMsg; + mapOrphanTransactions.erase(hash); +} + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CTransaction +// + +bool CTxIn::IsMine() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return true; + } + } + return false; +} + +int64 CTxIn::GetDebit() const +{ + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + if (prev.vout[prevout.n].IsMine()) + return prev.vout[prevout.n].nValue; + } + } + return 0; +} + +int64 CWalletTx::GetTxTime() const +{ + if (!fTimeReceivedIsTxTime && hashBlock != 0) + { + // If we did not receive the transaction directly, we rely on the block's + // time to figure out when it happened. We use the median over a range + // of blocks to try to filter out inaccurate block times. + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex) + return pindex->GetMedianTime(); + } + } + return nTimeReceived; +} + + + + + + +int CMerkleTx::SetMerkleBranch(const CBlock* pblock) +{ + if (fClient) + { + if (hashBlock == 0) + return 0; + } + else + { + CBlock blockTmp; + if (pblock == NULL) + { + // Load the block this tx is in + CTxIndex txindex; + if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) + return 0; + if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, true)) + return 0; + pblock = &blockTmp; + } + + // Update the tx's hashBlock + hashBlock = pblock->GetHash(); + + // Locate the transaction + for (nIndex = 0; nIndex < pblock->vtx.size(); nIndex++) + if (pblock->vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == pblock->vtx.size()) + { + vMerkleBranch.clear(); + nIndex = -1; + printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + return 0; + } + + // Fill in merkle branch + vMerkleBranch = pblock->GetMerkleBranch(nIndex); + } + + // Is the tx in a block that's in the main chain + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + return pindexBest->nHeight - pindex->nHeight + 1; +} + + + +void CWalletTx::AddSupportingTransactions(CTxDB& txdb) +{ + vtxPrev.clear(); + + const int COPY_DEPTH = 3; + if (SetMerkleBranch() < COPY_DEPTH) + { + vector vWorkQueue; + foreach(const CTxIn& txin, vin) + vWorkQueue.push_back(txin.prevout.hash); + + // This critsect is OK because txdb is already open + CRITICAL_BLOCK(cs_mapWallet) + { + map mapWalletPrev; + set setAlreadyDone; + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hash = vWorkQueue[i]; + if (setAlreadyDone.count(hash)) + continue; + setAlreadyDone.insert(hash); + + CMerkleTx tx; + if (mapWallet.count(hash)) + { + tx = mapWallet[hash]; + foreach(const CMerkleTx& txWalletPrev, mapWallet[hash].vtxPrev) + mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; + } + else if (mapWalletPrev.count(hash)) + { + tx = *mapWalletPrev[hash]; + } + else if (!fClient && txdb.ReadDiskTx(hash, tx)) + { + ; + } + else + { + printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); + continue; + } + + int nDepth = tx.SetMerkleBranch(); + vtxPrev.push_back(tx); + + if (nDepth < COPY_DEPTH) + foreach(const CTxIn& txin, tx.vin) + vWorkQueue.push_back(txin.prevout.hash); + } + } + } + + reverse(vtxPrev.begin(), vtxPrev.end()); +} + + + + + + + + + + + +bool CTransaction::AcceptTransaction(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) +{ + if (pfMissingInputs) + *pfMissingInputs = false; + + // Coinbase is only valid in a block, not as a loose transaction + if (IsCoinBase()) + return error("AcceptTransaction() : coinbase as individual tx"); + + if (!CheckTransaction()) + return error("AcceptTransaction() : CheckTransaction failed"); + + // Do we already have it? + uint256 hash = GetHash(); + CRITICAL_BLOCK(cs_mapTransactions) + if (mapTransactions.count(hash)) + return false; + if (fCheckInputs) + if (txdb.ContainsTx(hash)) + return false; + + // Check for conflicts with in-memory transactions + CTransaction* ptxOld = NULL; + for (int i = 0; i < vin.size(); i++) + { + COutPoint outpoint = vin[i].prevout; + if (mapNextTx.count(outpoint)) + { + // Allow replacing with a newer version of the same transaction + if (i != 0) + return false; + ptxOld = mapNextTx[outpoint].ptx; + if (!IsNewerThan(*ptxOld)) + return false; + for (int i = 0; i < vin.size(); i++) + { + COutPoint outpoint = vin[i].prevout; + if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + return false; + } + break; + } + } + + // Check against previous transactions + map mapUnused; + int64 nFees = 0; + if (fCheckInputs && !ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), 0, nFees, false, false)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + return error("AcceptTransaction() : ConnectInputs failed %s", hash.ToString().substr(0,6).c_str()); + } + + // Store transaction in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (ptxOld) + { + printf("mapTransaction.erase(%s) replacing with new version\n", ptxOld->GetHash().ToString().c_str()); + mapTransactions.erase(ptxOld->GetHash()); + } + AddToMemoryPool(); + } + + ///// are we sure this is ok when loading transactions or restoring block txes + // If updated, erase old tx from wallet + if (ptxOld) + EraseFromWallet(ptxOld->GetHash()); + + printf("AcceptTransaction(): accepted %s\n", hash.ToString().substr(0,6).c_str()); + return true; +} + + +bool CTransaction::AddToMemoryPool() +{ + // Add to memory pool without checking anything. Don't call this directly, + // call AcceptTransaction to properly check the transaction first. + CRITICAL_BLOCK(cs_mapTransactions) + { + uint256 hash = GetHash(); + mapTransactions[hash] = *this; + for (int i = 0; i < vin.size(); i++) + mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i); + nTransactionsUpdated++; + } + return true; +} + + +bool CTransaction::RemoveFromMemoryPool() +{ + // Remove transaction from memory pool + CRITICAL_BLOCK(cs_mapTransactions) + { + foreach(const CTxIn& txin, vin) + mapNextTx.erase(txin.prevout); + mapTransactions.erase(GetHash()); + nTransactionsUpdated++; + } + return true; +} + + + + + + +int CMerkleTx::GetDepthInMainChain() const +{ + if (hashBlock == 0 || nIndex == -1) + return 0; + + // Find the block it claims to be in + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !pindex->IsInMainChain()) + return 0; + + // Make sure the merkle branch connects to this block + if (!fMerkleVerified) + { + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + return 0; + fMerkleVerified = true; + } + + return pindexBest->nHeight - pindex->nHeight + 1; +} + + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptTransaction(CTxDB& txdb, bool fCheckInputs) +{ + if (fClient) + { + if (!IsInMainChain() && !ClientConnectInputs()) + return false; + return CTransaction::AcceptTransaction(txdb, false); + } + else + { + return CTransaction::AcceptTransaction(txdb, fCheckInputs); + } +} + + + +bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) +{ + CRITICAL_BLOCK(cs_mapTransactions) + { + foreach(CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!mapTransactions.count(hash) && !txdb.ContainsTx(hash)) + tx.AcceptTransaction(txdb, fCheckInputs); + } + } + if (!IsCoinBase()) + return AcceptTransaction(txdb, fCheckInputs); + } + return true; +} + +void ReacceptWalletTransactions() +{ + // Reaccept any txes of ours that aren't already in a block + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + if (!wtx.IsCoinBase() && !txdb.ContainsTx(wtx.GetHash())) + wtx.AcceptWalletTransaction(txdb, false); + } + } +} + + +void CWalletTx::RelayWalletTransaction(CTxDB& txdb) +{ + foreach(const CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!txdb.ContainsTx(hash)) + RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + } + } + if (!IsCoinBase()) + { + uint256 hash = GetHash(); + if (!txdb.ContainsTx(hash)) + { + printf("Relaying wtx %s\n", hash.ToString().substr(0,6).c_str()); + RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); + } + } +} + +void RelayWalletTransactions() +{ + static int64 nLastTime; + if (GetTime() - nLastTime < 10 * 60) + return; + nLastTime = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + printf("RelayWalletTransactions()\n"); + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + // Sort them in chronological order + multimap mapSorted; + foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) + { + CWalletTx& wtx = item.second; + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + foreach(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) + { + CWalletTx& wtx = *item.second; + wtx.RelayWalletTransaction(txdb); + } + } +} + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CBlock and CBlockIndex +// + +bool CBlock::ReadFromDisk(const CBlockIndex* pblockindex, bool fReadTransactions) +{ + return ReadFromDisk(pblockindex->nFile, pblockindex->nBlockPos, fReadTransactions); +} + +uint256 GetOrphanRoot(const CBlock* pblock) +{ + // Work back to the first block in the orphan chain + while (mapOrphanBlocks.count(pblock->hashPrevBlock)) + pblock = mapOrphanBlocks[pblock->hashPrevBlock]; + return pblock->GetHash(); +} + +int64 CBlock::GetBlockValue(int64 nFees) const +{ + int64 nSubsidy = 50 * COIN; + + // Subsidy is cut in half every 4 years + nSubsidy >>= (nBestHeight / 210000); + + return nSubsidy + nFees; +} + +unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast) +{ + // Bitflash: 2-minute target block time with a fast retarget (every 30 + // blocks, ~1h) so difficulty tracks the small, volatile CPU hashrate and + // brief disconnections no longer spawn divergent forks. + const unsigned int nTargetSpacing = 2 * 60; // 2 minutes + const unsigned int nInterval = 30; // retarget window + const unsigned int nTargetTimespan = nInterval * nTargetSpacing; // 3600s + + // Genesis block + if (pindexLast == NULL) + return bnProofOfWorkLimit.GetCompact(); + + // Only change once per interval + if ((pindexLast->nHeight+1) % nInterval != 0) + return pindexLast->nBits; + + // Go back by what we want to be 14 days worth of blocks + const CBlockIndex* pindexFirst = pindexLast; + for (int i = 0; pindexFirst && i < nInterval-1; i++) + pindexFirst = pindexFirst->pprev; + assert(pindexFirst); + + // Limit adjustment step + unsigned int nActualTimespan = pindexLast->nTime - pindexFirst->nTime; + printf(" nActualTimespan = %d before bounds\n", nActualTimespan); + if (nActualTimespan < nTargetTimespan/4) + nActualTimespan = nTargetTimespan/4; + if (nActualTimespan > nTargetTimespan*4) + nActualTimespan = nTargetTimespan*4; + + // Retarget + CBigNum bnNew; + bnNew.SetCompact(pindexLast->nBits); + bnNew *= nActualTimespan; + bnNew /= nTargetTimespan; + + if (bnNew > bnProofOfWorkLimit) + bnNew = bnProofOfWorkLimit; + + /// debug print + printf("\n\n\nGetNextWorkRequired RETARGET *****\n"); + printf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan); + printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); + printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); + + return bnNew.GetCompact(); +} + + + + + + + + + +bool CTransaction::DisconnectInputs(CTxDB& txdb) +{ + // Relinquish previous transactions' spent pointers + if (!IsCoinBase()) + { + foreach(const CTxIn& txin, vin) + { + COutPoint prevout = txin.prevout; + + // Get prev txindex from disk + CTxIndex txindex; + if (!txdb.ReadTxIndex(prevout.hash, txindex)) + return error("DisconnectInputs() : ReadTxIndex failed"); + + if (prevout.n >= txindex.vSpent.size()) + return error("DisconnectInputs() : prevout.n out of range"); + + // Mark outpoint as not spent + txindex.vSpent[prevout.n].SetNull(); + + // Write back + txdb.UpdateTxIndex(prevout.hash, txindex); + } + } + + // Remove transaction from index + if (!txdb.EraseTxIndex(*this)) + return error("DisconnectInputs() : EraseTxPos failed"); + + return true; +} + + +bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, int nHeight, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) +{ + // Take over previous transactions' spent pointers + if (!IsCoinBase()) + { + int64 nValueIn = 0; + for (int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + + // Read txindex + CTxIndex txindex; + bool fFound = true; + if (fMiner && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool[prevout.hash]; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,6).c_str(), prevout.hash.ToString().substr(0,6).c_str()); + + // Read txPrev + CTransaction txPrev; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (!mapTransactions.count(prevout.hash)) + return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,6).c_str(), prevout.hash.ToString().substr(0,6).c_str()); + txPrev = mapTransactions[prevout.hash]; + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!txPrev.ReadFromDisk(txindex.pos)) + return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,6).c_str(), prevout.hash.ToString().substr(0,6).c_str()); + } + + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + return error("ConnectInputs() : %s prevout.n out of range %d %d %d", GetHash().ToString().substr(0,6).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size()); + + // If prev is coinbase, check that it's matured + if (txPrev.IsCoinBase()) + for (CBlockIndex* pindex = pindexBest; pindex && nBestHeight - pindex->nHeight < COINBASE_MATURITY-1; pindex = pindex->pprev) + if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) + return error("ConnectInputs() : tried to spend coinbase at depth %d", nBestHeight - pindex->nHeight); + + // Verify signature + if (!VerifySignature(txPrev, *this, i)) + return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,6).c_str()); + + // Check for conflicts + if (!txindex.vSpent[prevout.n].IsNull()) + return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,6).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); + + // Mark outpoints as spent + txindex.vSpent[prevout.n] = posThisTx; + + // Write back + if (fBlock) + txdb.UpdateTxIndex(prevout.hash, txindex); + else if (fMiner) + mapTestPool[prevout.hash] = txindex; + + nValueIn += txPrev.vout[prevout.n].nValue; + if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) + return error("ConnectInputs() : txin values out of range"); + } + + // Tally transaction fees + int64 nTxFee = nValueIn - GetValueOut(); + if (nTxFee < 0) + return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,6).c_str()); + if (nTxFee < nMinFee) + return false; + nFees += nTxFee; + if (!MoneyRange(nFees)) + return error("ConnectInputs() : nFees out of range"); + } + + if (fBlock) + { + // Add transaction to disk index + if (!txdb.AddTxIndex(*this, posThisTx, nHeight)) + return error("ConnectInputs() : AddTxPos failed"); + } + else if (fMiner) + { + // Add transaction to test pool + mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size()); + } + + return true; +} + + +bool CTransaction::ClientConnectInputs() +{ + if (IsCoinBase()) + return false; + + // Take over previous transactions' spent pointers + CRITICAL_BLOCK(cs_mapTransactions) + { + int64 nValueIn = 0; + for (int i = 0; i < vin.size(); i++) + { + // Get prev tx from single transactions in memory + COutPoint prevout = vin[i].prevout; + if (!mapTransactions.count(prevout.hash)) + return false; + CTransaction& txPrev = mapTransactions[prevout.hash]; + + if (prevout.n >= txPrev.vout.size()) + return false; + + // Verify signature + if (!VerifySignature(txPrev, *this, i)) + return error("ConnectInputs() : VerifySignature failed"); + + ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of + ///// this has to go away now that posNext is gone + // // Check for conflicts + // if (!txPrev.vout[prevout.n].posNext.IsNull()) + // return error("ConnectInputs() : prev tx already used"); + // + // // Flag outpoints as used + // txPrev.vout[prevout.n].posNext = posThisTx; + + nValueIn += txPrev.vout[prevout.n].nValue; + } + if (GetValueOut() > nValueIn) + return false; + } + + return true; +} + + + + +bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) +{ + // Disconnect in reverse order + for (int i = vtx.size()-1; i >= 0; i--) + if (!vtx[i].DisconnectInputs(txdb)) + return false; + + // Update block index on disk without changing it in memory. + // The memory index structure will be changed after the db commits. + if (pindex->pprev) + { + CDiskBlockIndex blockindexPrev(pindex->pprev); + blockindexPrev.hashNext = 0; + txdb.WriteBlockIndex(blockindexPrev); + } + + return true; +} + +bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) +{ + //// issue here: it doesn't know the version + unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); + + map mapUnused; + int64 nFees = 0; + foreach(CTransaction& tx, vtx) + { + CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); + nTxPos += ::GetSerializeSize(tx, SER_DISK); + + if (!tx.ConnectInputs(txdb, mapUnused, posThisTx, pindex->nHeight, nFees, true, false)) + return false; + } + + if (vtx[0].GetValueOut() > GetBlockValue(nFees)) + return false; + + // Update block index on disk without changing it in memory. + // The memory index structure will be changed after the db commits. + if (pindex->pprev) + { + CDiskBlockIndex blockindexPrev(pindex->pprev); + blockindexPrev.hashNext = pindex->GetBlockHash(); + txdb.WriteBlockIndex(blockindexPrev); + } + + // Watch for transactions paying to me + foreach(CTransaction& tx, vtx) + AddToWalletIfMine(tx, this); + + return true; +} + + + +bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) +{ + printf("*** REORGANIZE ***\n"); + + // Find the fork + CBlockIndex* pfork = pindexBest; + CBlockIndex* plonger = pindexNew; + while (pfork != plonger) + { + if (!(pfork = pfork->pprev)) + return error("Reorganize() : pfork->pprev is null"); + while (plonger->nHeight > pfork->nHeight) + if (!(plonger = plonger->pprev)) + return error("Reorganize() : plonger->pprev is null"); + } + + // List of what to disconnect + vector vDisconnect; + for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) + vDisconnect.push_back(pindex); + + // List of what to connect + vector vConnect; + for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) + vConnect.push_back(pindex); + reverse(vConnect.begin(), vConnect.end()); + + // Disconnect shorter branch + vector vResurrect; + foreach(CBlockIndex* pindex, vDisconnect) + { + CBlock block; + if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true)) + return error("Reorganize() : ReadFromDisk for disconnect failed"); + if (!block.DisconnectBlock(txdb, pindex)) + return error("Reorganize() : DisconnectBlock failed"); + + // Queue memory transactions to resurrect + foreach(const CTransaction& tx, block.vtx) + if (!tx.IsCoinBase()) + vResurrect.push_back(tx); + } + + // Connect longer branch + vector vDelete; + for (int i = 0; i < vConnect.size(); i++) + { + CBlockIndex* pindex = vConnect[i]; + CBlock block; + if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true)) + return error("Reorganize() : ReadFromDisk for connect failed"); + if (!block.ConnectBlock(txdb, pindex)) + { + // Invalid block, delete the rest of this branch + txdb.TxnAbort(); + for (int j = i; j < vConnect.size(); j++) + { + CBlockIndex* pindex = vConnect[j]; + pindex->EraseBlockFromDisk(); + txdb.EraseBlockIndex(pindex->GetBlockHash()); + mapBlockIndex.erase(pindex->GetBlockHash()); + delete pindex; + } + return error("Reorganize() : ConnectBlock failed"); + } + + // Queue memory transactions to delete + foreach(const CTransaction& tx, block.vtx) + vDelete.push_back(tx); + } + if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) + return error("Reorganize() : WriteHashBestChain failed"); + + // Commit now because resurrecting could take some time + txdb.TxnCommit(); + + // Disconnect shorter branch + foreach(CBlockIndex* pindex, vDisconnect) + if (pindex->pprev) + pindex->pprev->pnext = NULL; + + // Connect longer branch + foreach(CBlockIndex* pindex, vConnect) + if (pindex->pprev) + pindex->pprev->pnext = pindex; + + // Resurrect memory transactions that were in the disconnected branch + foreach(CTransaction& tx, vResurrect) + tx.AcceptTransaction(txdb, false); + + // Delete redundant memory transactions that are in the connected branch + foreach(CTransaction& tx, vDelete) + tx.RemoveFromMemoryPool(); + + return true; +} + + +bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,14).c_str()); + + // Construct new block index object + CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); + if (!pindexNew) + return error("AddToBlockIndex() : new CBlockIndex failed"); + map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + pindexNew->phashBlock = &((*mi).first); + map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + if (miPrev != mapBlockIndex.end()) + { + pindexNew->pprev = (*miPrev).second; + pindexNew->nHeight = pindexNew->pprev->nHeight + 1; + } + + CTxDB txdb; + txdb.TxnBegin(); + txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); + + // New best + if (pindexNew->nHeight > nBestHeight) + { + if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) + { + pindexGenesisBlock = pindexNew; + txdb.WriteHashBestChain(hash); + } + else if (hashPrevBlock == hashBestChain) + { + // Adding to current best branch + if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + { + txdb.TxnAbort(); + pindexNew->EraseBlockFromDisk(); + mapBlockIndex.erase(pindexNew->GetBlockHash()); + delete pindexNew; + return error("AddToBlockIndex() : ConnectBlock failed"); + } + txdb.TxnCommit(); + pindexNew->pprev->pnext = pindexNew; + + // Delete redundant memory transactions + foreach(CTransaction& tx, vtx) + tx.RemoveFromMemoryPool(); + } + else + { + // New best branch + if (!Reorganize(txdb, pindexNew)) + { + txdb.TxnAbort(); + return error("AddToBlockIndex() : Reorganize failed"); + } + } + + // New best link + hashBestChain = hash; + pindexBest = pindexNew; + nBestHeight = pindexBest->nHeight; + nTransactionsUpdated++; + printf("AddToBlockIndex: new best=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight); + } + + txdb.TxnCommit(); + txdb.Close(); + + // Relay wallet transactions that haven't gotten in yet + if (pindexNew == pindexBest) + RelayWalletTransactions(); + + MainFrameRepaint(); + return true; +} + + + + +bool CBlock::CheckBlock() const +{ + // These are checks that are independent of context + // that can be verified before saving an orphan block. + + // Size limits + if (vtx.empty() || vtx.size() > MAX_SIZE || ::GetSerializeSize(*this, SER_DISK) > MAX_SIZE) + return error("CheckBlock() : size limits failed"); + + // Check timestamp + if (nTime > GetAdjustedTime() + 2 * 60 * 60) + return error("CheckBlock() : block timestamp too far in the future"); + + // First transaction must be coinbase, the rest must not be + if (vtx.empty() || !vtx[0].IsCoinBase()) + return error("CheckBlock() : first tx is not coinbase"); + for (int i = 1; i < vtx.size(); i++) + if (vtx[i].IsCoinBase()) + return error("CheckBlock() : more than one coinbase"); + + // Check transactions + foreach(const CTransaction& tx, vtx) + if (!tx.CheckTransaction()) + return error("CheckBlock() : CheckTransaction failed"); + + // Check proof of work matches claimed amount (memory-hard RandomX PoW) + if (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit) + return error("CheckBlock() : nBits below minimum work"); + if (GetPoWHash() > CBigNum().SetCompact(nBits).getuint256()) + return error("CheckBlock() : RandomX proof-of-work does not match nBits"); + + // Check merkleroot + if (hashMerkleRoot != BuildMerkleTree()) + return error("CheckBlock() : hashMerkleRoot mismatch"); + + return true; +} + +bool CBlock::AcceptBlock() +{ + // Check for duplicate + uint256 hash = GetHash(); + if (mapBlockIndex.count(hash)) + return error("AcceptBlock() : block already in mapBlockIndex"); + + // Get prev block index + map::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (mi == mapBlockIndex.end()) + return error("AcceptBlock() : prev block not found"); + CBlockIndex* pindexPrev = (*mi).second; + + // Check timestamp against prev + if (nTime <= pindexPrev->GetMedianTimePast()) + return error("AcceptBlock() : block's timestamp is too early"); + + // Check proof of work + if (nBits != GetNextWorkRequired(pindexPrev)) + return error("AcceptBlock() : incorrect proof of work"); + + // Write block to history file + unsigned int nFile; + unsigned int nBlockPos; + if (!WriteToDisk(!fClient, nFile, nBlockPos)) + return error("AcceptBlock() : WriteToDisk failed"); + if (!AddToBlockIndex(nFile, nBlockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + + if (hashBestChain == hash) + RelayInventory(CInv(MSG_BLOCK, hash)); + + // // Add atoms to user reviews for coins created + // vector vchPubKey; + // if (ExtractPubKey(vtx[0].vout[0].scriptPubKey, false, vchPubKey)) + // { + // unsigned short nAtom = GetRand(USHRT_MAX - 100) + 100; + // vector vAtoms(1, nAtom); + // AddAtomsAndPropagate(Hash(vchPubKey.begin(), vchPubKey.end()), vAtoms, true); + // } + + return true; +} + +bool ProcessBlock(CNode* pfrom, CBlock* pblock) +{ + // Check for duplicate + uint256 hash = pblock->GetHash(); + if (mapBlockIndex.count(hash)) + return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,14).c_str()); + if (mapOrphanBlocks.count(hash)) + return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,14).c_str()); + + // Preliminary checks + if (!pblock->CheckBlock()) + { + delete pblock; + return error("ProcessBlock() : CheckBlock FAILED"); + } + + // If don't already have its previous block, shunt it off to holding area until we get it + if (!mapBlockIndex.count(pblock->hashPrevBlock)) + { + printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,14).c_str()); + mapOrphanBlocks.insert(make_pair(hash, pblock)); + mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock)); + + // Ask this guy to fill in what we're missing + if (pfrom) + pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(pblock)); + return true; + } + + // Store to disk + if (!pblock->AcceptBlock()) + { + delete pblock; + return error("ProcessBlock() : AcceptBlock FAILED"); + } + delete pblock; + + // Recursively process any orphan blocks that depended on this one + vector vWorkQueue; + vWorkQueue.push_back(hash); + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); + mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); + ++mi) + { + CBlock* pblockOrphan = (*mi).second; + if (pblockOrphan->AcceptBlock()) + vWorkQueue.push_back(pblockOrphan->GetHash()); + mapOrphanBlocks.erase(pblockOrphan->GetHash()); + delete pblockOrphan; + } + mapOrphanBlocksByPrev.erase(hashPrev); + } + + printf("ProcessBlock: ACCEPTED\n"); + return true; +} + + + + + + + + +template +bool ScanMessageStart(Stream& s) +{ + // Scan ahead to the next pchMessageStart, which should normally be immediately + // at the file pointer. Leaves file pointer at end of pchMessageStart. + s.clear(0); + short prevmask = s.exceptions(0); + const char* p = BEGIN(pchMessageStart); + try + { + loop + { + char c; + s.read(&c, 1); + if (s.fail()) + { + s.clear(0); + s.exceptions(prevmask); + return false; + } + if (*p != c) + p = BEGIN(pchMessageStart); + if (*p == c) + { + if (++p == END(pchMessageStart)) + { + s.clear(0); + s.exceptions(prevmask); + return true; + } + } + } + } + catch (...) + { + s.clear(0); + s.exceptions(prevmask); + return false; + } +} + +string GetAppDir() +{ + string strDir; + if (!strSetDataDir.empty()) + { + strDir = strSetDataDir; + } +#ifdef _WIN32 + else if (getenv("APPDATA")) + { + strDir = strprintf("%s\\Bitflash", getenv("APPDATA")); + } + else if (getenv("USERPROFILE")) + { + string strAppData = strprintf("%s\\Application Data", getenv("USERPROFILE")); + static bool fMkdirDone; + if (!fMkdirDone) + { + fMkdirDone = true; + _mkdir(strAppData.c_str()); + } + strDir = strprintf("%s\\Bitflash", strAppData.c_str()); + } +#else + else if (getenv("HOME")) + { + strDir = strprintf("%s/.bitflash", getenv("HOME")); + } +#endif + else + { + return "."; + } + static bool fMkdirDone; + if (!fMkdirDone) + { + fMkdirDone = true; + _mkdir(strDir.c_str()); + } + return strDir; +} + +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) +{ + if (nFile == -1) + return NULL; + FILE* file = fopen(strprintf("%s/blk%04d.dat", GetAppDir().c_str(), nFile).c_str(), pszMode); + if (!file) + return NULL; + if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) + { + if (fseek(file, nBlockPos, SEEK_SET) != 0) + { + fclose(file); + return NULL; + } + } + return file; +} + +static unsigned int nCurrentBlockFile = 1; + +FILE* AppendBlockFile(unsigned int& nFileRet) +{ + nFileRet = 0; + loop + { + FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); + if (!file) + return NULL; + if (fseek(file, 0, SEEK_END) != 0) + return NULL; + // FAT32 filesize max 4GB, fseek and ftell max 2GB, so we must stay under 2GB + if (ftell(file) < 0x7F000000 - MAX_SIZE) + { + nFileRet = nCurrentBlockFile; + return file; + } + fclose(file); + nCurrentBlockFile++; + } +} + +bool LoadBlockIndex(bool fAllowNew) +{ + // Initialize the RandomX PoW before touching any block (both the genesis + // and verification depend on it). + if (!RandomXInit()) + return error("LoadBlockIndex() : RandomXInit failed"); + + // + // Load block index + // + CTxDB txdb("cr"); + if (!txdb.LoadBlockIndex()) + return false; + txdb.Close(); + + // + // Init with genesis block + // + if (mapBlockIndex.empty()) + { + if (!fAllowNew) + return false; + + + // Genesis Block: + // GetHash() = 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f + // hashMerkleRoot = 0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + // txNew.vin[0].scriptSig = 486604799 4 0x736B6E616220726F662074756F6C69616220646E6F63657320666F206B6E697262206E6F20726F6C6C65636E61684320393030322F6E614A2F33302073656D695420656854 + // txNew.vout[0].nValue = 5000000000 + // txNew.vout[0].scriptPubKey = 0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704 OP_CHECKSIG + // block.nVersion = 1 + // block.nTime = 1231006505 + // block.nBits = 0x1d00ffff + // block.nNonce = 2083236893 + // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) + // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) + // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) + // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) + // vMerkleTree: 4a5e1e + + // Bitflash genesis block -- own header (fair launch, 2026 stable relaunch) + char* pszTimestamp = "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine"; + CTransaction txNew; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = 50 * COIN; + txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG; + CBlock block; + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = 1753315200; // 2026-07-24 00:00:00 UTC (stable relaunch) + block.nBits = 0x1f0fffff; // = bnProofOfWorkLimit (Bitflash RandomX floor) + block.nNonce = GENESIS_NONCE; + + // If the nonce has not been found yet, mine the genesis once (RandomX) + // and print the values to be hardcoded in the constants above. + if (block.GetHash() != hashGenesisBlock) + { + printf("Mining Bitflash genesis block (RandomX)...\n"); + uint256 hashTarget = CBigNum().SetCompact(block.nBits).getuint256(); + while (block.GetPoWHash() > hashTarget) + { + if ((block.nNonce & 0x3ff) == 0) + printf("nonce %u\r", block.nNonce); + block.nNonce++; + if (block.nNonce == 0) + { + printf("nonce overflow, incrementing nTime\n"); + block.nTime++; + } + } + printf("\n=== BITFLASH GENESIS (RandomX) FOUND ===\n"); + printf("nTime = %u\n", block.nTime); + printf("nNonce = %u (0x%08x)\n", block.nNonce, block.nNonce); + printf("powhash = %s\n", block.GetPoWHash().ToString().c_str()); + printf("hash = %s\n", block.GetHash().ToString().c_str()); + printf("merkle = %s\n", block.hashMerkleRoot.ToString().c_str()); + printf("========================================\n"); + } + + // Asserts only apply once the genesis values are hardcoded + if (hashGenesisBlock != uint256(0)) + { + assert(block.hashMerkleRoot == hashGenesisMerkleRoot); + assert(block.GetHash() == hashGenesisBlock); + } + + // Start new block file + unsigned int nFile; + unsigned int nBlockPos; + if (!block.WriteToDisk(!fClient, nFile, nBlockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + if (!block.AddToBlockIndex(nFile, nBlockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + } + + return true; +} + + + +void PrintBlockTree() +{ + // precompute tree structure + map > mapNext; + for (map::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) + { + CBlockIndex* pindex = (*mi).second; + mapNext[pindex->pprev].push_back(pindex); + // test + //while (rand() % 3 == 0) + // mapNext[pindex->pprev].push_back(pindex); + } + + vector > vStack; + vStack.push_back(make_pair(0, pindexGenesisBlock)); + + int nPrevCol = 0; + while (!vStack.empty()) + { + int nCol = vStack.back().first; + CBlockIndex* pindex = vStack.back().second; + vStack.pop_back(); + + // print split or gap + if (nCol > nPrevCol) + { + for (int i = 0; i < nCol-1; i++) + printf("| "); + printf("|\\\n"); + } + else if (nCol < nPrevCol) + { + for (int i = 0; i < nCol; i++) + printf("| "); + printf("|\n"); + } + nPrevCol = nCol; + + // print columns + for (int i = 0; i < nCol; i++) + printf("| "); + + // print item + CBlock block; + block.ReadFromDisk(pindex, true); + printf("%d (%u,%u) %s %s tx %d", + pindex->nHeight, + pindex->nFile, + pindex->nBlockPos, + block.GetHash().ToString().substr(0,14).c_str(), + DateTimeStr(block.nTime).c_str(), + block.vtx.size()); + + CRITICAL_BLOCK(cs_mapWallet) + { + if (mapWallet.count(block.vtx[0].GetHash())) + { + CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; + printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + } + } + printf("\n"); + + + // put the main timechain first + vector& vNext = mapNext[pindex]; + for (int i = 0; i < vNext.size(); i++) + { + if (vNext[i]->pnext) + { + swap(vNext[0], vNext[i]); + break; + } + } + + // iterate children + for (int i = 0; i < vNext.size(); i++) + vStack.push_back(make_pair(nCol+i, vNext[i])); + } +} + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Messages +// + + +bool AlreadyHave(CTxDB& txdb, const CInv& inv) +{ + switch (inv.type) + { + case MSG_TX: return mapTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); + case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); + case MSG_REVIEW: return true; + case MSG_PRODUCT: return mapProducts.count(inv.hash); + } + // Don't know what it is, just say we already got one + return true; +} + + + + + + + +bool ProcessMessages(CNode* pfrom) +{ + CDataStream& vRecv = pfrom->vRecv; + if (vRecv.empty()) + return true; + printf("ProcessMessages(%d bytes)\n", vRecv.size()); + + // + // Message format + // (4) message start + // (12) command + // (4) size + // (x) data + // + + loop + { + // Scan for message start + CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart)); + if (vRecv.end() - pstart < sizeof(CMessageHeader)) + { + if (vRecv.size() > sizeof(CMessageHeader)) + { + printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); + vRecv.erase(vRecv.begin(), vRecv.end() - sizeof(CMessageHeader)); + } + break; + } + if (pstart - vRecv.begin() > 0) + printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", pstart - vRecv.begin()); + vRecv.erase(vRecv.begin(), pstart); + + // Read header + CMessageHeader hdr; + vRecv >> hdr; + if (!hdr.IsValid()) + { + printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + continue; + } + string strCommand = hdr.GetCommand(); + + // Message size + unsigned int nMessageSize = hdr.nMessageSize; + if (nMessageSize > vRecv.size()) + { + // Rewind and wait for rest of message + ///// need a mechanism to give up waiting for overlong message size error + printf("MESSAGE-BREAK 2\n"); + vRecv.insert(vRecv.begin(), BEGIN(hdr), END(hdr)); + Sleep(100); + break; + } + + // Copy message to its own buffer + CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); + vRecv.ignore(nMessageSize); + + // Process message + bool fRet = false; + try + { + CheckForShutdown(2); + CRITICAL_BLOCK(cs_main) + fRet = ProcessMessage(pfrom, strCommand, vMsg); + CheckForShutdown(2); + } + CATCH_PRINT_EXCEPTION("ProcessMessage()") + if (!fRet) + printf("ProcessMessage(%s, %d bytes) from %s to %s FAILED\n", strCommand.c_str(), nMessageSize, pfrom->addr.ToString().c_str(), addrLocalHost.ToString().c_str()); + } + + vRecv.Compact(); + return true; +} + + + + +bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +{ + static map > mapReuseKey; + printf("received: %-12s (%d bytes) ", strCommand.c_str(), vRecv.size()); + for (int i = 0; i < min(vRecv.size(), (unsigned int)25); i++) + printf("%02x ", vRecv[i] & 0xff); + printf("\n"); + if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0) + { + printf("dropmessages DROPPING RECV MESSAGE\n"); + return true; + } + + + + if (strCommand == "version") + { + // Can only do this once + if (pfrom->nVersion != 0) + return false; + + int64 nTime; + CAddress addrMe; + vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (pfrom->nVersion == 0) + return false; + + pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION)); + pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + if (pfrom->fClient) + { + pfrom->vSend.nType |= SER_BLOCKHEADERONLY; + pfrom->vRecv.nType |= SER_BLOCKHEADERONLY; + } + + AddTimeData(pfrom->addr.ip, nTime); + + // Ask the first connected node for block updates + static bool fAskedForBlocks; + if (!fAskedForBlocks && !pfrom->fClient) + { + fAskedForBlocks = true; + pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0)); + } + + printf("version addrMe = %s\n", addrMe.ToString().c_str()); + } + + + else if (pfrom->nVersion == 0) + { + // Must have a version message before anything else + return false; + } + + + else if (strCommand == "addr") + { + vector vAddr; + vRecv >> vAddr; + + // Store the new addresses + CAddrDB addrdb; + foreach(const CAddress& addr, vAddr) + { + if (fShutdown) + return true; + if (AddAddress(addrdb, addr)) + { + // Put on lists to send to other nodes + pfrom->setAddrKnown.insert(addr); + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (!pnode->setAddrKnown.count(addr)) + pnode->vAddrToSend.push_back(addr); + } + } + } + + + else if (strCommand == "inv") + { + vector vInv; + vRecv >> vInv; + + CTxDB txdb("r"); + foreach(const CInv& inv, vInv) + { + if (fShutdown) + return true; + pfrom->AddInventoryKnown(inv); + + bool fAlreadyHave = AlreadyHave(txdb, inv); + printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + + if (!fAlreadyHave) + pfrom->AskFor(inv); + else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) + pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(mapOrphanBlocks[inv.hash])); + } + } + + + else if (strCommand == "getdata") + { + vector vInv; + vRecv >> vInv; + + foreach(const CInv& inv, vInv) + { + if (fShutdown) + return true; + printf("received getdata for: %s\n", inv.ToString().c_str()); + + if (inv.type == MSG_BLOCK) + { + // Send block from disk + map::iterator mi = mapBlockIndex.find(inv.hash); + if (mi != mapBlockIndex.end()) + { + //// could optimize this to send header straight from blockindex for client + CBlock block; + block.ReadFromDisk((*mi).second, !pfrom->fClient); + pfrom->PushMessage("block", block); + } + } + else if (inv.IsKnownType()) + { + // Send stream from relay memory + CRITICAL_BLOCK(cs_mapRelay) + { + map::iterator mi = mapRelay.find(inv); + if (mi != mapRelay.end()) + pfrom->PushMessage(inv.GetCommand(), (*mi).second); + } + } + } + } + + + else if (strCommand == "getblocks") + { + CBlockLocator locator; + uint256 hashStop; + vRecv >> locator >> hashStop; + + // Find the first block the caller has in the main chain + CBlockIndex* pindex = locator.GetBlockIndex(); + + // Send the rest of the chain + if (pindex) + pindex = pindex->pnext; + printf("getblocks %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,14).c_str()); + for (; pindex; pindex = pindex->pnext) + { + if (pindex->GetBlockHash() == hashStop) + { + printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,14).c_str()); + break; + } + + // Bypass setInventoryKnown in case an inventory message got lost + CRITICAL_BLOCK(pfrom->cs_inventory) + { + CInv inv(MSG_BLOCK, pindex->GetBlockHash()); + // returns true if wasn't already contained in the set + if (pfrom->setInventoryKnown2.insert(inv).second) + { + pfrom->setInventoryKnown.erase(inv); + pfrom->vInventoryToSend.push_back(inv); + } + } + } + } + + + else if (strCommand == "tx") + { + vector vWorkQueue; + CDataStream vMsg(vRecv); + CTransaction tx; + vRecv >> tx; + + CInv inv(MSG_TX, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + bool fMissingInputs = false; + if (tx.AcceptTransaction(true, &fMissingInputs)) + { + AddToWalletIfMine(tx, NULL); + RelayMessage(inv, vMsg); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + + // Recursively process any orphan transactions that depended on this one + for (int i = 0; i < vWorkQueue.size(); i++) + { + uint256 hashPrev = vWorkQueue[i]; + for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev); + mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev); + ++mi) + { + const CDataStream& vMsg = *((*mi).second); + CTransaction tx; + CDataStream(vMsg) >> tx; + CInv inv(MSG_TX, tx.GetHash()); + + if (tx.AcceptTransaction(true)) + { + printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str()); + AddToWalletIfMine(tx, NULL); + RelayMessage(inv, vMsg); + mapAlreadyAskedFor.erase(inv); + vWorkQueue.push_back(inv.hash); + } + } + } + + foreach(uint256 hash, vWorkQueue) + EraseOrphanTx(hash); + } + else if (fMissingInputs) + { + printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,6).c_str()); + AddOrphanTx(vMsg); + } + } + + + else if (strCommand == "review") + { + CDataStream vMsg(vRecv); + CReview review; + vRecv >> review; + + CInv inv(MSG_REVIEW, review.GetHash()); + pfrom->AddInventoryKnown(inv); + + if (review.AcceptReview()) + { + // Relay the original message as-is in case it's a higher version than we know how to parse + RelayMessage(inv, vMsg); + mapAlreadyAskedFor.erase(inv); + } + } + + + else if (strCommand == "block") + { + auto_ptr pblock(new CBlock); + vRecv >> *pblock; + + //// debug print + printf("received block:\n"); pblock->print(); + + CInv inv(MSG_BLOCK, pblock->GetHash()); + pfrom->AddInventoryKnown(inv); + + if (ProcessBlock(pfrom, pblock.release())) + mapAlreadyAskedFor.erase(inv); + } + + + else if (strCommand == "getaddr") + { + pfrom->vAddrToSend.clear(); + //// need to expand the time range if not enough found + int64 nSince = GetAdjustedTime() - 60 * 60; // in the last hour + CRITICAL_BLOCK(cs_mapAddresses) + { + foreach(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + if (fShutdown) + return true; + const CAddress& addr = item.second; + if (addr.nTime > nSince) + pfrom->vAddrToSend.push_back(addr); + } + } + } + + + else if (strCommand == "checkorder") + { + uint256 hashReply; + CWalletTx order; + vRecv >> hashReply >> order; + + /// we have a chance to check the order here + + // Keep giving the same key to the same ip until they use it + if (!mapReuseKey.count(pfrom->addr.ip)) + mapReuseKey[pfrom->addr.ip] = GenerateNewKey(); + + // Send back approval of order and pubkey to use + CScript scriptPubKey; + scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; + pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); + } + + + else if (strCommand == "submitorder") + { + uint256 hashReply; + CWalletTx wtxNew; + vRecv >> hashReply >> wtxNew; + + // Broadcast + if (!wtxNew.AcceptWalletTransaction()) + { + pfrom->PushMessage("reply", hashReply, (int)1); + return error("submitorder AcceptWalletTransaction() failed, returning error 1"); + } + wtxNew.fTimeReceivedIsTxTime = true; + AddToWallet(wtxNew); + wtxNew.RelayWalletTransaction(); + mapReuseKey.erase(pfrom->addr.ip); + + // Send back confirmation + pfrom->PushMessage("reply", hashReply, (int)0); + } + + + else if (strCommand == "reply") + { + uint256 hashReply; + vRecv >> hashReply; + + CRequestTracker tracker; + CRITICAL_BLOCK(pfrom->cs_mapRequests) + { + map::iterator mi = pfrom->mapRequests.find(hashReply); + if (mi != pfrom->mapRequests.end()) + { + tracker = (*mi).second; + pfrom->mapRequests.erase(mi); + } + } + if (!tracker.IsNull()) + tracker.fn(tracker.param1, vRecv); + } + + + else + { + // Ignore unknown commands for extensibility + printf("ProcessMessage(%s) : Ignored unknown message\n", strCommand.c_str()); + } + + + if (!vRecv.empty()) + printf("ProcessMessage(%s) : %d extra bytes\n", strCommand.c_str(), vRecv.size()); + + return true; +} + + + + + + + + + +bool SendMessages(CNode* pto) +{ + CheckForShutdown(2); + CRITICAL_BLOCK(cs_main) + { + // Don't send anything until we get their version message + if (pto->nVersion == 0) + return true; + + + // + // Message: addr + // + vector vAddrToSend; + vAddrToSend.reserve(pto->vAddrToSend.size()); + foreach(const CAddress& addr, pto->vAddrToSend) + if (!pto->setAddrKnown.count(addr)) + vAddrToSend.push_back(addr); + pto->vAddrToSend.clear(); + if (!vAddrToSend.empty()) + pto->PushMessage("addr", vAddrToSend); + + + // + // Message: inventory + // + vector vInventoryToSend; + CRITICAL_BLOCK(pto->cs_inventory) + { + vInventoryToSend.reserve(pto->vInventoryToSend.size()); + foreach(const CInv& inv, pto->vInventoryToSend) + { + // returns true if wasn't already contained in the set + if (pto->setInventoryKnown.insert(inv).second) + vInventoryToSend.push_back(inv); + } + pto->vInventoryToSend.clear(); + pto->setInventoryKnown2.clear(); + } + if (!vInventoryToSend.empty()) + pto->PushMessage("inv", vInventoryToSend); + + + // + // Message: getdata + // + vector vAskFor; + int64 nNow = GetTime() * 1000000; + CTxDB txdb("r"); + while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) + { + const CInv& inv = (*pto->mapAskFor.begin()).second; + printf("sending getdata: %s\n", inv.ToString().c_str()); + if (!AlreadyHave(txdb, inv)) + vAskFor.push_back(inv); + pto->mapAskFor.erase(pto->mapAskFor.begin()); + } + if (!vAskFor.empty()) + pto->PushMessage("getdata", vAskFor); + + } + return true; +} + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// BitcoinMiner +// + +int FormatHashBlocks(void* pbuffer, unsigned int len) +{ + unsigned char* pdata = (unsigned char*)pbuffer; + unsigned int blocks = 1 + ((len + 8) / 64); + unsigned char* pend = pdata + 64 * blocks; + memset(pdata + len, 0, 64 * blocks - len); + pdata[len] = 0x80; + unsigned int bits = len * 8; + pend[-1] = (bits >> 0) & 0xff; + pend[-2] = (bits >> 8) & 0xff; + pend[-3] = (bits >> 16) & 0xff; + pend[-4] = (bits >> 24) & 0xff; + return blocks; +} + +using CryptoPP::ByteReverse; +static int detectlittleendian = 1; + +void BlockSHA256(const void* pin, unsigned int nBlocks, void* pout) +{ + unsigned int* pinput = (unsigned int*)pin; + unsigned int* pstate = (unsigned int*)pout; + + CryptoPP::SHA256::InitState(pstate); + + if (*(char*)&detectlittleendian != 0) + { + for (int n = 0; n < nBlocks; n++) + { + unsigned int pbuf[16]; + for (int i = 0; i < 16; i++) + pbuf[i] = ByteReverse(pinput[n * 16 + i]); + CryptoPP::SHA256::Transform(pstate, pbuf); + } + for (int i = 0; i < 8; i++) + pstate[i] = ByteReverse(pstate[i]); + } + else + { + for (int n = 0; n < nBlocks; n++) + CryptoPP::SHA256::Transform(pstate, pinput + n * 16); + } +} + + +bool BitcoinMiner() +{ + printf("BitcoinMiner started\n"); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); + + CKey key; + key.MakeNewKey(); + CBigNum bnExtraNonce = 0; + while (fGenerateBitcoins) + { + Sleep(50); + CheckForShutdown(3); + while (vNodes.empty() && !fSoloMineTest) + { + Sleep(1000); + CheckForShutdown(3); + } + + unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrev = pindexBest; + unsigned int nBits = GetNextWorkRequired(pindexPrev); + + + // + // Create coinbase tx + // + CTransaction txNew; + txNew.vin.resize(1); + txNew.vin[0].prevout.SetNull(); + txNew.vin[0].scriptSig << nBits << ++bnExtraNonce; + txNew.vout.resize(1); + txNew.vout[0].scriptPubKey << key.GetPubKey() << OP_CHECKSIG; + + + // + // Create new block + // + auto_ptr pblock(new CBlock()); + if (!pblock.get()) + return false; + + // Add our coinbase tx as first transaction + pblock->vtx.push_back(txNew); + + // Collect the latest transactions into the block + int64 nFees = 0; + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapTransactions) + { + CTxDB txdb("r"); + map mapTestPool; + vector vfAlreadyAdded(mapTransactions.size()); + bool fFoundSomething = true; + unsigned int nBlockSize = 0; + while (fFoundSomething && nBlockSize < MAX_SIZE/2) + { + fFoundSomething = false; + unsigned int n = 0; + for (map::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi, ++n) + { + if (vfAlreadyAdded[n]) + continue; + CTransaction& tx = (*mi).second; + if (tx.IsCoinBase() || !tx.IsFinal()) + continue; + + // Transaction fee requirements, mainly only needed for flood control + // Under 10K (about 80 inputs) is free for first 100 transactions + // Base rate is 0.01 per KB + int64 nMinFee = tx.GetMinFee(pblock->vtx.size() < 100); + + map mapTestPoolTmp(mapTestPool); + if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), 0, nFees, false, true, nMinFee)) + continue; + swap(mapTestPool, mapTestPoolTmp); + + pblock->vtx.push_back(tx); + nBlockSize += ::GetSerializeSize(tx, SER_NETWORK); + vfAlreadyAdded[n] = true; + fFoundSomething = true; + } + } + } + pblock->nBits = nBits; + pblock->vtx[0].vout[0].nValue = pblock->GetBlockValue(nFees); + printf("\n\nRunning BitcoinMiner with %d transactions in block\n", pblock->vtx.size()); + + + // + // Prepare the block header + // + pblock->hashPrevBlock = (pindexPrev ? pindexPrev->GetBlockHash() : 0); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + pblock->nTime = max((pindexPrev ? pindexPrev->GetMedianTimePast()+1 : 0), GetAdjustedTime()); + pblock->nBits = nBits; + pblock->nNonce = 1; + + + // + // Search (memory-hard RandomX PoW) -- each thread uses its own VM + // + void* rxvm = RandomXCreateMinerVM(); + if (!rxvm) + { + printf("BitcoinMiner: failed to create RandomX VM\n"); + Sleep(1000); + continue; + } + printf("BitcoinMiner: mining with RandomX (%s)\n", + RandomXFastReady() ? "fast 2GB" : "light 256MB"); + + unsigned int nStart = GetTime(); + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + loop + { + uint256 hash = RandomXHashWithVM(rxvm, (const void*)BEGIN(pblock->nVersion), + END(pblock->nNonce) - BEGIN(pblock->nVersion)); + + if (hash <= hashTarget) + { + //// debug print + printf("BitcoinMiner:\n"); + printf("RandomX proof-of-work found \n powhash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + CRITICAL_BLOCK(cs_main) + { + // Save key + if (!AddKey(key)) + { + RandomXDestroyMinerVM(rxvm); + return false; + } + key.MakeNewKey(); + + // Process this block the same as if we had received it from another node + if (!ProcessBlock(NULL, pblock.release())) + printf("ERROR in BitcoinMiner, ProcessBlock, block not accepted\n"); + } + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); + + Sleep(500); + break; + } + + // Check stop conditions periodically (RandomX is slow, so every + // ~256 hashes is already plenty of time) + if ((++pblock->nNonce & 0xff) == 0) + { + CheckForShutdown(3); + if (pblock->nNonce == 0) + break; + if (pindexPrev != pindexBest) + break; + if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) + break; + if (!fGenerateBitcoins) + break; + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + } + } + RandomXDestroyMinerVM(rxvm); + } + + return true; +} + + + + + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Actions +// + + +int64 GetBalance() +{ + int64 nStart, nEnd; + QueryPerformanceCounter((LARGE_INTEGER*)&nStart); + + int64 nTotal = 0; + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || pcoin->fSpent) + continue; + nTotal += pcoin->GetCredit(); + } + } + + QueryPerformanceCounter((LARGE_INTEGER*)&nEnd); + ///printf(" GetBalance() time = %16I64d\n", nEnd - nStart); + return nTotal; +} + + + +bool SelectCoins(int64 nTargetValue, set& setCoinsRet) +{ + setCoinsRet.clear(); + + // List of values less than target + int64 nLowestLarger = _I64_MAX; + CWalletTx* pcoinLowestLarger = NULL; + vector > vValue; + int64 nTotalLower = 0; + + CRITICAL_BLOCK(cs_mapWallet) + { + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || pcoin->fSpent) + continue; + int64 n = pcoin->GetCredit(); + if (n <= 0) + continue; + if (n < nTargetValue) + { + vValue.push_back(make_pair(n, pcoin)); + nTotalLower += n; + } + else if (n == nTargetValue) + { + setCoinsRet.insert(pcoin); + return true; + } + else if (n < nLowestLarger) + { + nLowestLarger = n; + pcoinLowestLarger = pcoin; + } + } + } + + if (nTotalLower < nTargetValue) + { + if (pcoinLowestLarger == NULL) + return false; + setCoinsRet.insert(pcoinLowestLarger); + return true; + } + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend()); + vector vfIncluded; + vector vfBest(vValue.size(), true); + int64 nBest = nTotalLower; + + for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + int64 nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (int i = 0; i < vValue.size(); i++) + { + if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) + { + nTotal += vValue[i].first; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].first; + vfIncluded[i] = false; + } + } + } + } + } + + // If the next larger is still closer, return it + if (pcoinLowestLarger && nLowestLarger - nTargetValue <= nBest - nTargetValue) + setCoinsRet.insert(pcoinLowestLarger); + else + { + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + setCoinsRet.insert(vValue[i].second); + + //// debug print + printf("SelectCoins() best subset: "); + for (int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + printf("%s ", FormatMoney(vValue[i].first).c_str()); + printf("total %s\n", FormatMoney(nBest).c_str()); + } + + return true; +} + + + + +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, int64& nFeeRequiredRet) +{ + nFeeRequiredRet = 0; + CRITICAL_BLOCK(cs_main) + { + // txdb must be opened before the mapWallet lock + CTxDB txdb("r"); + CRITICAL_BLOCK(cs_mapWallet) + { + int64 nFee = nTransactionFee; + loop + { + wtxNew.vin.clear(); + wtxNew.vout.clear(); + if (nValue < 0) + return false; + int64 nValueOut = nValue; + nValue += nFee; + + // Choose coins to use + set setCoins; + if (!SelectCoins(nValue, setCoins)) + return false; + int64 nValueIn = 0; + foreach(CWalletTx* pcoin, setCoins) + nValueIn += pcoin->GetCredit(); + + // Fill vout[0] to the payee + wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey)); + + // Fill vout[1] back to self with any change + if (nValueIn > nValue) + { + // Use the same key as one of the coins + vector vchPubKey; + CTransaction& txFirst = *(*setCoins.begin()); + foreach(const CTxOut& txout, txFirst.vout) + if (txout.IsMine()) + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + break; + if (vchPubKey.empty()) + return false; + + // Fill vout[1] to ourself + CScript scriptPubKey; + scriptPubKey << vchPubKey << OP_CHECKSIG; + wtxNew.vout.push_back(CTxOut(nValueIn - nValue, scriptPubKey)); + } + + // Fill vin + foreach(CWalletTx* pcoin, setCoins) + for (int nOut = 0; nOut < pcoin->vout.size(); nOut++) + if (pcoin->vout[nOut].IsMine()) + wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut)); + + // Sign + int nIn = 0; + foreach(CWalletTx* pcoin, setCoins) + for (int nOut = 0; nOut < pcoin->vout.size(); nOut++) + if (pcoin->vout[nOut].IsMine()) + SignSignature(*pcoin, wtxNew, nIn++); + + // Check that enough fee is included + if (nFee < wtxNew.GetMinFee(true)) + { + nFee = nFeeRequiredRet = wtxNew.GetMinFee(true); + continue; + } + + // Fill vtxPrev by copying from previous transactions vtxPrev + wtxNew.AddSupportingTransactions(txdb); + wtxNew.fTimeReceivedIsTxTime = true; + + break; + } + } + } + return true; +} + +// Call after CreateTransaction unless you want to abort +bool CommitTransactionSpent(const CWalletTx& wtxNew) +{ + CRITICAL_BLOCK(cs_main) + CRITICAL_BLOCK(cs_mapWallet) + { + //// todo: make this transactional, never want to add a transaction + //// without marking spent transactions + + // Add tx to wallet, because if it has change it's also ours, + // otherwise just for transaction history. + AddToWallet(wtxNew); + + // Mark old coins as spent + set setCoins; + foreach(const CTxIn& txin, wtxNew.vin) + setCoins.insert(&mapWallet[txin.prevout.hash]); + foreach(CWalletTx* pcoin, setCoins) + { + pcoin->fSpent = true; + pcoin->WriteToDisk(); + vWalletUpdated.push_back(make_pair(pcoin->GetHash(), false)); + } + } + MainFrameRepaint(); + return true; +} + + + + +bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew) +{ + CRITICAL_BLOCK(cs_main) + { + int64 nFeeRequired; + if (!CreateTransaction(scriptPubKey, nValue, wtxNew, nFeeRequired)) + { + string strError; + if (nValue + nFeeRequired > GetBalance()) + strError = strprintf("Error: This is an oversized transaction that requires a transaction fee of %s ", FormatMoney(nFeeRequired).c_str()); + else + strError = "Error: Transaction creation failed "; + wxMessageBox(strError, "Sending..."); + return error("SendMoney() : %s\n", strError.c_str()); + } + if (!CommitTransactionSpent(wtxNew)) + { + wxMessageBox("Error finalizing transaction", "Sending..."); + return error("SendMoney() : Error finalizing transaction"); + } + + printf("SendMoney: %s\n", wtxNew.GetHash().ToString().substr(0,6).c_str()); + + // Broadcast + if (!wtxNew.AcceptTransaction()) + { + // This must not fail. The transaction has already been signed and recorded. + throw runtime_error("SendMoney() : wtxNew.AcceptTransaction() failed\n"); + wxMessageBox("Error: Transaction not valid", "Sending..."); + return error("SendMoney() : Error: Transaction not valid"); + } + wtxNew.RelayWalletTransaction(); + } + MainFrameRepaint(); + return true; +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..a369476 --- /dev/null +++ b/src/main.h @@ -0,0 +1,1361 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +class COutPoint; +class CInPoint; +class CDiskTxPos; +class CCoinBase; +class CTxIn; +class CTxOut; +class CTransaction; +class CBlock; +class CBlockIndex; +class CWalletTx; +class CKeyItem; + +static const unsigned int MAX_SIZE = 0x02000000; +static const int64 COIN = 100000000; +// Total Bitflash emission: identical to Bitcoin (21 million). +// MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in +// 2010 allowed creating 184 billion coins in a single transaction. +static const int64 MAX_MONEY = 21000000 * COIN; +inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +static const int64 CENT = 1000000; +static const int COINBASE_MATURITY = 100; + +// Bitflash difficulty floor. The PoW is RandomX (memory-hard), ~1000x slower +// per hash than SHA-256d, so the target is much easier than Bitcoin's to let a +// CPU network start with little hashrate. Difficulty rises on its own (retarget +// toward 10-min blocks) as miners join. This does NOT affect emission +// (21 million, halving every 210,000 blocks). Tunable. +static const CBigNum bnProofOfWorkLimit(~uint256(0) >> 12); + + + + + + +extern CCriticalSection cs_main; +extern map mapBlockIndex; +extern uint256 hashGenesisBlock; +extern uint256 hashGenesisMerkleRoot; +extern unsigned int GENESIS_NONCE; +extern CBlockIndex* pindexGenesisBlock; +extern int nBestHeight; +extern uint256 hashBestChain; +extern CBlockIndex* pindexBest; +extern unsigned int nTransactionsUpdated; +extern string strSetDataDir; +extern int nDropMessagesTest; +extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) + +// Settings +extern int fGenerateBitcoins; +extern int64 nTransactionFee; +extern CAddress addrIncoming; + + + + + + + +string GetAppDir(); +FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); +FILE* AppendBlockFile(unsigned int& nFileRet); +bool AddKey(const CKey& key); +vector GenerateNewKey(); +bool AddToWallet(const CWalletTx& wtxIn); +void ReacceptWalletTransactions(); +void RelayWalletTransactions(); +bool LoadBlockIndex(bool fAllowNew=true); +void PrintBlockTree(); +bool BitcoinMiner(); +bool ProcessMessages(CNode* pfrom); +bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv); +bool SendMessages(CNode* pto); +int64 GetBalance(); +bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& txNew, int64& nFeeRequiredRet); +bool CommitTransactionSpent(const CWalletTx& wtxNew); +bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew); + + + + + + + + + + + +class CDiskTxPos +{ +public: + unsigned int nFile; + unsigned int nBlockPos; + unsigned int nTxPos; + + CDiskTxPos() + { + SetNull(); + } + + CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) + { + nFile = nFileIn; + nBlockPos = nBlockPosIn; + nTxPos = nTxPosIn; + } + + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { nFile = -1; nBlockPos = 0; nTxPos = 0; } + bool IsNull() const { return (nFile == -1); } + + friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) + { + return (a.nFile == b.nFile && + a.nBlockPos == b.nBlockPos && + a.nTxPos == b.nTxPos); + } + + friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b) + { + return !(a == b); + } + + string ToString() const + { + if (IsNull()) + return strprintf("null"); + else + return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos); + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + + + + +class CInPoint +{ +public: + CTransaction* ptx; + unsigned int n; + + CInPoint() { SetNull(); } + CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = -1; } + bool IsNull() const { return (ptx == NULL && n == -1); } +}; + + + + +class COutPoint +{ +public: + uint256 hash; + unsigned int n; + + COutPoint() { SetNull(); } + COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; } + IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) + void SetNull() { hash = 0; n = -1; } + bool IsNull() const { return (hash == 0 && n == -1); } + + friend bool operator<(const COutPoint& a, const COutPoint& b) + { + return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n)); + } + + friend bool operator==(const COutPoint& a, const COutPoint& b) + { + return (a.hash == b.hash && a.n == b.n); + } + + friend bool operator!=(const COutPoint& a, const COutPoint& b) + { + return !(a == b); + } + + string ToString() const + { + return strprintf("COutPoint(%s, %d)", hash.ToString().substr(0,6).c_str(), n); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +// +// An input of a transaction. It contains the location of the previous +// transaction's output that it claims and a signature that matches the +// output's public key. +// +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = UINT_MAX; + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + ) + + bool IsFinal() const + { + return (nSequence == UINT_MAX); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + string ToString() const + { + string str; + str += strprintf("CTxIn("); + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig.begin(), scriptSig.end(), false).c_str()); + else + str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); + if (nSequence != UINT_MAX) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } + + bool IsMine() const; + int64 GetDebit() const; +}; + + + + +// +// An output of a transaction. It contains the public key that the next input +// must be able to sign with to claim it. +// +class CTxOut +{ +public: + int64 nValue; + CScript scriptPubKey; + +public: + CTxOut() + { + SetNull(); + } + + CTxOut(int64 nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() + { + return (nValue == -1); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsMine() const + { + return ::IsMine(scriptPubKey); + } + + int64 GetCredit() const + { + if (IsMine()) + return nValue; + return 0; + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + string ToString() const + { + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + return strprintf("CTxOut(nValue=%I64d.%08I64d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,24).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +// +// The basic transaction that is broadcasted on the network and contained in +// blocks. A transaction can contain multiple inputs and outputs. +// +class CTransaction +{ +public: + int nVersion; + vector vin; + vector vout; + int nLockTime; + + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + void SetNull() + { + nVersion = 1; + vin.clear(); + vout.clear(); + nLockTime = 0; + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsFinal() const + { + if (nLockTime == 0 || nLockTime < nBestHeight) + return true; + foreach(const CTxIn& txin, vin) + if (!txin.IsFinal()) + return false; + return true; + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = UINT_MAX; + for (int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != old.vin[i].nSequence) + { + if (vin[i].nSequence <= nLowest) + { + fNewer = false; + nLowest = vin[i].nSequence; + } + if (old.vin[i].nSequence < nLowest) + { + fNewer = true; + nLowest = old.vin[i].nSequence; + } + } + } + return fNewer; + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + bool CheckTransaction() const + { + // Basic checks that don't depend on any context + if (vin.empty() || vout.empty()) + return error("CTransaction::CheckTransaction() : vin or vout empty"); + + // Check for negative or overflow output values (CVE-2010-5139) + int64 nValueOut = 0; + foreach(const CTxOut& txout, vout) + { + if (txout.nValue < 0) + return error("CTransaction::CheckTransaction() : txout.nValue negative"); + if (txout.nValue > MAX_MONEY) + return error("CTransaction::CheckTransaction() : txout.nValue too high"); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return error("CTransaction::CheckTransaction() : txout total out of range"); + } + + if (IsCoinBase()) + { + if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) + return error("CTransaction::CheckTransaction() : coinbase script size"); + } + else + { + foreach(const CTxIn& txin, vin) + if (txin.prevout.IsNull()) + return error("CTransaction::CheckTransaction() : prevout is null"); + } + + // Check for duplicate inputs (a tx spending the same output twice) + set vInOutPoints; + foreach(const CTxIn& txin, vin) + { + if (vInOutPoints.count(txin.prevout)) + return error("CTransaction::CheckTransaction() : duplicate inputs"); + vInOutPoints.insert(txin.prevout); + } + + return true; + } + + bool IsMine() const + { + foreach(const CTxOut& txout, vout) + if (txout.IsMine()) + return true; + return false; + } + + int64 GetDebit() const + { + int64 nDebit = 0; + foreach(const CTxIn& txin, vin) + nDebit += txin.GetDebit(); + return nDebit; + } + + int64 GetCredit() const + { + int64 nCredit = 0; + foreach(const CTxOut& txout, vout) + nCredit += txout.GetCredit(); + return nCredit; + } + + int64 GetValueOut() const + { + int64 nValueOut = 0; + foreach(const CTxOut& txout, vout) + { + if (txout.nValue < 0) + throw runtime_error("CTransaction::GetValueOut() : negative value"); + if (txout.nValue > MAX_MONEY) + throw runtime_error("CTransaction::GetValueOut() : value too high"); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + throw runtime_error("CTransaction::GetValueOut() : total out of range"); + } + return nValueOut; + } + + int64 GetMinFee(bool fDiscount=false) const + { + unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK); + if (fDiscount && nBytes < 10000) + return 0; + return (1 + (int64)nBytes / 1000) * CENT; + } + + + + bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) + { + CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"); + if (!filein) + return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); + + // Read transaction + if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) + return error("CTransaction::ReadFromDisk() : fseek failed"); + filein >> *this; + + // Return file pointer + if (pfileRet) + { + if (fseek(filein, pos.nTxPos, SEEK_SET) != 0) + return error("CTransaction::ReadFromDisk() : second fseek failed"); + *pfileRet = filein.release(); + } + return true; + } + + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + + string ToString() const + { + string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n", + GetHash().ToString().substr(0,6).c_str(), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; + } + + void print() const + { + printf("%s", ToString().c_str()); + } + + + + bool DisconnectInputs(CTxDB& txdb); + bool ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, int nHeight, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); + bool ClientConnectInputs(); + + bool AcceptTransaction(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); + + bool AcceptTransaction(bool fCheckInputs=true, bool* pfMissingInputs=NULL) + { + CTxDB txdb("r"); + return AcceptTransaction(txdb, fCheckInputs, pfMissingInputs); + } + +protected: + bool AddToMemoryPool(); +public: + bool RemoveFromMemoryPool(); +}; + + + + + +// +// A transaction with a merkle branch linking it to the block chain +// +class CMerkleTx : public CTransaction +{ +public: + uint256 hashBlock; + vector vMerkleBranch; + int nIndex; + + // memory only + mutable bool fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = 0; + nIndex = -1; + fMerkleVerified = false; + } + + int64 GetCredit() const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + return CTransaction::GetCredit(); + } + + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + ) + + + int SetMerkleBranch(const CBlock* pblock=NULL); + int GetDepthInMainChain() const; + bool IsInMainChain() const { return GetDepthInMainChain() > 0; } + int GetBlocksToMaturity() const; + bool AcceptTransaction(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptTransaction() { CTxDB txdb("r"); return AcceptTransaction(txdb); } +}; + + + + +// +// A transaction with a bunch of additional info that only the owner cares +// about. It includes any unrecorded transactions needed to link it back +// to the block chain. +// +class CWalletTx : public CMerkleTx +{ +public: + vector vtxPrev; + map mapValue; + vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; // time received by this node + char fFromMe; + char fSpent; + //// probably need to sign the order info so know it came from payer + + // memory only + mutable unsigned int nTimeDisplayed; + + + CWalletTx() + { + Init(); + } + + CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(); + } + + CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(); + } + + void Init() + { + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + fFromMe = false; + fSpent = false; + nTimeDisplayed = 0; + } + + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(vtxPrev); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + ) + + bool WriteToDisk() + { + return CWalletDB().WriteTx(GetHash(), *this); + } + + + int64 GetTxTime() const; + + void AddSupportingTransactions(CTxDB& txdb); + + bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); + bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } + + void RelayWalletTransaction(CTxDB& txdb); + void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } +}; + + + + +// +// A txdb record that contains the disk location of a transaction and the +// locations of transactions that spend its outputs. vSpent is really only +// used as a flag, but having the location is very helpful for debugging. +// +class CTxIndex +{ +public: + CDiskTxPos pos; + vector vSpent; + + CTxIndex() + { + SetNull(); + } + + CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs) + { + pos = posIn; + vSpent.resize(nOutputs); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(pos); + READWRITE(vSpent); + ) + + void SetNull() + { + pos.SetNull(); + vSpent.clear(); + } + + bool IsNull() + { + return pos.IsNull(); + } + + friend bool operator==(const CTxIndex& a, const CTxIndex& b) + { + if (a.pos != b.pos || a.vSpent.size() != b.vSpent.size()) + return false; + for (int i = 0; i < a.vSpent.size(); i++) + if (a.vSpent[i] != b.vSpent[i]) + return false; + return true; + } + + friend bool operator!=(const CTxIndex& a, const CTxIndex& b) + { + return !(a == b); + } +}; + + + + + +// +// Nodes collect new transactions into a block, hash them into a hash tree, +// and scan through nonce values to make the block's hash satisfy proof-of-work +// requirements. When they solve the proof-of-work, they broadcast the block +// to everyone and the block is added to the block chain. The first transaction +// in the block is a special one that creates a new coin owned by the creator +// of the block. +// +// Blocks are appended to blk0001.dat files on disk. Their location on disk +// is indexed by CBlockIndex objects in memory. +// +class CBlock +{ +public: + // header + int nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + // network and disk + vector vtx; + + // memory only + mutable vector vMerkleTree; + + + CBlock() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + + // ConnectBlock depends on vtx being last so it can calculate offset + if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY))) + READWRITE(vtx); + else if (fRead) + const_cast(this)->vtx.clear(); + ) + + void SetNull() + { + nVersion = 1; + hashPrevBlock = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + vtx.clear(); + vMerkleTree.clear(); + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const + { + // Block IDENTITY hash (SHA-256d), used for indexing. + return Hash(BEGIN(nVersion), END(nNonce)); + } + + uint256 GetPoWHash() const + { + // Memory-hard PROOF-OF-WORK hash (RandomX) over the 80-byte header. + // This is the one that must be <= target (nBits). + return RandomXPoWHash((const void*)BEGIN(nVersion), END(nNonce) - BEGIN(nVersion)); + } + + + uint256 BuildMerkleTree() const + { + vMerkleTree.clear(); + foreach(const CTransaction& tx, vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); + } + + vector GetMerkleBranch(int nIndex) const + { + if (vMerkleTree.empty()) + BuildMerkleTree(); + vector vMerkleBranch; + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; + } + + static uint256 CheckMerkleBranch(uint256 hash, const vector& vMerkleBranch, int nIndex) + { + if (nIndex == -1) + return 0; + foreach(const uint256& otherside, vMerkleBranch) + { + if (nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + nIndex >>= 1; + } + return hash; + } + + + bool WriteToDisk(bool fWriteTransactions, unsigned int& nFileRet, unsigned int& nBlockPosRet) + { + // Open history file to append + CAutoFile fileout = AppendBlockFile(nFileRet); + if (!fileout) + return error("CBlock::WriteToDisk() : AppendBlockFile failed"); + if (!fWriteTransactions) + fileout.nType |= SER_BLOCKHEADERONLY; + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(pchMessageStart) << nSize; + + // Write block + nBlockPosRet = ftell(fileout); + if (nBlockPosRet == -1) + return error("CBlock::WriteToDisk() : ftell failed"); + fileout << *this; + + return true; + } + + bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions) + { + SetNull(); + + // Open history file to read + CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb"); + if (!filein) + return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); + if (!fReadTransactions) + filein.nType |= SER_BLOCKHEADERONLY; + + // Read block + filein >> *this; + + // Check the header + if (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit) + return error("CBlock::ReadFromDisk() : nBits errors in block header"); + // NOTE: the proof of work is RandomX (GetPoWHash), validated once by + // CheckBlock when the block is accepted. Re-checking it on every disk + // read would be prohibitively slow, and checking the SHA-256d GetHash() + // here (a leftover from Bitcoin) is simply wrong for a RandomX chain. + + return true; + } + + + + void print() const + { + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n", + GetHash().ToString().substr(0,14).c_str(), + nVersion, + hashPrevBlock.ToString().substr(0,14).c_str(), + hashMerkleRoot.ToString().substr(0,6).c_str(), + nTime, nBits, nNonce, + vtx.size()); + for (int i = 0; i < vtx.size(); i++) + { + printf(" "); + vtx[i].print(); + } + printf(" vMerkleTree: "); + for (int i = 0; i < vMerkleTree.size(); i++) + printf("%s ", vMerkleTree[i].ToString().substr(0,6).c_str()); + printf("\n"); + } + + + int64 GetBlockValue(int64 nFees) const; + bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions); + bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); + bool CheckBlock() const; + bool AcceptBlock(); +}; + + + + + + +// +// The block chain is a tree shaped structure starting with the +// genesis block at the root, with each block potentially having multiple +// candidates to be the next block. pprev and pnext link a path through the +// main/longest chain. A blockindex may have multiple pprev pointing back +// to it, but pnext will only point forward to the longest branch, or will +// be null if the block is not part of the longest chain. +// +class CBlockIndex +{ +public: + const uint256* phashBlock; + CBlockIndex* pprev; + CBlockIndex* pnext; + unsigned int nFile; + unsigned int nBlockPos; + int nHeight; + + // block header + int nVersion; + uint256 hashMerkleRoot; + unsigned int nTime; + unsigned int nBits; + unsigned int nNonce; + + + CBlockIndex() + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nFile = 0; + nBlockPos = 0; + nHeight = 0; + + nVersion = 0; + hashMerkleRoot = 0; + nTime = 0; + nBits = 0; + nNonce = 0; + } + + CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) + { + phashBlock = NULL; + pprev = NULL; + pnext = NULL; + nFile = nFileIn; + nBlockPos = nBlockPosIn; + nHeight = 0; + + nVersion = block.nVersion; + hashMerkleRoot = block.hashMerkleRoot; + nTime = block.nTime; + nBits = block.nBits; + nNonce = block.nNonce; + } + + uint256 GetBlockHash() const + { + return *phashBlock; + } + + bool IsInMainChain() const + { + return (pnext || this == pindexBest); + } + + bool EraseBlockFromDisk() + { + // Open history file + CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+"); + if (!fileout) + return false; + + // Overwrite with empty null block + CBlock block; + block.SetNull(); + fileout << block; + + return true; + } + + enum { nMedianTimeSpan=11 }; + + int64 GetMedianTimePast() const + { + unsigned int pmedian[nMedianTimeSpan]; + unsigned int* pbegin = &pmedian[nMedianTimeSpan]; + unsigned int* pend = &pmedian[nMedianTimeSpan]; + + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) + *(--pbegin) = pindex->nTime; + + sort(pbegin, pend); + return pbegin[(pend - pbegin)/2]; + } + + int64 GetMedianTime() const + { + const CBlockIndex* pindex = this; + for (int i = 0; i < nMedianTimeSpan/2; i++) + { + if (!pindex->pnext) + return nTime; + pindex = pindex->pnext; + } + return pindex->GetMedianTimePast(); + } + + + + string ToString() const + { + return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, pnext, nFile, nBlockPos, nHeight, + hashMerkleRoot.ToString().substr(0,6).c_str(), + GetBlockHash().ToString().substr(0,14).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + +// +// Used to marshal pointers into hashes for db storage. +// +class CDiskBlockIndex : public CBlockIndex +{ +public: + uint256 hashPrev; + uint256 hashNext; + + CDiskBlockIndex() + { + hashPrev = 0; + hashNext = 0; + } + + explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) + { + hashPrev = (pprev ? pprev->GetBlockHash() : 0); + hashNext = (pnext ? pnext->GetBlockHash() : 0); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + + READWRITE(hashNext); + READWRITE(nFile); + READWRITE(nBlockPos); + READWRITE(nHeight); + + // block header + READWRITE(this->nVersion); + READWRITE(hashPrev); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + ) + + uint256 GetBlockHash() const + { + CBlock block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrev; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block.GetHash(); + } + + + string ToString() const + { + string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().substr(0,14).c_str(), + hashNext.ToString().substr(0,14).c_str()); + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + + + + + +// +// Describes a place in the block chain to another node such that if the +// other node doesn't have the same branch, it can find a recent common trunk. +// The further back it is, the further before the fork it may be. +// +class CBlockLocator +{ +protected: + vector vHave; +public: + + CBlockLocator() + { + } + + explicit CBlockLocator(const CBlockIndex* pindex) + { + Set(pindex); + } + + explicit CBlockLocator(uint256 hashBlock) + { + map::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + Set((*mi).second); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + ) + + void Set(const CBlockIndex* pindex) + { + vHave.clear(); + int nStep = 1; + while (pindex) + { + vHave.push_back(pindex->GetBlockHash()); + + // Exponentially larger steps back + for (int i = 0; pindex && i < nStep; i++) + pindex = pindex->pprev; + if (vHave.size() > 10) + nStep *= 2; + } + vHave.push_back(hashGenesisBlock); + } + + CBlockIndex* GetBlockIndex() + { + // Find the first block the caller has in the main chain + foreach(const uint256& hash, vHave) + { + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return pindex; + } + } + return pindexGenesisBlock; + } + + uint256 GetBlockHash() + { + // Find the first block the caller has in the main chain + foreach(const uint256& hash, vHave) + { + map::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return hash; + } + } + return hashGenesisBlock; + } + + int GetHeight() + { + CBlockIndex* pindex = GetBlockIndex(); + if (!pindex) + return 0; + return pindex->nHeight; + } +}; + + + + + + + + + + + + +extern map mapTransactions; +extern map mapWallet; +extern vector > vWalletUpdated; +extern CCriticalSection cs_mapWallet; +extern map, CPrivKey> mapKeys; +extern map > mapPubKeys; +extern CCriticalSection cs_mapKeys; +extern CKey keyUser; diff --git a/src/makefile b/src/makefile new file mode 100644 index 0000000..941a262 --- /dev/null +++ b/src/makefile @@ -0,0 +1,83 @@ +# Copyright (c) 2009 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +ifneq "$(BUILD)" "debug" +ifneq "$(BUILD)" "release" +BUILD=debug +endif +endif +ifeq "$(BUILD)" "debug" +D=d +# note: gcc 3.x profile doesn't work +#DEBUGFLAGS=-O0 -g -pg -D__WXDEBUG__ +DEBUGFLAGS=-g -D__WXDEBUG__ +endif + + + +INCLUDEPATHS=-I"/boost" -I"/DB/build_unix" -I"/OpenSSL/include" -I"/wxWidgets/lib/vc_lib/mswd" -I"/wxWidgets/include" +LIBPATHS=-L"/DB/build_unix" -L"/OpenSSL/out" -L"/wxWidgets/lib/gcc_lib" +LIBS= \ + -l db_cxx \ + -l eay32 \ + -l wxmsw28$(D)_richtext -l wxmsw28$(D)_html -l wxmsw28$(D)_core -l wxbase28$(D) -l wxtiff$(D) -l wxjpeg$(D) -l wxpng$(D) -l wxzlib$(D) -l wxregex$(D) -l wxexpat$(D) \ + -l kernel32 -l user32 -l gdi32 -l comdlg32 -l winspool -l winmm -l shell32 -l comctl32 -l ole32 -l oleaut32 -l uuid -l rpcrt4 -l advapi32 -l ws2_32 +WXDEFS=-DWIN32 -D__WXMSW__ -D_WINDOWS -DNOPCH +CFLAGS=-mthreads -O0 -w -Wno-invalid-offsetof -Wformat $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS) +HEADERS=headers.h util.h main.h serialize.h uint256.h key.h bignum.h script.h db.h base58.h + + + +all: bitcoin.exe + + +headers.h.gch: headers.h $(HEADERS) net.h irc.h market.h uibase.h ui.h + g++ -c $(CFLAGS) -o $@ $< + +obj/util.o: util.cpp $(HEADERS) + g++ -c $(CFLAGS) -o $@ $< + +obj/script.o: script.cpp $(HEADERS) + g++ -c $(CFLAGS) -o $@ $< + +obj/db.o: db.cpp $(HEADERS) market.h + g++ -c $(CFLAGS) -o $@ $< + +obj/net.o: net.cpp $(HEADERS) net.h + g++ -c $(CFLAGS) -o $@ $< + +obj/main.o: main.cpp $(HEADERS) net.h market.h sha.h + g++ -c $(CFLAGS) -o $@ $< + +obj/market.o: market.cpp $(HEADERS) market.h + g++ -c $(CFLAGS) -o $@ $< + +obj/ui.o: ui.cpp $(HEADERS) net.h uibase.h ui.h market.h + g++ -c $(CFLAGS) -o $@ $< + +obj/uibase.o: uibase.cpp uibase.h + g++ -c $(CFLAGS) -o $@ $< + +obj/sha.o: sha.cpp sha.h + g++ -c $(CFLAGS) -O3 -o $@ $< + +obj/irc.o: irc.cpp $(HEADERS) + g++ -c $(CFLAGS) -o $@ $< + +obj/ui_res.o: ui.rc rc/bitcoin.ico rc/check.ico rc/send16.bmp rc/send16mask.bmp rc/send16masknoshadow.bmp rc/send20.bmp rc/send20mask.bmp rc/addressbook16.bmp rc/addressbook16mask.bmp rc/addressbook20.bmp rc/addressbook20mask.bmp + windres $(WXDEFS) $(INCLUDEPATHS) -o $@ -i $< + + + +OBJS=obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o obj/market.o \ + obj/ui.o obj/uibase.o obj/sha.o obj/irc.o obj/ui_res.o + +bitcoin.exe: headers.h.gch $(OBJS) + -kill /f bitcoin.exe + g++ $(CFLAGS) -mwindows -Wl,--subsystem,windows -o $@ $(LIBPATHS) $(OBJS) $(LIBS) + +clean: + -del /Q obj\* + -del /Q headers.h.gch diff --git a/src/makefile.linux b/src/makefile.linux new file mode 100644 index 0000000..682c597 --- /dev/null +++ b/src/makefile.linux @@ -0,0 +1,48 @@ +# Bitflash headless node/miner -- Linux build (no wxWidgets, no GUI). +# Produces `bitflash-node`: loads the chain + wallet, runs the P2P/Nostr node +# with anonymous .btf auto-discovery, and mines with RandomX (-gen). +# +# Deps are set up by ../build-linux.sh. Override paths on the make line if your +# secp256k1 / RandomX live elsewhere: +# make -f makefile.linux SECP_INC=... SECP_LIB=... RANDOMX_DIR=... + +CXX ?= g++ + +# secp256k1 (schnorrsig + extrakeys), installed by build-linux.sh +SECP_INC ?= /usr/local/include +SECP_LIB ?= /usr/local/lib +# RandomX source tree (headers in src/, static lib in build/) +RANDOMX_DIR ?= $(HOME)/RandomX + +CXXFLAGS = -std=gnu++14 -O2 -w -fpermissive -pthread \ + -DSECP256K1_STATIC \ + -I$(SECP_INC) -I$(RANDOMX_DIR)/src + +LIBS = -L$(SECP_LIB) -lsecp256k1 \ + -L$(RANDOMX_DIR)/build -lrandomx \ + -lsodium -lssl -lcrypto -ldb_cxx -lpthread + +HEADERS = headers.h util.h main.h serialize.h uint256.h key.h bignum.h script.h \ + db.h base58.h net.h irc.h nostr.h randomx_pow.h btfaddr.h btfchan.h \ + btfrv.h btftunnel.h market.h compat.h + +OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o obj/market.o \ + obj/sha.o obj/irc.o obj/nostr.o obj/randomx_pow.o \ + obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/headless.o + +all: bitflash-node + +obj/%.o: %.cpp $(HEADERS) + @mkdir -p obj + $(CXX) -c $(CXXFLAGS) -o $@ $< + +# sha gets -O3 like the Windows makefile +obj/sha.o: sha.cpp sha.h + @mkdir -p obj + $(CXX) -c $(CXXFLAGS) -O3 -o $@ $< + +bitflash-node: $(OBJS) + $(CXX) $(CXXFLAGS) -o $@ $(OBJS) $(LIBS) + +clean: + rm -rf obj bitflash-node diff --git a/src/makefile.mingw b/src/makefile.mingw new file mode 100644 index 0000000..0ffac3f --- /dev/null +++ b/src/makefile.mingw @@ -0,0 +1,44 @@ +# Bitflash -- build for MSYS2 UCRT64 (modern g++ + wxWidgets 3.2 + OpenSSL 3 + Berkeley DB 6) +# Based on Satoshi Nakamoto's original 2009 makefile (MIT/X11) +# Usage: (UCRT64 shell) mingw32-make -f makefile.mingw + +CXX = g++ +WXCXXFLAGS := $(shell wx-config --cxxflags) +WXLIBS := $(shell wx-config --libs std,richtext,html) +WXRCFLAGS := $(filter -I%,$(shell wx-config --cxxflags)) $(filter -D%,$(shell wx-config --cxxflags)) + +DEFS = -DWIN32 -D__WXMSW__ -D_WINDOWS -DNOPCH +# Path to the project's built dependencies (libsecp256k1 with Schnorr, RandomX) +DEPS = ../deps +RANDOMX = ../RandomX +CXXFLAGS = -std=gnu++14 -O2 -w -fpermissive -Wno-deprecated-declarations -pthread $(DEFS) -I$(DEPS)/include -I$(RANDOMX)/src $(WXCXXFLAGS) +HEADERS = headers.h util.h main.h serialize.h uint256.h key.h bignum.h script.h db.h base58.h net.h irc.h nostr.h randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h market.h uibase.h ui.h + +LIBS = \ + -ldb_cxx \ + -L$(DEPS)/lib -lsecp256k1 \ + -L$(RANDOMX)/build -lrandomx \ + -lsodium \ + -lssl -lcrypto \ + -lkernel32 -luser32 -lgdi32 -lcomdlg32 -lwinspool -lwinmm -lshell32 -lcomctl32 \ + -lole32 -loleaut32 -luuid -lrpcrt4 -ladvapi32 -lws2_32 -lmswsock -lcrypt32 -pthread + +OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o obj/market.o \ + obj/ui.o obj/uibase.o obj/sha.o obj/irc.o obj/nostr.o obj/randomx_pow.o obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/ui_res.o + +all: bitflash.exe + +obj/%.o: %.cpp $(HEADERS) + $(CXX) -c $(CXXFLAGS) -o $@ $< + +obj/sha.o: sha.cpp sha.h + $(CXX) -c $(CXXFLAGS) -O3 -o $@ $< + +obj/ui_res.o: ui.rc rc/bitcoin.ico rc/check.ico + windres $(DEFS) $(WXRCFLAGS) -o $@ -i $< + +bitflash.exe: $(OBJS) + $(CXX) $(CXXFLAGS) -mwindows -Wl,--subsystem,windows -o $@ $(OBJS) $(WXLIBS) $(LIBS) + +clean: + rm -f obj/*.o bitflash.exe diff --git a/src/makefile.vc b/src/makefile.vc new file mode 100644 index 0000000..60dca78 --- /dev/null +++ b/src/makefile.vc @@ -0,0 +1,77 @@ +# Copyright (c) 2009 Satoshi Nakamoto +# Distributed under the MIT/X11 software license, see the accompanying +# file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +!IF "$(BUILD)" != "debug" && "$(BUILD)" != "release" +BUILD=debug +!ENDIF +!IF "$(BUILD)" == "debug" +D=d +DEBUGFLAGS=/Zi /Od /D__WXDEBUG__ +!ENDIF + + + +INCLUDEPATHS=/I"/boost" /I"/DB/build_windows" /I"/OpenSSL/include" /I"/wxWidgets/lib/vc_lib/mswd" /I"/wxWidgets/include" +LIBPATHS=/LIBPATH:"/DB/build_windows/$(BUILD)" /LIBPATH:"/OpenSSL/out" /LIBPATH:"/wxWidgets/lib/vc_lib" +LIBS= \ + libdb47s$(D).lib \ + libeay32.lib \ + wxmsw28$(D)_richtext.lib wxmsw28$(D)_html.lib wxmsw28$(D)_core.lib wxbase28$(D).lib wxtiff$(D).lib wxjpeg$(D).lib wxpng$(D).lib wxzlib$(D).lib wxregex$(D).lib wxexpat$(D).lib \ + kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib ws2_32.lib +WXDEFS=/DWIN32 /D__WXMSW__ /D_WINDOWS /DNOPCH +CFLAGS=/c /nologo /Ob0 /MD$(D) /EHsc /GR /Zm300 /YX /Fpobj/headers.pch $(DEBUGFLAGS) $(WXDEFS) $(INCLUDEPATHS) +HEADERS=headers.h util.h main.h serialize.h uint256.h key.h bignum.h script.h db.h base58.h + + + +all: bitcoin.exe + + +obj\util.obj: util.cpp $(HEADERS) + cl $(CFLAGS) /Fo$@ %s + +obj\script.obj: script.cpp $(HEADERS) + cl $(CFLAGS) /Fo$@ %s + +obj\db.obj: db.cpp $(HEADERS) market.h + cl $(CFLAGS) /Fo$@ %s + +obj\net.obj: net.cpp $(HEADERS) net.h + cl $(CFLAGS) /Fo$@ %s + +obj\main.obj: main.cpp $(HEADERS) net.h market.h + cl $(CFLAGS) /Fo$@ %s + +obj\market.obj: market.cpp $(HEADERS) market.h + cl $(CFLAGS) /Fo$@ %s + +obj\ui.obj: ui.cpp $(HEADERS) net.h uibase.h ui.h market.h + cl $(CFLAGS) /Fo$@ %s + +obj\uibase.obj: uibase.cpp uibase.h + cl $(CFLAGS) /Fo$@ %s + +obj\sha.obj: sha.cpp sha.h + cl $(CFLAGS) /O2 /Fo$@ %s + +obj\irc.obj: irc.cpp $(HEADERS) + cl $(CFLAGS) /Fo$@ %s + +obj\ui.res: ui.rc rc/bitcoin.ico rc/check.ico rc/send16.bmp rc/send16mask.bmp rc/send16masknoshadow.bmp rc/send20.bmp rc/send20mask.bmp rc/addressbook16.bmp rc/addressbook16mask.bmp rc/addressbook20.bmp rc/addressbook20mask.bmp + rc $(INCLUDEPATHS) $(WXDEFS) /Fo$@ %s + + + +OBJS=obj\util.obj obj\script.obj obj\db.obj obj\net.obj obj\main.obj obj\market.obj \ + obj\ui.obj obj\uibase.obj obj\sha.obj obj\irc.obj obj\ui.res + +bitcoin.exe: $(OBJS) + -kill /f bitcoin.exe & sleep 1 + link /nologo /DEBUG /SUBSYSTEM:WINDOWS /OUT:$@ $(LIBPATHS) $** $(LIBS) + +clean: + -del /Q obj\* + -del *.ilk + -del *.pdb diff --git a/src/market.cpp b/src/market.cpp new file mode 100644 index 0000000..e5d34f4 --- /dev/null +++ b/src/market.cpp @@ -0,0 +1,264 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + + + + + + + + + + +// +// Global state variables +// + +//// later figure out how these are persisted +map mapMyProducts; + + + + +map mapProducts; +CCriticalSection cs_mapProducts; + +bool AdvertInsert(const CProduct& product) +{ + uint256 hash = product.GetHash(); + bool fNew = false; + bool fUpdated = false; + + CRITICAL_BLOCK(cs_mapProducts) + { + // Insert or find existing product + pair::iterator, bool> item = mapProducts.insert(make_pair(hash, product)); + CProduct* pproduct = &(*(item.first)).second; + fNew = item.second; + + // Update if newer + if (product.nSequence > pproduct->nSequence) + { + *pproduct = product; + fUpdated = true; + } + } + + //if (fNew) + // NotifyProductAdded(hash); + //else if (fUpdated) + // NotifyProductUpdated(hash); + + return (fNew || fUpdated); +} + +void AdvertErase(const CProduct& product) +{ + uint256 hash = product.GetHash(); + CRITICAL_BLOCK(cs_mapProducts) + mapProducts.erase(hash); + //NotifyProductDeleted(hash); +} + + + + + + + + + + + + + + + + + + + +template +unsigned int Union(T& v1, T& v2) +{ + // v1 = v1 union v2 + // v1 and v2 must be sorted + // returns the number of elements added to v1 + + ///// need to check that this is equivalent, then delete this comment + //vector vUnion(v1.size() + v2.size()); + //vUnion.erase(set_union(v1.begin(), v1.end(), + // v2.begin(), v2.end(), + // vUnion.begin()), + // vUnion.end()); + + T vUnion; + vUnion.reserve(v1.size() + v2.size()); + set_union(v1.begin(), v1.end(), + v2.begin(), v2.end(), + back_inserter(vUnion)); + unsigned int nAdded = vUnion.size() - v1.size(); + if (nAdded > 0) + v1 = vUnion; + return nAdded; +} + +void CUser::AddAtom(unsigned short nAtom, bool fOrigin) +{ + // Ignore duplicates + if (binary_search(vAtomsIn.begin(), vAtomsIn.end(), nAtom) || + find(vAtomsNew.begin(), vAtomsNew.end(), nAtom) != vAtomsNew.end()) + return; + + //// instead of zero atom, should change to free atom that propagates, + //// limited to lower than a certain value like 5 so conflicts quickly + // The zero atom never propagates, + // new atoms always propagate through the user that created them + if (nAtom == 0 || fOrigin) + { + vector vTmp(1, nAtom); + Union(vAtomsIn, vTmp); + if (fOrigin) + vAtomsOut.push_back(nAtom); + return; + } + + vAtomsNew.push_back(nAtom); + + if (vAtomsNew.size() >= nFlowthroughRate || vAtomsOut.empty()) + { + // Select atom to flow through to vAtomsOut + vAtomsOut.push_back(vAtomsNew[GetRand(vAtomsNew.size())]); + + // Merge vAtomsNew into vAtomsIn + sort(vAtomsNew.begin(), vAtomsNew.end()); + Union(vAtomsIn, vAtomsNew); + vAtomsNew.clear(); + } +} + +bool AddAtomsAndPropagate(uint256 hashUserStart, const vector& vAtoms, bool fOrigin) +{ + CReviewDB reviewdb; + map > pmapPropagate[2]; + pmapPropagate[0][hashUserStart] = vAtoms; + + for (int side = 0; !pmapPropagate[side].empty(); side = 1 - side) + { + map >& mapFrom = pmapPropagate[side]; + map >& mapTo = pmapPropagate[1 - side]; + + for (map >::iterator mi = mapFrom.begin(); mi != mapFrom.end(); ++mi) + { + const uint256& hashUser = (*mi).first; + const vector& vReceived = (*mi).second; + + ///// this would be a lot easier on the database if it put the new atom at the beginning of the list, + ///// so the change would be right next to the vector size. + + // Read user + CUser user; + reviewdb.ReadUser(hashUser, user); + unsigned int nIn = user.vAtomsIn.size(); + unsigned int nNew = user.vAtomsNew.size(); + unsigned int nOut = user.vAtomsOut.size(); + + // Add atoms received + foreach(unsigned short nAtom, vReceived) + user.AddAtom(nAtom, fOrigin); + fOrigin = false; + + // Don't bother writing to disk if no changes + if (user.vAtomsIn.size() == nIn && user.vAtomsNew.size() == nNew) + continue; + + // Propagate + if (user.vAtomsOut.size() > nOut) + foreach(const uint256& hash, user.vLinksOut) + mapTo[hash].insert(mapTo[hash].end(), user.vAtomsOut.begin() + nOut, user.vAtomsOut.end()); + + // Write back + if (!reviewdb.WriteUser(hashUser, user)) + return false; + } + mapFrom.clear(); + } + return true; +} + + + + + + +bool CReview::AcceptReview() +{ + // Timestamp + nTime = GetTime(); + + // Check signature + if (!CKey::Verify(vchPubKeyFrom, GetSigHash(), vchSig)) + return false; + + CReviewDB reviewdb; + + // Add review text to recipient + vector vReviews; + reviewdb.ReadReviews(hashTo, vReviews); + vReviews.push_back(*this); + if (!reviewdb.WriteReviews(hashTo, vReviews)) + return false; + + // Add link from sender + CUser user; + uint256 hashFrom = Hash(vchPubKeyFrom.begin(), vchPubKeyFrom.end()); + reviewdb.ReadUser(hashFrom, user); + user.vLinksOut.push_back(hashTo); + if (!reviewdb.WriteUser(hashFrom, user)) + return false; + + reviewdb.Close(); + + // Propagate atoms to recipient + vector vZeroAtom(1, 0); + if (!AddAtomsAndPropagate(hashTo, user.vAtomsOut.size() ? user.vAtomsOut : vZeroAtom, false)) + return false; + + return true; +} + + + + + +bool CProduct::CheckSignature() +{ + return (CKey::Verify(vchPubKeyFrom, GetSigHash(), vchSig)); +} + +bool CProduct::CheckProduct() +{ + if (!CheckSignature()) + return false; + + // Make sure it's a summary product + if (!mapDetails.empty() || !vOrderForm.empty()) + return false; + + // Look up seller's atom count + CReviewDB reviewdb("r"); + CUser user; + reviewdb.ReadUser(GetUserHash(), user); + nAtoms = user.GetAtomCount(); + reviewdb.Close(); + + ////// delme, this is now done by AdvertInsert + //// Store to memory + //CRITICAL_BLOCK(cs_mapProducts) + // mapProducts[GetHash()] = *this; + + return true; +} diff --git a/src/market.h b/src/market.h new file mode 100644 index 0000000..b5c8b3b --- /dev/null +++ b/src/market.h @@ -0,0 +1,182 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +class CUser; +class CReview; +class CProduct; + +static const unsigned int nFlowthroughRate = 2; + + + + +bool AdvertInsert(const CProduct& product); +void AdvertErase(const CProduct& product); +bool AddAtomsAndPropagate(uint256 hashUserStart, const vector& vAtoms, bool fOrigin); + + + + + + + + +class CUser +{ +public: + vector vAtomsIn; + vector vAtomsNew; + vector vAtomsOut; + vector vLinksOut; + + CUser() + { + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vAtomsIn); + READWRITE(vAtomsNew); + READWRITE(vAtomsOut); + READWRITE(vLinksOut); + ) + + void SetNull() + { + vAtomsIn.clear(); + vAtomsNew.clear(); + vAtomsOut.clear(); + vLinksOut.clear(); + } + + uint256 GetHash() const { return SerializeHash(*this); } + + + int GetAtomCount() const + { + return (vAtomsIn.size() + vAtomsNew.size()); + } + + void AddAtom(unsigned short nAtom, bool fOrigin); +}; + + + + + + + +class CReview +{ +public: + int nVersion; + uint256 hashTo; + map mapValue; + vector vchPubKeyFrom; + vector vchSig; + + // memory only + unsigned int nTime; + int nAtoms; + + + CReview() + { + nVersion = 1; + hashTo = 0; + nTime = 0; + nAtoms = 0; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + if (!(nType & SER_DISK)) + READWRITE(hashTo); + READWRITE(mapValue); + READWRITE(vchPubKeyFrom); + if (!(nType & SER_GETHASH)) + READWRITE(vchSig); + ) + + uint256 GetHash() const { return SerializeHash(*this); } + uint256 GetSigHash() const { return SerializeHash(*this, SER_GETHASH|SER_SKIPSIG); } + uint256 GetUserHash() const { return Hash(vchPubKeyFrom.begin(), vchPubKeyFrom.end()); } + + + bool AcceptReview(); +}; + + + + + + + +class CProduct +{ +public: + int nVersion; + CAddress addr; + map mapValue; + map mapDetails; + vector > vOrderForm; + unsigned int nSequence; + vector vchPubKeyFrom; + vector vchSig; + + // disk only + int nAtoms; + + // memory only + set setSources; + + CProduct() + { + nVersion = 1; + nAtoms = 0; + nSequence = 0; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(addr); + READWRITE(mapValue); + if (!(nType & SER_GETHASH)) + { + READWRITE(mapDetails); + READWRITE(vOrderForm); + READWRITE(nSequence); + } + READWRITE(vchPubKeyFrom); + if (!(nType & SER_GETHASH)) + READWRITE(vchSig); + if (nType & SER_DISK) + READWRITE(nAtoms); + ) + + uint256 GetHash() const { return SerializeHash(*this); } + uint256 GetSigHash() const { return SerializeHash(*this, SER_GETHASH|SER_SKIPSIG); } + uint256 GetUserHash() const { return Hash(vchPubKeyFrom.begin(), vchPubKeyFrom.end()); } + + + bool CheckSignature(); + bool CheckProduct(); +}; + + + + + + + + +extern map mapProducts; +extern CCriticalSection cs_mapProducts; +extern map mapMyProducts; diff --git a/src/net.cpp b/src/net.cpp new file mode 100644 index 0000000..8ab9ccc --- /dev/null +++ b/src/net.cpp @@ -0,0 +1,1207 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#ifdef _WIN32 +#include +#endif +#include +#include "btfaddr.h" +#include "btftunnel.h" + +void ThreadMessageHandler2(void* parg); +void ThreadSocketHandler2(void* parg); +void ThreadOpenConnections2(void* parg); + + + + + + +// +// Global state variables +// +bool fClient = false; +uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); +CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); +unsigned short nListenPort = DEFAULT_PORT; // local P2P port (tunable via /port) +CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); +CNode* pnodeLocalHost = &nodeLocalHost; +bool fShutdown = false; +array vfThreadRunning; +vector vNodes; +CCriticalSection cs_vNodes; +map, CAddress> mapAddresses; +CCriticalSection cs_mapAddresses; +map mapRelay; +deque > vRelayExpiration; +CCriticalSection cs_mapRelay; +map mapAlreadyAskedFor; +string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) + + + +CAddress addrProxy; + +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) +{ + hSocketRet = INVALID_SOCKET; + + SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0); + if (hSocket == INVALID_SOCKET) + return false; + + bool fRoutable = !(addrConnect.GetByte(3) == 10 || (addrConnect.GetByte(3) == 192 && addrConnect.GetByte(2) == 168)); + bool fProxy = (addrProxy.ip && fRoutable); + struct sockaddr_in sockaddr = (fProxy ? addrProxy.GetSockAddr() : addrConnect.GetSockAddr()); + + if (connect(hSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + closesocket(hSocket); + return false; + } + + if (fProxy) + { + printf("Proxy connecting to %s\n", addrConnect.ToString().c_str()); + char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; + memcpy(pszSocks4IP + 2, &addrConnect.port, 2); + memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); + char* pszSocks4 = pszSocks4IP; + int nSize = sizeof(pszSocks4IP); + + int ret = send(hSocket, pszSocks4, nSize, 0); + if (ret != nSize) + { + closesocket(hSocket); + return error("Error sending to proxy\n"); + } + char pchRet[8]; + if (recv(hSocket, pchRet, 8, 0) != 8) + { + closesocket(hSocket); + return error("Error reading proxy response\n"); + } + if (pchRet[1] != 0x5a) + { + closesocket(hSocket); + return error("Proxy returned error %d\n", pchRet[1]); + } + printf("Proxy connection established %s\n", addrConnect.ToString().c_str()); + } + + hSocketRet = hSocket; + return true; +} + + +bool GetMyExternalIP(unsigned int& ipRet) +{ + // The 2009 whatismyip.com service (fixed IP) no longer exists; this avoids + // stalling startup on TCP timeouts. The external IP may come from Nostr + // relays in the future. + return false; + + CAddress addrConnect("72.233.89.199:80"); // whatismyip.com 198-200 + + SOCKET hSocket; + if (!ConnectSocket(addrConnect, hSocket)) + return error("GetMyExternalIP() : connection to %s failed\n", addrConnect.ToString().c_str()); + + char* pszGet = + "GET /automation/n09230945.asp HTTP/1.1\r\n" + "Host: www.whatismyip.com\r\n" + "User-Agent: Bitcoin/0.1\r\n" + "Connection: close\r\n" + "\r\n"; + send(hSocket, pszGet, strlen(pszGet), 0); + + string strLine; + while (RecvLine(hSocket, strLine)) + { + if (strLine.empty()) + { + if (!RecvLine(hSocket, strLine)) + { + closesocket(hSocket); + return false; + } + closesocket(hSocket); + CAddress addr(strLine.c_str()); + printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + if (addr.ip == 0) + return false; + ipRet = addr.ip; + return true; + } + } + closesocket(hSocket); + return error("GetMyExternalIP() : connection closed\n"); +} + + + + + + +bool AddAddress(CAddrDB& addrdb, const CAddress& addr) +{ + if (!addr.IsRoutable()) + return false; + if (addr.ip == addrLocalHost.ip) + return false; + CRITICAL_BLOCK(cs_mapAddresses) + { + map, CAddress>::iterator it = mapAddresses.find(addr.GetKey()); + if (it == mapAddresses.end()) + { + // New address + mapAddresses.insert(make_pair(addr.GetKey(), addr)); + addrdb.WriteAddress(addr); + return true; + } + else + { + CAddress& addrFound = (*it).second; + if ((addrFound.nServices | addr.nServices) != addrFound.nServices) + { + // Services have been added + addrFound.nServices |= addr.nServices; + addrdb.WriteAddress(addrFound); + return true; + } + } + } + return false; +} + + + + + +void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) +{ + // If the dialog might get closed before the reply comes back, + // call this in the destructor so it doesn't get called after it's deleted. + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + { + CRITICAL_BLOCK(pnode->cs_mapRequests) + { + for (map::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();) + { + CRequestTracker& tracker = (*mi).second; + if (tracker.fn == fn && tracker.param1 == param1) + pnode->mapRequests.erase(mi++); + else + mi++; + } + } + } + } +} + + + + + + + +// +// Subscription methods for the broadcast and subscription system. +// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. +// +// The subscription system uses a meet-in-the-middle strategy. +// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers +// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through. +// + +bool AnySubscribed(unsigned int nChannel) +{ + if (pnodeLocalHost->IsSubscribed(nChannel)) + return true; + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode->IsSubscribed(nChannel)) + return true; + return false; +} + +bool CNode::IsSubscribed(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return false; + return vfSubscribe[nChannel]; +} + +void CNode::Subscribe(unsigned int nChannel, unsigned int nHops) +{ + if (nChannel >= vfSubscribe.size()) + return; + + if (!AnySubscribed(nChannel)) + { + // Relay subscribe + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("subscribe", nChannel, nHops); + } + + vfSubscribe[nChannel] = true; +} + +void CNode::CancelSubscribe(unsigned int nChannel) +{ + if (nChannel >= vfSubscribe.size()) + return; + + // Prevent from relaying cancel if wasn't subscribed + if (!vfSubscribe[nChannel]) + return; + vfSubscribe[nChannel] = false; + + if (!AnySubscribed(nChannel)) + { + // Relay subscription cancel + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode != this) + pnode->PushMessage("sub-cancel", nChannel); + + // Clear memory, no longer subscribed + if (nChannel == MSG_PRODUCT) + CRITICAL_BLOCK(cs_mapProducts) + mapProducts.clear(); + } +} + + + + + + + + + +CNode* FindNode(unsigned int ip) +{ + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + if (pnode->addr.ip == ip) + return (pnode); + } + return NULL; +} + +CNode* FindNode(CAddress addr) +{ + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + if (pnode->addr == addr) + return (pnode); + } + return NULL; +} + +CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) +{ + if (addrConnect.ip == addrLocalHost.ip) + return NULL; + + // Look for an existing connection + CNode* pnode = FindNode(addrConnect.ip); + if (pnode) + { + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + return pnode; + } + + /// debug print + printf("trying %s\n", addrConnect.ToString().c_str()); + + // Connect + SOCKET hSocket; + if (ConnectSocket(addrConnect, hSocket)) + { + /// debug print + printf("connected %s\n", addrConnect.ToString().c_str()); + + // Add node + CNode* pnode = new CNode(hSocket, addrConnect, false); + if (nTimeout != 0) + pnode->AddRef(nTimeout); + else + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + + CRITICAL_BLOCK(cs_mapAddresses) + mapAddresses[addrConnect.GetKey()].nLastFailed = 0; + return pnode; + } + else + { + CRITICAL_BLOCK(cs_mapAddresses) + mapAddresses[addrConnect.GetKey()].nLastFailed = GetTime(); + return NULL; + } +} + +// Synthetic marker address for a tunneled `.btf` peer. The real endpoint is +// unknown by design (the tunnel hides it), so tag the CNode with a non-routable +// 10.x.x.x address (fails IsRoutable, so it is never gossiped as a real peer) +// that is stable per identity, keeping FindNode and the duplicate check working. +static CAddress BtfMarkerAddr(const unsigned char b5[5]) +{ + unsigned int ip; + unsigned char b[4] = { 10, b5[0], b5[1], b5[2] }; + memcpy(&ip, b, 4); + unsigned short port = htons((unsigned short)((b5[3] << 8) | b5[4])); + return CAddress(ip, port, nLocalServices); +} + +// Connect to a peer by its `.btf` address: resolve the self-certified +// descriptor over Nostr, then tunnel through its meeting node with the +// end-to-end channel. Neither side ever learns the other's IP. +CNode* ConnectNodeBtf(const string& strBtfAddr) +{ + unsigned char pk[32]; + if (!btf::ParseAddress(strBtfAddr, pk)) + { + printf("ConnectNodeBtf: invalid address %s\n", strBtfAddr.c_str()); + return NULL; + } + if (BtfLocalAddress() == strBtfAddr) + return NULL; // ourselves + + CAddress addr = BtfMarkerAddr(pk); + CNode* pnode = FindNode(addr.ip); + if (pnode) + { + pnode->AddRef(); + return pnode; + } + + /// debug print + printf("trying %s\n", strBtfAddr.c_str()); + + string strMeeting; + unsigned char enc_pub[32]; + if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) + { + printf("ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); + return NULL; + } + size_t colon = strMeeting.rfind(':'); + if (colon == string::npos) + return NULL; + string strHost = strMeeting.substr(0, colon); + int nPort = atoi(strMeeting.substr(colon + 1).c_str()); + if (nPort <= 0 || nPort > 65535) + return NULL; + + btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); + if (hSocket == INVALID_SOCKET) + { + printf("ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); + return NULL; + } + + /// debug print + printf("connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); + + // Add node + pnode = new CNode(hSocket, addr, false); + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + return pnode; +} + +// Anonymous inbound listener (this node's `.btf` hidden service). Registers at +// the meeting relay and blocks until a client dials our address; each pairing +// becomes a normal inbound CNode riding the end-to-end channel, then we +// register again for the next caller. +void ThreadBtfAccept(void* parg) +{ + printf("ThreadBtfAccept started\n"); + unsigned char pk[32]; + unsigned char sk[32]; + while (!BtfGetIdentity(pk, sk)) + { + if (fShutdown) + return; + Sleep(5000); + } + loop + { + if (fShutdown) + return; + string strMeeting = strBtfMeetingRelay; + size_t colon = strMeeting.rfind(':'); + if (colon == string::npos) + { + Sleep(10000); // no meeting relay configured + continue; + } + string strHost = strMeeting.substr(0, colon); + int nPort = atoi(strMeeting.substr(colon + 1).c_str()); + + // Blocks until a client is paired to us (or the relay drops us) + btf::RvSocket rv = btf::RvServiceRegister(strHost.c_str(), (unsigned short)nPort, pk); + if (fShutdown) + { + if (rv != btf::RV_INVALID) + btf::RvClose(rv); + return; + } + if (rv == btf::RV_INVALID) + { + Sleep(10000); // relay unreachable; retry + continue; + } + btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); + if (hSocket == INVALID_SOCKET) + continue; + + // The dialer is anonymous (its pubkey never reaches us), so tag the + // connection with a random marker address. + unsigned char rnd[5]; + RAND_bytes(rnd, sizeof(rnd)); + CAddress addr = BtfMarkerAddr(rnd); + + printf("accepted .btf connection via rendezvous %s\n", strMeeting.c_str()); + CNode* pnode = new CNode(hSocket, addr, true); + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + } +} + +// Keep an outbound connection to a specific `.btf` peer (from /connectbtf) -- +// the rendezvous analogue of addnode. Retries until connected, reconnects if +// the tunnel drops. +void ThreadBtfConnect(void* parg) +{ + printf("ThreadBtfConnect started\n"); + string strAddr = *(string*)parg; + delete (string*)parg; + loop + { + if (fShutdown) + return; + try + { + CNode* pnode = ConnectNodeBtf(strAddr); + if (pnode) + { + if (!pnode->fNetworkNode) + pnode->fNetworkNode = true; // keep the ref from ConnectNodeBtf + else + pnode->Release(); // already pinned; drop the extra ref + } + } + CATCH_PRINT_EXCEPTION("ThreadBtfConnect") + for (int i = 0; i < 30 && !fShutdown; i++) + Sleep(1000); + } +} + +void CNode::Disconnect() +{ + printf("disconnecting node %s\n", addr.ToString().c_str()); + + closesocket(hSocket); + + // All of a nodes broadcasts and subscriptions are automatically torn down + // when it goes down, so a node has to stay up to keep its broadcast going. + + CRITICAL_BLOCK(cs_mapProducts) + for (map::iterator mi = mapProducts.begin(); mi != mapProducts.end();) + AdvertRemoveSource(this, MSG_PRODUCT, 0, (*(mi++)).second); + + // Cancel subscriptions + for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++) + if (vfSubscribe[nChannel]) + CancelSubscribe(nChannel); +} + + + + + + + + + + + + + +void ThreadSocketHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadSocketHandler(parg)); + + loop + { + vfThreadRunning[0] = true; + CheckForShutdown(0); + try + { + ThreadSocketHandler2(parg); + } + CATCH_PRINT_EXCEPTION("ThreadSocketHandler()") + vfThreadRunning[0] = false; + Sleep(5000); + } +} + +void ThreadSocketHandler2(void* parg) +{ + printf("ThreadSocketHandler started\n"); + SOCKET hListenSocket = *(SOCKET*)parg; + list vNodesDisconnected; + int nPrevNodeCount = 0; + + loop + { + // + // Disconnect nodes + // + CRITICAL_BLOCK(cs_vNodes) + { + // Disconnect duplicate connections + map mapFirst; + foreach(CNode* pnode, vNodes) + { + if (pnode->fDisconnect) + continue; + unsigned int ip = pnode->addr.ip; + if (mapFirst.count(ip) && addrLocalHost.ip < ip) + { + // In case two nodes connect to each other at once, + // the lower ip disconnects its outbound connection + CNode* pnodeExtra = mapFirst[ip]; + + if (pnodeExtra->GetRefCount() > (pnodeExtra->fNetworkNode ? 1 : 0)) + swap(pnodeExtra, pnode); + + if (pnodeExtra->GetRefCount() <= (pnodeExtra->fNetworkNode ? 1 : 0)) + { + printf("(%d nodes) disconnecting duplicate: %s\n", vNodes.size(), pnodeExtra->addr.ToString().c_str()); + if (pnodeExtra->fNetworkNode && !pnode->fNetworkNode) + { + pnode->AddRef(); + swap(pnodeExtra->fNetworkNode, pnode->fNetworkNode); + pnodeExtra->Release(); + } + pnodeExtra->fDisconnect = true; + } + } + mapFirst[ip] = pnode; + } + + // Disconnect unused nodes + vector vNodesCopy = vNodes; + foreach(CNode* pnode, vNodesCopy) + { + if (pnode->ReadyToDisconnect() && pnode->vRecv.empty() && pnode->vSend.empty()) + { + // remove from vNodes + vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + pnode->Disconnect(); + + // hold in disconnected pool until all refs are released + pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 5 * 60); + if (pnode->fNetworkNode) + pnode->Release(); + vNodesDisconnected.push_back(pnode); + } + } + + // Delete disconnected nodes + list vNodesDisconnectedCopy = vNodesDisconnected; + foreach(CNode* pnode, vNodesDisconnectedCopy) + { + // wait until threads are done using it + if (pnode->GetRefCount() <= 0) + { + bool fDelete = false; + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + TRY_CRITICAL_BLOCK(pnode->cs_mapRequests) + TRY_CRITICAL_BLOCK(pnode->cs_inventory) + fDelete = true; + if (fDelete) + { + vNodesDisconnected.remove(pnode); + delete pnode; + } + } + } + } + if (vNodes.size() != nPrevNodeCount) + { + nPrevNodeCount = vNodes.size(); + MainFrameRepaint(); + } + + + // + // Find which sockets have data to receive + // + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // frequency to poll pnode->vSend + + fd_set fdsetRecv; + fd_set fdsetSend; + FD_ZERO(&fdsetRecv); + FD_ZERO(&fdsetSend); + SOCKET hSocketMax = 0; + FD_SET(hListenSocket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket); + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + { + FD_SET(pnode->hSocket, &fdsetRecv); + hSocketMax = max(hSocketMax, pnode->hSocket); + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + if (!pnode->vSend.empty()) + FD_SET(pnode->hSocket, &fdsetSend); + } + } + + vfThreadRunning[0] = false; + int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); + vfThreadRunning[0] = true; + CheckForShutdown(0); + if (nSelect == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + printf("select failed: %d\n", nErr); + for (int i = 0; i <= hSocketMax; i++) + { + FD_SET(i, &fdsetRecv); + FD_SET(i, &fdsetSend); + } + Sleep(timeout.tv_usec/1000); + } + RandAddSeed(); + + //// debug print + //foreach(CNode* pnode, vNodes) + //{ + // printf("vRecv = %-5d ", pnode->vRecv.size()); + // printf("vSend = %-5d ", pnode->vSend.size()); + //} + //printf("\n"); + + + // + // Accept new connections + // + if (FD_ISSET(hListenSocket, &fdsetRecv)) + { + struct sockaddr_in sockaddr; +#ifdef _WIN32 + int len = sizeof(sockaddr); +#else + socklen_t len = sizeof(sockaddr); +#endif + SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + CAddress addr(sockaddr); + if (hSocket == INVALID_SOCKET) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + printf("ERROR ThreadSocketHandler accept failed: %d\n", WSAGetLastError()); + } + else + { + printf("accepted connection from %s\n", addr.ToString().c_str()); + CNode* pnode = new CNode(hSocket, addr, true); + pnode->AddRef(); + CRITICAL_BLOCK(cs_vNodes) + vNodes.push_back(pnode); + } + } + + + // + // Service each socket + // + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + vNodesCopy = vNodes; + foreach(CNode* pnode, vNodesCopy) + { + CheckForShutdown(0); + SOCKET hSocket = pnode->hSocket; + + // + // Receive + // + if (FD_ISSET(hSocket, &fdsetRecv)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + { + CDataStream& vRecv = pnode->vRecv; + unsigned int nPos = vRecv.size(); + + // typical socket buffer is 8K-64K + const unsigned int nBufSize = 0x10000; + vRecv.resize(nPos + nBufSize); + int nBytes = recv(hSocket, &vRecv[nPos], nBufSize, 0); + vRecv.resize(nPos + max(nBytes, 0)); + if (nBytes == 0) + { + // socket closed gracefully + if (!pnode->fDisconnect) + printf("recv: socket closed\n"); + pnode->fDisconnect = true; + } + else if (nBytes < 0) + { + // socket error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + if (!pnode->fDisconnect) + printf("recv failed: %d\n", nErr); + pnode->fDisconnect = true; + } + } + } + } + + // + // Send + // + if (FD_ISSET(hSocket, &fdsetSend)) + { + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + { + CDataStream& vSend = pnode->vSend; + if (!vSend.empty()) + { + int nBytes = send(hSocket, &vSend[0], vSend.size(), 0); + if (nBytes > 0) + { + vSend.erase(vSend.begin(), vSend.begin() + nBytes); + } + else if (nBytes == 0) + { + if (pnode->ReadyToDisconnect()) + pnode->vSend.clear(); + } + else + { + printf("send error %d\n", nBytes); + if (pnode->ReadyToDisconnect()) + pnode->vSend.clear(); + } + } + } + } + } + + + Sleep(10); + } +} + + + + + + + + + + +void ThreadOpenConnections(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadOpenConnections(parg)); + + loop + { + vfThreadRunning[1] = true; + CheckForShutdown(1); + try + { + ThreadOpenConnections2(parg); + } + CATCH_PRINT_EXCEPTION("ThreadOpenConnections()") + vfThreadRunning[1] = false; + Sleep(5000); + } +} + +void ThreadOpenConnections2(void* parg) +{ + printf("ThreadOpenConnections started\n"); + + // Initiate network connections + const int nMaxConnections = 15; + loop + { + // Wait + vfThreadRunning[1] = false; + Sleep(500); + while (vNodes.size() >= nMaxConnections || vNodes.size() >= mapAddresses.size()) + { + CheckForShutdown(1); + Sleep(2000); + } + vfThreadRunning[1] = true; + CheckForShutdown(1); + + + // Make a list of unique class C's + unsigned char pchIPCMask[4] = { 0xff, 0xff, 0xff, 0x00 }; + unsigned int nIPCMask = *(unsigned int*)pchIPCMask; + vector vIPC; + CRITICAL_BLOCK(cs_mapAddresses) + { + vIPC.reserve(mapAddresses.size()); + unsigned int nPrev = 0; + foreach(const PAIRTYPE(vector, CAddress)& item, mapAddresses) + { + const CAddress& addr = item.second; + if (!addr.IsIPv4()) + continue; + + // Taking advantage of mapAddresses being in sorted order, + // with IPs of the same class C grouped together. + unsigned int ipC = addr.ip & nIPCMask; + if (ipC != nPrev) + vIPC.push_back(nPrev = ipC); + } + } + + // + // The IP selection process is designed to limit vulnerability to address flooding. + // Any class C (a.b.c.?) has an equal chance of being chosen, then an IP is + // chosen within the class C. An attacker may be able to allocate many IPs, but + // they would normally be concentrated in blocks of class C's. They can hog the + // attention within their class C, but not the whole IP address space overall. + // A lone node in a class C will get as much attention as someone holding all 255 + // IPs in another class C. + // + bool fSuccess = false; + int nLimit = vIPC.size(); + while (!fSuccess && nLimit-- > 0) + { + // Choose a random class C + unsigned int ipC = vIPC[GetRand(vIPC.size())]; + + // Organize all addresses in the class C by IP + map > mapIP; + CRITICAL_BLOCK(cs_mapAddresses) + { + unsigned int nDelay = ((30 * 60) << vNodes.size()); + if (nDelay > 8 * 60 * 60) + nDelay = 8 * 60 * 60; + for (map, CAddress>::iterator mi = mapAddresses.lower_bound(CAddress(ipC, 0).GetKey()); + mi != mapAddresses.upper_bound(CAddress(ipC | ~nIPCMask, 0xffff).GetKey()); + ++mi) + { + const CAddress& addr = (*mi).second; + unsigned int nRandomizer = (addr.nLastFailed * addr.ip * 7777U) % 20000; + if (GetTime() - addr.nLastFailed > nDelay * nRandomizer / 10000) + mapIP[addr.ip].push_back(addr); + } + } + if (mapIP.empty()) + break; + + // Choose a random IP in the class C + map >::iterator mi = mapIP.begin(); + advance(mi, GetRand(mapIP.size())); + + // Once we've chosen an IP, we'll try every given port before moving on + foreach(const CAddress& addrConnect, (*mi).second) + { + if (addrConnect.ip == addrLocalHost.ip || !addrConnect.IsIPv4() || FindNode(addrConnect.ip)) + continue; + + CNode* pnode = ConnectNode(addrConnect); + if (!pnode) + continue; + pnode->fNetworkNode = true; + + if (addrLocalHost.IsRoutable()) + { + // Advertise our address + vector vAddrToSend; + vAddrToSend.push_back(addrLocalHost); + pnode->PushMessage("addr", vAddrToSend); + } + + // Get as many addresses as we can + pnode->PushMessage("getaddr"); + + ////// should the one on the receiving end do this too? + // Subscribe our local subscription list + const unsigned int nHops = 0; + for (unsigned int nChannel = 0; nChannel < pnodeLocalHost->vfSubscribe.size(); nChannel++) + if (pnodeLocalHost->vfSubscribe[nChannel]) + pnode->PushMessage("subscribe", nChannel, nHops); + + fSuccess = true; + break; + } + } + } +} + + + + + + + + +void ThreadMessageHandler(void* parg) +{ + IMPLEMENT_RANDOMIZE_STACK(ThreadMessageHandler(parg)); + + loop + { + vfThreadRunning[2] = true; + CheckForShutdown(2); + try + { + ThreadMessageHandler2(parg); + } + CATCH_PRINT_EXCEPTION("ThreadMessageHandler()") + vfThreadRunning[2] = false; + Sleep(5000); + } +} + +void ThreadMessageHandler2(void* parg) +{ + printf("ThreadMessageHandler started\n"); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + loop + { + // Poll the connected nodes for messages + vector vNodesCopy; + CRITICAL_BLOCK(cs_vNodes) + vNodesCopy = vNodes; + foreach(CNode* pnode, vNodesCopy) + { + pnode->AddRef(); + + // Receive messages + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + ProcessMessages(pnode); + + // Send messages + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + SendMessages(pnode); + + pnode->Release(); + } + + // Wait and allow messages to bunch up + vfThreadRunning[2] = false; + Sleep(100); + vfThreadRunning[2] = true; + CheckForShutdown(2); + } +} + + + + + + + + + +//// todo: start one thread per processor, use getenv("NUMBER_OF_PROCESSORS") +void ThreadBitcoinMiner(void* parg) +{ + vfThreadRunning[3] = true; + CheckForShutdown(3); + try + { + bool fRet = BitcoinMiner(); + printf("BitcoinMiner returned %s\n\n\n", fRet ? "true" : "false"); + } + CATCH_PRINT_EXCEPTION("BitcoinMiner()") + vfThreadRunning[3] = false; +} + + + + + + + + + + + +bool StartNode(string& strError) +{ + strError = ""; + + // Sockets startup + WSADATA wsadata; + int ret = WSAStartup(MAKEWORD(2,2), &wsadata); + if (ret != NO_ERROR) + { + strError = strprintf("Error: TCP/IP socket library failed to start (WSAStartup returned error %d)", ret); + printf("%s\n", strError.c_str()); + return false; + } + + // Get local host ip + char pszHostName[255]; + if (gethostname(pszHostName, 255) == SOCKET_ERROR) + { + strError = strprintf("Error: Unable to get IP address of this computer (gethostname returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + struct hostent* pHostEnt = gethostbyname(pszHostName); + if (!pHostEnt) + { + strError = strprintf("Error: Unable to get IP address of this computer (gethostbyname returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + addrLocalHost = CAddress(*(long*)(pHostEnt->h_addr_list[0]), + nListenPort, + nLocalServices); + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); + + // Create socket for listening for incoming connections + SOCKET hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (hListenSocket == INVALID_SOCKET) + { + strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + // Set to nonblocking, incoming connections will also inherit this + u_long nOne = 1; + if (ioctlsocket(hListenSocket, FIONBIO, &nOne) == SOCKET_ERROR) + { + strError = strprintf("Error: Couldn't set properties on socket for incoming connections (ioctlsocket returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + // The sockaddr_in structure specifies the address family, + // IP address, and port for the socket that is being bound + int nRetryLimit = 15; + struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); + if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) + { + int nErr = WSAGetLastError(); + if (nErr == WSAEADDRINUSE) + strError = strprintf("Error: Unable to bind to port %s on this computer. The program is probably already running.", addrLocalHost.ToString().c_str()); + else + strError = strprintf("Error: Unable to bind to port %s on this computer (bind returned error %d)", addrLocalHost.ToString().c_str(), nErr); + printf("%s\n", strError.c_str()); + return false; + } + printf("bound to addrLocalHost = %s\n\n", addrLocalHost.ToString().c_str()); + + // Listen for incoming connections + if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) + { + strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); + printf("%s\n", strError.c_str()); + return false; + } + + // Get our external IP address for incoming connections + if (addrIncoming.ip) + addrLocalHost.ip = addrIncoming.ip; + + if (GetMyExternalIP(addrLocalHost.ip)) + { + addrIncoming = addrLocalHost; + CWalletDB().WriteSetting("addrIncoming", addrIncoming); + } + + // Peer discovery over Nostr relays (replaces the old IRC seed) + if (_beginthread(ThreadNostrSeed, 0, NULL) == -1) + printf("Error: _beginthread(ThreadNostrSeed) failed\n"); + + // Anonymous inbound: this node's .btf hidden service, reachable through the + // meeting relay without exposing our IP or needing a public port. + if (!strBtfMeetingRelay.empty()) + if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) + printf("Error: _beginthread(ThreadBtfAccept) failed\n"); + + // Anonymous outbound: keep a connection to a specific .btf peer, if asked. + if (!strBtfConnect.empty()) + if (_beginthread(ThreadBtfConnect, 0, new string(strBtfConnect)) == -1) + printf("Error: _beginthread(ThreadBtfConnect) failed\n"); + + // + // Start threads + // + if (_beginthread(ThreadSocketHandler, 0, new SOCKET(hListenSocket)) == -1) + { + strError = "Error: _beginthread(ThreadSocketHandler) failed"; + printf("%s\n", strError.c_str()); + return false; + } + + if (_beginthread(ThreadOpenConnections, 0, NULL) == -1) + { + strError = "Error: _beginthread(ThreadOpenConnections) failed"; + printf("%s\n", strError.c_str()); + return false; + } + + if (_beginthread(ThreadMessageHandler, 0, NULL) == -1) + { + strError = "Error: _beginthread(ThreadMessageHandler) failed"; + printf("%s\n", strError.c_str()); + return false; + } + + return true; +} + +bool StopNode() +{ + printf("StopNode()\n"); + fShutdown = true; + nTransactionsUpdated++; + while (count(vfThreadRunning.begin(), vfThreadRunning.end(), true)) + Sleep(10); + Sleep(50); + + // Sockets shutdown + WSACleanup(); + return true; +} + +void CheckForShutdown(int n) +{ + if (fShutdown) + { + if (n != -1) + vfThreadRunning[n] = false; + _endthread(); + } +} diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000..6e62126 --- /dev/null +++ b/src/net.h @@ -0,0 +1,863 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +class CMessageHeader; +class CAddress; +class CInv; +class CRequestTracker; +class CNode; + + + +static const unsigned short DEFAULT_PORT = htons(8433); +static const unsigned int PUBLISH_HOPS = 5; +enum +{ + NODE_NETWORK = (1 << 0), +}; + + + + + + +bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet); +bool GetMyExternalIP(unsigned int& ipRet); +bool AddAddress(CAddrDB& addrdb, const CAddress& addr); +CNode* FindNode(unsigned int ip); +CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); +CNode* ConnectNodeBtf(const string& strBtfAddr); +void ThreadBtfAccept(void* parg); +void ThreadBtfConnect(void* parg); +extern string strBtfConnect; +void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); +bool AnySubscribed(unsigned int nChannel); +void ThreadBitcoinMiner(void* parg); +bool StartNode(string& strError=REF(string())); +bool StopNode(); +void CheckForShutdown(int n); + + + + + + + + + +// +// Message header +// (4) message start +// (12) command +// (4) size + +// The message start string is designed to be unlikely to occur in normal data. +// The characters are rarely used upper ascii, not valid as UTF-8, and produce +// a large 4-byte int at any alignment. +// Magic bytes unique to the Bitflash network (0xbf = "BF"). Second byte bumped +// to 0x20 for the 2026 stable relaunch so the old test chain can't interfere. +static const char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; + +class CMessageHeader +{ +public: + enum { COMMAND_SIZE=12 }; + char pchMessageStart[sizeof(::pchMessageStart)]; + char pchCommand[COMMAND_SIZE]; + unsigned int nMessageSize; + + CMessageHeader() + { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memset(pchCommand, 0, sizeof(pchCommand)); + pchCommand[1] = 1; + nMessageSize = -1; + } + + CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn) + { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + strncpy(pchCommand, pszCommand, COMMAND_SIZE); + nMessageSize = nMessageSizeIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(FLATDATA(pchMessageStart)); + READWRITE(FLATDATA(pchCommand)); + READWRITE(nMessageSize); + ) + + string GetCommand() + { + if (pchCommand[COMMAND_SIZE-1] == 0) + return string(pchCommand, pchCommand + strlen(pchCommand)); + else + return string(pchCommand, pchCommand + COMMAND_SIZE); + } + + bool IsValid() + { + // Check start string + if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != 0) + return false; + + // Check the command string for errors + for (char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) + { + if (*p1 == 0) + { + // Must be all zeros after the first zero + for (; p1 < pchCommand + COMMAND_SIZE; p1++) + if (*p1 != 0) + return false; + } + else if (*p1 < ' ' || *p1 > 0x7E) + return false; + } + + // Message size + if (nMessageSize > 0x10000000) + { + printf("CMessageHeader::IsValid() : nMessageSize too large %u\n", nMessageSize); + return false; + } + + return true; + } +}; + + + + + + +static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + +class CAddress +{ +public: + uint64 nServices; + unsigned char pchReserved[12]; + unsigned int ip; + unsigned short port; + + // disk only + unsigned int nTime; + + // memory only + unsigned int nLastFailed; + + CAddress() + { + nServices = 0; + memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); + ip = 0; + port = DEFAULT_PORT; + nTime = GetAdjustedTime(); + nLastFailed = 0; + } + + CAddress(unsigned int ipIn, unsigned short portIn, uint64 nServicesIn=0) + { + nServices = nServicesIn; + memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); + ip = ipIn; + port = portIn; + nTime = GetAdjustedTime(); + nLastFailed = 0; + } + + explicit CAddress(const struct sockaddr_in& sockaddr, uint64 nServicesIn=0) + { + nServices = nServicesIn; + memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); + ip = sockaddr.sin_addr.s_addr; + port = sockaddr.sin_port; + nTime = GetAdjustedTime(); + nLastFailed = 0; + } + + explicit CAddress(const char* pszIn, uint64 nServicesIn=0) + { + nServices = nServicesIn; + memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); + ip = 0; + port = DEFAULT_PORT; + nTime = GetAdjustedTime(); + nLastFailed = 0; + + char psz[100]; + if (strlen(pszIn) > ARRAYLEN(psz)-1) + return; + strcpy(psz, pszIn); + unsigned int a, b, c, d, e; + if (sscanf(psz, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &e) < 4) + return; + char* pszPort = strchr(psz, ':'); + if (pszPort) + { + *pszPort++ = '\0'; + port = htons(atoi(pszPort)); + } + ip = inet_addr(psz); + } + + IMPLEMENT_SERIALIZE + ( + if (nType & SER_DISK) + { + READWRITE(nVersion); + READWRITE(nTime); + } + READWRITE(nServices); + READWRITE(FLATDATA(pchReserved)); + READWRITE(ip); + READWRITE(port); + ) + + friend inline bool operator==(const CAddress& a, const CAddress& b) + { + return (memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)) == 0 && + a.ip == b.ip && + a.port == b.port); + } + + friend inline bool operator<(const CAddress& a, const CAddress& b) + { + int ret = memcmp(a.pchReserved, b.pchReserved, sizeof(a.pchReserved)); + if (ret < 0) + return true; + else if (ret == 0) + { + if (ntohl(a.ip) < ntohl(b.ip)) + return true; + else if (a.ip == b.ip) + return ntohs(a.port) < ntohs(b.port); + } + return false; + } + + vector GetKey() const + { + CDataStream ss; + ss.reserve(18); + ss << FLATDATA(pchReserved) << ip << port; + + #if defined(_MSC_VER) && _MSC_VER < 1300 + return vector((unsigned char*)&ss.begin()[0], (unsigned char*)&ss.end()[0]); + #else + return vector(ss.begin(), ss.end()); + #endif + } + + struct sockaddr_in GetSockAddr() const + { + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = ip; + sockaddr.sin_port = port; + return sockaddr; + } + + bool IsIPv4() const + { + return (memcmp(pchReserved, pchIPv4, sizeof(pchIPv4)) == 0); + } + + bool IsRoutable() const + { + return !(GetByte(3) == 10 || (GetByte(3) == 192 && GetByte(2) == 168)); + } + + unsigned char GetByte(int n) const + { + return ((unsigned char*)&ip)[3-n]; + } + + string ToStringIPPort() const + { + return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); + } + + string ToStringIP() const + { + return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); + } + + string ToString() const + { + return strprintf("%u.%u.%u.%u:%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0), ntohs(port)); + //return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), GetByte(0)); + } + + void print() const + { + printf("CAddress(%s)\n", ToString().c_str()); + } +}; + + + + + + + +enum +{ + MSG_TX = 1, + MSG_BLOCK, + MSG_REVIEW, + MSG_PRODUCT, + MSG_TABLE, +}; + +static const char* ppszTypeName[] = +{ + "ERROR", + "tx", + "block", + "review", + "product", + "table", +}; + +class CInv +{ +public: + int type; + uint256 hash; + + CInv() + { + type = 0; + hash = 0; + } + + CInv(int typeIn, const uint256& hashIn) + { + type = typeIn; + hash = hashIn; + } + + CInv(const string& strType, const uint256& hashIn) + { + int i; + for (i = 1; i < ARRAYLEN(ppszTypeName); i++) + { + if (strType == ppszTypeName[i]) + { + type = i; + break; + } + } + if (i == ARRAYLEN(ppszTypeName)) + throw std::out_of_range(strprintf("CInv::CInv(string, uint256) : unknown type '%s'", strType.c_str())); + hash = hashIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(type); + READWRITE(hash); + ) + + friend inline bool operator<(const CInv& a, const CInv& b) + { + return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); + } + + bool IsKnownType() const + { + return (type >= 1 && type < ARRAYLEN(ppszTypeName)); + } + + const char* GetCommand() const + { + if (!IsKnownType()) + throw std::out_of_range(strprintf("CInv::GetCommand() : type=% unknown type", type)); + return ppszTypeName[type]; + } + + string ToString() const + { + return strprintf("%s %s", GetCommand(), hash.ToString().substr(0,14).c_str()); + } + + void print() const + { + printf("CInv(%s)\n", ToString().c_str()); + } +}; + + + + + +class CRequestTracker +{ +public: + void (*fn)(void*, CDataStream&); + void* param1; + + explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) + { + fn = fnIn; + param1 = param1In; + } + + bool IsNull() + { + return fn == NULL; + } +}; + + + + + +extern bool fClient; +extern uint64 nLocalServices; +extern CAddress addrLocalHost; +extern unsigned short nListenPort; +extern CNode* pnodeLocalHost; +extern bool fShutdown; +extern array vfThreadRunning; +extern vector vNodes; +extern CCriticalSection cs_vNodes; +extern map, CAddress> mapAddresses; +extern CCriticalSection cs_mapAddresses; +extern map mapRelay; +extern deque > vRelayExpiration; +extern CCriticalSection cs_mapRelay; +extern map mapAlreadyAskedFor; +extern CAddress addrProxy; + + + + + +class CNode +{ +public: + // socket + uint64 nServices; + SOCKET hSocket; + CDataStream vSend; + CDataStream vRecv; + CCriticalSection cs_vSend; + CCriticalSection cs_vRecv; + unsigned int nPushPos; + CAddress addr; + int nVersion; + bool fClient; + bool fInbound; + bool fNetworkNode; + bool fDisconnect; +protected: + int nRefCount; +public: + int64 nReleaseTime; + map mapRequests; + CCriticalSection cs_mapRequests; + + // flood + vector vAddrToSend; + set setAddrKnown; + + // inventory based relay + set setInventoryKnown; + set setInventoryKnown2; + vector vInventoryToSend; + CCriticalSection cs_inventory; + multimap mapAskFor; + + // publish and subscription + vector vfSubscribe; + + + CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) + { + nServices = 0; + hSocket = hSocketIn; + vSend.SetType(SER_NETWORK); + vRecv.SetType(SER_NETWORK); + nPushPos = -1; + addr = addrIn; + nVersion = 0; + fClient = false; // set by version message + fInbound = fInboundIn; + fNetworkNode = false; + fDisconnect = false; + nRefCount = 0; + nReleaseTime = 0; + vfSubscribe.assign(256, false); + + // Push a version message + /// when NTP implemented, change to just nTime = GetAdjustedTime() + int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); + PushMessage("version", VERSION, nLocalServices, nTime, addr); + } + + ~CNode() + { + if (hSocket != INVALID_SOCKET) + closesocket(hSocket); + } + +private: + CNode(const CNode&); + void operator=(const CNode&); +public: + + + bool ReadyToDisconnect() + { + return fDisconnect || GetRefCount() <= 0; + } + + int GetRefCount() + { + return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); + } + + void AddRef(int64 nTimeout=0) + { + if (nTimeout != 0) + nReleaseTime = max(nReleaseTime, GetTime() + nTimeout); + else + nRefCount++; + } + + void Release() + { + nRefCount--; + } + + + + void AddInventoryKnown(const CInv& inv) + { + CRITICAL_BLOCK(cs_inventory) + setInventoryKnown.insert(inv); + } + + void PushInventory(const CInv& inv) + { + CRITICAL_BLOCK(cs_inventory) + if (!setInventoryKnown.count(inv)) + vInventoryToSend.push_back(inv); + } + + void AskFor(const CInv& inv) + { + // We're using mapAskFor as a priority queue, + // the key is the earliest time the request can be sent + int64& nRequestTime = mapAlreadyAskedFor[inv]; + printf("askfor %s %I64d\n", inv.ToString().c_str(), nRequestTime); + + // Make sure not to reuse time indexes to keep things in the same order + int64 nNow = (GetTime() - 1) * 1000000; + static int64 nLastTime; + nLastTime = nNow = max(nNow, ++nLastTime); + + // Each retry is 2 minutes after the last + nRequestTime = max(nRequestTime + 2 * 60 * 1000000, nNow); + mapAskFor.insert(make_pair(nRequestTime, inv)); + } + + + + void BeginMessage(const char* pszCommand) + { + EnterCriticalSection(&cs_vSend); + if (nPushPos != -1) + AbortMessage(); + nPushPos = vSend.size(); + vSend << CMessageHeader(pszCommand, 0); + printf("sending: %-12s ", pszCommand); + } + + void AbortMessage() + { + if (nPushPos == -1) + return; + vSend.resize(nPushPos); + nPushPos = -1; + LeaveCriticalSection(&cs_vSend); + printf("(aborted)\n"); + } + + void EndMessage() + { + extern int nDropMessagesTest; + if (nDropMessagesTest > 0 && GetRand(nDropMessagesTest) == 0) + { + printf("dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + + if (nPushPos == -1) + return; + + // Patch in the size + unsigned int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader); + memcpy((char*)&vSend[nPushPos] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize)); + + printf("(%d bytes) ", nSize); + //for (int i = nPushPos+sizeof(CMessageHeader); i < min(vSend.size(), nPushPos+sizeof(CMessageHeader)+20U); i++) + // printf("%02x ", vSend[i] & 0xff); + printf("\n"); + + nPushPos = -1; + LeaveCriticalSection(&cs_vSend); + } + + void EndMessageAbortIfEmpty() + { + if (nPushPos == -1) + return; + int nSize = vSend.size() - nPushPos - sizeof(CMessageHeader); + if (nSize > 0) + EndMessage(); + else + AbortMessage(); + } + + const char* GetMessageCommand() const + { + if (nPushPos == -1) + return ""; + return &vSend[nPushPos] + offsetof(CMessageHeader, pchCommand); + } + + + + + void PushMessage(const char* pszCommand) + { + try + { + BeginMessage(pszCommand); + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1) + { + try + { + BeginMessage(pszCommand); + vSend << a1; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4) + { + try + { + BeginMessage(pszCommand); + vSend << a1 << a2 << a3 << a4; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + + void PushRequest(const char* pszCommand, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply); + } + + template + void PushRequest(const char* pszCommand, const T1& a1, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply, a1); + } + + template + void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, + void (*fn)(void*, CDataStream&), void* param1) + { + uint256 hashReply; + RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); + + CRITICAL_BLOCK(cs_mapRequests) + mapRequests[hashReply] = CRequestTracker(fn, param1); + + PushMessage(pszCommand, hashReply, a1, a2); + } + + + + bool IsSubscribed(unsigned int nChannel); + void Subscribe(unsigned int nChannel, unsigned int nHops=0); + void CancelSubscribe(unsigned int nChannel); + void Disconnect(); +}; + + + + + + + + + + +inline void RelayInventory(const CInv& inv) +{ + // Put on lists to offer to the other nodes + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + pnode->PushInventory(inv); +} + +template +void RelayMessage(const CInv& inv, const T& a) +{ + CDataStream ss(SER_NETWORK); + ss.reserve(10000); + ss << a; + RelayMessage(inv, ss); +} + +template<> +inline void RelayMessage<>(const CInv& inv, const CDataStream& ss) +{ + CRITICAL_BLOCK(cs_mapRelay) + { + // Expire old relay messages + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) + { + mapRelay.erase(vRelayExpiration.front().second); + vRelayExpiration.pop_front(); + } + + // Save original serialized message so newer versions are preserved + mapRelay[inv] = ss; + vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv)); + } + + RelayInventory(inv); +} + + + + + + + + +// +// Templates for the publish and subscription system. +// The object being published as T& obj needs to have: +// a set setSources member +// specializations of AdvertInsert and AdvertErase +// Currently implemented for CTable and CProduct. +// + +template +void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + // Add to sources + obj.setSources.insert(pfrom->addr.ip); + + if (!AdvertInsert(obj)) + return; + + // Relay + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) + pnode->PushMessage("publish", nChannel, nHops, obj); +} + +template +void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + uint256 hash = obj.GetHash(); + + CRITICAL_BLOCK(cs_vNodes) + foreach(CNode* pnode, vNodes) + if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) + pnode->PushMessage("pub-cancel", nChannel, nHops, hash); + + AdvertErase(obj); +} + +template +void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) +{ + // Remove a source + obj.setSources.erase(pfrom->addr.ip); + + // If no longer supported by any sources, cancel it + if (obj.setSources.empty()) + AdvertStopPublish(pfrom, nChannel, nHops, obj); +} diff --git a/src/nostr.cpp b/src/nostr.cpp new file mode 100644 index 0000000..8e2a07d --- /dev/null +++ b/src/nostr.cpp @@ -0,0 +1,838 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Peer discovery over Nostr relays. See nostr.h. + +#include "headers.h" +#include +#include +// We link libsecp256k1 statically (without this the header assumes DLL import) +#define SECP256K1_STATIC +#include +#include +#include + +// util.h redefines snprintf as a macro (2009 MSVC compat); this breaks +// nlohmann/json, which calls std::snprintf. Undo the macro just here. +#ifdef snprintf +#undef snprintf +#endif +#include + +#include "btfaddr.h" +#include "btfchan.h" + +using json = nlohmann::json; + +// Bitflash rendezvous descriptor: parameterized-replaceable Nostr event so each +// node keeps exactly one current descriptor (keyed by author pubkey). +static const int BTF_DESC_KIND = 38501; +// "-2" scopes the 2026 stable relaunch: new nodes discover only each other, and +// the abandoned test .btf descriptors lingering on the relays become invisible. +static const char* BTF_DESC_DTAG = "btf-descriptor-2"; + +// Nostr network/scope for Bitflash. Events are replaceable (NIP-78). +static const int NOSTR_KIND = 30078; +static const char* NOSTR_DTAG = "bitflash-mainnet-2"; + +// Anonymous auto-discovery: how many peers to keep connected via .btf rendezvous +// before we stop dialing more, and how many new dials to attempt per cycle. +static const unsigned int BTF_TARGET_CONN = 8; +static const int BTF_DIALS_PER_PASS = 4; + +// Other nodes' .btf addresses learned from their descriptors on the relays. +static set g_btfPeers; +static CCriticalSection cs_btfPeers; + +// Public discovery relays. Tunable. +const char* pszNostrRelays[] = { + "wss://relay.damus.io", + "wss://nos.lol", + "wss://relay.nostr.band", + "wss://relay.snort.social", +}; +const int nNostrRelays = ARRAYLEN(pszNostrRelays); + +// Rendezvous meeting relay this node registers at (ThreadBtfAccept in net.cpp) +// and advertises in its .btf descriptor. Seed default; /rvrelay overrides. +string strBtfMeetingRelay = "90.156.222.107:8434"; + + +// +// Utilities +// +static string HexEncode(const unsigned char* p, size_t n) +{ + static const char* h = "0123456789abcdef"; + string s; + s.reserve(n * 2); + for (size_t i = 0; i < n; i++) + { + s += h[p[i] >> 4]; + s += h[p[i] & 0xf]; + } + return s; +} + +static bool HexDecode(const string& s, unsigned char* out, size_t n) +{ + if (s.size() != n * 2) + return false; + for (size_t i = 0; i < n; i++) + { + unsigned int b; + if (sscanf(s.c_str() + i * 2, "%2x", &b) != 1) + return false; + out[i] = (unsigned char)b; + } + return true; +} + +static string Base64Encode(const unsigned char* p, size_t n) +{ + static const char* tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + string s; + for (size_t i = 0; i < n; i += 3) + { + unsigned int b = p[i] << 16; + if (i + 1 < n) b |= p[i + 1] << 8; + if (i + 2 < n) b |= p[i + 2]; + s += tbl[(b >> 18) & 0x3f]; + s += tbl[(b >> 12) & 0x3f]; + s += (i + 1 < n) ? tbl[(b >> 6) & 0x3f] : '='; + s += (i + 2 < n) ? tbl[b & 0x3f] : '='; + } + return s; +} + + +// +// Node Nostr key -- secp256k1 x-only (BIP340), persisted in /nostr.key +// +class CNostrKey +{ +public: + secp256k1_context* ctx; + secp256k1_keypair keypair; + unsigned char seckey[32]; // node's secp256k1 secret (for descriptor signing) + unsigned char xonly[32]; // node's x-only pubkey (also its .btf identity) + unsigned char enc_pk[32]; // node's x25519 public key (echan `enc` field) + unsigned char enc_sk[32]; // node's x25519 secret key + bool fValid; + + CNostrKey() : ctx(NULL), fValid(false) {} + + ~CNostrKey() + { + if (ctx) secp256k1_context_destroy(ctx); + } + + // Load or generate a 32-byte key persisted as hex in /. + static void LoadOrGenSecp(secp256k1_context* ctx, const string& file, unsigned char sk[32]) + { + FILE* f = fopen(file.c_str(), "rb"); + string strHex; + if (f) { char buf[65] = {0}; fread(buf, 1, 64, f); fclose(f); strHex = buf; } + if (!HexDecode(strHex, sk, 32) || !secp256k1_ec_seckey_verify(ctx, sk)) + { + do { RAND_bytes(sk, sizeof(unsigned char) * 32); } while (!secp256k1_ec_seckey_verify(ctx, sk)); + FILE* fw = fopen(file.c_str(), "wb"); + if (fw) { string h = HexEncode(sk, 32); fwrite(h.c_str(), 1, h.size(), fw); fclose(fw); } + } + } + + bool Init() + { + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (!ctx) + return false; + unsigned char seed[32]; + RAND_bytes(seed, sizeof(seed)); + secp256k1_context_randomize(ctx, seed); + + LoadOrGenSecp(ctx, GetAppDir() + "/nostr.key", seckey); + + if (!secp256k1_keypair_create(ctx, &keypair, seckey)) + return false; + secp256k1_xonly_pubkey xpub; + if (!secp256k1_keypair_xonly_pub(ctx, &xpub, NULL, &keypair)) + return false; + secp256k1_xonly_pubkey_serialize(ctx, xonly, &xpub); + + // x25519 encryption keypair for the end-to-end channel, persisted separately. + btf::ChanInit(); + string strEncFile = GetAppDir() + "/btf_enc.key"; + FILE* fe = fopen(strEncFile.c_str(), "rb"); + bool fLoaded = false; + if (fe) + { + char buf[65] = {0}; + if (fread(buf, 1, 64, fe) == 64 && HexDecode(string(buf), enc_sk, 32)) + fLoaded = true; + fclose(fe); + } + if (fLoaded) + { + // Derive the public key from the stored secret (stable identity). + btf::ChanPublicFromSecret(enc_pk, enc_sk); + } + else + { + btf::ChanKeypair(enc_pk, enc_sk); + FILE* few = fopen(strEncFile.c_str(), "wb"); + if (few) { string h = HexEncode(enc_sk, 32); fwrite(h.c_str(), 1, h.size(), few); fclose(few); } + } + + fValid = true; + return true; + } + + string PubKeyHex() const { return HexEncode(xonly, 32); } + string EncPubHex() const { return HexEncode(enc_pk, 32); } + string BtfAddress() const { return btf::Address(xonly); } + + bool SignId(const unsigned char id[32], string& sigHexOut) + { + unsigned char sig[64]; + unsigned char aux[32]; + RAND_bytes(aux, sizeof(aux)); + if (!secp256k1_schnorrsig_sign32(ctx, sig, id, &keypair, aux)) + return false; + sigHexOut = HexEncode(sig, 64); + return true; + } +}; + +// The node's single Nostr/.btf identity, shared by the seeding thread here and +// the rendezvous transport in net.cpp. Lazily initialized under a lock; the +// secp256k1 context is immutable after Init, so concurrent use is safe. +static CNostrKey g_nostrKey; +static CCriticalSection cs_nostrKey; + +static bool EnsureNostrKey() +{ + CRITICAL_BLOCK(cs_nostrKey) + if (!g_nostrKey.fValid) + g_nostrKey.Init(); + return g_nostrKey.fValid; +} + +bool BtfGetIdentity(unsigned char pubkey[32], unsigned char enc_sk[32]) +{ + if (!EnsureNostrKey()) + return false; + memcpy(pubkey, g_nostrKey.xonly, 32); + memcpy(enc_sk, g_nostrKey.enc_sk, 32); + return true; +} + +std::string BtfLocalAddress() +{ + if (!EnsureNostrKey()) + return ""; + return g_nostrKey.BtfAddress(); +} + + +// Build and sign a Nostr event (NIP-01). Returns the event JSON. +static bool BuildSignedEvent(CNostrKey& key, int kind, const json& tags, + const string& content, json& eventOut) +{ + int64 created = GetTime(); + // Canonical serialization for the id: [0,pubkey,created_at,kind,tags,content] + json ser = json::array({ 0, key.PubKeyHex(), created, kind, tags, content }); + string s = ser.dump(); + + unsigned char id[32]; + SHA256((const unsigned char*)s.data(), s.size(), id); + + string sigHex; + if (!key.SignId(id, sigHex)) + return false; + + eventOut = json::object(); + eventOut["id"] = HexEncode(id, 32); + eventOut["pubkey"] = key.PubKeyHex(); + eventOut["created_at"] = created; + eventOut["kind"] = kind; + eventOut["tags"] = tags; + eventOut["content"] = content; + eventOut["sig"] = sigHex; + return true; +} + + +// +// Minimal WebSocket client over TLS (wss) or TCP (ws), blocking with timeout. +// +class CWebSocket +{ +public: + SOCKET hSocket; + SSL_CTX* sslctx; + SSL* ssl; + bool fTls; + string recvbuf; + + CWebSocket() : hSocket(INVALID_SOCKET), sslctx(NULL), ssl(NULL), fTls(false) {} + ~CWebSocket() { Close(); } + + void Close() + { + if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); ssl = NULL; } + if (sslctx) { SSL_CTX_free(sslctx); sslctx = NULL; } + if (hSocket != INVALID_SOCKET) { closesocket(hSocket); hSocket = INVALID_SOCKET; } + } + + int RawRead(char* buf, int len) + { + if (fTls) return SSL_read(ssl, buf, len); + return recv(hSocket, buf, len, 0); + } + + bool RawWrite(const char* buf, int len) + { + int off = 0; + while (off < len) + { + int n = fTls ? SSL_write(ssl, buf + off, len - off) + : send(hSocket, buf + off, len - off, 0); + if (n <= 0) return false; + off += n; + } + return true; + } + + // Connect and perform the WebSocket handshake. url = wss://host[:port]/path + bool Connect(const string& url, int timeoutSec) + { + string u = url; + int port = 443; + fTls = true; + if (u.compare(0, 6, "wss://") == 0) { u = u.substr(6); fTls = true; port = 443; } + else if (u.compare(0, 5, "ws://") == 0) { u = u.substr(5); fTls = false; port = 80; } + else return false; + + string host, path = "/"; + size_t slash = u.find('/'); + if (slash != string::npos) { host = u.substr(0, slash); path = u.substr(slash); } + else host = u; + size_t colon = host.find(':'); + if (colon != string::npos) { port = atoi(host.substr(colon + 1).c_str()); host = host.substr(0, colon); } + + // Resolve and connect (TCP) + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + char portstr[16]; sprintf(portstr, "%d", port); + if (getaddrinfo(host.c_str(), portstr, &hints, &res) != 0 || !res) + return error("Nostr: getaddrinfo %s failed", host.c_str()); + hSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); return false; } + + // Socket receive/send timeout: Windows takes a DWORD of milliseconds; + // POSIX takes a struct timeval. +#ifdef _WIN32 + DWORD tv = timeoutSec * 1000; + setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)); + setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv)); +#else + struct timeval tv; + tv.tv_sec = timeoutSec; + tv.tv_usec = 0; + setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)); + setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv)); +#endif + + if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0) + { + freeaddrinfo(res); + return error("Nostr: connect %s:%d failed", host.c_str(), port); + } + freeaddrinfo(res); + + // TLS + if (fTls) + { + sslctx = SSL_CTX_new(TLS_client_method()); + if (!sslctx) return false; + ssl = SSL_new(sslctx); + SSL_set_fd(ssl, (int)hSocket); + SSL_set_tlsext_host_name(ssl, host.c_str()); // SNI + if (SSL_connect(ssl) != 1) + return error("Nostr: TLS handshake with %s failed", host.c_str()); + } + + // WebSocket handshake (HTTP Upgrade) + unsigned char keybytes[16]; + RAND_bytes(keybytes, sizeof(keybytes)); + string wskey = Base64Encode(keybytes, sizeof(keybytes)); + string req = + "GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: " + wskey + "\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n"; + if (!RawWrite(req.data(), (int)req.size())) + return false; + + // Read the handshake response until \r\n\r\n + string resp; + char c; + while (resp.find("\r\n\r\n") == string::npos) + { + int n = RawRead(&c, 1); + if (n <= 0) return error("Nostr: no handshake response from %s", host.c_str()); + resp += c; + if (resp.size() > 4096) break; + } + if (resp.find(" 101 ") == string::npos) + return error("Nostr: handshake refused by %s", host.c_str()); + return true; + } + + // Send a text message (masked frame, as required by the protocol) + bool SendText(const string& msg) + { + string frame; + frame += (char)0x81; // FIN + opcode texto + size_t len = msg.size(); + if (len < 126) + frame += (char)(0x80 | len); + else if (len <= 0xffff) + { + frame += (char)(0x80 | 126); + frame += (char)((len >> 8) & 0xff); + frame += (char)(len & 0xff); + } + else + { + frame += (char)(0x80 | 127); + for (int i = 7; i >= 0; i--) frame += (char)((len >> (8 * i)) & 0xff); + } + unsigned char mask[4]; + RAND_bytes(mask, 4); + frame.append((char*)mask, 4); + string masked = msg; + for (size_t i = 0; i < masked.size(); i++) + masked[i] ^= mask[i & 3]; + frame += masked; + return RawWrite(frame.data(), (int)frame.size()); + } + + // Receive the next complete text message. Returns false on error/timeout. + bool RecvText(string& out) + { + out.clear(); + for (;;) + { + unsigned char hdr[2]; + if (!ReadN((char*)hdr, 2)) return false; + bool fin = hdr[0] & 0x80; + int opcode = hdr[0] & 0x0f; + bool masked = hdr[1] & 0x80; + uint64 len = hdr[1] & 0x7f; + if (len == 126) + { + unsigned char e[2]; + if (!ReadN((char*)e, 2)) return false; + len = (e[0] << 8) | e[1]; + } + else if (len == 127) + { + unsigned char e[8]; + if (!ReadN((char*)e, 8)) return false; + len = 0; + for (int i = 0; i < 8; i++) len = (len << 8) | e[i]; + } + unsigned char mask[4] = {0,0,0,0}; + if (masked && !ReadN((char*)mask, 4)) return false; + + string payload; + payload.resize((size_t)len); + if (len && !ReadN(&payload[0], (int)len)) return false; + if (masked) + for (size_t i = 0; i < payload.size(); i++) + payload[i] ^= mask[i & 3]; + + if (opcode == 0x8) return false; // close + if (opcode == 0x9) { SendPong(payload); continue; } // ping + if (opcode == 0xA) continue; // pong + out += payload; // text/continuation + if (fin) return true; + } + } + +private: + bool ReadN(char* buf, int n) + { + int off = 0; + while (off < n) + { + int r = RawRead(buf + off, n - off); + if (r <= 0) return false; + off += r; + } + return true; + } + + void SendPong(const string& payload) + { + string frame; + frame += (char)0x8A; + frame += (char)(0x80 | (payload.size() & 0x7f)); + unsigned char mask[4]; RAND_bytes(mask, 4); + frame.append((char*)mask, 4); + string m = payload; + for (size_t i = 0; i < m.size(); i++) m[i] ^= mask[i & 3]; + frame += m; + RawWrite(frame.data(), (int)frame.size()); + } +}; + + +// Handle a received event: extract "ip:port" and register the address. +static void HandlePeerEvent(const json& ev) +{ + if (!ev.contains("content")) return; + string content = ev["content"].get(); + // expected content: "ip:port" + CAddress addr(content.c_str()); + if (addr.ip == 0 || addr.ip == INADDR_NONE) + return; + if (addr.ip == addrLocalHost.ip && addr.port == addrLocalHost.port) + return; // not ourselves (same ip:port) + // Peer received from the relay (before AddAddress's routability filter). + printf("Nostr: peer received from relay: %s\n", addr.ToStringIPPort().c_str()); + CAddrDB addrdb; + if (AddAddress(addrdb, addr)) + printf("Nostr: new peer added %s\n", addr.ToStringIPPort().c_str()); +} + + +// Publish this node's self-certifying `.btf` descriptor: "reach me (this key) +// via , encrypting to my x25519 key ". Signed by the node +// key so only the address's owner can publish it (no hijacking). +static void PublishDescriptor(CWebSocket& ws, CNostrKey& key) +{ + // meeting_node is the rendezvous relay this node's hidden service + // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. + string meeting_node = strBtfMeetingRelay.empty() ? "rendezvous-pending" + : strBtfMeetingRelay; + string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), + meeting_node, (uint64_t)GetTime()); + if (desc.empty()) + return; + json tags = json::array({ json::array({ "d", BTF_DESC_DTAG }) }); + json ev; + if (BuildSignedEvent(key, BTF_DESC_KIND, tags, desc, ev)) + { + json pub = json::array({ "EVENT", ev }); + ws.SendText(pub.dump()); + } +} + +// Resolve a `.btf` address via a relay: fetch the descriptor published by that +// address's key and verify it self-certifies under the decoded pubkey. +static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, + btf::Descriptor& out) +{ + unsigned char pk[32]; + if (!btf::ParseAddress(btfAddr, pk)) + return false; + json filter = json::object(); + filter["authors"] = json::array({ HexEncode(pk, 32) }); + filter["kinds"] = json::array({ BTF_DESC_KIND }); + filter["limit"] = 1; + json req = json::array({ "REQ", "btf-resolve", filter }); + if (!ws.SendText(req.dump())) + return false; + for (;;) + { + string msg; + if (!ws.RecvText(msg)) + break; + json j; + try { j = json::parse(msg); } catch (...) { continue; } + if (!j.is_array() || j.empty()) continue; + string t = j[0].get(); + if (t == "EVENT" && j.size() >= 3) + { + const json& ev = j[2]; + if (ev.contains("content") && + btf::VerifyDescriptor(ctx, ev["content"].get(), pk, out)) + return true; + } + else if (t == "EOSE") + break; + } + return false; +} + +// Resolve a `.btf` address into its rendezvous coordinates by querying the +// public discovery relays for the owner's self-certified descriptor. Used by +// ConnectNodeBtf in net.cpp. +bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, + unsigned char enc_pub[32]) +{ + if (!EnsureNostrKey()) + return false; + for (int i = 0; i < nNostrRelays && !fShutdown; i++) + { + try + { + CWebSocket ws; + if (!ws.Connect(pszNostrRelays[i], 10)) + continue; + btf::Descriptor d; + if (!ResolveDescriptor(ws, g_nostrKey.ctx, btfAddr, d)) + continue; + if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") + continue; // descriptor predates the owner's relay config + if (!HexDecode(d.enc, enc_pub, 32)) + continue; + meetingHostPort = d.meeting_node; + return true; + } + CATCH_PRINT_EXCEPTION("BtfResolve") + } + return false; +} + + +// Connect to a relay: publish our address and collect the others'. +static bool SeedFromRelay(CNostrKey& key, const string& relay) +{ + CWebSocket ws; + if (!ws.Connect(relay, 10)) + return false; + printf("Nostr: connected to %s\n", relay.c_str()); + + // Publish our announcement (replaceable event with our ip:port) + if (addrLocalHost.ip != 0) + { + json tags = json::array({ + json::array({ "d", NOSTR_DTAG }), + json::array({ "t", NOSTR_DTAG }) + }); + json ev; + if (BuildSignedEvent(key, NOSTR_KIND, tags, addrLocalHost.ToStringIPPort(), ev)) + { + json pub = json::array({ "EVENT", ev }); + ws.SendText(pub.dump()); + } + } + + // Publish our self-certifying .btf descriptor (rendezvous discovery) + PublishDescriptor(ws, key); + + // Subscribe to receive the announcements of other nodes + json filter = json::object(); + filter["kinds"] = json::array({ NOSTR_KIND }); + filter["#d"] = json::array({ NOSTR_DTAG }); + filter["limit"] = 500; + json req = json::array({ "REQ", "bf-sub", filter }); + if (!ws.SendText(req.dump())) + return false; + + // Read responses until EOSE (end of stored events) or timeout + int nEvents = 0; + for (;;) + { + string msg; + if (!ws.RecvText(msg)) + break; // timeout or closed connection + json j; + try { j = json::parse(msg); } catch (...) { continue; } + if (!j.is_array() || j.empty()) continue; + string type = j[0].get(); + if (type == "EVENT" && j.size() >= 3) + { + HandlePeerEvent(j[2]); + if (++nEvents > 1000) break; + } + else if (type == "EOSE") + { + // We received all stored events; close this relay. + break; + } + else if (type == "NOTICE" || type == "OK" || type == "CLOSED") + { + // informational + } + } + printf("Nostr: %s returned %d events\n", relay.c_str(), nEvents); + + // Discover other nodes' .btf descriptors so we can auto-connect anonymously + // (no manual /connectbtf). Each node publishes one addressable descriptor + // (kind 38501, d-tag btf-descriptor); we learn every peer's .btf address by + // deriving it from the event author. Reachability is verified later, when + // the rendezvous tunnel boxes to that key's static x25519 key. + ws.SendText(json::array({ "CLOSE", "bf-sub" }).dump()); + { + json dfilter = json::object(); + dfilter["kinds"] = json::array({ BTF_DESC_KIND }); + dfilter["#d"] = json::array({ BTF_DESC_DTAG }); + dfilter["limit"] = 200; + json dreq = json::array({ "REQ", "btf-disc", dfilter }); + int nPeers = 0; + if (ws.SendText(dreq.dump())) + { + for (;;) + { + string msg; + if (!ws.RecvText(msg)) + break; + json j; + try { j = json::parse(msg); } catch (...) { continue; } + if (!j.is_array() || j.empty()) continue; + string type = j[0].get(); + if (type == "EVENT" && j.size() >= 3) + { + const json& ev = j[2]; + if (!ev.contains("pubkey") || !ev.contains("kind")) continue; + if (ev["kind"].get() != BTF_DESC_KIND) continue; + unsigned char pkb[32]; + if (!HexDecode(ev["pubkey"].get(), pkb, 32)) continue; + if (memcmp(pkb, key.xonly, 32) == 0) continue; // not ourselves + string peerAddr = btf::Address(pkb); + bool fNew = false; + CRITICAL_BLOCK(cs_btfPeers) + fNew = g_btfPeers.insert(peerAddr).second; + if (fNew) + printf("Nostr: discovered .btf peer %s\n", peerAddr.c_str()); + if (++nPeers > 500) break; + } + else if (type == "EOSE") + break; + } + } + } + return true; +} + + +// Per-peer backoff: don't redial a .btf that just failed until this time. Keeps +// dead/stale descriptors (e.g. abandoned test identities that linger on the +// relays) from consuming the whole per-pass dial budget and starving the live +// peers. Backoff grows on repeated failures, capped. +static map g_btfPeerBackoff; // addr -> next-try time +static map g_btfPeerFails; // addr -> consecutive failures + +// Dial the .btf peers discovered on the relays, up to a target connection count. +// Mirrors ThreadOpenConnections' ref handling: a kept connection holds one ref +// (owned by fNetworkNode, released on disconnect); a redundant one is released. +static void ConnectDiscoveredBtfPeers() +{ + vector peers; + CRITICAL_BLOCK(cs_btfPeers) + peers.assign(g_btfPeers.begin(), g_btfPeers.end()); + + // Shuffle: the candidate set is sorted, so without this a handful of dead + // peers at the front would be retried every pass and the live peers further + // down the list would never be reached within the per-pass dial budget. + for (size_t i = peers.size(); i > 1; i--) + swap(peers[i - 1], peers[(size_t)GetRand(i)]); + + int64 now = GetTime(); + int nDials = 0; + foreach(const string& addr, peers) + { + if (fShutdown) break; + if (vNodes.size() >= BTF_TARGET_CONN) break; + if (nDials >= BTF_DIALS_PER_PASS) break; + + // Skip peers still in backoff from a recent failure. + map::iterator bi = g_btfPeerBackoff.find(addr); + if (bi != g_btfPeerBackoff.end() && now < bi->second) + continue; + + CNode* pnode = ConnectNodeBtf(addr); + if (pnode) + { + if (!pnode->fNetworkNode) + pnode->fNetworkNode = true; // keep the ref from ConnectNodeBtf + else + pnode->Release(); // already connected; drop the extra ref + g_btfPeerBackoff.erase(addr); // reachable again + g_btfPeerFails.erase(addr); + } + else + { + // Exponential-ish backoff: 1,2,4,... minutes, capped at 30. + int n = ++g_btfPeerFails[addr]; + int64 mins = 1; + for (int k = 1; k < n && mins < 30; k++) mins *= 2; + if (mins > 30) mins = 30; + g_btfPeerBackoff[addr] = now + mins * 60; + nDials++; // only count real (failed) dial attempts against the budget + } + } +} + + +void ThreadNostrSeed(void* parg) +{ + printf("ThreadNostrSeed started\n"); + + if (!EnsureNostrKey()) + { + printf("Nostr: failed to initialize secp256k1 key\n"); + return; + } + CNostrKey& key = g_nostrKey; + printf("Nostr: node pubkey = %s\n", key.PubKeyHex().c_str()); + printf("Nostr: node .btf address = %s\n", key.BtfAddress().c_str()); + + // One-time self-test of the rendezvous: publish our descriptor to a relay, + // then resolve our OWN .btf address back and verify it self-certifies. Proves + // the .btf publish/resolve/verify chain works against real public relays. + for (int i = 0; i < nNostrRelays && !fShutdown; i++) + { + try + { + CWebSocket ws; + if (!ws.Connect(pszNostrRelays[i], 10)) continue; + PublishDescriptor(ws, key); + Sleep(700); // let the relay store the replaceable event + btf::Descriptor d; + if (ResolveDescriptor(ws, key.ctx, key.BtfAddress(), d)) + { + printf("Nostr: .btf self-resolve OK via %s (meeting_node=%s, enc=%s...)\n", + pszNostrRelays[i], d.meeting_node.c_str(), d.enc.substr(0, 16).c_str()); + break; + } + } + CATCH_PRINT_EXCEPTION("btf self-test") + } + + // OpenSSL is already initialized by the rest of the app; TLS_client_method is enough. + loop + { + for (int i = 0; i < nNostrRelays; i++) + { + if (fShutdown) return; + try + { + SeedFromRelay(key, pszNostrRelays[i]); + } + CATCH_PRINT_EXCEPTION("SeedFromRelay") + } + + // Auto-connect to discovered .btf peers over the rendezvous relay -- the + // anonymous, CGNAT-friendly replacement for the old IP-based connect. No + // manual /connectbtf needed: nodes find each other purely via Nostr. + try + { + ConnectDiscoveredBtfPeers(); + } + CATCH_PRINT_EXCEPTION("ConnectDiscoveredBtfPeers") + + // Re-announce/re-discover periodically (the replaceable event keeps the + // relay up to date and picks up nodes that joined after us) + for (int i = 0; i < 60 && !fShutdown; i++) + Sleep(1000); + } +} diff --git a/src/nostr.h b/src/nostr.h new file mode 100644 index 0000000..5ec9f65 --- /dev/null +++ b/src/nostr.h @@ -0,0 +1,39 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Peer discovery over Nostr relays -- replaces the old ThreadIRCSeed. Each +// node publishes a replaceable event (NIP-78, kind 30078) containing its +// "ip:port", signed with Schnorr/secp256k1 (BIP340, the same scheme used by +// Nostr), and subscribes to receive the announcements of other nodes, feeding +// them into the Bitflash address manager. + +#ifndef BITFLASH_NOSTR_H +#define BITFLASH_NOSTR_H + +#include + +// Thread entry point (signature compatible with _beginthread) +void ThreadNostrSeed(void* parg); + +// Public relays used for discovery. Tunable. +extern const char* pszNostrRelays[]; +extern const int nNostrRelays; + +// Rendezvous meeting relay ("host:port") this node registers at and advertises +// in its .btf descriptor. Overridable with /rvrelay=host:port. +extern std::string strBtfMeetingRelay; + +// Copy this node's .btf identity (lazily loading or generating it): the x-only +// pubkey (= the .btf address) and the x25519 secret for the end-to-end channel. +bool BtfGetIdentity(unsigned char pubkey[32], unsigned char enc_sk[32]); + +// This node's printable .btf address, or "" if the identity failed to load. +std::string BtfLocalAddress(); + +// Resolve a .btf address via the public discovery relays: fetch the owner's +// self-certified descriptor and return its meeting node ("host:port") and +// x25519 public key for the end-to-end channel. +bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, + unsigned char enc_pub[32]); + +#endif diff --git a/src/randomx_pow.cpp b/src/randomx_pow.cpp new file mode 100644 index 0000000..17bf455 --- /dev/null +++ b/src/randomx_pow.cpp @@ -0,0 +1,143 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// RandomX PoW implementation. See randomx_pow.h. + +#include "headers.h" +#include +#include +#include + +// Fixed key (seed) that determines the RandomX cache/dataset. Keeping it +// constant avoids per-epoch "reseed" logic; for the CPU+RAM fairness goal +// this is sufficient (RandomX already neutralizes GPU/ASIC by design). +static const char* RANDOMX_KEY = "Bitflash/RandomX/v1/one-cpu-one-vote"; + +static randomx_flags g_flags = RANDOMX_FLAG_DEFAULT; +static randomx_cache* g_cache = NULL; +static randomx_dataset* g_dataset = NULL; +static randomx_vm* g_vmVerify = NULL; +static CCriticalSection g_csVerify; +static bool g_fInit = false; +static bool g_fFast = false; + + +bool RandomXInit() +{ + if (g_fInit) + return true; + + // Detect the best flags for this CPU (JIT, hardware AES, Argon2). + g_flags = randomx_get_flags(); + + g_cache = randomx_alloc_cache(g_flags); + if (!g_cache) + { + // Try without JIT as a fallback + g_flags = RANDOMX_FLAG_DEFAULT; + g_cache = randomx_alloc_cache(g_flags); + if (!g_cache) + return error("RandomX: failed to allocate cache (256 MB)"); + } + randomx_init_cache(g_cache, RANDOMX_KEY, strlen(RANDOMX_KEY)); + + // Verification VM in light mode (cache only) + g_vmVerify = randomx_create_vm(g_flags, g_cache, NULL); + if (!g_vmVerify) + return error("RandomX: failed to create verification VM"); + + g_fInit = true; + printf("RandomX: initialized (cache 256 MB, flags=%d)\n", (int)g_flags); + return true; +} + + +bool RandomXInitDataset(int nThreads) +{ + if (!g_fInit && !RandomXInit()) + return false; + if (g_fFast) + return true; + + randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM); + g_dataset = randomx_alloc_dataset(fastFlags); + if (!g_dataset) + { + printf("RandomX: not enough memory for the 2 GB dataset, staying in light mode\n"); + return false; + } + + unsigned long total = randomx_dataset_item_count(); + if (nThreads < 1) nThreads = 1; + printf("RandomX: initializing ~2 GB dataset with %d threads...\n", nThreads); + + std::vector workers; + unsigned long per = total / nThreads; + for (int i = 0; i < nThreads; i++) + { + unsigned long start = i * per; + unsigned long count = (i == nThreads - 1) ? (total - start) : per; + workers.emplace_back([start, count]() { + randomx_init_dataset(g_dataset, g_cache, start, count); + }); + } + for (auto& w : workers) w.join(); + + g_fFast = true; + printf("RandomX: dataset ready (fast mode active)\n"); + return true; +} + + +bool RandomXFastReady() +{ + return g_fFast; +} + + +uint256 RandomXPoWHash(const void* pHeader, size_t nSize) +{ + uint256 result = 0; + if (!g_fInit && !RandomXInit()) + return result; + CRITICAL_BLOCK(g_csVerify) + { + unsigned char hash[RANDOMX_HASH_SIZE]; + randomx_calculate_hash(g_vmVerify, pHeader, nSize, hash); + memcpy(&result, hash, RANDOMX_HASH_SIZE); + } + return result; +} + + +void* RandomXCreateMinerVM() +{ + if (!g_fInit && !RandomXInit()) + return NULL; + if (g_fFast && g_dataset) + { + randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM); + return randomx_create_vm(fastFlags, NULL, g_dataset); + } + // Light mode: own VM sharing the cache + return randomx_create_vm(g_flags, g_cache, NULL); +} + + +void RandomXDestroyMinerVM(void* vm) +{ + if (vm) + randomx_destroy_vm((randomx_vm*)vm); +} + + +uint256 RandomXHashWithVM(void* vm, const void* pHeader, size_t nSize) +{ + uint256 result = 0; + if (!vm) + return result; + unsigned char hash[RANDOMX_HASH_SIZE]; + randomx_calculate_hash((randomx_vm*)vm, pHeader, nSize, hash); + memcpy(&result, hash, RANDOMX_HASH_SIZE); + return result; +} diff --git a/src/randomx_pow.h b/src/randomx_pow.h new file mode 100644 index 0000000..0027571 --- /dev/null +++ b/src/randomx_pow.h @@ -0,0 +1,44 @@ +// Copyright (c) 2026 Bitflash developers +// Distributed under the MIT/X11 software license. +// +// Memory-hard Proof of Work (CPU + RAM) via RandomX -- replaces SHA-256d as +// the PoW algorithm. The block IDENTITY hash stays SHA-256d (GetHash), +// preserving all indexing; what changes is the PROOF-OF-WORK hash +// (GetPoWHash), computed with RandomX. +// +// Modes: +// - Verification (all nodes): 256 MB cache, "light" VM. Cheap. +// - Mining (optional, fast): ~2 GB dataset, "full" VMs. Fast. +// GPUs and ASICs are neutralized by RandomX's memory cost/latency. + +#ifndef BITFLASH_RANDOMX_POW_H +#define BITFLASH_RANDOMX_POW_H + +// uint256 comes from uint256.h, already included by headers.h before this file. + +// Initialize the cache (256 MB) and the verification VM. Call once at startup. +// Returns false if allocation fails. +bool RandomXInit(); + +// Allocate and initialize the ~2 GB dataset for fast mining (fast mode), +// using nThreads to speed it up. Idempotent. Returns false on failure (the +// miner then falls back to light mode automatically). +bool RandomXInitDataset(int nThreads); + +// PoW hash of an 80-byte header, using the verification VM (thread-safe, +// guarded by a critical section). +uint256 RandomXPoWHash(const void* pHeader, size_t nSize); + +// Create a VM for a mining thread (own VM, not shared). Uses the dataset +// (fast) if available, otherwise the cache (light). Release with +// RandomXDestroyMinerVM. Returns NULL on failure. +void* RandomXCreateMinerVM(); +void RandomXDestroyMinerVM(void* vm); + +// Compute a PoW hash on an owned mining VM (not guarded). +uint256 RandomXHashWithVM(void* vm, const void* pHeader, size_t nSize); + +// True when the 2 GB dataset is ready (fast mode active). +bool RandomXFastReady(); + +#endif diff --git a/src/rc/addressbook16.bmp b/src/rc/addressbook16.bmp new file mode 100644 index 0000000..c557691 Binary files /dev/null and b/src/rc/addressbook16.bmp differ diff --git a/src/rc/addressbook16mask.bmp b/src/rc/addressbook16mask.bmp new file mode 100644 index 0000000..d3a478d Binary files /dev/null and b/src/rc/addressbook16mask.bmp differ diff --git a/src/rc/addressbook20.bmp b/src/rc/addressbook20.bmp new file mode 100644 index 0000000..2b33b22 Binary files /dev/null and b/src/rc/addressbook20.bmp differ diff --git a/src/rc/addressbook20mask.bmp b/src/rc/addressbook20mask.bmp new file mode 100644 index 0000000..56ce612 Binary files /dev/null and b/src/rc/addressbook20mask.bmp differ diff --git a/src/rc/bitcoin.ico b/src/rc/bitcoin.ico new file mode 100644 index 0000000..b768077 Binary files /dev/null and b/src/rc/bitcoin.ico differ diff --git a/src/rc/bitcoin_bc_original.ico b/src/rc/bitcoin_bc_original.ico new file mode 100644 index 0000000..88cc240 Binary files /dev/null and b/src/rc/bitcoin_bc_original.ico differ diff --git a/src/rc/check.ico b/src/rc/check.ico new file mode 100644 index 0000000..0c4e6e8 Binary files /dev/null and b/src/rc/check.ico differ diff --git a/src/rc/send16.bmp b/src/rc/send16.bmp new file mode 100644 index 0000000..676b5c4 Binary files /dev/null and b/src/rc/send16.bmp differ diff --git a/src/rc/send16mask.bmp b/src/rc/send16mask.bmp new file mode 100644 index 0000000..06c747f Binary files /dev/null and b/src/rc/send16mask.bmp differ diff --git a/src/rc/send16masknoshadow.bmp b/src/rc/send16masknoshadow.bmp new file mode 100644 index 0000000..faf24e0 Binary files /dev/null and b/src/rc/send16masknoshadow.bmp differ diff --git a/src/rc/send20.bmp b/src/rc/send20.bmp new file mode 100644 index 0000000..2b90422 Binary files /dev/null and b/src/rc/send20.bmp differ diff --git a/src/rc/send20mask.bmp b/src/rc/send20mask.bmp new file mode 100644 index 0000000..f124d0d Binary files /dev/null and b/src/rc/send20mask.bmp differ diff --git a/src/readme.txt b/src/readme.txt new file mode 100644 index 0000000..c72fcec --- /dev/null +++ b/src/readme.txt @@ -0,0 +1,54 @@ +Bitflash (BTF) v0.1 ALPHA +========================= + +A revival of Satoshi Nakamoto's original Bitcoin v0.1 client (2009), ported to +a modern toolchain and turned into its own Layer 1 network. + +Philosophy: fair launch, no pre-mine, no ICO. One CPU, one vote. + +Based on code Copyright (c) 2009 Satoshi Nakamoto. +Copyright (c) 2026 The Bitflash developers. +Distributed under the MIT/X11 software license, see the accompanying +file license.txt or http://www.opensource.org/licenses/mit-license.php. +This product includes software developed by the OpenSSL Project. + + +Differences from the original Bitcoin +------------------------------------- +- Own genesis block (22/Jul/2026 headline, independent chain) +- Own magic bytes (0xbf 0x1a 0x5c 0xfd) and P2P port 8433 +- Addresses start with "B" (ADDRESSVERSION = 25) +- Consensus fixes applied: value overflow (CVE-2010-5139), duplicate inputs, + money range checks (MoneyRange) +- IRC peer discovery replaced by Nostr relay discovery +- Memory-hard Proof of Work (RandomX, CPU + RAM) instead of SHA-256d +- Economics identical to Bitcoin: 50 BTF/block, halving every 210,000 blocks, + 21 million total + + +Build (Windows, MSYS2 UCRT64) +----------------------------- +Modern toolchain: + g++ 16 (MinGW-w64 UCRT), wxWidgets 3.2, OpenSSL 3, Berkeley DB 6, Boost, + libsecp256k1 (Schnorr), nlohmann/json, RandomX + +Bundled dependencies are built from source into ../deps and ../RandomX. +In the MSYS2 UCRT64 shell: + bash build.sh (uses src/makefile.mingw) + +The resulting executable is src/bitflash.exe. + + +Command-line options (in addition to the original ones) +------------------------------------------------------- + /port=N listen on P2P port N (default 8433) + /solomine mine without requiring a connected peer (local testing) + + +Roadmap +------- +Milestone 0 Compile the original client on a modern toolchain [done] +Milestone 1 Bitflash identity + genesis + consensus fixes [done] +Milestone 2 Peer discovery over Nostr relays [done] +Milestone 3 Memory-hard PoW (RandomX) instead of SHA-256d [done] +Milestone 4 Public testnet diff --git a/src/script.cpp b/src/script.cpp new file mode 100644 index 0000000..d2111ed --- /dev/null +++ b/src/script.cpp @@ -0,0 +1,1127 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + +bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + + + +typedef vector valtype; +static const valtype vchFalse(0); +static const valtype vchZero(0); +static const valtype vchTrue(1, 1); +static const CBigNum bnZero(0); +static const CBigNum bnOne(1); +static const CBigNum bnFalse(0); +static const CBigNum bnTrue(1); + + +bool CastToBool(const valtype& vch) +{ + return (CBigNum(vch) != bnZero); +} + +void MakeSameSize(valtype& vch1, valtype& vch2) +{ + // Lengthen the shorter one + if (vch1.size() < vch2.size()) + vch1.resize(vch2.size(), 0); + if (vch2.size() < vch1.size()) + vch2.resize(vch1.size(), 0); +} + + + +// +// Script is a stack machine (like Forth) that evaluates a predicate +// returning a bool indicating valid or not. There are no loops. +// +#define stacktop(i) (stack.at(stack.size()+(i))) +#define altstacktop(i) (altstack.at(altstack.size()+(i))) + +bool EvalScript(const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, + vector >* pvStackRet) +{ + CAutoBN_CTX pctx; + CScript::const_iterator pc = script.begin(); + CScript::const_iterator pend = script.end(); + CScript::const_iterator pbegincodehash = script.begin(); + vector vfExec; + vector stack; + vector altstack; + if (pvStackRet) + pvStackRet->clear(); + + + while (pc < pend) + { + bool fExec = !count(vfExec.begin(), vfExec.end(), false); + + // + // Read instruction + // + opcodetype opcode; + valtype vchPushValue; + if (!script.GetOp(pc, opcode, vchPushValue)) + return false; + + if (fExec && opcode <= OP_PUSHDATA4) + stack.push_back(vchPushValue); + else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) + switch (opcode) + { + // + // Push value + // + case OP_1NEGATE: + case OP_1: + case OP_2: + case OP_3: + case OP_4: + case OP_5: + case OP_6: + case OP_7: + case OP_8: + case OP_9: + case OP_10: + case OP_11: + case OP_12: + case OP_13: + case OP_14: + case OP_15: + case OP_16: + { + // ( -- value) + CBigNum bn((int)opcode - (int)(OP_1 - 1)); + stack.push_back(bn.getvch()); + } + break; + + + // + // Control + // + case OP_NOP: + break; + + case OP_VER: + { + CBigNum bn(VERSION); + stack.push_back(bn.getvch()); + } + break; + + case OP_IF: + case OP_NOTIF: + case OP_VERIF: + case OP_VERNOTIF: + { + // if [statements] [else [statements]] endif + bool fValue = false; + if (fExec) + { + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + if (opcode == OP_VERIF || opcode == OP_VERNOTIF) + fValue = (CBigNum(VERSION) >= CBigNum(vch)); + else + fValue = CastToBool(vch); + if (opcode == OP_NOTIF || opcode == OP_VERNOTIF) + fValue = !fValue; + stack.pop_back(); + } + vfExec.push_back(fValue); + } + break; + + case OP_ELSE: + { + if (vfExec.empty()) + return false; + vfExec.back() = !vfExec.back(); + } + break; + + case OP_ENDIF: + { + if (vfExec.empty()) + return false; + vfExec.pop_back(); + } + break; + + case OP_VERIFY: + { + // (true -- ) or + // (false -- false) and return + if (stack.size() < 1) + return false; + bool fValue = CastToBool(stacktop(-1)); + if (fValue) + stack.pop_back(); + else + pc = pend; + } + break; + + case OP_RETURN: + { + pc = pend; + } + break; + + + // + // Stack ops + // + case OP_TOALTSTACK: + { + if (stack.size() < 1) + return false; + altstack.push_back(stacktop(-1)); + stack.pop_back(); + } + break; + + case OP_FROMALTSTACK: + { + if (altstack.size() < 1) + return false; + stack.push_back(altstacktop(-1)); + altstack.pop_back(); + } + break; + + case OP_2DROP: + { + // (x1 x2 -- ) + stack.pop_back(); + stack.pop_back(); + } + break; + + case OP_2DUP: + { + // (x1 x2 -- x1 x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch1 = stacktop(-2); + valtype vch2 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_3DUP: + { + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack.size() < 3) + return false; + valtype vch1 = stacktop(-3); + valtype vch2 = stacktop(-2); + valtype vch3 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + stack.push_back(vch3); + } + break; + + case OP_2OVER: + { + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack.size() < 4) + return false; + valtype vch1 = stacktop(-4); + valtype vch2 = stacktop(-3); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2ROT: + { + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (stack.size() < 6) + return false; + valtype vch1 = stacktop(-6); + valtype vch2 = stacktop(-5); + stack.erase(stack.end()-6, stack.end()-4); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2SWAP: + { + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + if (stack.size() < 4) + return false; + swap(stacktop(-4), stacktop(-2)); + swap(stacktop(-3), stacktop(-1)); + } + break; + + case OP_IFDUP: + { + // (x - 0 | x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + if (CastToBool(vch)) + stack.push_back(vch); + } + break; + + case OP_DEPTH: + { + // -- stacksize + CBigNum bn(stack.size()); + stack.push_back(bn.getvch()); + } + break; + + case OP_DROP: + { + // (x -- ) + if (stack.size() < 1) + return false; + stack.pop_back(); + } + break; + + case OP_DUP: + { + // (x -- x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + stack.push_back(vch); + } + break; + + case OP_NIP: + { + // (x1 x2 -- x2) + if (stack.size() < 2) + return false; + stack.erase(stack.end() - 2); + } + break; + + case OP_OVER: + { + // (x1 x2 -- x1 x2 x1) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-2); + stack.push_back(vch); + } + break; + + case OP_PICK: + case OP_ROLL: + { + // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) + // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) + if (stack.size() < 2) + return false; + int n = CBigNum(stacktop(-1)).getint(); + stack.pop_back(); + if (n < 0 || n >= stack.size()) + return false; + valtype vch = stacktop(-n-1); + if (opcode == OP_ROLL) + stack.erase(stack.end()-n-1); + stack.push_back(vch); + } + break; + + case OP_ROT: + { + // (x1 x2 x3 -- x2 x3 x1) + // x2 x1 x3 after first swap + // x2 x3 x1 after second swap + if (stack.size() < 3) + return false; + swap(stacktop(-3), stacktop(-2)); + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_SWAP: + { + // (x1 x2 -- x2 x1) + if (stack.size() < 2) + return false; + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_TUCK: + { + // (x1 x2 -- x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-1); + stack.insert(stack.end()-2, vch); + } + break; + + + // + // Splice ops + // + case OP_CAT: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + vch1.insert(vch1.end(), vch2.begin(), vch2.end()); + stack.pop_back(); + } + break; + + case OP_SUBSTR: + { + // (in begin size -- out) + if (stack.size() < 3) + return false; + valtype& vch = stacktop(-3); + int nBegin = CBigNum(stacktop(-2)).getint(); + int nEnd = nBegin + CBigNum(stacktop(-1)).getint(); + if (nBegin < 0 || nEnd < nBegin) + return false; + if (nBegin > vch.size()) + nBegin = vch.size(); + if (nEnd > vch.size()) + nEnd = vch.size(); + vch.erase(vch.begin() + nEnd, vch.end()); + vch.erase(vch.begin(), vch.begin() + nBegin); + stack.pop_back(); + stack.pop_back(); + } + break; + + case OP_LEFT: + case OP_RIGHT: + { + // (in size -- out) + if (stack.size() < 2) + return false; + valtype& vch = stacktop(-2); + int nSize = CBigNum(stacktop(-1)).getint(); + if (nSize < 0) + return false; + if (nSize > vch.size()) + nSize = vch.size(); + if (opcode == OP_LEFT) + vch.erase(vch.begin() + nSize, vch.end()); + else + vch.erase(vch.begin(), vch.end() - nSize); + stack.pop_back(); + } + break; + + case OP_SIZE: + { + // (in -- in size) + if (stack.size() < 1) + return false; + CBigNum bn(stacktop(-1).size()); + stack.push_back(bn.getvch()); + } + break; + + + // + // Bitwise logic + // + case OP_INVERT: + { + // (in - out) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + for (int i = 0; i < vch.size(); i++) + vch[i] = ~vch[i]; + } + break; + + case OP_AND: + case OP_OR: + case OP_XOR: + { + // (x1 x2 - out) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + MakeSameSize(vch1, vch2); + if (opcode == OP_AND) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] &= vch2[i]; + } + else if (opcode == OP_OR) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] |= vch2[i]; + } + else if (opcode == OP_XOR) + { + for (int i = 0; i < vch1.size(); i++) + vch1[i] ^= vch2[i]; + } + stack.pop_back(); + } + break; + + case OP_EQUAL: + case OP_EQUALVERIFY: + //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL + { + // (x1 x2 - bool) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + bool fEqual = (vch1 == vch2); + // OP_NOTEQUAL is disabled because it would be too easy to say + // something like n != 1 and have some wiseguy pass in 1 with extra + // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) + //if (opcode == OP_NOTEQUAL) + // fEqual = !fEqual; + stack.pop_back(); + stack.pop_back(); + stack.push_back(fEqual ? vchTrue : vchFalse); + if (opcode == OP_EQUALVERIFY) + { + if (fEqual) + stack.pop_back(); + else + pc = pend; + } + } + break; + + + // + // Numeric + // + case OP_1ADD: + case OP_1SUB: + case OP_2MUL: + case OP_2DIV: + case OP_NEGATE: + case OP_ABS: + case OP_NOT: + case OP_0NOTEQUAL: + { + // (in -- out) + if (stack.size() < 1) + return false; + CBigNum bn(stacktop(-1)); + switch (opcode) + { + case OP_1ADD: bn += bnOne; break; + case OP_1SUB: bn -= bnOne; break; + case OP_2MUL: bn <<= 1; break; + case OP_2DIV: bn >>= 1; break; + case OP_NEGATE: bn = -bn; break; + case OP_ABS: if (bn < bnZero) bn = -bn; break; + case OP_NOT: bn = (bn == bnZero); break; + case OP_0NOTEQUAL: bn = (bn != bnZero); break; + } + stack.pop_back(); + stack.push_back(bn.getvch()); + } + break; + + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_MOD: + case OP_LSHIFT: + case OP_RSHIFT: + case OP_BOOLAND: + case OP_BOOLOR: + case OP_NUMEQUAL: + case OP_NUMEQUALVERIFY: + case OP_NUMNOTEQUAL: + case OP_LESSTHAN: + case OP_GREATERTHAN: + case OP_LESSTHANOREQUAL: + case OP_GREATERTHANOREQUAL: + case OP_MIN: + case OP_MAX: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + CBigNum bn1(stacktop(-2)); + CBigNum bn2(stacktop(-1)); + CBigNum bn; + switch (opcode) + { + case OP_ADD: + bn = bn1 + bn2; + break; + + case OP_SUB: + bn = bn1 - bn2; + break; + + case OP_MUL: + if (!BN_mul(bn.get(), bn1.get(), bn2.get(), pctx)) + return false; + break; + + case OP_DIV: + if (!BN_div(bn.get(), NULL, bn1.get(), bn2.get(), pctx)) + return false; + break; + + case OP_MOD: + if (!BN_mod(bn.get(), bn1.get(), bn2.get(), pctx)) + return false; + break; + + case OP_LSHIFT: + if (bn2 < bnZero) + return false; + bn = bn1 << bn2.getulong(); + break; + + case OP_RSHIFT: + if (bn2 < bnZero) + return false; + bn = bn1 >> bn2.getulong(); + break; + + case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; + case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; + case OP_NUMEQUAL: bn = (bn1 == bn2); break; + case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; + case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; + case OP_LESSTHAN: bn = (bn1 < bn2); break; + case OP_GREATERTHAN: bn = (bn1 > bn2); break; + case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; + case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; + case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; + case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; + } + stack.pop_back(); + stack.pop_back(); + stack.push_back(bn.getvch()); + + if (opcode == OP_NUMEQUALVERIFY) + { + if (CastToBool(stacktop(-1))) + stack.pop_back(); + else + pc = pend; + } + } + break; + + case OP_WITHIN: + { + // (x min max -- out) + if (stack.size() < 3) + return false; + CBigNum bn1(stacktop(-3)); + CBigNum bn2(stacktop(-2)); + CBigNum bn3(stacktop(-1)); + bool fValue = (bn2 <= bn1 && bn1 < bn3); + stack.pop_back(); + stack.pop_back(); + stack.pop_back(); + stack.push_back(fValue ? vchTrue : vchFalse); + } + break; + + + // + // Crypto + // + case OP_RIPEMD160: + case OP_SHA1: + case OP_SHA256: + case OP_HASH160: + case OP_HASH256: + { + // (in -- hash) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + valtype vchHash(opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160 ? 20 : 32); + if (opcode == OP_RIPEMD160) + RIPEMD160(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA1) + SHA1(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA256) + SHA256(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_HASH160) + { + uint160 hash160 = Hash160(vch); + memcpy(&vchHash[0], &hash160, sizeof(hash160)); + } + else if (opcode == OP_HASH256) + { + uint256 hash = Hash(vch.begin(), vch.end()); + memcpy(&vchHash[0], &hash, sizeof(hash)); + } + stack.pop_back(); + stack.push_back(vchHash); + } + break; + + case OP_CODESEPARATOR: + { + // Hash starts after the code separator + pbegincodehash = pc; + } + break; + + case OP_CHECKSIG: + case OP_CHECKSIGVERIFY: + { + // (sig pubkey -- bool) + if (stack.size() < 2) + return false; + + valtype& vchSig = stacktop(-2); + valtype& vchPubKey = stacktop(-1); + + ////// debug print + //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); + //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signature, since there's no way for a signature to sign itself + scriptCode.FindAndDelete(CScript(vchSig)); + + bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + + stack.pop_back(); + stack.pop_back(); + stack.push_back(fSuccess ? vchTrue : vchFalse); + if (opcode == OP_CHECKSIGVERIFY) + { + if (fSuccess) + stack.pop_back(); + else + pc = pend; + } + } + break; + + case OP_CHECKMULTISIG: + case OP_CHECKMULTISIGVERIFY: + { + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + + int i = 1; + if (stack.size() < i) + return false; + + int nKeysCount = CBigNum(stacktop(-i)).getint(); + if (nKeysCount < 0) + return false; + int ikey = ++i; + i += nKeysCount; + if (stack.size() < i) + return false; + + int nSigsCount = CBigNum(stacktop(-i)).getint(); + if (nSigsCount < 0 || nSigsCount > nKeysCount) + return false; + int isig = ++i; + i += nSigsCount; + if (stack.size() < i) + return false; + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signatures, since there's no way for a signature to sign itself + for (int i = 0; i < nSigsCount; i++) + { + valtype& vchSig = stacktop(-isig-i); + scriptCode.FindAndDelete(CScript(vchSig)); + } + + bool fSuccess = true; + while (fSuccess && nSigsCount > 0) + { + valtype& vchSig = stacktop(-isig); + valtype& vchPubKey = stacktop(-ikey); + + // Check signature + if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType)) + { + isig++; + nSigsCount--; + } + ikey++; + nKeysCount--; + + // If there are more signatures left than keys left, + // then too many signatures have failed + if (nSigsCount > nKeysCount) + fSuccess = false; + } + + while (i-- > 0) + stack.pop_back(); + stack.push_back(fSuccess ? vchTrue : vchFalse); + + if (opcode == OP_CHECKMULTISIGVERIFY) + { + if (fSuccess) + stack.pop_back(); + else + pc = pend; + } + } + break; + + default: + return false; + } + } + + + if (pvStackRet) + *pvStackRet = stack; + return (stack.empty() ? false : CastToBool(stack.back())); +} + +#undef top + + + + + + + + + +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) + { + printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; + } + CTransaction txTmp(txTo); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + + // Blank out other inputs' signatures + for (int i = 0; i < txTmp.vin.size(); i++) + txTmp.vin[i].scriptSig = CScript(); + txTmp.vin[nIn].scriptSig = scriptCode; + + // Blank out some of the outputs + if ((nHashType & 0x1f) == SIGHASH_NONE) + { + // Wildcard payee + txTmp.vout.clear(); + + // Let the others update at will + for (int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lockin the txout payee at same index as txin + unsigned int nOut = nIn; + if (nOut >= txTmp.vout.size()) + { + printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + return 1; + } + txTmp.vout.resize(nOut+1); + for (int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + + // Blank out other inputs completely, not recommended for open transactions + if (nHashType & SIGHASH_ANYONECANPAY) + { + txTmp.vin[0] = txTmp.vin[nIn]; + txTmp.vin.resize(1); + } + + // Serialize and hash + CDataStream ss(SER_GETHASH); + ss.reserve(10000); + ss << txTmp << nHashType; + return Hash(ss.begin(), ss.end()); +} + + +bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, + const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + + // Hash type is one byte tacked on to the end of the signature + if (vchSig.empty()) + return false; + if (nHashType == 0) + nHashType = vchSig.back(); + else if (nHashType != vchSig.back()) + return false; + vchSig.pop_back(); + + if (key.Verify(SignatureHash(scriptCode, txTo, nIn, nHashType), vchSig)) + return true; + + return false; +} + + + + + + + + + + + +bool Solver(const CScript& scriptPubKey, vector >& vSolutionRet) +{ + // Templates + static vector vTemplates; + if (vTemplates.empty()) + { + // Standard tx, sender provides pubkey, receiver adds signature + vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG); + + // Short account number tx, sender provides hash of pubkey, receiver provides signature and pubkey + vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG); + } + + // Scan templates + const CScript& script1 = scriptPubKey; + foreach(const CScript& script2, vTemplates) + { + vSolutionRet.clear(); + opcodetype opcode1, opcode2; + vector vch1, vch2; + + // Compare + CScript::const_iterator pc1 = script1.begin(); + CScript::const_iterator pc2 = script2.begin(); + loop + { + bool f1 = script1.GetOp(pc1, opcode1, vch1); + bool f2 = script2.GetOp(pc2, opcode2, vch2); + if (!f1 && !f2) + { + // Success + reverse(vSolutionRet.begin(), vSolutionRet.end()); + return true; + } + else if (f1 != f2) + { + break; + } + else if (opcode2 == OP_PUBKEY) + { + if (vch1.size() <= sizeof(uint256)) + break; + vSolutionRet.push_back(make_pair(opcode2, vch1)); + } + else if (opcode2 == OP_PUBKEYHASH) + { + if (vch1.size() != sizeof(uint160)) + break; + vSolutionRet.push_back(make_pair(opcode2, vch1)); + } + else if (opcode1 != opcode2) + { + break; + } + } + } + + vSolutionRet.clear(); + return false; +} + + +bool Solver(const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + scriptSigRet.clear(); + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + // Compile solution + CRITICAL_BLOCK(cs_mapKeys) + { + foreach(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + if (item.first == OP_PUBKEY) + { + // Sign + const valtype& vchPubKey = item.second; + if (!mapKeys.count(vchPubKey)) + return false; + if (hash != 0) + { + vector vchSig; + if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + } + } + else if (item.first == OP_PUBKEYHASH) + { + // Sign and give pubkey + map::iterator mi = mapPubKeys.find(uint160(item.second)); + if (mi == mapPubKeys.end()) + return false; + const vector& vchPubKey = (*mi).second; + if (!mapKeys.count(vchPubKey)) + return false; + if (hash != 0) + { + vector vchSig; + if (!CKey::Sign(mapKeys[vchPubKey], hash, vchSig)) + return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig << vchPubKey; + } + } + } + } + + return true; +} + + +bool IsMine(const CScript& scriptPubKey) +{ + CScript scriptSig; + return Solver(scriptPubKey, 0, 0, scriptSig); +} + + +bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vector& vchPubKeyRet) +{ + vchPubKeyRet.clear(); + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + CRITICAL_BLOCK(cs_mapKeys) + { + foreach(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + valtype vchPubKey; + if (item.first == OP_PUBKEY) + { + vchPubKey = item.second; + } + else if (item.first == OP_PUBKEYHASH) + { + map::iterator mi = mapPubKeys.find(uint160(item.second)); + if (mi == mapPubKeys.end()) + continue; + vchPubKey = (*mi).second; + } + if (!fMineOnly || mapKeys.count(vchPubKey)) + { + vchPubKeyRet = vchPubKey; + return true; + } + } + } + return false; +} + + +bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret) +{ + hash160Ret = 0; + + vector > vSolution; + if (!Solver(scriptPubKey, vSolution)) + return false; + + foreach(PAIRTYPE(opcodetype, valtype)& item, vSolution) + { + if (item.first == OP_PUBKEYHASH) + { + hash160Ret = uint160(item.second); + return true; + } + } + return false; +} + + +bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq) +{ + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + // Leave out the signature from the hash, since a signature can't sign itself. + // The checksig op will also drop the signatures from its hash. + uint256 hash = SignatureHash(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType); + + if (!Solver(txout.scriptPubKey, hash, nHashType, txin.scriptSig)) + return false; + + txin.scriptSig = scriptPrereq + txin.scriptSig; + + // Test solution + if (scriptPrereq.empty()) + if (!EvalScript(txin.scriptSig + CScript(OP_CODESEPARATOR) + txout.scriptPubKey, txTo, nIn)) + return false; + + return true; +} + + +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + assert(nIn < txTo.vin.size()); + const CTxIn& txin = txTo.vin[nIn]; + if (txin.prevout.n >= txFrom.vout.size()) + return false; + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + if (txin.prevout.hash != txFrom.GetHash()) + return false; + + return EvalScript(txin.scriptSig + CScript(OP_CODESEPARATOR) + txout.scriptPubKey, txTo, nIn, nHashType); +} diff --git a/src/script.h b/src/script.h new file mode 100644 index 0000000..d52d06b --- /dev/null +++ b/src/script.h @@ -0,0 +1,597 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +class CTransaction; + +enum +{ + SIGHASH_ALL = 1, + SIGHASH_NONE = 2, + SIGHASH_SINGLE = 3, + SIGHASH_ANYONECANPAY = 0x80, +}; + + + +enum opcodetype +{ + // push value + OP_0=0, + OP_FALSE=OP_0, + OP_PUSHDATA1=76, + OP_PUSHDATA2, + OP_PUSHDATA4, + OP_1NEGATE, + OP_RESERVED, + OP_1, + OP_TRUE=OP_1, + OP_2, + OP_3, + OP_4, + OP_5, + OP_6, + OP_7, + OP_8, + OP_9, + OP_10, + OP_11, + OP_12, + OP_13, + OP_14, + OP_15, + OP_16, + + // control + OP_NOP, + OP_VER, + OP_IF, + OP_NOTIF, + OP_VERIF, + OP_VERNOTIF, + OP_ELSE, + OP_ENDIF, + OP_VERIFY, + OP_RETURN, + + // stack ops + OP_TOALTSTACK, + OP_FROMALTSTACK, + OP_2DROP, + OP_2DUP, + OP_3DUP, + OP_2OVER, + OP_2ROT, + OP_2SWAP, + OP_IFDUP, + OP_DEPTH, + OP_DROP, + OP_DUP, + OP_NIP, + OP_OVER, + OP_PICK, + OP_ROLL, + OP_ROT, + OP_SWAP, + OP_TUCK, + + // splice ops + OP_CAT, + OP_SUBSTR, + OP_LEFT, + OP_RIGHT, + OP_SIZE, + + // bit logic + OP_INVERT, + OP_AND, + OP_OR, + OP_XOR, + OP_EQUAL, + OP_EQUALVERIFY, + OP_RESERVED1, + OP_RESERVED2, + + // numeric + OP_1ADD, + OP_1SUB, + OP_2MUL, + OP_2DIV, + OP_NEGATE, + OP_ABS, + OP_NOT, + OP_0NOTEQUAL, + + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_LSHIFT, + OP_RSHIFT, + + OP_BOOLAND, + OP_BOOLOR, + OP_NUMEQUAL, + OP_NUMEQUALVERIFY, + OP_NUMNOTEQUAL, + OP_LESSTHAN, + OP_GREATERTHAN, + OP_LESSTHANOREQUAL, + OP_GREATERTHANOREQUAL, + OP_MIN, + OP_MAX, + + OP_WITHIN, + + // crypto + OP_RIPEMD160, + OP_SHA1, + OP_SHA256, + OP_HASH160, + OP_HASH256, + OP_CODESEPARATOR, + OP_CHECKSIG, + OP_CHECKSIGVERIFY, + OP_CHECKMULTISIG, + OP_CHECKMULTISIGVERIFY, + + + // multi-byte opcodes + OP_SINGLEBYTE_END = 0xF0, + OP_DOUBLEBYTE_BEGIN = 0xF000, + + // template matching params + OP_PUBKEY, + OP_PUBKEYHASH, + + + + OP_INVALIDOPCODE = 0xFFFF, +}; + + + + + + + + +inline const char* GetOpName(opcodetype opcode) +{ + switch (opcode) + { + // push value + case OP_0 : return "0"; + case OP_PUSHDATA1 : return "OP_PUSHDATA1"; + case OP_PUSHDATA2 : return "OP_PUSHDATA2"; + case OP_PUSHDATA4 : return "OP_PUSHDATA4"; + case OP_1NEGATE : return "-1"; + case OP_RESERVED : return "OP_RESERVED"; + case OP_1 : return "1"; + case OP_2 : return "2"; + case OP_3 : return "3"; + case OP_4 : return "4"; + case OP_5 : return "5"; + case OP_6 : return "6"; + case OP_7 : return "7"; + case OP_8 : return "8"; + case OP_9 : return "9"; + case OP_10 : return "10"; + case OP_11 : return "11"; + case OP_12 : return "12"; + case OP_13 : return "13"; + case OP_14 : return "14"; + case OP_15 : return "15"; + case OP_16 : return "16"; + + // control + case OP_NOP : return "OP_NOP"; + case OP_VER : return "OP_VER"; + case OP_IF : return "OP_IF"; + case OP_NOTIF : return "OP_NOTIF"; + case OP_VERIF : return "OP_VERIF"; + case OP_VERNOTIF : return "OP_VERNOTIF"; + case OP_ELSE : return "OP_ELSE"; + case OP_ENDIF : return "OP_ENDIF"; + case OP_VERIFY : return "OP_VERIFY"; + case OP_RETURN : return "OP_RETURN"; + + // stack ops + case OP_TOALTSTACK : return "OP_TOALTSTACK"; + case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; + case OP_2DROP : return "OP_2DROP"; + case OP_2DUP : return "OP_2DUP"; + case OP_3DUP : return "OP_3DUP"; + case OP_2OVER : return "OP_2OVER"; + case OP_2ROT : return "OP_2ROT"; + case OP_2SWAP : return "OP_2SWAP"; + case OP_IFDUP : return "OP_IFDUP"; + case OP_DEPTH : return "OP_DEPTH"; + case OP_DROP : return "OP_DROP"; + case OP_DUP : return "OP_DUP"; + case OP_NIP : return "OP_NIP"; + case OP_OVER : return "OP_OVER"; + case OP_PICK : return "OP_PICK"; + case OP_ROLL : return "OP_ROLL"; + case OP_ROT : return "OP_ROT"; + case OP_SWAP : return "OP_SWAP"; + case OP_TUCK : return "OP_TUCK"; + + // splice ops + case OP_CAT : return "OP_CAT"; + case OP_SUBSTR : return "OP_SUBSTR"; + case OP_LEFT : return "OP_LEFT"; + case OP_RIGHT : return "OP_RIGHT"; + case OP_SIZE : return "OP_SIZE"; + + // bit logic + case OP_INVERT : return "OP_INVERT"; + case OP_AND : return "OP_AND"; + case OP_OR : return "OP_OR"; + case OP_XOR : return "OP_XOR"; + case OP_EQUAL : return "OP_EQUAL"; + case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; + case OP_RESERVED1 : return "OP_RESERVED1"; + case OP_RESERVED2 : return "OP_RESERVED2"; + + // numeric + case OP_1ADD : return "OP_1ADD"; + case OP_1SUB : return "OP_1SUB"; + case OP_2MUL : return "OP_2MUL"; + case OP_2DIV : return "OP_2DIV"; + case OP_NEGATE : return "OP_NEGATE"; + case OP_ABS : return "OP_ABS"; + case OP_NOT : return "OP_NOT"; + case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; + case OP_ADD : return "OP_ADD"; + case OP_SUB : return "OP_SUB"; + case OP_MUL : return "OP_MUL"; + case OP_DIV : return "OP_DIV"; + case OP_MOD : return "OP_MOD"; + case OP_LSHIFT : return "OP_LSHIFT"; + case OP_RSHIFT : return "OP_RSHIFT"; + case OP_BOOLAND : return "OP_BOOLAND"; + case OP_BOOLOR : return "OP_BOOLOR"; + case OP_NUMEQUAL : return "OP_NUMEQUAL"; + case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; + case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; + case OP_LESSTHAN : return "OP_LESSTHAN"; + case OP_GREATERTHAN : return "OP_GREATERTHAN"; + case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; + case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; + case OP_MIN : return "OP_MIN"; + case OP_MAX : return "OP_MAX"; + case OP_WITHIN : return "OP_WITHIN"; + + // crypto + case OP_RIPEMD160 : return "OP_RIPEMD160"; + case OP_SHA1 : return "OP_SHA1"; + case OP_SHA256 : return "OP_SHA256"; + case OP_HASH160 : return "OP_HASH160"; + case OP_HASH256 : return "OP_HASH256"; + case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; + case OP_CHECKSIG : return "OP_CHECKSIG"; + case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; + case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; + case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + + + + // multi-byte opcodes + case OP_SINGLEBYTE_END : return "OP_SINGLEBYTE_END"; + case OP_DOUBLEBYTE_BEGIN : return "OP_DOUBLEBYTE_BEGIN"; + case OP_PUBKEY : return "OP_PUBKEY"; + case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; + + + + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; + default: + return "UNKNOWN_OPCODE"; + } +}; + + + + +inline string ValueString(const vector& vch) +{ + if (vch.size() <= 4) + return strprintf("%d", CBigNum(vch).getint()); + else + return HexNumStr(vch.begin(), vch.end()); + //return string("(") + HexStr(vch.begin(), vch.end()) + string(")"); +} + +inline string StackString(const vector >& vStack) +{ + string str; + foreach(const vector& vch, vStack) + { + if (!str.empty()) + str += " "; + str += ValueString(vch); + } + return str; +} + + + + + + + + + +class CScript : public vector +{ +protected: + CScript& push_int64(int64 n) + { + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return (*this); + } + + CScript& push_uint64(uint64 n) + { + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back(n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return (*this); + } + +public: + CScript() { } + CScript(const CScript& b) : vector(b.begin(), b.end()) { } + CScript(const_iterator pbegin, const_iterator pend) : vector(pbegin, pend) { } +#ifndef _MSC_VER + CScript(const unsigned char* pbegin, const unsigned char* pend) : vector(pbegin, pend) { } +#endif + + CScript& operator+=(const CScript& b) + { + insert(end(), b.begin(), b.end()); + return *this; + } + + friend CScript operator+(const CScript& a, const CScript& b) + { + CScript ret = a; + ret += b; + return (ret); + } + + + explicit CScript(char b) { operator<<(b); } + explicit CScript(short b) { operator<<(b); } + explicit CScript(int b) { operator<<(b); } + explicit CScript(long b) { operator<<(b); } + explicit CScript(int64 b) { operator<<(b); } + explicit CScript(unsigned char b) { operator<<(b); } + explicit CScript(unsigned int b) { operator<<(b); } + explicit CScript(unsigned short b) { operator<<(b); } + explicit CScript(unsigned long b) { operator<<(b); } + explicit CScript(uint64 b) { operator<<(b); } + + explicit CScript(opcodetype b) { operator<<(b); } + explicit CScript(const uint256& b) { operator<<(b); } + explicit CScript(const CBigNum& b) { operator<<(b); } + explicit CScript(const vector& b) { operator<<(b); } + + + CScript& operator<<(char b) { return (push_int64(b)); } + CScript& operator<<(short b) { return (push_int64(b)); } + CScript& operator<<(int b) { return (push_int64(b)); } + CScript& operator<<(long b) { return (push_int64(b)); } + CScript& operator<<(int64 b) { return (push_int64(b)); } + CScript& operator<<(unsigned char b) { return (push_uint64(b)); } + CScript& operator<<(unsigned int b) { return (push_uint64(b)); } + CScript& operator<<(unsigned short b) { return (push_uint64(b)); } + CScript& operator<<(unsigned long b) { return (push_uint64(b)); } + CScript& operator<<(uint64 b) { return (push_uint64(b)); } + + CScript& operator<<(opcodetype opcode) + { + if (opcode <= OP_SINGLEBYTE_END) + { + insert(end(), (unsigned char)opcode); + } + else + { + assert(opcode >= OP_DOUBLEBYTE_BEGIN); + insert(end(), (unsigned char)(opcode >> 8)); + insert(end(), (unsigned char)(opcode & 0xFF)); + } + return (*this); + } + + CScript& operator<<(const uint160& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return (*this); + } + + CScript& operator<<(const uint256& b) + { + insert(end(), sizeof(b)); + insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b)); + return (*this); + } + + CScript& operator<<(const CBigNum& b) + { + *this << b.getvch(); + return (*this); + } + + CScript& operator<<(const vector& b) + { + if (b.size() < OP_PUSHDATA1) + { + insert(end(), (unsigned char)b.size()); + } + else if (b.size() <= 0xff) + { + insert(end(), OP_PUSHDATA1); + insert(end(), (unsigned char)b.size()); + } + else + { + insert(end(), OP_PUSHDATA2); + unsigned short nSize = b.size(); + insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize)); + } + insert(end(), b.begin(), b.end()); + return (*this); + } + + CScript& operator<<(const CScript& b) + { + // I'm not sure if this should push the script or concatenate scripts. + // If there's ever a use for pushing a script onto a script, delete this member fn + assert(("warning: pushing a CScript onto a CScript with << is probably not intended, use + to concatenate", false)); + return (*this); + } + + + bool GetOp(iterator& pc, opcodetype& opcodeRet, vector& vchRet) + { + // This is why people hate C++ + const_iterator pc2 = pc; + bool fRet = GetOp(pc2, opcodeRet, vchRet); + pc = begin() + (pc2 - begin()); + return fRet; + } + + bool GetOp(const_iterator& pc, opcodetype& opcodeRet, vector& vchRet) const + { + opcodeRet = OP_INVALIDOPCODE; + vchRet.clear(); + if (pc >= end()) + return false; + + // Read instruction + unsigned int opcode = *pc++; + if (opcode >= OP_SINGLEBYTE_END) + { + if (pc + 1 > end()) + return false; + opcode <<= 8; + opcode |= *pc++; + } + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + unsigned int nSize = opcode; + if (opcode == OP_PUSHDATA1) + { + if (pc + 1 > end()) + return false; + nSize = *pc++; + } + else if (opcode == OP_PUSHDATA2) + { + if (pc + 2 > end()) + return false; + nSize = 0; + memcpy(&nSize, &pc[0], 2); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (pc + 4 > end()) + return false; + memcpy(&nSize, &pc[0], 4); + pc += 4; + } + if (pc + nSize > end()) + return false; + vchRet.assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = (opcodetype)opcode; + return true; + } + + + void FindAndDelete(const CScript& b) + { + iterator pc = begin(); + opcodetype opcode; + vector vchPushValue; + int count = 0; + do + { + while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) + { + erase(pc, pc + b.size()); + count++; + } + } + while (GetOp(pc, opcode, vchPushValue)); + //printf("FindAndDeleted deleted %d items\n", count); /// debug + } + + + void PrintHex() const + { + printf("CScript(%s)\n", HexStr(begin(), end()).c_str()); + } + + string ToString() const + { + string str; + opcodetype opcode; + vector vch; + const_iterator it = begin(); + while (GetOp(it, opcode, vch)) + { + if (!str.empty()) + str += " "; + if (opcode <= OP_PUSHDATA4) + str += ValueString(vch); + else + str += GetOpName(opcode); + } + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + + + + + +bool EvalScript(const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType=0, + vector >* pvStackRet=NULL); +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +bool IsMine(const CScript& scriptPubKey); +bool ExtractPubKey(const CScript& scriptPubKey, bool fMineOnly, vector& vchPubKeyRet); +bool ExtractHash160(const CScript& scriptPubKey, uint160& hash160Ret); +bool SignSignature(const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0); diff --git a/src/serialize.h b/src/serialize.h new file mode 100644 index 0000000..0e03cf7 --- /dev/null +++ b/src/serialize.h @@ -0,0 +1,1151 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif +class CScript; +class CDataStream; +class CAutoFile; + +static const int VERSION = 101; + + + + + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, int) and .write(char*, int) +// + +enum +{ + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), + + // modifiers + SER_SKIPSIG = (1 << 16), + SER_BLOCKHEADERONLY = (1 << 17), +}; + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + s.nType = nType; \ + s.nVersion = nVersion; \ + {statements} \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const \ + { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + {statements} \ + } \ + template \ + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) \ + { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + {statements} \ + } + +#define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + + + + + + +// +// Basic types +// +#define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) +#define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) + +inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } + +template inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } +template inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } + +template inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } +template inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } + +inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } +template inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } +template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f; READDATA(s, f); a=f; } + + + + + + +// +// Compact size +// size < 253 -- 1 byte +// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) +// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) +// size > UINT_MAX -- 9 bytes (255 + 8 bytes) +// +inline unsigned int GetSizeOfCompactSize(uint64 nSize) +{ + if (nSize < UCHAR_MAX-2) return sizeof(unsigned char); + else if (nSize <= USHRT_MAX) return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= UINT_MAX) return sizeof(unsigned char) + sizeof(unsigned int); + else return sizeof(unsigned char) + sizeof(uint64); +} + +template +void WriteCompactSize(Stream& os, uint64 nSize) +{ + if (nSize < UCHAR_MAX-2) + { + unsigned char chSize = nSize; + WRITEDATA(os, chSize); + } + else if (nSize <= USHRT_MAX) + { + unsigned char chSize = UCHAR_MAX-2; + unsigned short xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else if (nSize <= UINT_MAX) + { + unsigned char chSize = UCHAR_MAX-1; + unsigned int xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + else + { + unsigned char chSize = UCHAR_MAX; + WRITEDATA(os, chSize); + WRITEDATA(os, nSize); + } + return; +} + +template +uint64 ReadCompactSize(Stream& is) +{ + unsigned char chSize; + READDATA(is, chSize); + if (chSize < UCHAR_MAX-2) + { + return chSize; + } + else if (chSize == UCHAR_MAX-2) + { + unsigned short nSize; + READDATA(is, nSize); + return nSize; + } + else if (chSize == UCHAR_MAX-1) + { + unsigned int nSize; + READDATA(is, nSize); + return nSize; + } + else + { + uint64 nSize; + READDATA(is, nSize); + return nSize; + } +} + + + +// +// Wrapper for serializing arrays and POD +// There's a clever template way to make arrays serialize normally, but MSVC6 doesn't support it +// +#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) +class CFlatData +{ +protected: + char* pbegin; + char* pend; +public: + CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + char* begin() { return pbegin; } + const char* begin() const { return pbegin; } + char* end() { return pend; } + const char* end() const { return pend; } + + unsigned int GetSerializeSize(int, int=0) const + { + return pend - pbegin; + } + + template + void Serialize(Stream& s, int, int=0) const + { + s.write(pbegin, pend - pbegin); + } + + template + void Unserialize(Stream& s, int, int=0) + { + s.read(pbegin, pend - pbegin); + } +}; + + + +// +// string stored as a fixed length field +// +template +class CFixedFieldString +{ +protected: + const string* pcstr; + string* pstr; +public: + explicit CFixedFieldString(const string& str) : pcstr(&str), pstr(NULL) { } + explicit CFixedFieldString(string& str) : pcstr(&str), pstr(&str) { } + + unsigned int GetSerializeSize(int, int=0) const + { + return LEN; + } + + template + void Serialize(Stream& s, int, int=0) const + { + char pszBuf[LEN]; + strncpy(pszBuf, pcstr->c_str(), LEN); + s.write(pszBuf, LEN); + } + + template + void Unserialize(Stream& s, int, int=0) + { + if (pstr == NULL) + throw std::ios_base::failure("CFixedFieldString::Unserialize : trying to unserialize to const string"); + char pszBuf[LEN+1]; + s.read(pszBuf, LEN); + pszBuf[LEN] = '\0'; + *pstr = pszBuf; + } +}; + + + + + +// +// Forward declarations +// + +// string +template unsigned int GetSerializeSize(const basic_string& str, int, int=0); +template void Serialize(Stream& os, const basic_string& str, int, int=0); +template void Unserialize(Stream& is, basic_string& str, int, int=0); + +// vector +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&); +template unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion=VERSION); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion=VERSION); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&); +template void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&); +template inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion=VERSION); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const CScript& v, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, CScript& v, int nType, int nVersion=VERSION); + +// pair +template unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::pair& item, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::pair& item, int nType, int nVersion=VERSION); + +// map +template unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::map& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::map& m, int nType, int nVersion=VERSION); + +// set +template unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion=VERSION); +template void Serialize(Stream& os, const std::set& m, int nType, int nVersion=VERSION); +template void Unserialize(Stream& is, std::set& m, int nType, int nVersion=VERSION); + + + + + +// +// If none of the specialized versions above matched, default to calling member function. +// "int nType" is changed to "long nType" to keep from getting an ambiguous overload error. +// The compiler will only cast int to long if none of the other templates matched. +// Thanks to Boost serialization for this idea. +// +template +inline unsigned int GetSerializeSize(const T& a, long nType, int nVersion=VERSION) +{ + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream& os, const T& a, long nType, int nVersion=VERSION) +{ + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream& is, T& a, long nType, int nVersion=VERSION) +{ + a.Unserialize(is, (int)nType, nVersion); +} + + + + + +// +// string +// +template +unsigned int GetSerializeSize(const basic_string& str, int, int) +{ + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream& os, const basic_string& str, int, int) +{ + WriteCompactSize(os, str.size()); + if (!str.empty()) + os.write((char*)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream& is, basic_string& str, int, int) +{ + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) + is.read((char*)&str[0], nSize * sizeof(str[0])); +} + + + +// +// vector +// +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream& os, const std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream& os, const std::vector& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); +} + + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::true_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //is.read((char*)&v[0], nSize * sizeof(T)); + + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = min(nSize - i, 1 + 4999999 / sizeof(T)); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream& is, std::vector& v, int nType, int nVersion, const boost::false_type&) +{ + //unsigned int nSize = ReadCompactSize(is); + //v.resize(nSize); + //for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) + // Unserialize(is, (*vi), nType, nVersion); + + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream& is, std::vector& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); +} + + + +// +// others derived from vector +// +inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) +{ + return GetSerializeSize((const vector&)v, nType, nVersion); +} + +template +void Serialize(Stream& os, const CScript& v, int nType, int nVersion) +{ + Serialize(os, (const vector&)v, nType, nVersion); +} + +template +void Unserialize(Stream& is, CScript& v, int nType, int nVersion) +{ + Unserialize(is, (vector&)v, nType, nVersion); +} + + + +// +// pair +// +template +unsigned int GetSerializeSize(const std::pair& item, int nType, int nVersion) +{ + return GetSerializeSize(item.first, nType, nVersion) + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream& os, const std::pair& item, int nType, int nVersion) +{ + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream& is, std::pair& item, int nType, int nVersion) +{ + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + + + +// +// map +// +template +unsigned int GetSerializeSize(const std::map& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::map& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::map& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + + + +// +// set +// +template +unsigned int GetSerializeSize(const std::set& m, int nType, int nVersion) +{ + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream& os, const std::set& m, int nType, int nVersion) +{ + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream& is, std::set& m, int nType, int nVersion) +{ + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) + { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + + + +// +// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// +class CSerActionGetSerializeSize { }; +class CSerActionSerialize { }; +class CSerActionUnserialize { }; + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) +{ + return ::GetSerializeSize(obj, nType, nVersion); +} + +template +inline unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) +{ + ::Serialize(s, obj, nType, nVersion); + return 0; +} + +template +inline unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) +{ + ::Unserialize(s, obj, nType, nVersion); + return 0; +} + +struct ser_streamplaceholder +{ + int nType; + int nVersion; +}; + + + + + + + + + +// +// Allocator that clears its contents before deletion +// +template +struct secure_allocator : public std::allocator +{ + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + secure_allocator() throw() {} + secure_allocator(const secure_allocator& a) throw() : base(a) {} + ~secure_allocator() throw() {} + template struct rebind + { typedef secure_allocator<_Other> other; }; + + void deallocate(T* p, std::size_t n) + { + if (p != NULL) + memset(p, 0, sizeof(T) * n); + allocator::deallocate(p, n); + } +}; + + + +// +// Double ended buffer combining vector and stream-like interfaces. +// >> and << read and write unformatted data using the above serialization templates. +// Fills with data in linear time; some stringstream implementations take N^2 time. +// +class CDataStream +{ +protected: + typedef vector > vector_type; + vector_type vch; + unsigned int nReadPos; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef vector_type::allocator_type allocator_type; + typedef vector_type::size_type size_type; + typedef vector_type::difference_type difference_type; + typedef vector_type::reference reference; + typedef vector_type::const_reference const_reference; + typedef vector_type::value_type value_type; + typedef vector_type::iterator iterator; + typedef vector_type::const_iterator const_iterator; + typedef vector_type::reverse_iterator reverse_iterator; + + explicit CDataStream(int nTypeIn=0, int nVersionIn=VERSION) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn=0, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char* pbegin, const char* pend, int nTypeIn=0, int nVersionIn=VERSION) : vch(pbegin, pend) + { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type& vchIn, int nTypeIn=0, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const vector& vchIn, int nTypeIn=0, int nVersionIn=VERSION) : vch(vchIn.begin(), vchIn.end()) + { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const vector& vchIn, int nTypeIn=0, int nVersionIn=VERSION) : vch((char*)&vchIn.begin()[0], (char*)&vchIn.end()[0]) + { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn=0, int nVersionIn=VERSION) + { + nReadPos = 0; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = ios::badbit | ios::failbit; + } + + CDataStream& operator+=(const CDataStream& b) + { + vch.insert(vch.end(), b.begin(), b.end()); + return *this; + } + + friend CDataStream operator+(const CDataStream& a, const CDataStream& b) + { + CDataStream ret = a; + ret += b; + return (ret); + } + + string str() const + { + return (string(begin(), end())); + } + + + // + // Vector subset + // + const_iterator begin() const { return vch.begin() + nReadPos; } + iterator begin() { return vch.begin() + nReadPos; } + const_iterator end() const { return vch.end(); } + iterator end() { return vch.end(); } + size_type size() const { return vch.size() - nReadPos; } + bool empty() const { return vch.size() == nReadPos; } + void resize(size_type n, value_type c=0) { vch.resize(n + nReadPos, c); } + void reserve(size_type n) { vch.reserve(n + nReadPos); } + const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } + reference operator[](size_type pos) { return vch[pos + nReadPos]; } + void clear() { vch.clear(); nReadPos = 0; } + iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } + void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } + + void insert(iterator it, const_iterator first, const_iterator last) + { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) + { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } + else + vch.insert(it, first, last); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char* first, const char* last) + { + insert(it, (const_iterator)first, (const_iterator)last); + } +#endif + + iterator erase(iterator it) + { + if (it == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (++nReadPos >= vch.size()) + { + // whenever we reach the end, we take the opportunity to clear the buffer + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + return vch.begin() + nReadPos; + } + else + return vch.erase(it); + } + + iterator erase(iterator first, iterator last) + { + if (first == vch.begin() + nReadPos) + { + // special case for erasing from the front + if (last == vch.end()) + { + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + else + { + nReadPos = (last - vch.begin()); + return last; + } + } + else + return vch.erase(first, last); + } + + inline void Compact() + { + vch.erase(vch.begin(), vch.begin() + nReadPos); + nReadPos = 0; + } + + bool Rewind(size_type n) + { + // Rewind by n characters if the buffer hasn't been compacted yet + if (n > nReadPos) + return false; + nReadPos -= n; + return true; + } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool eof() const { return size() == 0; } + bool fail() const { return state & (ios::badbit | ios::failbit); } + bool good() const { return !eof() && (state == 0); } + void clear(short n) { state = n; } // name conflict with vector clear() + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CDataStream"); return prev; } + CDataStream* rdbuf() { return this; } + int in_avail() { return size(); } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CDataStream& read(char* pch, int nSize) + { + // Read from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(ios::failbit, "CDataStream::read() : end of data"); + memset(pch, 0, nSize); + nSize = vch.size() - nReadPos; + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = 0; + vch.clear(); + return (*this); + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& ignore(int nSize) + { + // Ignore from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) + { + if (nReadPosNext > vch.size()) + { + setstate(ios::failbit, "CDataStream::ignore() : end of data"); + nSize = vch.size() - nReadPos; + } + nReadPos = 0; + vch.clear(); + return (*this); + } + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream& write(const char* pch, int nSize) + { + // Write to the end of the buffer + assert(nSize >= 0); + vch.insert(vch.end(), pch, pch + nSize); + return (*this); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + // Special case: stream << stream concatenates like stream += stream + if (!vch.empty()) + s.write((char*)&vch[0], vch.size() * sizeof(vch[0])); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CDataStream& operator<<(const T& obj) + { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CDataStream& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#ifdef TESTCDATASTREAM +// VC6sp6 +// CDataStream: +// n=1000 0 seconds +// n=2000 0 seconds +// n=4000 0 seconds +// n=8000 0 seconds +// n=16000 0 seconds +// n=32000 0 seconds +// n=64000 1 seconds +// n=128000 1 seconds +// n=256000 2 seconds +// n=512000 4 seconds +// n=1024000 8 seconds +// n=2048000 16 seconds +// n=4096000 32 seconds +// stringstream: +// n=1000 1 seconds +// n=2000 1 seconds +// n=4000 13 seconds +// n=8000 87 seconds +// n=16000 400 seconds +// n=32000 1660 seconds +// n=64000 6749 seconds +// n=128000 27241 seconds +// n=256000 109804 seconds +#include +int main(int argc, char *argv[]) +{ + vector vch(0xcc, 250); + printf("CDataStream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + CDataStream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } + printf("stringstream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) + { + stringstream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char*)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } +} +#endif + + + + + + + + + + +// +// Automatic closing wrapper for FILE* +// - Will automatically close the file when it goes out of scope if not null. +// - If you're returning the file pointer, return file.release(). +// - If you need to close the file early, use file.fclose() instead of fclose(file). +// +class CAutoFile +{ +protected: + FILE* file; + short state; + short exceptmask; +public: + int nType; + int nVersion; + + typedef FILE element_type; + + CAutoFile(FILE* filenew=NULL, int nTypeIn=SER_DISK, int nVersionIn=VERSION) + { + file = filenew; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = ios::badbit | ios::failbit; + } + + ~CAutoFile() + { + fclose(); + } + + void fclose() + { + if (file != NULL && file != stdin && file != stdout && file != stderr) + ::fclose(file); + file = NULL; + } + + FILE* release() { FILE* ret = file; file = NULL; return ret; } + operator FILE*() { return file; } + FILE* operator->() { return file; } + FILE& operator*() { return *file; } + FILE** operator&() { return &file; } + FILE* operator=(FILE* pnew) { return file = pnew; } + bool operator!() { return (file == NULL); } + + + // + // Stream subset + // + void setstate(short bits, const char* psz) + { + state |= bits; + if (state & exceptmask) + throw std::ios_base::failure(psz); + } + + bool fail() const { return state & (ios::badbit | ios::failbit); } + bool good() const { return state == 0; } + void clear(short n = 0) { state = n; } + short exceptions() { return exceptmask; } + short exceptions(short mask) { short prev = exceptmask; exceptmask = mask; setstate(0, "CAutoFile"); return prev; } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CAutoFile& read(char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::read : file handle is NULL"); + if (fread(pch, 1, nSize, file) != nSize) + setstate(ios::failbit, feof(file) ? "CAutoFile::read : end of file" : "CAutoFile::read : fread failed"); + return (*this); + } + + CAutoFile& write(const char* pch, int nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::write : file handle is NULL"); + if (fwrite(pch, 1, nSize, file) != nSize) + setstate(ios::failbit, "CAutoFile::write : write failed"); + return (*this); + } + + template + unsigned int GetSerializeSize(const T& obj) + { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template + CAutoFile& operator<<(const T& obj) + { + // Serialize to this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator<< : file handle is NULL"); + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template + CAutoFile& operator>>(T& obj) + { + // Unserialize from this stream + if (!file) + throw std::ios_base::failure("CAutoFile::operator>> : file handle is NULL"); + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; diff --git a/src/sha.cpp b/src/sha.cpp new file mode 100644 index 0000000..e691674 --- /dev/null +++ b/src/sha.cpp @@ -0,0 +1,554 @@ +// This file is public domain +// SHA routines extracted as a standalone file from: +// Crypto++: a C++ Class Library of Cryptographic Schemes +// Version 5.5.2 (9/24/2007) +// http://www.cryptopp.com + +// sha.cpp - modified by Wei Dai from Steve Reid's public domain sha1.c + +// Steve Reid implemented SHA-1. Wei Dai implemented SHA-2. +// Both are in the public domain. + +#include +#include +#include "sha.h" + +namespace CryptoPP +{ + +// start of Steve Reid's code + +#define blk0(i) (W[i] = data[i]) +#define blk1(i) (W[i&15] = rotlFixed(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) + +void SHA1::InitState(HashWordType *state) +{ + state[0] = 0x67452301L; + state[1] = 0xEFCDAB89L; + state[2] = 0x98BADCFEL; + state[3] = 0x10325476L; + state[4] = 0xC3D2E1F0L; +} + +#define f1(x,y,z) (z^(x&(y^z))) +#define f2(x,y,z) (x^y^z) +#define f3(x,y,z) ((x&y)|(z&(x|y))) +#define f4(x,y,z) (x^y^z) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); +#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); +#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rotlFixed(v,5);w=rotlFixed(w,30); +#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rotlFixed(v,5);w=rotlFixed(w,30); +#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rotlFixed(v,5);w=rotlFixed(w,30); + +void SHA1::Transform(word32 *state, const word32 *data) +{ + word32 W[16]; + /* Copy context->state[] to working vars */ + word32 a = state[0]; + word32 b = state[1]; + word32 c = state[2]; + word32 d = state[3]; + word32 e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + +// end of Steve Reid's code + +// ************************************************************* + +void SHA224::InitState(HashWordType *state) +{ + static const word32 s[8] = {0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4}; + memcpy(state, s, sizeof(s)); +} + +void SHA256::InitState(HashWordType *state) +{ + static const word32 s[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + memcpy(state, s, sizeof(s)); +} + +static const word32 SHA256_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +#define blk2(i) (W[i&15]+=s1(W[(i-2)&15])+W[(i-7)&15]+s0(W[(i-15)&15])) + +#define Ch(x,y,z) (z^(x&(y^z))) +#define Maj(x,y,z) ((x&y)|(z&(x|y))) + +#define a(i) T[(0-i)&7] +#define b(i) T[(1-i)&7] +#define c(i) T[(2-i)&7] +#define d(i) T[(3-i)&7] +#define e(i) T[(4-i)&7] +#define f(i) T[(5-i)&7] +#define g(i) T[(6-i)&7] +#define h(i) T[(7-i)&7] + +#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA256_K[i+j]+(j?blk2(i):blk0(i));\ + d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) + +// for SHA256 +#define S0(x) (rotrFixed(x,2)^rotrFixed(x,13)^rotrFixed(x,22)) +#define S1(x) (rotrFixed(x,6)^rotrFixed(x,11)^rotrFixed(x,25)) +#define s0(x) (rotrFixed(x,7)^rotrFixed(x,18)^(x>>3)) +#define s1(x) (rotrFixed(x,17)^rotrFixed(x,19)^(x>>10)) + +void SHA256::Transform(word32 *state, const word32 *data) +{ + word32 W[16]; + word32 T[8]; + /* Copy context->state[] to working vars */ + memcpy(T, state, sizeof(T)); + /* 64 operations, partially loop unrolled */ + for (unsigned int j=0; j<64; j+=16) + { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + /* Add the working vars back into context.state[] */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +} + +/* +// smaller but slower +void SHA256_Transform(word32 *state, const word32 *data) +{ + word32 T[20]; + word32 W[32]; + unsigned int i = 0, j = 0; + word32 *t = T+8; + + memcpy(t, state, 8*4); + word32 e = t[4], a = t[0]; + + do + { + word32 w = data[j]; + W[j] = w; + w += K[j]; + w += t[7]; + w += S1(e); + w += Ch(e, t[5], t[6]); + e = t[3] + w; + t[3] = t[3+8] = e; + w += S0(t[0]); + a = w + Maj(a, t[1], t[2]); + t[-1] = t[7] = a; + --t; + ++j; + if (j%8 == 0) + t += 8; + } while (j<16); + + do + { + i = j&0xf; + word32 w = s1(W[i+16-2]) + s0(W[i+16-15]) + W[i] + W[i+16-7]; + W[i+16] = W[i] = w; + w += K[j]; + w += t[7]; + w += S1(e); + w += Ch(e, t[5], t[6]); + e = t[3] + w; + t[3] = t[3+8] = e; + w += S0(t[0]); + a = w + Maj(a, t[1], t[2]); + t[-1] = t[7] = a; + + w = s1(W[(i+1)+16-2]) + s0(W[(i+1)+16-15]) + W[(i+1)] + W[(i+1)+16-7]; + W[(i+1)+16] = W[(i+1)] = w; + w += K[j+1]; + w += (t-1)[7]; + w += S1(e); + w += Ch(e, (t-1)[5], (t-1)[6]); + e = (t-1)[3] + w; + (t-1)[3] = (t-1)[3+8] = e; + w += S0((t-1)[0]); + a = w + Maj(a, (t-1)[1], (t-1)[2]); + (t-1)[-1] = (t-1)[7] = a; + + t-=2; + j+=2; + if (j%8 == 0) + t += 8; + } while (j<64); + + state[0] += a; + state[1] += t[1]; + state[2] += t[2]; + state[3] += t[3]; + state[4] += e; + state[5] += t[5]; + state[6] += t[6]; + state[7] += t[7]; +} +*/ + +#undef S0 +#undef S1 +#undef s0 +#undef s1 +#undef R + +// ************************************************************* + +#ifdef WORD64_AVAILABLE + +void SHA384::InitState(HashWordType *state) +{ + static const word64 s[8] = { + W64LIT(0xcbbb9d5dc1059ed8), W64LIT(0x629a292a367cd507), + W64LIT(0x9159015a3070dd17), W64LIT(0x152fecd8f70e5939), + W64LIT(0x67332667ffc00b31), W64LIT(0x8eb44a8768581511), + W64LIT(0xdb0c2e0d64f98fa7), W64LIT(0x47b5481dbefa4fa4)}; + memcpy(state, s, sizeof(s)); +} + +void SHA512::InitState(HashWordType *state) +{ + static const word64 s[8] = { + W64LIT(0x6a09e667f3bcc908), W64LIT(0xbb67ae8584caa73b), + W64LIT(0x3c6ef372fe94f82b), W64LIT(0xa54ff53a5f1d36f1), + W64LIT(0x510e527fade682d1), W64LIT(0x9b05688c2b3e6c1f), + W64LIT(0x1f83d9abfb41bd6b), W64LIT(0x5be0cd19137e2179)}; + memcpy(state, s, sizeof(s)); +} + +CRYPTOPP_ALIGN_DATA(16) static const word64 SHA512_K[80] CRYPTOPP_SECTION_ALIGN16 = { + W64LIT(0x428a2f98d728ae22), W64LIT(0x7137449123ef65cd), + W64LIT(0xb5c0fbcfec4d3b2f), W64LIT(0xe9b5dba58189dbbc), + W64LIT(0x3956c25bf348b538), W64LIT(0x59f111f1b605d019), + W64LIT(0x923f82a4af194f9b), W64LIT(0xab1c5ed5da6d8118), + W64LIT(0xd807aa98a3030242), W64LIT(0x12835b0145706fbe), + W64LIT(0x243185be4ee4b28c), W64LIT(0x550c7dc3d5ffb4e2), + W64LIT(0x72be5d74f27b896f), W64LIT(0x80deb1fe3b1696b1), + W64LIT(0x9bdc06a725c71235), W64LIT(0xc19bf174cf692694), + W64LIT(0xe49b69c19ef14ad2), W64LIT(0xefbe4786384f25e3), + W64LIT(0x0fc19dc68b8cd5b5), W64LIT(0x240ca1cc77ac9c65), + W64LIT(0x2de92c6f592b0275), W64LIT(0x4a7484aa6ea6e483), + W64LIT(0x5cb0a9dcbd41fbd4), W64LIT(0x76f988da831153b5), + W64LIT(0x983e5152ee66dfab), W64LIT(0xa831c66d2db43210), + W64LIT(0xb00327c898fb213f), W64LIT(0xbf597fc7beef0ee4), + W64LIT(0xc6e00bf33da88fc2), W64LIT(0xd5a79147930aa725), + W64LIT(0x06ca6351e003826f), W64LIT(0x142929670a0e6e70), + W64LIT(0x27b70a8546d22ffc), W64LIT(0x2e1b21385c26c926), + W64LIT(0x4d2c6dfc5ac42aed), W64LIT(0x53380d139d95b3df), + W64LIT(0x650a73548baf63de), W64LIT(0x766a0abb3c77b2a8), + W64LIT(0x81c2c92e47edaee6), W64LIT(0x92722c851482353b), + W64LIT(0xa2bfe8a14cf10364), W64LIT(0xa81a664bbc423001), + W64LIT(0xc24b8b70d0f89791), W64LIT(0xc76c51a30654be30), + W64LIT(0xd192e819d6ef5218), W64LIT(0xd69906245565a910), + W64LIT(0xf40e35855771202a), W64LIT(0x106aa07032bbd1b8), + W64LIT(0x19a4c116b8d2d0c8), W64LIT(0x1e376c085141ab53), + W64LIT(0x2748774cdf8eeb99), W64LIT(0x34b0bcb5e19b48a8), + W64LIT(0x391c0cb3c5c95a63), W64LIT(0x4ed8aa4ae3418acb), + W64LIT(0x5b9cca4f7763e373), W64LIT(0x682e6ff3d6b2b8a3), + W64LIT(0x748f82ee5defb2fc), W64LIT(0x78a5636f43172f60), + W64LIT(0x84c87814a1f0ab72), W64LIT(0x8cc702081a6439ec), + W64LIT(0x90befffa23631e28), W64LIT(0xa4506cebde82bde9), + W64LIT(0xbef9a3f7b2c67915), W64LIT(0xc67178f2e372532b), + W64LIT(0xca273eceea26619c), W64LIT(0xd186b8c721c0c207), + W64LIT(0xeada7dd6cde0eb1e), W64LIT(0xf57d4f7fee6ed178), + W64LIT(0x06f067aa72176fba), W64LIT(0x0a637dc5a2c898a6), + W64LIT(0x113f9804bef90dae), W64LIT(0x1b710b35131c471b), + W64LIT(0x28db77f523047d84), W64LIT(0x32caab7b40c72493), + W64LIT(0x3c9ebe0a15c9bebc), W64LIT(0x431d67c49c100d4c), + W64LIT(0x4cc5d4becb3e42b6), W64LIT(0x597f299cfc657e2a), + W64LIT(0x5fcb6fab3ad6faec), W64LIT(0x6c44198c4a475817) +}; + +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 +// put assembly version in separate function, otherwise MSVC 2005 SP1 doesn't generate correct code for the non-assembly version +CRYPTOPP_NAKED static void CRYPTOPP_FASTCALL SHA512_SSE2_Transform(word64 *state, const word64 *data) +{ +#ifdef __GNUC__ + __asm__ __volatile__ + ( + ".intel_syntax noprefix;" + AS1( push ebx) + AS2( mov ebx, eax) +#else + AS1( push ebx) + AS1( push esi) + AS1( push edi) + AS2( lea ebx, SHA512_K) +#endif + + AS2( mov eax, esp) + AS2( and esp, 0xfffffff0) + AS2( sub esp, 27*16) // 17*16 for expanded data, 20*8 for state + AS1( push eax) + AS2( xor eax, eax) + AS2( lea edi, [esp+4+8*8]) // start at middle of state buffer. will decrement pointer each round to avoid copying + AS2( lea esi, [esp+4+20*8+8]) // 16-byte alignment, then add 8 + + AS2( movq mm4, [ecx+0*8]) + AS2( movq [edi+0*8], mm4) + AS2( movq mm0, [ecx+1*8]) + AS2( movq [edi+1*8], mm0) + AS2( movq mm0, [ecx+2*8]) + AS2( movq [edi+2*8], mm0) + AS2( movq mm0, [ecx+3*8]) + AS2( movq [edi+3*8], mm0) + AS2( movq mm5, [ecx+4*8]) + AS2( movq [edi+4*8], mm5) + AS2( movq mm0, [ecx+5*8]) + AS2( movq [edi+5*8], mm0) + AS2( movq mm0, [ecx+6*8]) + AS2( movq [edi+6*8], mm0) + AS2( movq mm0, [ecx+7*8]) + AS2( movq [edi+7*8], mm0) + ASJ( jmp, 0, f) + +#define SSE2_S0_S1(r, a, b, c) \ + AS2( movq mm6, r)\ + AS2( psrlq r, a)\ + AS2( movq mm7, r)\ + AS2( psllq mm6, 64-c)\ + AS2( pxor mm7, mm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor mm7, r)\ + AS2( psllq mm6, c-b)\ + AS2( pxor mm7, mm6)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, mm7)\ + AS2( psllq mm6, b-a)\ + AS2( pxor r, mm6) + +#define SSE2_s0(r, a, b, c) \ + AS2( movdqa xmm6, r)\ + AS2( psrlq r, a)\ + AS2( movdqa xmm7, r)\ + AS2( psllq xmm6, 64-c)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor xmm7, r)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, xmm7)\ + AS2( psllq xmm6, c-a)\ + AS2( pxor r, xmm6) + +#define SSE2_s1(r, a, b, c) \ + AS2( movdqa xmm6, r)\ + AS2( psrlq r, a)\ + AS2( movdqa xmm7, r)\ + AS2( psllq xmm6, 64-c)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, b-a)\ + AS2( pxor xmm7, r)\ + AS2( psllq xmm6, c-b)\ + AS2( pxor xmm7, xmm6)\ + AS2( psrlq r, c-b)\ + AS2( pxor r, xmm7) + + ASL(SHA512_Round) + // k + w is in mm0, a is in mm4, e is in mm5 + AS2( paddq mm0, [edi+7*8]) // h + AS2( movq mm2, [edi+5*8]) // f + AS2( movq mm3, [edi+6*8]) // g + AS2( pxor mm2, mm3) + AS2( pand mm2, mm5) + SSE2_S0_S1(mm5,14,18,41) + AS2( pxor mm2, mm3) + AS2( paddq mm0, mm2) // h += Ch(e,f,g) + AS2( paddq mm5, mm0) // h += S1(e) + AS2( movq mm2, [edi+1*8]) // b + AS2( movq mm1, mm2) + AS2( por mm2, mm4) + AS2( pand mm2, [edi+2*8]) // c + AS2( pand mm1, mm4) + AS2( por mm1, mm2) + AS2( paddq mm1, mm5) // temp = h + Maj(a,b,c) + AS2( paddq mm5, [edi+3*8]) // e = d + h + AS2( movq [edi+3*8], mm5) + AS2( movq [edi+11*8], mm5) + SSE2_S0_S1(mm4,28,34,39) // S0(a) + AS2( paddq mm4, mm1) // a = temp + S0(a) + AS2( movq [edi-8], mm4) + AS2( movq [edi+7*8], mm4) + AS1( ret) + + // first 16 rounds + ASL(0) + AS2( movq mm0, [edx+eax*8]) + AS2( movq [esi+eax*8], mm0) + AS2( movq [esi+eax*8+16*8], mm0) + AS2( paddq mm0, [ebx+eax*8]) + ASC( call, SHA512_Round) + AS1( inc eax) + AS2( sub edi, 8) + AS2( test eax, 7) + ASJ( jnz, 0, b) + AS2( add edi, 8*8) + AS2( cmp eax, 16) + ASJ( jne, 0, b) + + // rest of the rounds + AS2( movdqu xmm0, [esi+(16-2)*8]) + ASL(1) + // data expansion, W[i-2] already in xmm0 + AS2( movdqu xmm3, [esi]) + AS2( paddq xmm3, [esi+(16-7)*8]) + AS2( movdqa xmm2, [esi+(16-15)*8]) + SSE2_s1(xmm0, 6, 19, 61) + AS2( paddq xmm0, xmm3) + SSE2_s0(xmm2, 1, 7, 8) + AS2( paddq xmm0, xmm2) + AS2( movdq2q mm0, xmm0) + AS2( movhlps xmm1, xmm0) + AS2( paddq mm0, [ebx+eax*8]) + AS2( movlps [esi], xmm0) + AS2( movlps [esi+8], xmm1) + AS2( movlps [esi+8*16], xmm0) + AS2( movlps [esi+8*17], xmm1) + // 2 rounds + ASC( call, SHA512_Round) + AS2( sub edi, 8) + AS2( movdq2q mm0, xmm1) + AS2( paddq mm0, [ebx+eax*8+8]) + ASC( call, SHA512_Round) + // update indices and loop + AS2( add esi, 16) + AS2( add eax, 2) + AS2( sub edi, 8) + AS2( test eax, 7) + ASJ( jnz, 1, b) + // do housekeeping every 8 rounds + AS2( mov esi, 0xf) + AS2( and esi, eax) + AS2( lea esi, [esp+4+20*8+8+esi*8]) + AS2( add edi, 8*8) + AS2( cmp eax, 80) + ASJ( jne, 1, b) + +#define SSE2_CombineState(i) \ + AS2( movq mm0, [edi+i*8])\ + AS2( paddq mm0, [ecx+i*8])\ + AS2( movq [ecx+i*8], mm0) + + SSE2_CombineState(0) + SSE2_CombineState(1) + SSE2_CombineState(2) + SSE2_CombineState(3) + SSE2_CombineState(4) + SSE2_CombineState(5) + SSE2_CombineState(6) + SSE2_CombineState(7) + + AS1( pop esp) + AS1( emms) + +#if defined(__GNUC__) + AS1( pop ebx) + ".att_syntax prefix;" + : + : "a" (SHA512_K), "c" (state), "d" (data) + : "%esi", "%edi", "memory", "cc" + ); +#else + AS1( pop edi) + AS1( pop esi) + AS1( pop ebx) + AS1( ret) +#endif +} +#endif // #if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE + +void SHA512::Transform(word64 *state, const word64 *data) +{ +#if CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE && CRYPTOPP_BOOL_X86 + if (HasSSE2()) + { + SHA512_SSE2_Transform(state, data); + return; + } +#endif + +#define S0(x) (rotrFixed(x,28)^rotrFixed(x,34)^rotrFixed(x,39)) +#define S1(x) (rotrFixed(x,14)^rotrFixed(x,18)^rotrFixed(x,41)) +#define s0(x) (rotrFixed(x,1)^rotrFixed(x,8)^(x>>7)) +#define s1(x) (rotrFixed(x,19)^rotrFixed(x,61)^(x>>6)) + +#define R(i) h(i)+=S1(e(i))+Ch(e(i),f(i),g(i))+SHA512_K[i+j]+(j?blk2(i):blk0(i));\ + d(i)+=h(i);h(i)+=S0(a(i))+Maj(a(i),b(i),c(i)) + + word64 W[16]; + word64 T[8]; + /* Copy context->state[] to working vars */ + memcpy(T, state, sizeof(T)); + /* 80 operations, partially loop unrolled */ + for (unsigned int j=0; j<80; j+=16) + { + R( 0); R( 1); R( 2); R( 3); + R( 4); R( 5); R( 6); R( 7); + R( 8); R( 9); R(10); R(11); + R(12); R(13); R(14); R(15); + } + /* Add the working vars back into context.state[] */ + state[0] += a(0); + state[1] += b(0); + state[2] += c(0); + state[3] += d(0); + state[4] += e(0); + state[5] += f(0); + state[6] += g(0); + state[7] += h(0); +} + +#endif + +} diff --git a/src/sha.h b/src/sha.h new file mode 100644 index 0000000..e3af7b4 --- /dev/null +++ b/src/sha.h @@ -0,0 +1,177 @@ +// This file is public domain +// SHA routines extracted as a standalone file from: +// Crypto++: a C++ Class Library of Cryptographic Schemes +// Version 5.5.2 (9/24/2007) +// http://www.cryptopp.com +#ifndef CRYPTOPP_SHA_H +#define CRYPTOPP_SHA_H +#include + +namespace CryptoPP +{ + +// +// Dependencies +// + +typedef unsigned char byte; +typedef unsigned short word16; +typedef unsigned int word32; +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 word64; +#else +typedef unsigned long long word64; +#endif + +template inline T rotlFixed(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x<>(sizeof(T)*8-y))); +} + +template inline T rotrFixed(T x, unsigned int y) +{ + assert(y < sizeof(T)*8); + return T((x>>y) | (x<<(sizeof(T)*8-y))); +} + +// ************** endian reversal *************** + +#ifdef _MSC_VER + #if _MSC_VER >= 1400 + #define CRYPTOPP_FAST_ROTATE(x) 1 + #elif _MSC_VER >= 1300 + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32 | (x) == 64) + #else + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) + #endif +#elif (defined(__MWERKS__) && TARGET_CPU_PPC) || \ + (defined(__GNUC__) && (defined(_ARCH_PWR2) || defined(_ARCH_PWR) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_ARCH_COM))) + #define CRYPTOPP_FAST_ROTATE(x) ((x) == 32) +#elif defined(__GNUC__) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X86) // depend on GCC's peephole optimization to generate rotate instructions + #define CRYPTOPP_FAST_ROTATE(x) 1 +#else + #define CRYPTOPP_FAST_ROTATE(x) 0 +#endif + +inline byte ByteReverse(byte value) +{ + return value; +} + +inline word16 ByteReverse(word16 value) +{ +#ifdef CRYPTOPP_BYTESWAP_AVAILABLE + return bswap_16(value); +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + return _byteswap_ushort(value); +#else + return rotlFixed(value, 8U); +#endif +} + +inline word32 ByteReverse(word32 value) +{ +#if defined(__GNUC__) + __asm__ ("bswap %0" : "=r" (value) : "0" (value)); + return value; +#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) + return bswap_32(value); +#elif defined(__MWERKS__) && TARGET_CPU_PPC + return (word32)__lwbrx(&value,0); +#elif _MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL)) + return _byteswap_ulong(value); +#elif CRYPTOPP_FAST_ROTATE(32) + // 5 instructions with rotate instruction, 9 without + return (rotrFixed(value, 8U) & 0xff00ff00) | (rotlFixed(value, 8U) & 0x00ff00ff); +#else + // 6 instructions with rotate instruction, 8 without + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return rotlFixed(value, 16U); +#endif +} + +#ifdef WORD64_AVAILABLE +inline word64 ByteReverse(word64 value) +{ +#if defined(__GNUC__) && defined(__x86_64__) + __asm__ ("bswap %0" : "=r" (value) : "0" (value)); + return value; +#elif defined(CRYPTOPP_BYTESWAP_AVAILABLE) + return bswap_64(value); +#elif defined(_MSC_VER) && _MSC_VER >= 1300 + return _byteswap_uint64(value); +#elif defined(CRYPTOPP_SLOW_WORD64) + return (word64(ByteReverse(word32(value))) << 32) | ByteReverse(word32(value>>32)); +#else + value = ((value & W64LIT(0xFF00FF00FF00FF00)) >> 8) | ((value & W64LIT(0x00FF00FF00FF00FF)) << 8); + value = ((value & W64LIT(0xFFFF0000FFFF0000)) >> 16) | ((value & W64LIT(0x0000FFFF0000FFFF)) << 16); + return rotlFixed(value, 32U); +#endif +} +#endif + + +// +// SHA +// + +// http://www.weidai.com/scan-mirror/md.html#SHA-1 +class SHA1 +{ +public: + typedef word32 HashWordType; + static void InitState(word32 *state); + static void Transform(word32 *digest, const word32 *data); + static const char * StaticAlgorithmName() {return "SHA-1";} +}; + +typedef SHA1 SHA; // for backwards compatibility + +// implements the SHA-256 standard +class SHA256 +{ +public: + typedef word32 HashWordType; + static void InitState(word32 *state); + static void Transform(word32 *digest, const word32 *data); + static const char * StaticAlgorithmName() {return "SHA-256";} +}; + +// implements the SHA-224 standard +class SHA224 +{ +public: + typedef word32 HashWordType; + static void InitState(word32 *state); + static void Transform(word32 *digest, const word32 *data) {SHA256::Transform(digest, data);} + static const char * StaticAlgorithmName() {return "SHA-224";} +}; + +#ifdef WORD64_AVAILABLE + +// implements the SHA-512 standard +class SHA512 +{ +public: + typedef word64 HashWordType; + static void InitState(word64 *state); + static void Transform(word64 *digest, const word64 *data); + static const char * StaticAlgorithmName() {return "SHA-512";} +}; + +// implements the SHA-384 standard +class SHA384 +{ +public: + typedef word64 HashWordType; + static void InitState(word64 *state); + static void Transform(word64 *digest, const word64 *data) {SHA512::Transform(digest, data);} + static const char * StaticAlgorithmName() {return "SHA-384";} +}; + +#endif + +} + +#endif diff --git a/src/test_btfaddr.cpp b/src/test_btfaddr.cpp new file mode 100644 index 0000000..b40dcc1 --- /dev/null +++ b/src/test_btfaddr.cpp @@ -0,0 +1,142 @@ +// Standalone tests for btfaddr (ported from Itzal hidden.rs tests). +// Build (UCRT64): +// g++ -std=gnu++14 -DSECP256K1_STATIC -I../deps/include test_btfaddr.cpp btfaddr.cpp \ +// -L../deps/lib -lsecp256k1 -lssl -lcrypto -o test_btfaddr.exe + +#include "btfaddr.h" +#include +#define SECP256K1_STATIC +#include +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +static int g_fail = 0; +#define CHECK(cond, name) do { \ + if (cond) { printf(" ok %s\n", name); } \ + else { printf(" FAIL %s\n", name); g_fail++; } } while(0) + +static std::string ToUpper(const std::string& s) +{ + std::string r = s; + for (char& c : r) c = (char)toupper((unsigned char)c); + return r; +} + +int main() +{ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + unsigned char seed[32]; RAND_bytes(seed, 32); secp256k1_context_randomize(ctx, seed); + + printf("base32_roundtrips\n"); + { + std::vector> cases; + std::string txt = "hello btf shadow 0123456789"; + cases.push_back(std::vector(txt.begin(), txt.end())); + cases.push_back(std::vector(34, 0x00)); + cases.push_back(std::vector(34, 0xff)); + for (auto& data : cases) + { + std::string enc = btf::Base32Encode(data.data(), data.size()); + std::vector dec; + bool ok = btf::Base32Decode(enc, dec); + // decode may include trailing partial byte handling; compare the meaningful prefix + CHECK(ok && dec.size() >= data.size() && + memcmp(dec.data(), data.data(), data.size()) == 0, "roundtrip"); + } + } + + printf("address_roundtrips_and_is_lowercase_hostname\n"); + { + unsigned char pk[32]; memset(pk, 7, 32); + std::string a = btf::Address(pk); + CHECK(a.size() > 4 && a.compare(a.size()-4, 4, ".btf") == 0, "ends with .btf"); + CHECK(btf::IsBtf(a), "IsBtf true"); + std::string label = a.substr(0, a.size()-4); + bool lc = true; + for (unsigned char c : label) + if (!((c >= 'a' && c <= 'z') || (c >= '2' && c <= '7'))) lc = false; + CHECK(lc, "label is lowercase base32 hostname"); + unsigned char out[32]; + CHECK(btf::ParseAddress(a, out) && memcmp(out, pk, 32) == 0, "parse round-trips"); + CHECK(btf::ParseAddress(ToUpper(a), out) && memcmp(out, pk, 32) == 0, "parse case-insensitive"); + } + + printf("rejects_tampered_address\n"); + { + unsigned char pk[32]; memset(pk, 9, 32); + std::string a = btf::Address(pk); + std::string t = a; + t[0] = (t[0] == 'a') ? 'b' : 'a'; + unsigned char out[32]; + CHECK(!(btf::ParseAddress(t, out) && memcmp(out, pk, 32) == 0), "tampered address rejected"); + } + + printf("rejects_non_btf\n"); + { + unsigned char out[32]; + CHECK(!btf::IsBtf("example.com"), "example.com not btf"); + CHECK(!btf::ParseAddress("example.com", out), "parse example.com fails"); + CHECK(!btf::ParseAddress("not-base-32-!!!.btf", out), "parse invalid base32 fails"); + } + + const std::string ENC_HEX = "aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"; + + // helper: fresh keypair -> (seckey, xonly pubkey) + auto newKey = [&](unsigned char sk[32], unsigned char pk[32]) { + do { RAND_bytes(sk, 32); } while (!secp256k1_ec_seckey_verify(ctx, sk)); + secp256k1_keypair kp; secp256k1_keypair_create(ctx, &kp, sk); + secp256k1_xonly_pubkey xp; secp256k1_keypair_xonly_pub(ctx, &xp, NULL, &kp); + secp256k1_xonly_pubkey_serialize(ctx, pk, &xp); + }; + + printf("descriptor_signs_and_self_certifies\n"); + { + unsigned char sk[32], pk[32]; newKey(sk, pk); + std::string d = btf::SignDescriptor(ctx, sk, ENC_HEX, "meeting-node-abc", 1000); + CHECK(!d.empty(), "sign produced descriptor"); + btf::Descriptor got; + CHECK(btf::VerifyDescriptor(ctx, d, pk, got), "own key verifies"); + CHECK(got.meeting_node == "meeting-node-abc" && got.enc == ENC_HEX && got.created == 1000, "fields match"); + unsigned char sk2[32], pk2[32]; newKey(sk2, pk2); + btf::Descriptor bad; + CHECK(!btf::VerifyDescriptor(ctx, d, pk2, bad), "different key rejected"); + } + + printf("descriptor_rejects_tampering\n"); + { + unsigned char sk[32], pk[32]; newKey(sk, pk); + std::string d = btf::SignDescriptor(ctx, sk, ENC_HEX, "node-1", 5); + json j = json::parse(d); j["meeting_node"] = "evil-node"; + btf::Descriptor o; + CHECK(!btf::VerifyDescriptor(ctx, j.dump(), pk, o), "tampered meeting_node rejected"); + json j2 = json::parse(d); j2["enc"] = "00000000000000000000000000000000000000000000000000000000deadbeef"; + CHECK(!btf::VerifyDescriptor(ctx, j2.dump(), pk, o), "tampered enc rejected"); + } + + printf("full_resolve_chain_from_address\n"); + { + unsigned char sk[32], pk[32]; newKey(sk, pk); + std::string addr = btf::Address(pk); + unsigned char resolved[32]; + CHECK(btf::ParseAddress(addr, resolved), "address parses"); + std::string d = btf::SignDescriptor(ctx, sk, ENC_HEX, "meeting", 1); + btf::Descriptor o; + CHECK(btf::VerifyDescriptor(ctx, d, resolved, o), "descriptor verifies under resolved key"); + } + + // Show a sample address so we can eyeball the format + { + unsigned char sk[32], pk[32]; newKey(sk, pk); + printf("\nsample .btf address:\n %s\n", btf::Address(pk).c_str()); + } + + printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail); + secp256k1_context_destroy(ctx); + return g_fail == 0 ? 0 : 1; +} diff --git a/src/test_btfchan.cpp b/src/test_btfchan.cpp new file mode 100644 index 0000000..5a66560 --- /dev/null +++ b/src/test_btfchan.cpp @@ -0,0 +1,83 @@ +// Standalone tests for btfchan (ported from Itzal echan.rs behavior). +// Build (UCRT64): +// g++ -std=gnu++14 test_btfchan.cpp btfchan.cpp -lsodium -o test_btfchan.exe + +#include "btfchan.h" +#include +#include +#include +#include + +using namespace btf; +static int g_fail = 0; +#define CHECK(cond, name) do { \ + if (cond) { printf(" ok %s\n", name); } \ + else { printf(" FAIL %s\n", name); g_fail++; } } while(0) + +int main() +{ + if (!ChanInit()) { printf("sodium init failed\n"); return 2; } + + // Endpoint (service) has a static keypair; client only knows the static pub. + unsigned char ep_pk[32], ep_sk[32]; + ChanKeypair(ep_pk, ep_sk); + + printf("handshake_derives_same_key\n"); + unsigned char eph_pub[32], k_client[32], k_endpoint[32]; + CHECK(ChanClientHandshake(ep_pk, eph_pub, k_client), "client handshake ok"); + CHECK(ChanEndpointHandshake(eph_pub, ep_sk, k_endpoint), "endpoint handshake ok"); + CHECK(memcmp(k_client, k_endpoint, 32) == 0, "both sides derive the SAME shared key"); + + printf("encrypt_decrypt_roundtrip\n"); + { + std::string msg = "one CPU one vote -- blinded over the wire"; + std::vector frame = ChanEncryptFrame(k_client, + (const unsigned char*)msg.data(), msg.size()); + // frame = [len:4][nonce:24][ct||tag] + size_t bodylen = (frame[0]<<24)|(frame[1]<<16)|(frame[2]<<8)|frame[3]; + CHECK(bodylen == frame.size() - 4, "length prefix correct"); + std::vector pt; + CHECK(ChanDecryptBody(k_endpoint, &frame[4], bodylen, pt), "endpoint decrypts"); + CHECK(pt.size() == msg.size() && memcmp(pt.data(), msg.data(), msg.size()) == 0, "plaintext matches"); + } + + printf("tampered_ciphertext_fails\n"); + { + std::string msg = "secret block"; + std::vector frame = ChanEncryptFrame(k_client, + (const unsigned char*)msg.data(), msg.size()); + size_t bodylen = frame.size() - 4; + frame[frame.size()-1] ^= 0x01; // flip a bit in the tag/ciphertext + std::vector pt; + CHECK(!ChanDecryptBody(k_endpoint, &frame[4], bodylen, pt), "tampered frame rejected"); + } + + printf("wrong_key_fails\n"); + { + unsigned char other_pk[32], other_sk[32]; + ChanKeypair(other_pk, other_sk); + unsigned char eph2[32], kbad[32]; + ChanClientHandshake(other_pk, eph2, kbad); // box to a DIFFERENT endpoint + std::string msg = "for someone else"; + std::vector frame = ChanEncryptFrame(kbad, + (const unsigned char*)msg.data(), msg.size()); + size_t bodylen = frame.size() - 4; + std::vector pt; + CHECK(!ChanDecryptBody(k_endpoint, &frame[4], bodylen, pt), "our endpoint can't open someone else's box"); + } + + printf("empty_and_large_payloads\n"); + { + std::vector empty; + std::vector f0 = ChanEncryptFrame(k_client, NULL, 0); + std::vector o0; + CHECK(ChanDecryptBody(k_endpoint, &f0[4], f0.size()-4, o0) && o0.empty(), "empty payload roundtrips"); + std::vector big(200000, 0xAB); + std::vector fb = ChanEncryptFrame(k_client, big.data(), big.size()); + std::vector ob; + CHECK(ChanDecryptBody(k_endpoint, &fb[4], fb.size()-4, ob) && ob == big, "200KB payload roundtrips"); + } + + printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail); + return g_fail == 0 ? 0 : 1; +} diff --git a/src/test_btfrv.cpp b/src/test_btfrv.cpp new file mode 100644 index 0000000..291e660 --- /dev/null +++ b/src/test_btfrv.cpp @@ -0,0 +1,116 @@ +// Standalone loopback test of the rendezvous transport + echan on top. +// A relay, a service and a client all run in one process on 127.0.0.1 (a unit +// test of the protocol logic -- NOT a network/LAN test). Proves: service +// registers, client pairs by pubkey through the relay, and they exchange data +// end-to-end encrypted so the relay only ever forwards ciphertext. +// +// Build (UCRT64): +// g++ -std=gnu++14 test_btfrv.cpp btfrv.cpp btfchan.cpp -lsodium -lws2_32 -o test_btfrv.exe + +#include "btfrv.h" +#include "btfchan.h" +#include +#include +#include +#include +#include +#include + +using namespace btf; +static int g_fail = 0; +#define CHECK(cond, name) do { \ + if (cond) { printf(" ok %s\n", name); } \ + else { printf(" FAIL %s\n", name); g_fail++; } } while(0) + +static const unsigned short PORT = 18443; + +// echan framing over a rendezvous socket +static bool WriteFrame(RvSocket s, const unsigned char shared[32], const std::string& pt) +{ + std::vector f = ChanEncryptFrame(shared, (const unsigned char*)pt.data(), pt.size()); + return RvWriteN(s, f.data(), (int)f.size()); +} +static bool ReadFrame(RvSocket s, const unsigned char shared[32], std::string& out) +{ + unsigned char len[4]; + if (!RvReadN(s, len, 4)) return false; + size_t n = ((size_t)len[0]<<24)|((size_t)len[1]<<16)|((size_t)len[2]<<8)|len[3]; + if (n == 0 || n > (1u<<20)) return false; + std::vector body(n); + if (!RvReadN(s, body.data(), (int)n)) return false; + std::vector pt; + if (!ChanDecryptBody(shared, body.data(), (int)n, pt)) return false; + out.assign((char*)pt.data(), pt.size()); + return true; +} + +static std::string g_svcGot; +static bool g_svcOk = false; + +static void ServiceThread(std::vector pairing_id, std::vector svc_enc_sk) +{ + RvSocket s = RvServiceRegister("127.0.0.1", PORT, pairing_id.data()); + if (s == RV_INVALID) return; + // read the client's ephemeral x25519 public key + unsigned char eph[32]; + if (!RvReadN(s, eph, 32)) { RvClose(s); return; } + unsigned char shared[32]; + if (!ChanEndpointHandshake(eph, svc_enc_sk.data(), shared)) { RvClose(s); return; } + std::string msg; + if (!ReadFrame(s, shared, msg)) { RvClose(s); return; } + g_svcGot = msg; + WriteFrame(s, shared, std::string("block accepted: ") + msg); + g_svcOk = true; + RvClose(s); +} + +int main() +{ + if (!ChanInit() || !RvInit()) { printf("init failed\n"); return 2; } + + // service identity: a 32-byte .btf pairing id + an x25519 enc keypair + unsigned char svc_enc_pk[32], svc_enc_sk[32]; + ChanKeypair(svc_enc_pk, svc_enc_sk); + std::vector pairing_id(32); + for (int i = 0; i < 32; i++) pairing_id[i] = (unsigned char)(i * 7 + 1); + + printf("rendezvous_relay_pairs_and_forwards_encrypted\n"); + + std::thread relay([]{ RvRelayRun(PORT); }); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); // let it bind + + std::thread svc(ServiceThread, pairing_id, std::vector(svc_enc_sk, svc_enc_sk+32)); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); // let service register + + // client dials the pairing id through the relay + RvSocket c = RvClientConnect("127.0.0.1", PORT, pairing_id.data()); + CHECK(c != RV_INVALID, "client paired to service via relay"); + if (c != RV_INVALID) + { + unsigned char eph_pub[32], shared_client[32]; + CHECK(ChanClientHandshake(svc_enc_pk, eph_pub, shared_client), "client handshake"); + CHECK(RvWriteN(c, eph_pub, 32), "client sends ephemeral pubkey"); + CHECK(WriteFrame(c, shared_client, "hello one-cpu-one-vote"), "client sends encrypted frame"); + std::string reply; + CHECK(ReadFrame(c, shared_client, reply), "client reads encrypted reply"); + CHECK(reply == "block accepted: hello one-cpu-one-vote", "end-to-end message round-trips through relay"); + RvClose(c); + } + + svc.join(); + CHECK(g_svcOk && g_svcGot == "hello one-cpu-one-vote", "service received the decrypted plaintext"); + + printf("unknown_pubkey_is_rejected\n"); + { + unsigned char other[32]; + for (int i = 0; i < 32; i++) other[i] = 0xEE; + RvSocket bad = RvClientConnect("127.0.0.1", PORT, other); + CHECK(bad == RV_INVALID, "client for unregistered .btf is rejected"); + } + + RvRelayStop(); + relay.join(); + + printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail); + return g_fail == 0 ? 0 : 1; +} diff --git a/src/test_btftunnel.cpp b/src/test_btftunnel.cpp new file mode 100644 index 0000000..df01da8 --- /dev/null +++ b/src/test_btftunnel.cpp @@ -0,0 +1,97 @@ +// Full-stack loopback test of the anonymous tunnel: relay + service + client in +// one process on 127.0.0.1 (unit test of the logic, NOT a LAN/network test). +// The app-facing sockets are plain TCP; the wire between them is echan over the +// rendezvous relay. Proves CNode can use a .btf peer as an ordinary socket. +// +// Build (UCRT64): +// g++ -std=gnu++14 test_btftunnel.cpp btftunnel.cpp btfrv.cpp btfchan.cpp -lsodium -lws2_32 -o test_btftunnel.exe + +#include "btftunnel.h" +#include "btfrv.h" +#include "btfchan.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace btf; +static int g_fail = 0; +#define CHECK(cond, name) do { \ + if (cond) { printf(" ok %s\n", name); } \ + else { printf(" FAIL %s\n", name); g_fail++; } } while(0) + +static const unsigned short PORT = 18444; + +static bool RecvN(btf_socket_t s, void* buf, int n) +{ + char* p = (char*)buf; int off = 0; + while (off < n) { int r = recv((SOCKET)s, p + off, n - off, 0); if (r <= 0) return false; off += r; } + return true; +} +static bool SendN(btf_socket_t s, const void* buf, int n) +{ + const char* p = (const char*)buf; int off = 0; + while (off < n) { int r = send((SOCKET)s, p + off, n - off, 0); if (r <= 0) return false; off += r; } + return true; +} + +static bool g_svcOk = false; +static std::string g_svcGot; + +static void ServiceThread(std::vector pairing_id, std::vector enc_sk, int msglen) +{ + RvSocket rv = RvServiceRegister("127.0.0.1", PORT, pairing_id.data()); + if (rv == RV_INVALID) return; + btf_socket_t app = BtfServiceWrap(rv, enc_sk.data()); + if (app == INVALID_SOCKET) return; + // Read the client's plaintext message and echo it back (as a normal socket). + std::vector buf(msglen); + if (!RecvN(app, buf.data(), msglen)) { closesocket((SOCKET)app); return; } + g_svcGot.assign(buf.data(), msglen); + g_svcOk = SendN(app, buf.data(), msglen); + closesocket((SOCKET)app); +} + +int main() +{ + if (!TunnelInit()) { printf("init failed\n"); return 2; } + + unsigned char svc_enc_pk[32], svc_enc_sk[32]; + ChanKeypair(svc_enc_pk, svc_enc_sk); + std::vector pairing_id(32); + for (int i = 0; i < 32; i++) pairing_id[i] = (unsigned char)(i * 3 + 5); + + std::string msg = "hello one-cpu-one-vote via anonymous .btf tunnel"; + int msglen = (int)msg.size(); + + printf("anonymous_tunnel_full_stack\n"); + + std::thread relay([]{ RvRelayRun(PORT); }); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + std::thread svc(ServiceThread, pairing_id, + std::vector(svc_enc_sk, svc_enc_sk+32), msglen); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + btf_socket_t app = BtfClientTunnel("127.0.0.1", PORT, pairing_id.data(), svc_enc_pk); + CHECK(app != INVALID_SOCKET, "client built anonymous tunnel to service"); + if (app != INVALID_SOCKET) + { + CHECK(SendN(app, msg.data(), msglen), "app writes plaintext to the tunnel socket"); + std::vector echo(msglen); + CHECK(RecvN(app, echo.data(), msglen), "app reads echo from the tunnel socket"); + CHECK(std::string(echo.data(), msglen) == msg, "plaintext round-trips end-to-end through the relay"); + closesocket((SOCKET)app); + } + + svc.join(); + CHECK(g_svcOk && g_svcGot == msg, "service saw the correct decrypted plaintext"); + + RvRelayStop(); + relay.join(); + + printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail); + return g_fail == 0 ? 0 : 1; +} diff --git a/src/test_relayproc.cpp b/src/test_relayproc.cpp new file mode 100644 index 0000000..f9d7f07 --- /dev/null +++ b/src/test_relayproc.cpp @@ -0,0 +1,68 @@ +// Tests the tunnel against a SEPARATE running bitflashd relay process (not an +// in-process relay) -- i.e. exactly the VPS deployment shape, on loopback. +// Usage: test_relayproc.exe (a bitflashd must already listen there) +// +// Build: g++ -std=gnu++14 -O2 -pthread test_relayproc.cpp btftunnel.cpp btfrv.cpp btfchan.cpp -lsodium -lws2_32 -o test_relayproc.exe + +#include "btftunnel.h" +#include "btfrv.h" +#include "btfchan.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace btf; +static int g_fail = 0; +#define CHECK(c,n) do{ if(c){printf(" ok %s\n",n);} else {printf(" FAIL %s\n",n); g_fail++;} }while(0) + +static unsigned short PORT = 18446; +static const char* HOST = "127.0.0.1"; +static bool RecvN(SOCKET s, void* b, int n){char*p=(char*)b;int o=0;while(o id, std::vector sk, int len) +{ + RvSocket rv = RvServiceRegister(HOST, PORT, id.data()); + if (rv==RV_INVALID) return; + btf_socket_t app = BtfServiceWrap(rv, sk.data()); + if (app==INVALID_SOCKET) return; + std::vector buf(len); + if(!RecvN((SOCKET)app, buf.data(), len)){closesocket((SOCKET)app);return;} + g_got.assign(buf.data(), len); + g_ok = SendN((SOCKET)app, buf.data(), len); + closesocket((SOCKET)app); +} + +int main(int argc, char* argv[]) +{ + if (argc>=2) HOST=argv[1]; if (argc>=3) PORT=(unsigned short)atoi(argv[2]); + if(!TunnelInit()){printf("init failed\n");return 2;} + unsigned char pk[32], sk[32]; ChanKeypair(pk,sk); + std::vector id(32); for(int i=0;i<32;i++) id[i]=(unsigned char)(i*11+3); + std::string msg="anonymous hop through a real bitflashd relay process"; + int len=(int)msg.size(); + + printf("tunnel_through_external_relay_process (port %u)\n", PORT); + std::thread svc(Service, id, std::vector(sk,sk+32), len); + std::this_thread::sleep_for(std::chrono::milliseconds(400)); + + btf_socket_t app = BtfClientTunnel(HOST, PORT, id.data(), pk); + CHECK(app!=INVALID_SOCKET, "client tunneled through the running bitflashd"); + if(app!=INVALID_SOCKET){ + CHECK(SendN((SOCKET)app,msg.data(),len), "app writes plaintext"); + std::vector echo(len); + CHECK(RecvN((SOCKET)app,echo.data(),len), "app reads echo"); + CHECK(std::string(echo.data(),len)==msg, "round-trips end-to-end via the relay process"); + closesocket((SOCKET)app); + } + svc.join(); + CHECK(g_ok && g_got==msg, "service saw correct plaintext"); + printf("\n%s (%d failures)\n", g_fail==0?"ALL TESTS PASSED":"TESTS FAILED", g_fail); + return g_fail==0?0:1; +} diff --git a/src/ui.cpp b/src/ui.cpp new file mode 100644 index 0000000..3e734a5 --- /dev/null +++ b/src/ui.cpp @@ -0,0 +1,3265 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" +#ifdef _MSC_VER +#include +#endif + + + +DEFINE_EVENT_TYPE(wxEVT_CROSSTHREADCALL) + +// wx3 is Unicode: conversion bridges for the C functions inherited from 2009 +static inline bool ParseMoney(const wxString& str, int64& nRet) +{ + return ParseMoney(std::string(str.mb_str()).c_str(), nRet); +} +DEFINE_EVENT_TYPE(wxEVT_REPLY1) +DEFINE_EVENT_TYPE(wxEVT_REPLY2) +DEFINE_EVENT_TYPE(wxEVT_REPLY3) +DEFINE_EVENT_TYPE(wxEVT_TABLEADDED) +DEFINE_EVENT_TYPE(wxEVT_TABLEUPDATED) +DEFINE_EVENT_TYPE(wxEVT_TABLEDELETED) + +CMainFrame* pframeMain = NULL; +map mapAddressBook; + + +void ThreadRequestProductDetails(void* parg); +void ThreadRandSendTest(void* parg); +bool fRandSendTest = false; +void RandSend(); + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Util +// + +void HandleCtrlA(wxKeyEvent& event) +{ + // Ctrl-a select all + wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject(); + if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A') + textCtrl->SetSelection(-1, -1); + event.Skip(); +} + +bool Is24HourTime() +{ + //char pszHourFormat[256]; + //pszHourFormat[0] = '\0'; + //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256); + //return (pszHourFormat[0] != '0'); + return true; +} + +string DateStr(int64 nTime) +{ + return (string)wxDateTime((time_t)nTime).FormatDate(); +} + +string DateTimeStr(int64 nTime) +{ + wxDateTime datetime((time_t)nTime); + if (Is24HourTime()) + return (string)datetime.Format("%x %H:%M"); + else + return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p"); +} + +wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn) +{ + // Helper to simplify access to listctrl + wxListItem item; + item.m_itemId = nIndex; + item.m_col = nColumn; + item.m_mask = wxLIST_MASK_TEXT; + if (!listCtrl->GetItem(item)) + return ""; + return item.GetText(); +} + +int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1) +{ + int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0); + listCtrl->SetItem(nIndex, 1, str1); + return nIndex; +} + +int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4) +{ + int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0); + listCtrl->SetItem(nIndex, 1, str1); + listCtrl->SetItem(nIndex, 2, str2); + listCtrl->SetItem(nIndex, 3, str3); + listCtrl->SetItem(nIndex, 4, str4); + return nIndex; +} + +int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4) +{ + int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0); + listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata); + listCtrl->SetItem(nIndex, 1, str1); + listCtrl->SetItem(nIndex, 2, str2); + listCtrl->SetItem(nIndex, 3, str3); + listCtrl->SetItem(nIndex, 4, str4); + return nIndex; +} + +void SetSelection(wxListCtrl* listCtrl, int nIndex) +{ + int nSize = listCtrl->GetItemCount(); + long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED); + for (int i = 0; i < nSize; i++) + listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState); +} + +int GetSelection(wxListCtrl* listCtrl) +{ + int nSize = listCtrl->GetItemCount(); + for (int i = 0; i < nSize; i++) + if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED)) + return i; + return -1; +} + + +string HtmlEscape(const char* psz, bool fMultiLine=false) +{ + int len = 0; + for (const char* p = psz; *p; p++) + { + if (*p == '<') len += 4; + else if (*p == '>') len += 4; + else if (*p == '&') len += 5; + else if (*p == '"') len += 6; + else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6; + else if (*p == '\n' && fMultiLine) len += 5; + else + len++; + } + string str; + str.reserve(len); + for (const char* p = psz; *p; p++) + { + if (*p == '<') str += "<"; + else if (*p == '>') str += ">"; + else if (*p == '&') str += "&"; + else if (*p == '"') str += """; + else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " "; + else if (*p == '\n' && fMultiLine) str += "
\n"; + else + str += *p; + } + return str; +} + +string HtmlEscape(const string& str, bool fMultiLine=false) +{ + return HtmlEscape(str.c_str(), fMultiLine); +} + +void AddToMyProducts(CProduct product) +{ + CProduct& productInsert = mapMyProducts[product.GetHash()]; + productInsert = product; + InsertLine(pframeMain->m_listCtrlProductsSent, &productInsert, + product.mapValue["category"], + product.mapValue["title"].substr(0, 100), + product.mapValue["description"].substr(0, 100), + product.mapValue["price"], + ""); +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// Custom events +// + +set setCallbackAvailable; +CCriticalSection cs_setCallbackAvailable; + +void AddCallbackAvailable(void* p) +{ + CRITICAL_BLOCK(cs_setCallbackAvailable) + setCallbackAvailable.insert(p); +} + +void RemoveCallbackAvailable(void* p) +{ + CRITICAL_BLOCK(cs_setCallbackAvailable) + setCallbackAvailable.erase(p); +} + +bool IsCallbackAvailable(void* p) +{ + CRITICAL_BLOCK(cs_setCallbackAvailable) + return setCallbackAvailable.count(p); + return false; +} + +template +void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T pbeginIn, const T pendIn) +{ + if (!pevthandler) + return; + + const char* pbegin = (pendIn != pbeginIn) ? (const char*)&pbeginIn[0] : NULL; + const char* pend = pbegin + (pendIn - pbeginIn) * sizeof(pbeginIn[0]); + wxCommandEvent event(nEventID); + // wxString in wx3 is Unicode and does not carry raw bytes; + // the serialized data travels in a buffer that the receiver frees + event.SetClientData(new std::string(pbegin, pend)); + + pevthandler->AddPendingEvent(event); +} + +template +void AddPendingCustomEvent(wxEvtHandler* pevthandler, int nEventID, const T& obj) +{ + CDataStream ss; + ss << obj; + AddPendingCustomEvent(pevthandler, nEventID, ss.begin(), ss.end()); +} + +void AddPendingReplyEvent1(void* pevthandler, CDataStream& vRecv) +{ + if (IsCallbackAvailable(pevthandler)) + AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY1, vRecv.begin(), vRecv.end()); +} + +void AddPendingReplyEvent2(void* pevthandler, CDataStream& vRecv) +{ + if (IsCallbackAvailable(pevthandler)) + AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY2, vRecv.begin(), vRecv.end()); +} + +void AddPendingReplyEvent3(void* pevthandler, CDataStream& vRecv) +{ + if (IsCallbackAvailable(pevthandler)) + AddPendingCustomEvent((wxEvtHandler*)pevthandler, wxEVT_REPLY3, vRecv.begin(), vRecv.end()); +} + +CDataStream GetStreamFromEvent(const wxCommandEvent& event) +{ + std::string* pstrData = (std::string*)event.GetClientData(); + if (pstrData == NULL) + return CDataStream(SER_NETWORK); + CDataStream ss(pstrData->data(), pstrData->data() + pstrData->size(), SER_NETWORK); + delete pstrData; + ((wxCommandEvent&)event).SetClientData(NULL); + return ss; +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CMainFrame +// + +CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent) +{ + Connect(wxEVT_CROSSTHREADCALL, wxCommandEventHandler(CMainFrame::OnCrossThreadCall), NULL, this); + + // Init + fRefreshListCtrl = false; + fRefreshListCtrlRunning = false; + fOnSetFocusAddress = false; + pindexBestLast = NULL; + m_choiceFilter->SetSelection(0); + m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " "); + m_listCtrl->SetFocus(); + SetIcon(wxICON(bitcoin)); + m_menuOptions->Check(wxID_OPTIONSGENERATEBITCOINS, fGenerateBitcoins); + + // Init toolbar with transparency masked bitmaps + m_toolBar->ClearTools(); + + //// shouldn't have to do mask separately anymore, bitmap alpha support added in wx 2.8.9, + wxBitmap bmpSend(wxT("send20"), wxBITMAP_TYPE_RESOURCE); + bmpSend.SetMask(new wxMask(wxBitmap(wxT("send20mask"), wxBITMAP_TYPE_RESOURCE))); + m_toolBar->AddTool(wxID_BUTTONSEND, wxT("&Send Coins"), bmpSend, wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString); + + wxBitmap bmpAddressBook(wxT("addressbook20"), wxBITMAP_TYPE_RESOURCE); + bmpAddressBook.SetMask(new wxMask(wxBitmap(wxT("addressbook20mask"), wxBITMAP_TYPE_RESOURCE))); + m_toolBar->AddTool(wxID_BUTTONRECEIVE, wxT("&Address Book"), bmpAddressBook, wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString); + + m_toolBar->Realize(); + + // Init column headers + int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8; + m_listCtrl->InsertColumn(0, "", wxLIST_FORMAT_LEFT, 0); + m_listCtrl->InsertColumn(1, "", wxLIST_FORMAT_LEFT, 0); + m_listCtrl->InsertColumn(2, "Status", wxLIST_FORMAT_LEFT, 90); + m_listCtrl->InsertColumn(3, "Date", wxLIST_FORMAT_LEFT, nDateWidth); + m_listCtrl->InsertColumn(4, "Description", wxLIST_FORMAT_LEFT, 409 - nDateWidth); + m_listCtrl->InsertColumn(5, "Debit", wxLIST_FORMAT_RIGHT, 79); + m_listCtrl->InsertColumn(6, "Credit", wxLIST_FORMAT_RIGHT, 79); + + //m_listCtrlProductsSent->InsertColumn(0, "Category", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlProductsSent->InsertColumn(1, "Title", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlProductsSent->InsertColumn(2, "Description", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlProductsSent->InsertColumn(3, "Price", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlProductsSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100); + + //m_listCtrlOrdersSent->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersSent->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersSent->InsertColumn(2, "", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersSent->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersSent->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100); + + //m_listCtrlOrdersReceived->InsertColumn(0, "Time", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersReceived->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersReceived->InsertColumn(2, "Payment Status", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersReceived->InsertColumn(3, "", wxLIST_FORMAT_LEFT, 100); + //m_listCtrlOrdersReceived->InsertColumn(4, "", wxLIST_FORMAT_LEFT, 100); + + // Init status bar + int pnWidths[3] = { -100, 81, 286 }; + m_statusBar->SetFieldsCount(3, pnWidths); + + // Fill your address text box + vector vchPubKey; + if (CWalletDB("r").ReadDefaultKey(vchPubKey)) + m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey)); + + // Fill listctrl with wallet transactions + RefreshListCtrl(); +} + +CMainFrame::~CMainFrame() +{ + pframeMain = NULL; +} + +void Shutdown(void* parg) +{ + static CCriticalSection cs_Shutdown; + CRITICAL_BLOCK(cs_Shutdown) + { + fShutdown = true; + nTransactionsUpdated++; + DBFlush(false); + StopNode(); + DBFlush(true); + + printf("Bitcoin exiting\n"); + exit(0); + } +} + +void CMainFrame::OnClose(wxCloseEvent& event) +{ + Destroy(); + _beginthread(Shutdown, 0, NULL); +} + +void CMainFrame::OnMouseEvents(wxMouseEvent& event) +{ + RandAddSeed(); + RAND_add(&event.m_x, sizeof(event.m_x), 0.25); + RAND_add(&event.m_y, sizeof(event.m_y), 0.25); +} + +void CMainFrame::OnListColBeginDrag(wxListEvent& event) +{ + // Hidden columns not resizeable + if (event.GetColumn() <= 1 && !fDebug) + event.Veto(); +} + +void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6) +{ + string str0 = strSort; + long nData = *(long*)&hashKey; + + if (fNew) + { + nIndex = m_listCtrl->InsertItem(0, str0); + } + else + { + if (nIndex == -1) + { + // Find item + while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1) + if (GetItemText(m_listCtrl, nIndex, 1) == hashKey.ToString()) + break; + if (nIndex == -1) + { + printf("CMainFrame::InsertLine : Couldn't find item to be updated\n"); + return; + } + } + + // If sort key changed, must delete and reinsert to make it relocate + if (GetItemText(m_listCtrl, nIndex, 0) != str0) + { + m_listCtrl->DeleteItem(nIndex); + nIndex = m_listCtrl->InsertItem(0, str0); + } + } + + m_listCtrl->SetItem(nIndex, 1, hashKey.ToString()); + m_listCtrl->SetItem(nIndex, 2, str2); + m_listCtrl->SetItem(nIndex, 3, str3); + m_listCtrl->SetItem(nIndex, 4, str4); + m_listCtrl->SetItem(nIndex, 5, str5); + m_listCtrl->SetItem(nIndex, 6, str6); + m_listCtrl->SetItemData(nIndex, nData); +} + +string FormatTxStatus(const CWalletTx& wtx) +{ + // Status + int nDepth = wtx.GetDepthInMainChain(); + if (!wtx.IsFinal()) + return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime); + else if (nDepth < 6) + return strprintf("%d/unconfirmed", nDepth); + else + return strprintf("%d blocks", nDepth); +} + +void CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex) +{ + int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + uint256 hash = wtx.GetHash(); + string strStatus = FormatTxStatus(wtx); + map mapValue = wtx.mapValue; + + // Find the block the tx is in + CBlockIndex* pindex = NULL; + map::iterator mi = mapBlockIndex.find(wtx.hashBlock); + if (mi != mapBlockIndex.end()) + pindex = (*mi).second; + + // Sort order, unrecorded transactions sort to the top + string strSort = strprintf("%010d-%01d-%010u", + (pindex ? pindex->nHeight : INT_MAX), + (wtx.IsCoinBase() ? 1 : 0), + wtx.nTimeReceived); + + // Insert line + if (nNet > 0 || wtx.IsCoinBase()) + { + // + // Credit + // + string strDescription; + + if (wtx.IsCoinBase()) + { + // Coinbase + strDescription = "Generated"; + if (nCredit == 0) + { + int64 nUnmatured = 0; + foreach(const CTxOut& txout, wtx.vout) + nUnmatured += txout.GetCredit(); + if (wtx.IsInMainChain()) + strDescription += strprintf(" (%s matures in %d blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + else + strDescription += " (not accepted)"; + } + } + else if (!mapValue["from"].empty() || !mapValue["message"].empty()) + { + // Online transaction + if (!mapValue["from"].empty()) + strDescription += "From: " + mapValue["from"]; + if (!mapValue["message"].empty()) + { + if (!strDescription.empty()) + strDescription += " - "; + strDescription += mapValue["message"]; + } + } + else + { + // Offline transaction + foreach(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + { + vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + { + string strAddress = PubKeyToAddress(vchPubKey); + if (mapAddressBook.count(strAddress)) + { + strDescription += "Received with: "; + if (!mapAddressBook[strAddress].empty()) + strDescription += mapAddressBook[strAddress] + " "; + strDescription += strAddress; + } + } + break; + } + } + } + + string strDescription2; + foreach(int c, strDescription) + if (c >= ' ') + strDescription2 += c; + + InsertLine(fNew, nIndex, hash, strSort, + strStatus, + nTime ? DateTimeStr(nTime) : "", + strDescription2, + "", + FormatMoney(nNet, true)); + } + else + { + bool fAllFromMe = true; + foreach(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && txin.IsMine(); + + bool fAllToMe = true; + foreach(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && txout.IsMine(); + + if (fAllFromMe && fAllToMe) + { + // Payment to self + int64 nValue = wtx.vout[0].nValue; + InsertLine(fNew, nIndex, hash, strSort, + strStatus, + nTime ? DateTimeStr(nTime) : "", + "Payment to yourself", + FormatMoney(nNet - nValue, true), + FormatMoney(nValue, true)); + } + else if (fAllFromMe) + { + // + // Debit + // + int64 nTxFee = nDebit - wtx.GetValueOut(); + for (int nOut = 0; nOut < wtx.vout.size(); nOut++) + { + const CTxOut& txout = wtx.vout[nOut]; + if (txout.IsMine()) + continue; + + string strAddress; + if (!mapValue["to"].empty()) + { + // Online transaction + strAddress = mapValue["to"]; + } + else + { + // Offline transaction + uint160 hash160; + if (ExtractHash160(txout.scriptPubKey, hash160)) + strAddress = Hash160ToAddress(hash160); + } + + string strDescription = "To: "; + if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) + strDescription += mapAddressBook[strAddress] + " "; + strDescription += strAddress; + if (!mapValue["message"].empty()) + { + if (!strDescription.empty()) + strDescription += " - "; + strDescription += mapValue["message"]; + } + + string strDescription2; + foreach(int c, strDescription) + if (c >= ' ') + strDescription2 += c; + + int64 nValue = txout.nValue; + if (nOut == 0 && nTxFee > 0) + nValue += nTxFee; + + InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), + strStatus, + nTime ? DateTimeStr(nTime) : "", + strDescription2, + FormatMoney(-nValue, true), + ""); + } + } + else + { + // + // Mixed debit transaction, can't break down payees + // + bool fAllMine = true; + foreach(const CTxOut& txout, wtx.vout) + fAllMine = fAllMine && txout.IsMine(); + foreach(const CTxIn& txin, wtx.vin) + fAllMine = fAllMine && txin.IsMine(); + + InsertLine(fNew, nIndex, hash, strSort, + strStatus, + nTime ? DateTimeStr(nTime) : "", + "", + FormatMoney(nNet, true), + ""); + } + } +} + +void CMainFrame::RefreshStatus() +{ + static int nLastTop; + int nTop = m_listCtrl->GetTopItem(); + if (nTop == nLastTop && pindexBestLast == pindexBest) + return; + + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + int nStart = nTop; + int nEnd = min(nStart + 100, m_listCtrl->GetItemCount()); + if (pindexBestLast == pindexBest) + { + if (nStart >= nLastTop && nStart < nLastTop + 100) + nStart = nLastTop + 100; + if (nEnd >= nLastTop && nEnd < nLastTop + 100) + nEnd = nLastTop; + } + nLastTop = nTop; + pindexBestLast = pindexBest; + + for (int nIndex = nStart; nIndex < nEnd; nIndex++) + { + uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1)); + map::iterator mi = mapWallet.find(hash); + if (mi == mapWallet.end()) + { + printf("CMainFrame::RefreshStatus() : tx not found in mapWallet\n"); + continue; + } + const CWalletTx& wtx = (*mi).second; + if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed) + InsertTransaction(wtx, false, nIndex); + else + m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx)); + } + } +} + +void CMainFrame::RefreshListCtrl() +{ + fRefreshListCtrl = true; + ::wxWakeUpIdle(); +} + +void CMainFrame::OnIdle(wxIdleEvent& event) +{ + if (fRefreshListCtrl) + { + // Collect list of wallet transactions and sort newest first + bool fEntered = false; + vector > vSorted; + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + printf("RefreshListCtrl starting\n"); + fEntered = true; + fRefreshListCtrl = false; + vWalletUpdated.clear(); + + // Do the newest transactions first + vSorted.reserve(mapWallet.size()); + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + unsigned int nTime = UINT_MAX - wtx.GetTxTime(); + vSorted.push_back(make_pair(nTime, (*it).first)); + } + m_listCtrl->DeleteAllItems(); + } + if (!fEntered) + return; + + sort(vSorted.begin(), vSorted.end()); + + // Fill list control + for (int i = 0; i < vSorted.size();) + { + if (fShutdown) + return; + bool fEntered = false; + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + fEntered = true; + uint256& hash = vSorted[i++].second; + map::iterator mi = mapWallet.find(hash); + if (mi != mapWallet.end()) + InsertTransaction((*mi).second, true); + } + if (!fEntered || i == 100 || i % 500 == 0) + wxYield(); + } + + printf("RefreshListCtrl done\n"); + } + else + { + // Check for time updates + static int64 nLastTime; + if (GetTime() > nLastTime + 30) + { + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + nLastTime = GetTime(); + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + CWalletTx& wtx = (*it).second; + if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime()) + InsertTransaction(wtx, false); + } + } + } + } +} + +void CMainFrame::OnPaint(wxPaintEvent& event) +{ + event.Skip(); +} + +void CMainFrame::OnPaintListCtrl(wxPaintEvent& event) +{ + // Update listctrl contents + if (!vWalletUpdated.empty()) + { + TRY_CRITICAL_BLOCK(cs_mapWallet) + { + pair item; + foreach(item, vWalletUpdated) + { + bool fNew = item.second; + map::iterator mi = mapWallet.find(item.first); + if (mi != mapWallet.end()) + { + printf("vWalletUpdated: %s %s\n", (*mi).second.GetHash().ToString().substr(0,6).c_str(), fNew ? "new" : ""); + InsertTransaction((*mi).second, fNew); + } + } + m_listCtrl->ScrollList(0, INT_MAX); + vWalletUpdated.clear(); + } + } + + // Update status column of visible items only + RefreshStatus(); + + // Update status bar + string strGen = ""; + if (fGenerateBitcoins) + strGen = " Generating"; + if (fGenerateBitcoins && vNodes.empty()) + strGen = "(not connected)"; + m_statusBar->SetStatusText(strGen, 1); + + string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, m_listCtrl->GetItemCount()); + m_statusBar->SetStatusText(strStatus, 2); + + // Balance total + TRY_CRITICAL_BLOCK(cs_mapWallet) + m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " "); + + m_listCtrl->OnPaint(event); +} + +void CrossThreadCall(wxCommandEvent& event) +{ + if (pframeMain) + pframeMain->GetEventHandler()->AddPendingEvent(event); +} + +void CrossThreadCall(int nID, void* pdata) +{ + wxCommandEvent event; + event.SetInt(nID); + event.SetClientData(pdata); + if (pframeMain) + pframeMain->GetEventHandler()->AddPendingEvent(event); +} + +void CMainFrame::OnCrossThreadCall(wxCommandEvent& event) +{ + void* pdata = event.GetClientData(); + switch (event.GetInt()) + { + case UICALL_ADDORDER: + { + break; + } + + case UICALL_UPDATEORDER: + { + break; + } + } +} + +void CMainFrame::OnMenuFileExit(wxCommandEvent& event) +{ + Close(true); +} + +void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event) +{ + fGenerateBitcoins = event.IsChecked(); + nTransactionsUpdated++; + CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); + + if (fGenerateBitcoins) + if (_beginthread(ThreadBitcoinMiner, 0, NULL) == -1) + printf("Error: _beginthread(ThreadBitcoinMiner) failed\n"); + + Refresh(); + Update(); +} + +void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event) +{ + COptionsDialog dialog(this); + dialog.ShowModal(); +} + +void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event) +{ + CAboutDialog dialog(this); + dialog.ShowModal(); +} + +void CMainFrame::OnButtonSend(wxCommandEvent& event) +{ + /// debug test + if (fRandSendTest) + { + RandSend(); + return; + } + + // Toolbar: Send + CSendDialog dialog(this); + dialog.ShowModal(); +} + +void CMainFrame::OnButtonAddressBook(wxCommandEvent& event) +{ + // Toolbar: Address Book + CAddressBookDialog dialogAddr(this, "", false); + if (dialogAddr.ShowModal() == 2) + { + // Send + CSendDialog dialogSend(this, dialogAddr.GetAddress()); + dialogSend.ShowModal(); + } +} + +void CMainFrame::OnSetFocusAddress(wxFocusEvent& event) +{ + // Automatically select-all when entering window + m_textCtrlAddress->SetSelection(-1, -1); + fOnSetFocusAddress = true; + event.Skip(); +} + +void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event) +{ + if (fOnSetFocusAddress) + m_textCtrlAddress->SetSelection(-1, -1); + fOnSetFocusAddress = false; + event.Skip(); +} + +void CMainFrame::OnButtonCopy(wxCommandEvent& event) +{ + // Copy address box to clipboard + if (wxTheClipboard->Open()) + { + wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue())); + wxTheClipboard->Close(); + } +} + +void CMainFrame::OnButtonChange(wxCommandEvent& event) +{ + CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue())); + if (!dialog.ShowModal()) + return; + string strAddress = (string)dialog.GetAddress(); + if (strAddress != m_textCtrlAddress->GetValue()) + { + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + return; + if (!mapPubKeys.count(hash160)) + return; + CWalletDB().WriteDefaultKey(mapPubKeys[hash160]); + m_textCtrlAddress->SetValue(strAddress); + } +} + +void CMainFrame::OnListItemActivatedAllTransactions(wxListEvent& event) +{ + uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1)); + CWalletTx wtx; + CRITICAL_BLOCK(cs_mapWallet) + { + map::iterator mi = mapWallet.find(hash); + if (mi == mapWallet.end()) + { + printf("CMainFrame::OnListItemActivatedAllTransactions() : tx not found in mapWallet\n"); + return; + } + wtx = (*mi).second; + } + CTxDetailsDialog dialog(this, wtx); + dialog.ShowModal(); + //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx); + //pdialog->Show(); +} + +void CMainFrame::OnListItemActivatedProductsSent(wxListEvent& event) +{ + CProduct& product = *(CProduct*)event.GetItem().GetData(); + CEditProductDialog* pdialog = new CEditProductDialog(this); + pdialog->SetProduct(product); + pdialog->Show(); +} + +void CMainFrame::OnListItemActivatedOrdersSent(wxListEvent& event) +{ + CWalletTx& order = *(CWalletTx*)event.GetItem().GetData(); + CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, false); + pdialog->Show(); +} + +void CMainFrame::OnListItemActivatedOrdersReceived(wxListEvent& event) +{ + CWalletTx& order = *(CWalletTx*)event.GetItem().GetData(); + CViewOrderDialog* pdialog = new CViewOrderDialog(this, order, true); + pdialog->Show(); +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CTxDetailsDialog +// + +CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent) +{ + string strHTML; + strHTML.reserve(4000); + + int64 nTime = wtx.GetTxTime(); + int64 nCredit = wtx.GetCredit(); + int64 nDebit = wtx.GetDebit(); + int64 nNet = nCredit - nDebit; + + + + + strHTML += "Status: " + FormatTxStatus(wtx) + "
"; + strHTML += "Date: " + (nTime ? DateTimeStr(nTime) : "") + "
"; + + + // + // From + // + if (wtx.IsCoinBase()) + { + strHTML += "Source: Generated
"; + } + else if (!wtx.mapValue["from"].empty()) + { + // Online transaction + if (!wtx.mapValue["from"].empty()) + strHTML += "From: " + HtmlEscape(wtx.mapValue["from"]) + "
"; + } + else + { + // Offline transaction + foreach(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + { + vector vchPubKey; + if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey)) + { + string strAddress = PubKeyToAddress(vchPubKey); + if (mapAddressBook.count(strAddress)) + { + strHTML += "Received with: "; + if (!mapAddressBook[strAddress].empty()) + strHTML += mapAddressBook[strAddress] + " "; + strHTML += HtmlEscape(strAddress); + strHTML += "
"; + } + } + break; + } + } + } + + + // + // To + // + string strAddress; + if (!wtx.mapValue["to"].empty()) + { + // Online transaction + strAddress = wtx.mapValue["to"]; + strHTML += "To: "; + if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) + strHTML += mapAddressBook[strAddress] + " "; + strHTML += HtmlEscape(strAddress) + "
"; + } + + + // + // Amount + // + if (wtx.IsCoinBase() && nCredit == 0) + { + // + // Coinbase + // + int64 nUnmatured = 0; + foreach(const CTxOut& txout, wtx.vout) + nUnmatured += txout.GetCredit(); + if (wtx.IsInMainChain()) + strHTML += strprintf("Credit: (%s matures in %d blocks)
", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + else + strHTML += "Credit: (not accepted)
"; + } + else if (nNet > 0) + { + // + // Credit + // + strHTML += "Credit: " + FormatMoney(nNet) + "
"; + } + else + { + bool fAllFromMe = true; + foreach(const CTxIn& txin, wtx.vin) + fAllFromMe = fAllFromMe && txin.IsMine(); + + bool fAllToMe = true; + foreach(const CTxOut& txout, wtx.vout) + fAllToMe = fAllToMe && txout.IsMine(); + + if (fAllFromMe) + { + // + // Debit + // + foreach(const CTxOut& txout, wtx.vout) + { + if (txout.IsMine()) + continue; + + string strAddress; + if (!wtx.mapValue["to"].empty()) + { + // Online transaction + strAddress = wtx.mapValue["to"]; + } + else + { + // Offline transaction + uint160 hash160; + if (ExtractHash160(txout.scriptPubKey, hash160)) + strAddress = Hash160ToAddress(hash160); + } + + strHTML += "Debit: " + FormatMoney(-txout.nValue) + "    "; + strHTML += "(to "; + if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty()) + strHTML += mapAddressBook[strAddress] + " "; + strHTML += strAddress; + strHTML += ")
"; + } + + if (fAllToMe) + { + // Payment to self + int64 nValue = wtx.vout[0].nValue; + strHTML += "Debit: " + FormatMoney(-nValue) + "
"; + strHTML += "Credit: " + FormatMoney(nValue) + "
"; + } + + int64 nTxFee = nDebit - wtx.GetValueOut(); + if (nTxFee > 0) + strHTML += "Transaction fee: " + FormatMoney(-nTxFee) + "
"; + } + else + { + // + // Mixed debit transaction + // + foreach(const CTxIn& txin, wtx.vin) + if (txin.IsMine()) + strHTML += "Debit: " + FormatMoney(-txin.GetDebit()) + "
"; + foreach(const CTxOut& txout, wtx.vout) + if (txout.IsMine()) + strHTML += "Credit: " + FormatMoney(txout.GetCredit()) + "
"; + } + } + + strHTML += "Net amount: " + FormatMoney(nNet, true) + "
"; + + + // + // Message + // + if (!wtx.mapValue["message"].empty()) + strHTML += "
Message:
" + HtmlEscape(wtx.mapValue["message"], true) + "
"; + + + // + // Debug view + // + if (fDebug) + { + strHTML += "

debug print

"; + foreach(const CTxIn& txin, wtx.vin) + if (txin.IsMine()) + strHTML += "Debit: " + FormatMoney(-txin.GetDebit()) + "
"; + foreach(const CTxOut& txout, wtx.vout) + if (txout.IsMine()) + strHTML += "Credit: " + FormatMoney(txout.GetCredit()) + "
"; + + strHTML += "Inputs:
"; + CRITICAL_BLOCK(cs_mapWallet) + { + foreach(const CTxIn& txin, wtx.vin) + { + COutPoint prevout = txin.prevout; + map::iterator mi = mapWallet.find(prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (prevout.n < prev.vout.size()) + { + strHTML += HtmlEscape(prev.ToString(), true); + strHTML += "    " + FormatTxStatus(prev) + ", "; + strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "
"; + } + } + } + } + + strHTML += "


Transaction:
"; + strHTML += HtmlEscape(wtx.ToString(), true); + } + + + string(strHTML.begin(), strHTML.end()).swap(strHTML); + m_htmlWin->SetPage(strHTML); + m_buttonOK->SetFocus(); +} + +void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event) +{ + EndModal(true); // wx3: modal dialogs must EndModal, not Close() + //Destroy(); +} + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// COptionsDialog +// + +COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent) +{ + m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee)); + m_buttonOK->SetFocus(); +} + +void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event) +{ + int64 nTmp = nTransactionFee; + ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp); + m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp)); +} + +void COptionsDialog::OnButtonOK(wxCommandEvent& event) +{ + // nTransactionFee + int64 nPrevTransactionFee = nTransactionFee; + if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee) + CWalletDB().WriteSetting("nTransactionFee", nTransactionFee); + + EndModal(true); // wx3: modal dialogs must EndModal, not Close() +} + +void COptionsDialog::OnButtonCancel(wxCommandEvent& event) +{ + EndModal(false); // wx3: modal dialogs must EndModal, not Close() +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CAboutDialog +// + +CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent) +{ + m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d Alpha", VERSION/100, VERSION%100)); + + // (wx3 is natively Unicode; the wx2.8 UTF-8 workaround is no longer needed) +} + +void CAboutDialog::OnButtonOK(wxCommandEvent& event) +{ + EndModal(true); // wx3: modal dialogs must EndModal, not Close() +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CSendDialog +// + +CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent) +{ + // Init + m_textCtrlAddress->SetValue(strAddress); + m_choiceTransferType->SetSelection(0); + m_bitmapCheckMark->Show(false); + //// todo: should add a display of your balance for convenience + + // Set Icon + wxBitmap bmpSend(wxT("send16"), wxBITMAP_TYPE_RESOURCE); + bmpSend.SetMask(new wxMask(wxBitmap(wxT("send16masknoshadow"), wxBITMAP_TYPE_RESOURCE))); + wxIcon iconSend; + iconSend.CopyFromBitmap(bmpSend); + SetIcon(iconSend); + + wxCommandEvent event; + OnTextAddress(event); + + // Fixup the tab order + m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel); + m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste); + this->Layout(); +} + +void CSendDialog::OnTextAddress(wxCommandEvent& event) +{ + // Check mark + bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue()); + m_bitmapCheckMark->Show(fBitcoinAddress); + + // Grey out message if bitcoin address + bool fEnable = !fBitcoinAddress; + m_staticTextFrom->Enable(fEnable); + m_textCtrlFrom->Enable(fEnable); + m_staticTextMessage->Enable(fEnable); + m_textCtrlMessage->Enable(fEnable); + m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE)); +} + +void CSendDialog::OnKillFocusAmount(wxFocusEvent& event) +{ + // Reformat the amount + if (m_textCtrlAmount->GetValue().Trim().empty()) + return; + int64 nTmp; + if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp)) + m_textCtrlAmount->SetValue(FormatMoney(nTmp)); +} + +void CSendDialog::OnButtonAddressBook(wxCommandEvent& event) +{ + // Open address book + CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true); + if (dialog.ShowModal()) + m_textCtrlAddress->SetValue(dialog.GetAddress()); +} + +void CSendDialog::OnButtonPaste(wxCommandEvent& event) +{ + // Copy clipboard to address box + if (wxTheClipboard->Open()) + { + if (wxTheClipboard->IsSupported(wxDF_TEXT)) + { + wxTextDataObject data; + wxTheClipboard->GetData(data); + m_textCtrlAddress->SetValue(data.GetText()); + } + wxTheClipboard->Close(); + } +} + +void CSendDialog::OnButtonSend(wxCommandEvent& event) +{ + CWalletTx wtx; + string strAddress = (string)m_textCtrlAddress->GetValue(); + + // Parse amount + int64 nValue = 0; + if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0) + { + wxMessageBox("Error in amount "); + return; + } + if (nValue > GetBalance()) + { + wxMessageBox("Amount exceeds your balance "); + return; + } + if (nValue + nTransactionFee > GetBalance()) + { + wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included "); + return; + } + + // Parse bitcoin address + uint160 hash160; + bool fBitcoinAddress = AddressToHash160(strAddress, hash160); + + if (fBitcoinAddress) + { + // Send to bitcoin address + CScript scriptPubKey; + scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + + if (!SendMoney(scriptPubKey, nValue, wtx)) + return; + + wxMessageBox("Payment sent ", "Sending..."); + } + else + { + // Parse IP address + CAddress addr(strAddress.c_str()); + if (addr.ip == 0) + { + wxMessageBox("Invalid address "); + return; + } + + // Message + wtx.mapValue["to"] = strAddress; + wtx.mapValue["from"] = m_textCtrlFrom->GetValue(); + wtx.mapValue["message"] = m_textCtrlMessage->GetValue(); + + // Send to IP address + CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx); + if (!pdialog->ShowModal()) + return; + } + + if (!mapAddressBook.count(strAddress)) + SetAddressBookName(strAddress, ""); + + EndModal(true); +} + +void CSendDialog::OnButtonCancel(wxCommandEvent& event) +{ + // Cancel + EndModal(false); +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CSendingDialog +// + +CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn) : CSendingDialogBase(NULL) // we have to give null so parent can't destroy us +{ + addr = addrIn; + nPrice = nPriceIn; + wtx = wtxIn; + start = wxDateTime::UNow(); + strStatus = ""; + fCanCancel = true; + fAbort = false; + fSuccess = false; + fUIDone = false; + fWorkDone = false; + + SetTitle(strprintf("Sending %s to %s...", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str())); + m_textCtrlStatus->SetValue(""); + + _beginthread(SendingDialogStartTransfer, 0, this); +} + +CSendingDialog::~CSendingDialog() +{ + printf("~CSendingDialog()\n"); +} + +void CSendingDialog::Close() +{ + // Last one out turn out the lights. + // fWorkDone signals that work side is done and UI thread should call destroy. + // fUIDone signals that UI window has closed and work thread should call destroy. + // This allows the window to disappear and end modality when cancelled + // without making the user wait for ConnectNode to return. The dialog object + // hangs around in the background until the work thread exits. + if (IsModal()) + EndModal(fSuccess); + else + Show(false); + if (fWorkDone) + Destroy(); + else + fUIDone = true; +} + +void CSendingDialog::OnClose(wxCloseEvent& event) +{ + if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel) + { + Close(); + } + else + { + event.Veto(); + wxCommandEvent cmdevent; + OnButtonCancel(cmdevent); + } +} + +void CSendingDialog::OnButtonOK(wxCommandEvent& event) +{ + if (fWorkDone) + Close(); +} + +void CSendingDialog::OnButtonCancel(wxCommandEvent& event) +{ + if (fCanCancel) + fAbort = true; +} + +void CSendingDialog::OnPaint(wxPaintEvent& event) +{ + if (strStatus.size() > 130) + m_textCtrlStatus->SetValue(string("\n") + strStatus); + else + m_textCtrlStatus->SetValue(string("\n\n") + strStatus); + m_staticTextSending->SetFocus(); + if (!fCanCancel) + m_buttonCancel->Enable(false); + if (fWorkDone) + { + m_buttonOK->Enable(true); + m_buttonOK->SetFocus(); + m_buttonCancel->Enable(false); + } + if (fAbort && fCanCancel && IsShown()) + { + strStatus = "CANCELLED"; + m_buttonOK->Enable(true); + m_buttonOK->SetFocus(); + m_buttonCancel->Enable(false); + m_buttonCancel->SetLabel("Cancelled"); + Close(); + wxMessageBox("Transfer cancelled ", "Sending...", wxOK, this); + } + event.Skip(); + + /// debug test + if (fRandSendTest && fWorkDone && fSuccess) + { + Close(); + Sleep(1000); + RandSend(); + } +} + + +// +// Everything from here on is not in the UI thread and must only communicate +// with the rest of the dialog through variables and calling repaint. +// + +void CSendingDialog::Repaint() +{ + // Called from worker threads: repaint only on the GUI thread + if (wxTheApp) + wxTheApp->CallAfter([this] { Refresh(); }); +} + +bool CSendingDialog::Status() +{ + if (fUIDone) + { + Destroy(); + return false; + } + if (fAbort && fCanCancel) + { + strStatus = "CANCELLED"; + Repaint(); + fWorkDone = true; + return false; + } + return true; +} + +bool CSendingDialog::Status(const string& str) +{ + if (!Status()) + return false; + strStatus = str; + Repaint(); + return true; +} + +bool CSendingDialog::Error(const string& str) +{ + fCanCancel = false; + fWorkDone = true; + Status(string("Error: ") + str); + return false; +} + +void SendingDialogStartTransfer(void* parg) +{ + ((CSendingDialog*)parg)->StartTransfer(); +} + +void CSendingDialog::StartTransfer() +{ + // Make sure we have enough money + if (nPrice + nTransactionFee > GetBalance()) + { + Error("You don't have enough money"); + return; + } + + // We may have connected already for product details + if (!Status("Connecting...")) + return; + CNode* pnode = ConnectNode(addr, 5 * 60); + if (!pnode) + { + Error("Unable to connect"); + return; + } + + // Send order to seller, with response going to OnReply2 via event handler + if (!Status("Requesting public key...")) + return; + pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this); +} + +void SendingDialogOnReply2(void* parg, CDataStream& vRecv) +{ + ((CSendingDialog*)parg)->OnReply2(vRecv); +} + +void CSendingDialog::OnReply2(CDataStream& vRecv) +{ + if (!Status("Received public key...")) + return; + + CScript scriptPubKey; + int nRet; + try + { + vRecv >> nRet; + if (nRet > 0) + { + string strMessage; + vRecv >> strMessage; + Error("Transfer was not accepted"); + //// todo: enlarge the window and enable a hidden white box to put seller's message + return; + } + vRecv >> scriptPubKey; + } + catch (...) + { + //// what do we want to do about this? + Error("Invalid response received"); + return; + } + + // Should already be connected + CNode* pnode = ConnectNode(addr, 5 * 60); + if (!pnode) + { + Error("Lost connection"); + return; + } + + // Pause to give the user a chance to cancel + while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000)) + { + Sleep(200); + if (!Status()) + return; + } + + CRITICAL_BLOCK(cs_main) + { + // Pay + if (!Status("Creating transaction...")) + return; + if (nPrice + nTransactionFee > GetBalance()) + { + Error("You don't have enough money"); + return; + } + int64 nFeeRequired; + if (!CreateTransaction(scriptPubKey, nPrice, wtx, nFeeRequired)) + { + if (nPrice + nFeeRequired > GetBalance()) + Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str())); + else + Error("Transaction creation failed"); + return; + } + + // Last chance to cancel + Sleep(50); + if (!Status()) + return; + fCanCancel = false; + if (fAbort) + { + fCanCancel = true; + if (!Status()) + return; + fCanCancel = false; + } + if (!Status("Sending payment...")) + return; + + // Commit + if (!CommitTransactionSpent(wtx)) + { + Error("Error finalizing payment"); + return; + } + + // Send payment tx to seller, with response going to OnReply3 via event handler + pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this); + + // Accept and broadcast transaction + if (!wtx.AcceptTransaction()) + printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str()); + wtx.RelayWalletTransaction(); + + Status("Waiting for confirmation..."); + MainFrameRepaint(); + } +} + +void SendingDialogOnReply3(void* parg, CDataStream& vRecv) +{ + ((CSendingDialog*)parg)->OnReply3(vRecv); +} + +void CSendingDialog::OnReply3(CDataStream& vRecv) +{ + int nRet; + try + { + vRecv >> nRet; + if (nRet > 0) + { + Error("The payment was sent, but the recipient was unable to verify it.\n" + "The transaction is recorded and will credit to the recipient if it is valid,\n" + "but without comment information."); + return; + } + } + catch (...) + { + //// what do we want to do about this? + Error("Payment was sent, but an invalid response was received"); + return; + } + + fSuccess = true; + fWorkDone = true; + Status("Payment completed"); +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CYourAddressDialog +// + +CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent) +{ + // Init column headers + m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200); + m_listCtrl->InsertColumn(1, "Bitflash Address", wxLIST_FORMAT_LEFT, 350); + m_listCtrl->SetFocus(); + + // Fill listctrl with address book data + CRITICAL_BLOCK(cs_mapKeys) + { + foreach(const PAIRTYPE(string, string)& item, mapAddressBook) + { + string strAddress = item.first; + string strName = item.second; + uint160 hash160; + bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160)); + if (fMine) + { + int nIndex = InsertLine(m_listCtrl, strName, strAddress); + if (strAddress == strInitSelected) + m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED); + } + } + } +} + +wxString CYourAddressDialog::GetAddress() +{ + int nIndex = GetSelection(m_listCtrl); + if (nIndex == -1) + return ""; + return GetItemText(m_listCtrl, nIndex, 1); +} + +void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event) +{ + // Update address book with edited name + if (event.IsEditCancelled()) + return; + string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1); + SetAddressBookName(strAddress, string(event.GetText())); + pframeMain->RefreshListCtrl(); +} + +void CYourAddressDialog::OnListItemSelected(wxListEvent& event) +{ +} + +void CYourAddressDialog::OnListItemActivated(wxListEvent& event) +{ + // Doubleclick returns selection + EndModal(true); +} + +void CYourAddressDialog::OnButtonRename(wxCommandEvent& event) +{ + // Ask new name + int nIndex = GetSelection(m_listCtrl); + if (nIndex == -1) + return; + string strName = (string)m_listCtrl->GetItemText(nIndex); + string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1); + CGetTextFromUserDialog dialog(this, "Rename Bitflash Address", "New Name", strName); + if (!dialog.ShowModal()) + return; + strName = dialog.GetValue(); + + // Change name + SetAddressBookName(strAddress, strName); + m_listCtrl->SetItemText(nIndex, strName); + pframeMain->RefreshListCtrl(); +} + +void CYourAddressDialog::OnButtonNew(wxCommandEvent& event) +{ + // Ask name + CGetTextFromUserDialog dialog(this, "New Bitflash Address", "Name", ""); + if (!dialog.ShowModal()) + return; + string strName = dialog.GetValue(); + + // Generate new key + string strAddress = PubKeyToAddress(GenerateNewKey()); + SetAddressBookName(strAddress, strName); + + // Add to list and select it + int nIndex = InsertLine(m_listCtrl, strName, strAddress); + SetSelection(m_listCtrl, nIndex); + m_listCtrl->SetFocus(); +} + +void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event) +{ + // Copy address box to clipboard + if (wxTheClipboard->Open()) + { + wxTheClipboard->SetData(new wxTextDataObject(GetAddress())); + wxTheClipboard->Close(); + } +} + +void CYourAddressDialog::OnButtonOK(wxCommandEvent& event) +{ + // OK + EndModal(true); +} + +void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event) +{ + // Cancel + EndModal(false); +} + +void CYourAddressDialog::OnClose(wxCloseEvent& event) +{ + // Close + EndModal(false); +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CAddressBookDialog +// + +CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent) +{ + fSending = fSendingIn; + if (!fSending) + m_buttonCancel->Show(false); + + // Init column headers + m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200); + m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350); + m_listCtrl->SetFocus(); + + // Set Icon + wxBitmap bmpAddressBook(wxT("addressbook16"), wxBITMAP_TYPE_RESOURCE); + bmpAddressBook.SetMask(new wxMask(wxBitmap(wxT("addressbook16mask"), wxBITMAP_TYPE_RESOURCE))); + wxIcon iconAddressBook; + iconAddressBook.CopyFromBitmap(bmpAddressBook); + SetIcon(iconAddressBook); + + // Fill listctrl with address book data + CRITICAL_BLOCK(cs_mapKeys) + { + foreach(const PAIRTYPE(string, string)& item, mapAddressBook) + { + string strAddress = item.first; + string strName = item.second; + uint160 hash160; + bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160)); + if (!fMine) + { + int nIndex = InsertLine(m_listCtrl, strName, strAddress); + if (strAddress == strInitSelected) + m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED); + } + } + } +} + +wxString CAddressBookDialog::GetAddress() +{ + int nIndex = GetSelection(m_listCtrl); + if (nIndex == -1) + return ""; + return GetItemText(m_listCtrl, nIndex, 1); +} + +void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event) +{ + // Update address book with edited name + if (event.IsEditCancelled()) + return; + string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1); + SetAddressBookName(strAddress, string(event.GetText())); + pframeMain->RefreshListCtrl(); +} + +void CAddressBookDialog::OnListItemSelected(wxListEvent& event) +{ +} + +void CAddressBookDialog::OnListItemActivated(wxListEvent& event) +{ + if (fSending) + { + // Doubleclick returns selection + EndModal(GetAddress() != "" ? 2 : 0); + } + else + { + // Doubleclick edits item + wxCommandEvent event; + OnButtonEdit(event); + } +} + +void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event) +{ + // Ask new name + int nIndex = GetSelection(m_listCtrl); + if (nIndex == -1) + return; + string strName = (string)m_listCtrl->GetItemText(nIndex); + string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1); + string strAddressOrg = strAddress; + CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress); + if (!dialog.ShowModal()) + return; + strName = dialog.GetValue1(); + strAddress = dialog.GetValue2(); + + // Change name + if (strAddress != strAddressOrg) + CWalletDB().EraseName(strAddressOrg); + SetAddressBookName(strAddress, strName); + m_listCtrl->SetItem(nIndex, 1, strAddress); + m_listCtrl->SetItemText(nIndex, strName); + pframeMain->RefreshListCtrl(); +} + +void CAddressBookDialog::OnButtonNew(wxCommandEvent& event) +{ + // Ask name + CGetTextFromUserDialog dialog(this, "New Address", "Name", "", "Address", ""); + if (!dialog.ShowModal()) + return; + string strName = dialog.GetValue1(); + string strAddress = dialog.GetValue2(); + + // Add to list and select it + SetAddressBookName(strAddress, strName); + int nIndex = InsertLine(m_listCtrl, strName, strAddress); + SetSelection(m_listCtrl, nIndex); + m_listCtrl->SetFocus(); +} + +void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event) +{ + for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--) + { + if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED)) + { + string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1); + CWalletDB().EraseName(strAddress); + m_listCtrl->DeleteItem(nIndex); + } + } + pframeMain->RefreshListCtrl(); +} + +void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event) +{ + // Copy address box to clipboard + if (wxTheClipboard->Open()) + { + wxTheClipboard->SetData(new wxTextDataObject(GetAddress())); + wxTheClipboard->Close(); + } +} + +void CAddressBookDialog::OnButtonOK(wxCommandEvent& event) +{ + // OK + EndModal(GetAddress() != "" ? 1 : 0); +} + +void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event) +{ + // Cancel + EndModal(0); +} + +void CAddressBookDialog::OnClose(wxCloseEvent& event) +{ + // Close + EndModal(0); +} + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CProductsDialog +// + +bool CompareIntStringPairBestFirst(const pair& item1, const pair& item2) +{ + return (item1.first > item2.first); +} + +CProductsDialog::CProductsDialog(wxWindow* parent) : CProductsDialogBase(parent) +{ + // Init column headers + m_listCtrl->InsertColumn(0, "Title", wxLIST_FORMAT_LEFT, 200); + m_listCtrl->InsertColumn(1, "Price", wxLIST_FORMAT_LEFT, 80); + m_listCtrl->InsertColumn(2, "Seller", wxLIST_FORMAT_LEFT, 80); + m_listCtrl->InsertColumn(3, "Stars", wxLIST_FORMAT_LEFT, 50); + m_listCtrl->InsertColumn(4, "Power", wxLIST_FORMAT_LEFT, 50); + + // Tally top categories + map mapTopCategories; + CRITICAL_BLOCK(cs_mapProducts) + for (map::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi) + mapTopCategories[(*mi).second.mapValue["category"]]++; + + // Sort top categories + vector > vTopCategories; + for (map::iterator mi = mapTopCategories.begin(); mi != mapTopCategories.end(); ++mi) + vTopCategories.push_back(make_pair((*mi).second, (*mi).first)); + sort(vTopCategories.begin(), vTopCategories.end(), CompareIntStringPairBestFirst); + + // Fill categories combo box + int nLimit = 250; + for (vector >::iterator it = vTopCategories.begin(); it != vTopCategories.end() && nLimit-- > 0; ++it) + m_comboBoxCategory->Append((*it).second); + + // Fill window with initial search + //wxCommandEvent event; + //OnButtonSearch(event); +} + +void CProductsDialog::OnCombobox(wxCommandEvent& event) +{ + OnButtonSearch(event); +} + +bool CompareProductsBestFirst(const CProduct* p1, const CProduct* p2) +{ + return (p1->nAtoms > p2->nAtoms); +} + +void CProductsDialog::OnButtonSearch(wxCommandEvent& event) +{ + string strCategory = (string)m_comboBoxCategory->GetValue(); + string strSearch = (string)m_textCtrlSearch->GetValue(); + + // Search products + vector vProductsFound; + CRITICAL_BLOCK(cs_mapProducts) + { + for (map::iterator mi = mapProducts.begin(); mi != mapProducts.end(); ++mi) + { + CProduct& product = (*mi).second; + if (product.mapValue["category"].find(strCategory) != -1) + { + if (product.mapValue["title"].find(strSearch) != -1 || + product.mapValue["description"].find(strSearch) != -1 || + product.mapValue["seller"].find(strSearch) != -1) + { + vProductsFound.push_back(&product); + } + } + } + } + + // Sort + sort(vProductsFound.begin(), vProductsFound.end(), CompareProductsBestFirst); + + // Display + foreach(CProduct* pproduct, vProductsFound) + { + InsertLine(m_listCtrl, + pproduct->mapValue["title"], + pproduct->mapValue["price"], + pproduct->mapValue["seller"], + pproduct->mapValue["stars"], + itostr(pproduct->nAtoms)); + } +} + +void CProductsDialog::OnListItemActivated(wxListEvent& event) +{ + // Doubleclick opens product + CViewProductDialog* pdialog = new CViewProductDialog(this, m_vProduct[event.GetIndex()]); + pdialog->Show(); +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CEditProductDialog +// + +CEditProductDialog::CEditProductDialog(wxWindow* parent) : CEditProductDialogBase(parent) +{ + m_textCtrlLabel[0 ] = m_textCtrlLabel0; + m_textCtrlLabel[1 ] = m_textCtrlLabel1; + m_textCtrlLabel[2 ] = m_textCtrlLabel2; + m_textCtrlLabel[3 ] = m_textCtrlLabel3; + m_textCtrlLabel[4 ] = m_textCtrlLabel4; + m_textCtrlLabel[5 ] = m_textCtrlLabel5; + m_textCtrlLabel[6 ] = m_textCtrlLabel6; + m_textCtrlLabel[7 ] = m_textCtrlLabel7; + m_textCtrlLabel[8 ] = m_textCtrlLabel8; + m_textCtrlLabel[9 ] = m_textCtrlLabel9; + m_textCtrlLabel[10] = m_textCtrlLabel10; + m_textCtrlLabel[11] = m_textCtrlLabel11; + m_textCtrlLabel[12] = m_textCtrlLabel12; + m_textCtrlLabel[13] = m_textCtrlLabel13; + m_textCtrlLabel[14] = m_textCtrlLabel14; + m_textCtrlLabel[15] = m_textCtrlLabel15; + m_textCtrlLabel[16] = m_textCtrlLabel16; + m_textCtrlLabel[17] = m_textCtrlLabel17; + m_textCtrlLabel[18] = m_textCtrlLabel18; + m_textCtrlLabel[19] = m_textCtrlLabel19; + + m_textCtrlField[0 ] = m_textCtrlField0; + m_textCtrlField[1 ] = m_textCtrlField1; + m_textCtrlField[2 ] = m_textCtrlField2; + m_textCtrlField[3 ] = m_textCtrlField3; + m_textCtrlField[4 ] = m_textCtrlField4; + m_textCtrlField[5 ] = m_textCtrlField5; + m_textCtrlField[6 ] = m_textCtrlField6; + m_textCtrlField[7 ] = m_textCtrlField7; + m_textCtrlField[8 ] = m_textCtrlField8; + m_textCtrlField[9 ] = m_textCtrlField9; + m_textCtrlField[10] = m_textCtrlField10; + m_textCtrlField[11] = m_textCtrlField11; + m_textCtrlField[12] = m_textCtrlField12; + m_textCtrlField[13] = m_textCtrlField13; + m_textCtrlField[14] = m_textCtrlField14; + m_textCtrlField[15] = m_textCtrlField15; + m_textCtrlField[16] = m_textCtrlField16; + m_textCtrlField[17] = m_textCtrlField17; + m_textCtrlField[18] = m_textCtrlField18; + m_textCtrlField[19] = m_textCtrlField19; + + m_buttonDel[0 ] = m_buttonDel0; + m_buttonDel[1 ] = m_buttonDel1; + m_buttonDel[2 ] = m_buttonDel2; + m_buttonDel[3 ] = m_buttonDel3; + m_buttonDel[4 ] = m_buttonDel4; + m_buttonDel[5 ] = m_buttonDel5; + m_buttonDel[6 ] = m_buttonDel6; + m_buttonDel[7 ] = m_buttonDel7; + m_buttonDel[8 ] = m_buttonDel8; + m_buttonDel[9 ] = m_buttonDel9; + m_buttonDel[10] = m_buttonDel10; + m_buttonDel[11] = m_buttonDel11; + m_buttonDel[12] = m_buttonDel12; + m_buttonDel[13] = m_buttonDel13; + m_buttonDel[14] = m_buttonDel14; + m_buttonDel[15] = m_buttonDel15; + m_buttonDel[16] = m_buttonDel16; + m_buttonDel[17] = m_buttonDel17; + m_buttonDel[18] = m_buttonDel18; + m_buttonDel[19] = m_buttonDel19; + + for (int i = 1; i < FIELDS_MAX; i++) + ShowLine(i, false); + + LayoutAll(); +} + +void CEditProductDialog::LayoutAll() +{ + m_scrolledWindow->Layout(); + m_scrolledWindow->GetSizer()->Fit(m_scrolledWindow); + this->Layout(); +} + +void CEditProductDialog::ShowLine(int i, bool fShow) +{ + m_textCtrlLabel[i]->Show(fShow); + m_textCtrlField[i]->Show(fShow); + m_buttonDel[i]->Show(fShow); +} + +void CEditProductDialog::OnButtonDel0(wxCommandEvent& event) { OnButtonDel(event, 0); } +void CEditProductDialog::OnButtonDel1(wxCommandEvent& event) { OnButtonDel(event, 1); } +void CEditProductDialog::OnButtonDel2(wxCommandEvent& event) { OnButtonDel(event, 2); } +void CEditProductDialog::OnButtonDel3(wxCommandEvent& event) { OnButtonDel(event, 3); } +void CEditProductDialog::OnButtonDel4(wxCommandEvent& event) { OnButtonDel(event, 4); } +void CEditProductDialog::OnButtonDel5(wxCommandEvent& event) { OnButtonDel(event, 5); } +void CEditProductDialog::OnButtonDel6(wxCommandEvent& event) { OnButtonDel(event, 6); } +void CEditProductDialog::OnButtonDel7(wxCommandEvent& event) { OnButtonDel(event, 7); } +void CEditProductDialog::OnButtonDel8(wxCommandEvent& event) { OnButtonDel(event, 8); } +void CEditProductDialog::OnButtonDel9(wxCommandEvent& event) { OnButtonDel(event, 9); } +void CEditProductDialog::OnButtonDel10(wxCommandEvent& event) { OnButtonDel(event, 10); } +void CEditProductDialog::OnButtonDel11(wxCommandEvent& event) { OnButtonDel(event, 11); } +void CEditProductDialog::OnButtonDel12(wxCommandEvent& event) { OnButtonDel(event, 12); } +void CEditProductDialog::OnButtonDel13(wxCommandEvent& event) { OnButtonDel(event, 13); } +void CEditProductDialog::OnButtonDel14(wxCommandEvent& event) { OnButtonDel(event, 14); } +void CEditProductDialog::OnButtonDel15(wxCommandEvent& event) { OnButtonDel(event, 15); } +void CEditProductDialog::OnButtonDel16(wxCommandEvent& event) { OnButtonDel(event, 16); } +void CEditProductDialog::OnButtonDel17(wxCommandEvent& event) { OnButtonDel(event, 17); } +void CEditProductDialog::OnButtonDel18(wxCommandEvent& event) { OnButtonDel(event, 18); } +void CEditProductDialog::OnButtonDel19(wxCommandEvent& event) { OnButtonDel(event, 19); } + +void CEditProductDialog::OnButtonDel(wxCommandEvent& event, int n) +{ + Freeze(); + int x, y; + m_scrolledWindow->GetViewStart(&x, &y); + int i; + for (i = n; i < FIELDS_MAX-1; i++) + { + m_textCtrlLabel[i]->SetValue(m_textCtrlLabel[i+1]->GetValue()); + m_textCtrlField[i]->SetValue(m_textCtrlField[i+1]->GetValue()); + if (!m_buttonDel[i+1]->IsShown()) + break; + } + m_textCtrlLabel[i]->SetValue(""); + m_textCtrlField[i]->SetValue(""); + ShowLine(i, false); + m_buttonAddField->Enable(true); + LayoutAll(); + m_scrolledWindow->Scroll(0, y); + Thaw(); +} + +void CEditProductDialog::OnButtonAddField(wxCommandEvent& event) +{ + for (int i = 0; i < FIELDS_MAX; i++) + { + if (!m_buttonDel[i]->IsShown()) + { + Freeze(); + ShowLine(i, true); + if (i == FIELDS_MAX-1) + m_buttonAddField->Enable(false); + LayoutAll(); + m_scrolledWindow->Scroll(0, 99999); + Thaw(); + break; + } + } +} + +void CEditProductDialog::OnButtonSend(wxCommandEvent& event) +{ + CProduct product; + GetProduct(product); + + // Sign the detailed product + product.vchPubKeyFrom = keyUser.GetPubKey(); + if (!keyUser.Sign(product.GetSigHash(), product.vchSig)) + { + wxMessageBox("Error digitally signing the product "); + return; + } + + // Save detailed product + AddToMyProducts(product); + + // Strip down to summary product + product.mapDetails.clear(); + product.vOrderForm.clear(); + + // Sign the summary product + if (!keyUser.Sign(product.GetSigHash(), product.vchSig)) + { + wxMessageBox("Error digitally signing the product "); + return; + } + + // Verify + if (!product.CheckProduct()) + { + wxMessageBox("Errors found in product "); + return; + } + + // Broadcast + AdvertStartPublish(pnodeLocalHost, MSG_PRODUCT, 0, product); + + Destroy(); +} + +void CEditProductDialog::OnButtonPreview(wxCommandEvent& event) +{ + CProduct product; + GetProduct(product); + CViewProductDialog* pdialog = new CViewProductDialog(this, product); + pdialog->Show(); +} + +void CEditProductDialog::OnButtonCancel(wxCommandEvent& event) +{ + Destroy(); +} + +void CEditProductDialog::SetProduct(const CProduct& productIn) +{ + CProduct product = productIn; + + m_comboBoxCategory->SetValue(product.mapValue["category"]); + m_textCtrlTitle->SetValue(product.mapValue["title"]); + m_textCtrlPrice->SetValue(product.mapValue["price"]); + m_textCtrlDescription->SetValue(product.mapValue["description"]); + m_textCtrlInstructions->SetValue(product.mapValue["instructions"]); + + for (int i = 0; i < FIELDS_MAX; i++) + { + bool fUsed = i < product.vOrderForm.size(); + m_buttonDel[i]->Show(fUsed); + m_textCtrlLabel[i]->Show(fUsed); + m_textCtrlField[i]->Show(fUsed); + if (!fUsed) + continue; + + m_textCtrlLabel[i]->SetValue(product.vOrderForm[i].first); + string strControl = product.vOrderForm[i].second; + if (strControl.substr(0, 5) == "text=") + m_textCtrlField[i]->SetValue(""); + else if (strControl.substr(0, 7) == "choice=") + m_textCtrlField[i]->SetValue(strControl.substr(7)); + else + m_textCtrlField[i]->SetValue(strControl); + } +} + +void CEditProductDialog::GetProduct(CProduct& product) +{ + // map mapValue; + // vector > vOrderForm; + + product.mapValue["category"] = m_comboBoxCategory->GetValue().Trim(); + product.mapValue["title"] = m_textCtrlTitle->GetValue().Trim(); + product.mapValue["price"] = m_textCtrlPrice->GetValue().Trim(); + product.mapValue["description"] = m_textCtrlDescription->GetValue().Trim(); + product.mapValue["instructions"] = m_textCtrlInstructions->GetValue().Trim(); + + for (int i = 0; i < FIELDS_MAX; i++) + { + if (m_buttonDel[i]->IsShown()) + { + string strLabel = (string)m_textCtrlLabel[i]->GetValue().Trim(); + string strControl = (string)m_textCtrlField[i]->GetValue(); + if (strControl.empty()) + strControl = "text="; + else + strControl = "choice=" + strControl; + product.vOrderForm.push_back(make_pair(strLabel, strControl)); + } + } +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CViewProductDialog +// + +CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& productIn) : CViewProductDialogBase(parent) +{ + Connect(wxEVT_REPLY1, wxCommandEventHandler(CViewProductDialog::OnReply1), NULL, this); + AddCallbackAvailable(GetEventHandler()); + + // Fill display with product summary while waiting for details + product = productIn; + UpdateProductDisplay(false); + + m_buttonBack->Enable(false); + m_buttonNext->Enable(!product.vOrderForm.empty()); + m_htmlWinReviews->Show(true); + m_scrolledWindow->Show(false); + this->Layout(); + + // Request details from seller + _beginthread(ThreadRequestProductDetails, 0, new pair(product, GetEventHandler())); +} + +CViewProductDialog::~CViewProductDialog() +{ + RemoveCallbackAvailable(GetEventHandler()); +} + +void ThreadRequestProductDetails(void* parg) +{ + // Extract parameters + pair* pitem = (pair*)parg; + CProduct product = pitem->first; + wxEvtHandler* pevthandler = pitem->second; + delete pitem; + + // Connect to seller + CNode* pnode = ConnectNode(product.addr, 5 * 60); + if (!pnode) + { + CDataStream ssEmpty; + AddPendingReplyEvent1(pevthandler, ssEmpty); + return; + } + + // Request detailed product, with response going to OnReply1 via dialog's event handler + pnode->PushRequest("getdetails", product.GetHash(), AddPendingReplyEvent1, (void*)pevthandler); +} + +void CViewProductDialog::OnReply1(wxCommandEvent& event) +{ + CDataStream ss = GetStreamFromEvent(event); + if (ss.empty()) + { + product.mapValue["description"] = "-- CAN'T CONNECT TO SELLER --\n"; + UpdateProductDisplay(true); + return; + } + + int nRet; + CProduct product2; + try + { + ss >> nRet; + if (nRet > 0) + throw false; + ss >> product2; + if (product2.GetHash() != product.GetHash()) + throw false; + if (!product2.CheckSignature()) + throw false; + } + catch (...) + { + product.mapValue["description"] = "-- INVALID RESPONSE --\n"; + UpdateProductDisplay(true); + return; + } + + product = product2; + UpdateProductDisplay(true); +} + +bool CompareReviewsBestFirst(const CReview* p1, const CReview* p2) +{ + return (p1->nAtoms > p2->nAtoms); +} + +void CViewProductDialog::UpdateProductDisplay(bool fDetails) +{ + // Product and reviews + string strHTML; + strHTML.reserve(4000); + strHTML += "\n" + "\n" + "\n" + "\n" + "\n"; + strHTML += "Category: " + HtmlEscape(product.mapValue["category"]) + "
\n"; + strHTML += "Title: " + HtmlEscape(product.mapValue["title"]) + "
\n"; + strHTML += "Price: " + HtmlEscape(product.mapValue["price"]) + "
\n"; + + if (!fDetails) + strHTML += "Loading details...
\n
\n"; + else + strHTML += HtmlEscape(product.mapValue["description"], true) + "
\n
\n"; + + strHTML += "Reviews:
\n
\n"; + + if (!product.vchPubKeyFrom.empty()) + { + CReviewDB reviewdb("r"); + + // Get reviews + vector vReviews; + reviewdb.ReadReviews(product.GetUserHash(), vReviews); + + // Get reviewer's number of atoms + vector vSortedReviews; + vSortedReviews.reserve(vReviews.size()); + for (vector::reverse_iterator it = vReviews.rbegin(); it != vReviews.rend(); ++it) + { + CReview& review = *it; + CUser user; + reviewdb.ReadUser(review.GetUserHash(), user); + review.nAtoms = user.GetAtomCount(); + vSortedReviews.push_back(&review); + } + + reviewdb.Close(); + + // Sort + stable_sort(vSortedReviews.begin(), vSortedReviews.end(), CompareReviewsBestFirst); + + // Format reviews + foreach(CReview* preview, vSortedReviews) + { + CReview& review = *preview; + int nStars = atoi(review.mapValue["stars"].c_str()); + if (nStars < 1 || nStars > 5) + continue; + + strHTML += "" + itostr(nStars) + (nStars == 1 ? " star" : " stars") + ""; + strHTML += "     "; + strHTML += DateStr(atoi64(review.mapValue["date"])) + "
\n"; + strHTML += HtmlEscape(review.mapValue["review"], true); + strHTML += "
\n
\n"; + } + } + + strHTML += "\n\n"; + + // Shrink capacity to fit + string(strHTML.begin(), strHTML.end()).swap(strHTML); + + m_htmlWinReviews->SetPage(strHTML); + + ///// need to find some other indicator to use so can allow empty order form + if (product.vOrderForm.empty()) + return; + + // Order form + m_staticTextInstructions->SetLabel(product.mapValue["instructions"]); + for (int i = 0; i < FIELDS_MAX; i++) + { + m_staticTextLabel[i] = NULL; + m_textCtrlField[i] = NULL; + m_choiceField[i] = NULL; + } + + // Construct flexgridsizer + wxBoxSizer* bSizer21 = (wxBoxSizer*)m_scrolledWindow->GetSizer(); + wxFlexGridSizer* fgSizer; + fgSizer = new wxFlexGridSizer(0, 2, 0, 0); + fgSizer->AddGrowableCol(1); + fgSizer->SetFlexibleDirection(wxBOTH); + fgSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + + // Construct order form fields + wxWindow* windowLast = NULL; + for (int i = 0; i < product.vOrderForm.size(); i++) + { + string strLabel = product.vOrderForm[i].first; + string strControl = product.vOrderForm[i].second; + + if (strLabel.size() < 20) + strLabel.insert(strLabel.begin(), 20 - strLabel.size(), ' '); + + m_staticTextLabel[i] = new wxStaticText(m_scrolledWindow, wxID_ANY, strLabel, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + m_staticTextLabel[i]->Wrap(-1); + fgSizer->Add(m_staticTextLabel[i], 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5); + + if (strControl.substr(0, 5) == "text=") + { + m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5); + windowLast = m_textCtrlField[i]; + } + else if (strControl.substr(0, 7) == "choice=") + { + vector vChoices; + ParseString(strControl.substr(7), ',', vChoices); + + wxArrayString arraystring; + foreach(const string& str, vChoices) + arraystring.Add(str); + + m_choiceField[i] = new wxChoice(m_scrolledWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, arraystring, 0); + fgSizer->Add(m_choiceField[i], 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + windowLast = m_choiceField[i]; + } + else + { + m_textCtrlField[i] = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + fgSizer->Add(m_textCtrlField[i], 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5); + m_staticTextLabel[i]->Show(false); + m_textCtrlField[i]->Show(false); + } + } + + // Insert after instructions and before submit/cancel buttons + bSizer21->Insert(2, fgSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 5); + m_scrolledWindow->Layout(); + bSizer21->Fit(m_scrolledWindow); + this->Layout(); + + // Fixup the tab order + m_buttonSubmitForm->MoveAfterInTabOrder(windowLast); + m_buttonCancelForm->MoveAfterInTabOrder(m_buttonSubmitForm); + //m_buttonBack->MoveAfterInTabOrder(m_buttonCancelForm); + //m_buttonNext->MoveAfterInTabOrder(m_buttonBack); + //m_buttonCancel->MoveAfterInTabOrder(m_buttonNext); + this->Layout(); +} + +void CViewProductDialog::GetOrder(CWalletTx& wtx) +{ + wtx.SetNull(); + for (int i = 0; i < product.vOrderForm.size(); i++) + { + string strValue; + if (m_textCtrlField[i]) + strValue = m_textCtrlField[i]->GetValue().Trim(); + else + strValue = m_choiceField[i]->GetStringSelection(); + wtx.vOrderForm.push_back(make_pair(m_staticTextLabel[i]->GetLabel(), strValue)); + } +} + +void CViewProductDialog::OnButtonSubmitForm(wxCommandEvent& event) +{ + m_buttonSubmitForm->Enable(false); + m_buttonCancelForm->Enable(false); + + CWalletTx wtx; + GetOrder(wtx); + + CSendingDialog* pdialog = new CSendingDialog(this, product.addr, atoi64(product.mapValue["price"]), wtx); + if (!pdialog->ShowModal()) + { + m_buttonSubmitForm->Enable(true); + m_buttonCancelForm->Enable(true); + return; + } +} + +void CViewProductDialog::OnButtonCancelForm(wxCommandEvent& event) +{ + Destroy(); +} + +void CViewProductDialog::OnButtonBack(wxCommandEvent& event) +{ + Freeze(); + m_htmlWinReviews->Show(true); + m_scrolledWindow->Show(false); + m_buttonBack->Enable(false); + m_buttonNext->Enable(!product.vOrderForm.empty()); + this->Layout(); + Thaw(); +} + +void CViewProductDialog::OnButtonNext(wxCommandEvent& event) +{ + if (!product.vOrderForm.empty()) + { + Freeze(); + m_htmlWinReviews->Show(false); + m_scrolledWindow->Show(true); + m_buttonBack->Enable(true); + m_buttonNext->Enable(false); + this->Layout(); + Thaw(); + } +} + +void CViewProductDialog::OnButtonCancel(wxCommandEvent& event) +{ + Destroy(); +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CViewOrderDialog +// + +CViewOrderDialog::CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived) : CViewOrderDialogBase(parent) +{ + int64 nPrice = (fReceived ? order.GetCredit() : order.GetDebit()); + + string strHTML; + strHTML.reserve(4000); + strHTML += "\n" + "\n" + "\n" + "\n" + "\n"; + strHTML += "Time: " + HtmlEscape(DateTimeStr(order.nTimeReceived)) + "
\n"; + strHTML += "Price: " + HtmlEscape(FormatMoney(nPrice)) + "
\n"; + strHTML += "Status: " + HtmlEscape(FormatTxStatus(order)) + "
\n"; + + strHTML += "\n"; + for (int i = 0; i < order.vOrderForm.size(); i++) + { + strHTML += " "; + strHTML += "\n"; + } + strHTML += "
" + HtmlEscape(order.vOrderForm[i].first) + ":" + HtmlEscape(order.vOrderForm[i].second) + "
\n"; + + strHTML += "\n\n"; + + // Shrink capacity to fit + // (strings are ref counted, so it may live on in SetPage) + string(strHTML.begin(), strHTML.end()).swap(strHTML); + + m_htmlWin->SetPage(strHTML); +} + +void CViewOrderDialog::OnButtonOK(wxCommandEvent& event) +{ + Destroy(); +} + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CEditReviewDialog +// + +CEditReviewDialog::CEditReviewDialog(wxWindow* parent) : CEditReviewDialogBase(parent) +{ +} + +void CEditReviewDialog::OnButtonSubmit(wxCommandEvent& event) +{ + if (m_choiceStars->GetSelection() == -1) + { + wxMessageBox("Please select a rating "); + return; + } + + CReview review; + GetReview(review); + + // Sign the review + review.vchPubKeyFrom = keyUser.GetPubKey(); + if (!keyUser.Sign(review.GetSigHash(), review.vchSig)) + { + wxMessageBox("Unable to digitally sign the review "); + return; + } + + // Broadcast + if (!review.AcceptReview()) + { + wxMessageBox("Save failed "); + return; + } + RelayMessage(CInv(MSG_REVIEW, review.GetHash()), review); + + Destroy(); +} + +void CEditReviewDialog::OnButtonCancel(wxCommandEvent& event) +{ + Destroy(); +} + +void CEditReviewDialog::GetReview(CReview& review) +{ + review.mapValue["time"] = i64tostr(GetAdjustedTime()); + review.mapValue["stars"] = itostr(m_choiceStars->GetSelection()+1); + review.mapValue["review"] = m_textCtrlReview->GetValue(); +} + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// CMyApp +// + +// Define a new application +class CMyApp: public wxApp +{ + public: + CMyApp(){}; + ~CMyApp(){}; + bool OnInit(); + bool OnInit2(); + int OnExit(); + + // The MSYS2 release wxWidgets DLLs ship with asserts enabled. Log any + // assertion to debug.log instead of showing a modal "Debug Alert" popup, + // so the packaged build runs cleanly by double-click. + virtual void OnAssertFailure(const wxChar* file, int line, const wxChar* func, + const wxChar* cond, const wxChar* msg) + { + printf("wxAssert: %s(%d) %s: %s %s\n", + (const char*)wxString(file).mb_str(), line, + (const char*)wxString(func).mb_str(), + (const char*)wxString(cond).mb_str(), + (const char*)wxString(msg).mb_str()); + } + + // 2nd-level exception handling: we get all the exceptions occurring in any + // event handler here + virtual bool OnExceptionInMainLoop(); + + // 3rd, and final, level exception handling: whenever an unhandled + // exception is caught, this function is called + virtual void OnUnhandledException(); + + // and now for something different: this function is called in case of a + // crash (e.g. dereferencing null pointer, division by 0, ...) + virtual void OnFatalException(); +}; + +IMPLEMENT_APP(CMyApp) + +bool CMyApp::OnInit() +{ + // The 2009 wxFormBuilder layout mixes wxEXPAND with alignment flags, which + // modern wxWidgets flags as an assertion. Disable those sizer consistency + // checks globally so no "Debug Alert" popup ever blocks the UI. + wxSizerFlags::DisableConsistencyChecks(); + try + { + return OnInit2(); + } + catch (std::exception& e) { + PrintException(&e, "OnInit()"); + } catch (...) { + PrintException(NULL, "OnInit()"); + } + return false; +} + +map ParseParameters(int argc, char* argv[]) +{ + map mapArgs; + for (int i = 0; i < argc; i++) + { + char psz[10000]; + strcpy(psz, argv[i]); + char* pszValue = ""; + if (strchr(psz, '=')) + { + pszValue = strchr(psz, '='); + *pszValue++ = '\0'; + } + strlwr(psz); + if (psz[0] == '-') + psz[0] = '/'; + mapArgs[psz] = pszValue; + } + return mapArgs; +} + +bool CMyApp::OnInit2() +{ +#ifdef _MSC_VER + // Turn off microsoft heap dump noise for now + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); +#endif + + //// debug print + printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + printf("Bitcoin CMyApp::OnInit()\n"); + + // + // Parameters (parsed before the lock so /datadir and /port feed the mutex) + // + wxImage::AddHandler(new wxPNGHandler); + map mapArgs = ParseParameters(argc, argv); + + if (mapArgs.count("/datadir")) + strSetDataDir = mapArgs["/datadir"]; + + if (mapArgs.count("/port")) + nListenPort = htons(atoi(mapArgs["/port"].c_str())); + + // + // Limit to single instance per user/datadir + // The mutex includes the datadir to allow multiple nodes on one machine. + // + const char* pszHomePath = getenv("HOMEPATH"); + wxString strMutexName = wxString("Bitflash.") + (pszHomePath ? pszHomePath : "default") + "." + strSetDataDir.c_str(); + for (int i = 0; i < strMutexName.size(); i++) + if (!isalnum(strMutexName[i])) + strMutexName[i] = '.'; + wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); + if (psingleinstancechecker->IsAnotherRunning()) + { + printf("Existing instance found\n"); + unsigned int nStart = GetTime(); + loop + { + // Show the previous instance and exit + HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitflash"); + if (hwndPrev) + { + if (IsIconic(hwndPrev)) + ShowWindow(hwndPrev, SW_RESTORE); + SetForegroundWindow(hwndPrev); + return false; + } + + if (GetTime() > nStart + 60) + return false; + + // Resume this instance if the other exits + delete psingleinstancechecker; + Sleep(1000); + psingleinstancechecker = new wxSingleInstanceChecker(strMutexName); + if (!psingleinstancechecker->IsAnotherRunning()) + break; + } + } + + if (mapArgs.count("/proxy")) + addrProxy = CAddress(mapArgs["/proxy"].c_str()); + + // Rendezvous meeting relay this node registers its .btf service at and + // advertises in its descriptor (default is the seed relay; empty disables). + if (mapArgs.count("/rvrelay")) + strBtfMeetingRelay = mapArgs["/rvrelay"]; + + // Keep an anonymous outbound connection to a specific .btf peer. + if (mapArgs.count("/connectbtf")) + strBtfConnect = mapArgs["/connectbtf"]; + + if (mapArgs.count("/debug")) + fDebug = true; + + if (mapArgs.count("/dropmessages")) + { + nDropMessagesTest = atoi(mapArgs["/dropmessages"]); + if (nDropMessagesTest == 0) + nDropMessagesTest = 20; + } + + if (mapArgs.count("/loadblockindextest")) + { + CTxDB txdb("r"); + txdb.LoadBlockIndex(); + PrintBlockTree(); + ExitProcess(0); + } + + // + // Load data files + // + string strErrors; + int64 nStart, nEnd; + + printf("Loading addresses...\n"); + QueryPerformanceCounter((LARGE_INTEGER*)&nStart); + if (!LoadAddresses()) + strErrors += "Error loading addr.dat \n"; + QueryPerformanceCounter((LARGE_INTEGER*)&nEnd); + printf(" addresses %20I64d\n", nEnd - nStart); + + printf("Loading block index...\n"); + QueryPerformanceCounter((LARGE_INTEGER*)&nStart); + if (!LoadBlockIndex()) + strErrors += "Error loading blkindex.dat \n"; + QueryPerformanceCounter((LARGE_INTEGER*)&nEnd); + printf(" block index %20I64d\n", nEnd - nStart); + + printf("Loading wallet...\n"); + QueryPerformanceCounter((LARGE_INTEGER*)&nStart); + if (!LoadWallet()) + strErrors += "Error loading wallet.dat \n"; + QueryPerformanceCounter((LARGE_INTEGER*)&nEnd); + printf(" wallet %20I64d\n", nEnd - nStart); + + printf("Done loading\n"); + + //// debug print + printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size()); + printf("nBestHeight = %d\n", nBestHeight); + printf("mapKeys.size() = %d\n", mapKeys.size()); + printf("mapPubKeys.size() = %d\n", mapPubKeys.size()); + printf("mapWallet.size() = %d\n", mapWallet.size()); + printf("mapAddressBook.size() = %d\n", mapAddressBook.size()); + + if (!strErrors.empty()) + { + wxMessageBox(strErrors); + OnExit(); + return false; + } + + // Add wallet transactions that aren't already in a block to mapTransactions + ReacceptWalletTransactions(); + + // + // Parameters + // + if (mapArgs.count("/printblockindex") || mapArgs.count("/printblocktree")) + { + PrintBlockTree(); + OnExit(); + return false; + } + + if (mapArgs.count("/solomine")) + fSoloMineTest = true; + + if (mapArgs.count("/gen")) + { + if (mapArgs["/gen"].empty()) + fGenerateBitcoins = true; + else + fGenerateBitcoins = atoi(mapArgs["/gen"].c_str()); + } + + // + // Create the main frame window + // + { + pframeMain = new CMainFrame(NULL); + pframeMain->Show(); + + if (!StartNode(strErrors)) + wxMessageBox(strErrors); + + if (fGenerateBitcoins) + if (_beginthread(ThreadBitcoinMiner, 0, NULL) == -1) + printf("Error: _beginthread(ThreadBitcoinMiner) failed\n"); + + // + // Tests + // + if (argc >= 2 && wxString(argv[1]).CmpNoCase("/send") == 0) + { + int64 nValue = 1; + if (argc >= 3) + ParseMoney(argv[2], nValue); + + string strAddress; + if (argc >= 4) + strAddress = argv[3]; + CAddress addr(strAddress.c_str()); + + CWalletTx wtx; + wtx.mapValue["to"] = strAddress; + wtx.mapValue["from"] = addrLocalHost.ToString(); + wtx.mapValue["message"] = "command line send"; + + // Send to IP address + CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx); + if (!pdialog->ShowModal()) + return false; + } + + if (mapArgs.count("/randsendtest")) + { + if (!mapArgs["/randsendtest"].empty()) + _beginthread(ThreadRandSendTest, 0, new string(mapArgs["/randsendtest"])); + else + fRandSendTest = true; + fDebug = true; + } + } + + return true; +} + +int CMyApp::OnExit() +{ + Shutdown(NULL); + return wxApp::OnExit(); +} + +bool CMyApp::OnExceptionInMainLoop() +{ + try + { + throw; + } + catch (std::exception& e) + { + PrintException(&e, "CMyApp::OnExceptionInMainLoop()"); + wxLogWarning(_T("Exception %s %s"), typeid(e).name(), e.what()); + Sleep(1000); + throw; + } + catch (...) + { + PrintException(NULL, "CMyApp::OnExceptionInMainLoop()"); + wxLogWarning(_T("Unknown exception")); + Sleep(1000); + throw; + } + + return true; +} + +void CMyApp::OnUnhandledException() +{ + // this shows how we may let some exception propagate uncaught + try + { + throw; + } + catch (std::exception& e) + { + PrintException(&e, "CMyApp::OnUnhandledException()"); + wxLogWarning(_T("Exception %s %s"), typeid(e).name(), e.what()); + Sleep(1000); + throw; + } + catch (...) + { + PrintException(NULL, "CMyApp::OnUnhandledException()"); + wxLogWarning(_T("Unknown exception")); + Sleep(1000); + throw; + } +} + +void CMyApp::OnFatalException() +{ + wxMessageBox("Program has crashed and will terminate. ", "", wxOK | wxICON_ERROR); +} + + + +void MainFrameRepaint() +{ + if (pframeMain) + { + printf("MainFrameRepaint()\n"); + // Called from worker threads: repaint only on the GUI thread + wxTheApp->CallAfter([] { if (pframeMain) pframeMain->Refresh(); }); + } +} + + + + + + + +// randsendtest to bitcoin address +void ThreadRandSendTest(void* parg) +{ + string strAddress = *(string*)parg; + uint160 hash160; + if (!AddressToHash160(strAddress, hash160)) + { + wxMessageBox(strprintf("ThreadRandSendTest: Bitcoin address '%s' not valid ", strAddress.c_str())); + return; + } + + loop + { + Sleep(GetRand(30) * 1000 + 100); + + // Message + CWalletTx wtx; + wtx.mapValue["to"] = strAddress; + wtx.mapValue["from"] = addrLocalHost.ToString(); + static int nRep; + wtx.mapValue["message"] = strprintf("randsendtest %d\n", ++nRep); + + // Value + int64 nValue = (GetRand(9) + 1) * 100 * CENT; + if (GetBalance() < nValue) + { + wxMessageBox("Out of money "); + return; + } + nValue += (nRep % 100) * CENT; + + // Send to bitcoin address + CScript scriptPubKey; + scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + + if (!SendMoney(scriptPubKey, nValue, wtx)) + return; + } +} + + +// randsendtest to any connected node +void RandSend() +{ + CWalletTx wtx; + + while (vNodes.empty()) + Sleep(1000); + CAddress addr; + CRITICAL_BLOCK(cs_vNodes) + addr = vNodes[GetRand(vNodes.size())]->addr; + + // Message + wtx.mapValue["to"] = addr.ToString(); + wtx.mapValue["from"] = addrLocalHost.ToString(); + static int nRep; + wtx.mapValue["message"] = strprintf("randsendtest %d\n", ++nRep); + + // Value + int64 nValue = (GetRand(999) + 1) * CENT; + if (GetBalance() < nValue) + { + wxMessageBox("Out of money "); + return; + } + + // Send to IP address + CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx); + if (!pdialog->Show()) + wxMessageBox("ShowModal Failed "); +} diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..2efeace --- /dev/null +++ b/src/ui.h @@ -0,0 +1,417 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + + + + +DECLARE_EVENT_TYPE(wxEVT_CROSSTHREADCALL, -1) +DECLARE_EVENT_TYPE(wxEVT_REPLY1, -1) +DECLARE_EVENT_TYPE(wxEVT_REPLY2, -1) +DECLARE_EVENT_TYPE(wxEVT_REPLY3, -1) +DECLARE_EVENT_TYPE(wxEVT_TABLEADDED, -1) +DECLARE_EVENT_TYPE(wxEVT_TABLEUPDATED, -1) +DECLARE_EVENT_TYPE(wxEVT_TABLEDELETED, -1) + +enum +{ + UICALL_ADDORDER = 1, + UICALL_UPDATEORDER, +}; + + + +extern void HandleCtrlA(wxKeyEvent& event); +extern string DateTimeStr(int64 nTime); +extern string FormatTxStatus(const CWalletTx& wtx); +extern void CrossThreadCall(int nID, void* pdata); +extern void MainFrameRepaint(); + + + + + + +class CMainFrame : public CMainFrameBase +{ +protected: + // Event handlers + void OnClose(wxCloseEvent& event); + void OnMouseEvents(wxMouseEvent& event); + void OnKeyDown(wxKeyEvent& event) { HandleCtrlA(event); } + void OnIdle(wxIdleEvent& event); + void OnPaint(wxPaintEvent& event); + void OnPaintListCtrl(wxPaintEvent& event); + void OnMenuFileExit(wxCommandEvent& event); + void OnMenuOptionsGenerate(wxCommandEvent& event); + void OnMenuOptionsOptions(wxCommandEvent& event); + void OnMenuHelpAbout(wxCommandEvent& event); + void OnButtonSend(wxCommandEvent& event); + void OnButtonAddressBook(wxCommandEvent& event); + void OnSetFocusAddress(wxFocusEvent& event); + void OnMouseEventsAddress(wxMouseEvent& event); + void OnButtonCopy(wxCommandEvent& event); + void OnButtonChange(wxCommandEvent& event); + void OnListColBeginDrag(wxListEvent& event); + void OnListItemActivatedAllTransactions(wxListEvent& event); + void OnListItemActivatedProductsSent(wxListEvent& event); + void OnListItemActivatedOrdersSent(wxListEvent& event); + void OnListItemActivatedOrdersReceived(wxListEvent& event); + +public: + /** Constructor */ + CMainFrame(wxWindow* parent); + ~CMainFrame(); + + // Custom + bool fRefreshListCtrl; + bool fRefreshListCtrlRunning; + bool fOnSetFocusAddress; + CBlockIndex* pindexBestLast; + set setUnmaturedDisplayed; + + void OnCrossThreadCall(wxCommandEvent& event); + void InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5); + void InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex=-1); + void RefreshListCtrl(); + void RefreshStatus(); +}; + + + + +class CTxDetailsDialog : public CTxDetailsDialogBase +{ +protected: + // Event handlers + void OnButtonOK(wxCommandEvent& event); + +public: + /** Constructor */ + CTxDetailsDialog(wxWindow* parent, CWalletTx wtx); + + // State + CWalletTx wtx; +}; + + + +class COptionsDialog : public COptionsDialogBase +{ +protected: + // Event handlers + void OnKillFocusTransactionFee(wxFocusEvent& event); + void OnButtonOK(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + +public: + /** Constructor */ + COptionsDialog(wxWindow* parent); +}; + + + +class CAboutDialog : public CAboutDialogBase +{ +protected: + // Event handlers + void OnButtonOK(wxCommandEvent& event); + +public: + /** Constructor */ + CAboutDialog(wxWindow* parent); +}; + + + +class CSendDialog : public CSendDialogBase +{ +protected: + // Event handlers + void OnKeyDown(wxKeyEvent& event) { HandleCtrlA(event); } + void OnTextAddress(wxCommandEvent& event); + void OnKillFocusAmount(wxFocusEvent& event); + void OnButtonAddressBook(wxCommandEvent& event); + void OnButtonPaste(wxCommandEvent& event); + void OnButtonSend(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + +public: + /** Constructor */ + CSendDialog(wxWindow* parent, const wxString& strAddress=""); +}; + + + +class CSendingDialog : public CSendingDialogBase +{ +public: + // Event handlers + void OnClose(wxCloseEvent& event); + void OnButtonOK(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + void OnPaint(wxPaintEvent& event); + +public: + /** Constructor */ + CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 nPriceIn, const CWalletTx& wtxIn); + ~CSendingDialog(); + + // State + CAddress addr; + int64 nPrice; + CWalletTx wtx; + wxDateTime start; + string strStatus; + bool fCanCancel; + bool fAbort; + bool fSuccess; + bool fUIDone; + bool fWorkDone; + + void Close(); + void Repaint(); + bool Status(); + bool Status(const string& str); + bool Error(const string& str); + void StartTransfer(); + void OnReply2(CDataStream& vRecv); + void OnReply3(CDataStream& vRecv); +}; + +void SendingDialogStartTransfer(void* parg); +void SendingDialogOnReply2(void* parg, CDataStream& vRecv); +void SendingDialogOnReply3(void* parg, CDataStream& vRecv); + + + +class CYourAddressDialog : public CYourAddressDialogBase +{ +protected: + // Event handlers + void OnListEndLabelEdit(wxListEvent& event); + void OnListItemSelected(wxListEvent& event); + void OnListItemActivated(wxListEvent& event); + void OnButtonRename(wxCommandEvent& event); + void OnButtonNew(wxCommandEvent& event); + void OnButtonCopy(wxCommandEvent& event); + void OnButtonOK(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + void OnClose(wxCloseEvent& event); + +public: + /** Constructor */ + CYourAddressDialog(wxWindow* parent); + CYourAddressDialog(wxWindow* parent, const string& strInitSelected); + + // Custom + wxString GetAddress(); +}; + + + +class CAddressBookDialog : public CAddressBookDialogBase +{ +protected: + // Event handlers + void OnListEndLabelEdit(wxListEvent& event); + void OnListItemSelected(wxListEvent& event); + void OnListItemActivated(wxListEvent& event); + void OnButtonEdit(wxCommandEvent& event); + void OnButtonDelete(wxCommandEvent& event); + void OnButtonNew(wxCommandEvent& event); + void OnButtonCopy(wxCommandEvent& event); + void OnButtonOK(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + void OnClose(wxCloseEvent& event); + +public: + /** Constructor */ + CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn); + + // Custom + bool fSending; + wxString GetAddress(); +}; + + + +class CProductsDialog : public CProductsDialogBase +{ +protected: + // Event handlers + void OnKeyDown(wxKeyEvent& event) { HandleCtrlA(event); } + void OnCombobox(wxCommandEvent& event); + void OnButtonSearch(wxCommandEvent& event); + void OnListItemActivated(wxListEvent& event); + +public: + /** Constructor */ + CProductsDialog(wxWindow* parent); + + // Custom + vector m_vProduct; +}; + + + +class CEditProductDialog : public CEditProductDialogBase +{ +protected: + // Event handlers + void OnKeyDown(wxKeyEvent& event) { HandleCtrlA(event); } + void OnButtonDel0(wxCommandEvent& event); + void OnButtonDel1(wxCommandEvent& event); + void OnButtonDel2(wxCommandEvent& event); + void OnButtonDel3(wxCommandEvent& event); + void OnButtonDel4(wxCommandEvent& event); + void OnButtonDel5(wxCommandEvent& event); + void OnButtonDel6(wxCommandEvent& event); + void OnButtonDel7(wxCommandEvent& event); + void OnButtonDel8(wxCommandEvent& event); + void OnButtonDel9(wxCommandEvent& event); + void OnButtonDel10(wxCommandEvent& event); + void OnButtonDel11(wxCommandEvent& event); + void OnButtonDel12(wxCommandEvent& event); + void OnButtonDel13(wxCommandEvent& event); + void OnButtonDel14(wxCommandEvent& event); + void OnButtonDel15(wxCommandEvent& event); + void OnButtonDel16(wxCommandEvent& event); + void OnButtonDel17(wxCommandEvent& event); + void OnButtonDel18(wxCommandEvent& event); + void OnButtonDel19(wxCommandEvent& event); + void OnButtonAddField(wxCommandEvent& event); + void OnButtonSend(wxCommandEvent& event); + void OnButtonPreview(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + +public: + /** Constructor */ + CEditProductDialog(wxWindow* parent); + + // Custom + enum { FIELDS_MAX = 20 }; + wxTextCtrl* m_textCtrlLabel[FIELDS_MAX]; + wxTextCtrl* m_textCtrlField[FIELDS_MAX]; + wxButton* m_buttonDel[FIELDS_MAX]; + + void LayoutAll(); + void ShowLine(int i, bool fShow=true); + void OnButtonDel(wxCommandEvent& event, int n); + void SetProduct(const CProduct& productIn); + void GetProduct(CProduct& product); + +}; + + + +class CViewProductDialog : public CViewProductDialogBase +{ +protected: + // Event handlers + void OnButtonSubmitForm(wxCommandEvent& event); + void OnButtonCancelForm(wxCommandEvent& event); + void OnButtonBack(wxCommandEvent& event); + void OnButtonNext(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + +public: + /** Constructor */ + CViewProductDialog(wxWindow* parent, const CProduct& productIn); + ~CViewProductDialog(); + + // Custom + CProduct product; + enum { FIELDS_MAX = 20 }; + wxStaticText* m_staticTextLabel[FIELDS_MAX]; + wxTextCtrl* m_textCtrlField[FIELDS_MAX]; + wxChoice* m_choiceField[FIELDS_MAX]; + + void GetOrder(CWalletTx& order); + void UpdateProductDisplay(bool fDetails); + void OnReply1(wxCommandEvent& event); +}; + + + +class CViewOrderDialog : public CViewOrderDialogBase +{ +protected: + // Event handlers + void OnButtonOK(wxCommandEvent& event); + +public: + /** Constructor */ + CViewOrderDialog(wxWindow* parent, CWalletTx order, bool fReceived); + + // Custom + bool fReceived; +}; + + + +class CEditReviewDialog : public CEditReviewDialogBase +{ +protected: + // Event handlers + void OnKeyDown(wxKeyEvent& event) { HandleCtrlA(event); } + void OnButtonSubmit(wxCommandEvent& event); + void OnButtonCancel(wxCommandEvent& event); + +public: + /** Constructor */ + CEditReviewDialog(wxWindow* parent); + + // Custom + void GetReview(CReview& review); +}; + + + +class CGetTextFromUserDialog : public CGetTextFromUserDialogBase +{ +protected: + // Event handlers + void OnButtonOK(wxCommandEvent& event) { EndModal(true); } + void OnButtonCancel(wxCommandEvent& event) { EndModal(false); } + void OnClose(wxCloseEvent& event) { EndModal(false); } + + void OnKeyDown(wxKeyEvent& event) + { + if (event.GetKeyCode() == '\r' || event.GetKeyCode() == WXK_NUMPAD_ENTER) + EndModal(true); + else + HandleCtrlA(event); + } + +public: + /** Constructor */ + CGetTextFromUserDialog(wxWindow* parent, + const string& strCaption, + const string& strMessage1, + const string& strValue1="", + const string& strMessage2="", + const string& strValue2="") : CGetTextFromUserDialogBase(parent, wxID_ANY, strCaption) + { + m_staticTextMessage1->SetLabel(strMessage1); + m_textCtrl1->SetValue(strValue1); + if (!strMessage2.empty()) + { + m_staticTextMessage2->Show(true); + m_staticTextMessage2->SetLabel(strMessage2); + m_textCtrl2->Show(true); + m_textCtrl2->SetValue(strValue2); + SetSize(wxDefaultCoord, 180); + } + } + + // Custom + string GetValue() { return (string)m_textCtrl1->GetValue(); } + string GetValue1() { return (string)m_textCtrl1->GetValue(); } + string GetValue2() { return (string)m_textCtrl2->GetValue(); } +}; + + + + + diff --git a/src/ui.rc b/src/ui.rc new file mode 100644 index 0000000..ec8b142 --- /dev/null +++ b/src/ui.rc @@ -0,0 +1,14 @@ +bitcoin ICON "rc/bitcoin.ico" + +#include "wx/msw/wx.rc" + +check ICON "rc/check.ico" +send16 BITMAP "rc/send16.bmp" +send16mask BITMAP "rc/send16mask.bmp" +send16masknoshadow BITMAP "rc/send16masknoshadow.bmp" +send20 BITMAP "rc/send20.bmp" +send20mask BITMAP "rc/send20mask.bmp" +addressbook16 BITMAP "rc/addressbook16.bmp" +addressbook16mask BITMAP "rc/addressbook16mask.bmp" +addressbook20 BITMAP "rc/addressbook20.bmp" +addressbook20mask BITMAP "rc/addressbook20mask.bmp" diff --git a/src/uibase.cpp b/src/uibase.cpp new file mode 100644 index 0000000..95ff887 --- /dev/null +++ b/src/uibase.cpp @@ -0,0 +1,1808 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Apr 16 2008) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "uibase.h" + +/////////////////////////////////////////////////////////////////////////// + +CMainFrameBase::CMainFrameBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + + m_menubar = new wxMenuBar(0); + m_menubar->SetBackgroundColour(wxColour(240, 240, 240)); + + m_menuFile = new wxMenu(); + wxMenuItem* m_menuFileExit; + m_menuFileExit = new wxMenuItem(m_menuFile, wxID_ANY, wxString(wxT("E&xit")) , wxEmptyString, wxITEM_NORMAL); + m_menuFile->Append(m_menuFileExit); + + m_menubar->Append(m_menuFile, wxT("&File")); + + m_menuOptions = new wxMenu(); + wxMenuItem* m_menuOptionsGenerateBitcoins; + m_menuOptionsGenerateBitcoins = new wxMenuItem(m_menuOptions, wxID_OPTIONSGENERATEBITCOINS, wxString(wxT("&Generate Coins (CPU)")), wxEmptyString, wxITEM_CHECK); + m_menuOptions->Append(m_menuOptionsGenerateBitcoins); + + wxMenuItem* m_menuOptionsOptions; + m_menuOptionsOptions = new wxMenuItem(m_menuOptions, wxID_ANY, wxString(wxT("&Options...")) , wxEmptyString, wxITEM_NORMAL); + m_menuOptions->Append(m_menuOptionsOptions); + + m_menubar->Append(m_menuOptions, wxT("&Options")); + + m_menuHelp = new wxMenu(); + wxMenuItem* m_menuHelpAbout; + m_menuHelpAbout = new wxMenuItem(m_menuHelp, wxID_ANY, wxString(wxT("&About...")) , wxEmptyString, wxITEM_NORMAL); + m_menuHelp->Append(m_menuHelpAbout); + + m_menubar->Append(m_menuHelp, wxT("&Help")); + + this->SetMenuBar(m_menubar); + + m_toolBar = this->CreateToolBar(wxTB_FLAT|wxTB_HORZ_TEXT, wxID_ANY); + m_toolBar->SetToolBitmapSize(wxSize(20,20)); + m_toolBar->SetToolSeparation(1); + m_toolBar->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString)); + + m_toolBar->AddTool(wxID_BUTTONSEND, wxT("&Send Coins"), wxBitmap(wxT("send20"), wxBITMAP_TYPE_RESOURCE), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString); + m_toolBar->AddTool(wxID_BUTTONRECEIVE, wxT("&Address Book"), wxBitmap(wxT("addressbook20"), wxBITMAP_TYPE_RESOURCE), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString); + m_toolBar->Realize(); + + m_statusBar = this->CreateStatusBar(1, wxST_SIZEGRIP, wxID_ANY); + m_statusBar->SetBackgroundColour(wxColour(240, 240, 240)); + + wxBoxSizer* bSizer2; + bSizer2 = new wxBoxSizer(wxVERTICAL); + + + bSizer2->Add(0, 2, 0, wxEXPAND, 5); + + wxBoxSizer* bSizer85; + bSizer85 = new wxBoxSizer(wxHORIZONTAL); + + m_staticText32 = new wxStaticText(this, wxID_ANY, wxT("Your Bitflash Address:"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText32->Wrap(-1); + bSizer85->Add(m_staticText32, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5); + + m_textCtrlAddress = new wxTextCtrl(this, wxID_TEXTCTRLADDRESS, wxEmptyString, wxDefaultPosition, wxSize(250,-1), wxTE_READONLY); + m_textCtrlAddress->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); + + bSizer85->Add(m_textCtrlAddress, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_buttonCopy = new wxButton(this, wxID_BUTTONCOPY, wxT("&Copy to Clipboard"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + bSizer85->Add(m_buttonCopy, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); + + m_button91 = new wxButton(this, wxID_BUTTONCHANGE, wxT("C&hange..."), wxDefaultPosition, wxDefaultSize, 0); + bSizer85->Add(m_button91, 0, wxRIGHT, 5); + + + bSizer85->Add(0, 0, 0, wxEXPAND, 5); + + bSizer2->Add(bSizer85, 0, wxEXPAND|wxRIGHT|wxLEFT, 5); + + wxBoxSizer* bSizer3; + bSizer3 = new wxBoxSizer(wxHORIZONTAL); + + m_panel14 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + wxBoxSizer* bSizer66; + bSizer66 = new wxBoxSizer(wxHORIZONTAL); + + m_staticText41 = new wxStaticText(m_panel14, wxID_ANY, wxT("Balance:"), wxDefaultPosition, wxSize(-1,15), 0); + m_staticText41->Wrap(-1); + bSizer66->Add(m_staticText41, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5); + + m_staticTextBalance = new wxStaticText(m_panel14, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(120,15), wxALIGN_RIGHT|wxST_NO_AUTORESIZE); + m_staticTextBalance->Wrap(-1); + m_staticTextBalance->SetFont(wxFont(8, 70, 90, 90, false, wxEmptyString)); + m_staticTextBalance->SetBackgroundColour(wxColour(240, 240, 240)); + + bSizer66->Add(m_staticTextBalance, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + m_panel14->SetSizer(bSizer66); + m_panel14->Layout(); + bSizer66->Fit(m_panel14); + bSizer3->Add(m_panel14, 1, wxEXPAND|wxALIGN_BOTTOM|wxALL, 5); + + + bSizer3->Add(0, 0, 0, wxEXPAND, 5); + + wxString m_choiceFilterChoices[] = { wxT(" All"), wxT(" Sent"), wxT(" Received"), wxT(" In Progress") }; + int m_choiceFilterNChoices = sizeof(m_choiceFilterChoices) / sizeof(wxString); + m_choiceFilter = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxSize(110,-1), m_choiceFilterNChoices, m_choiceFilterChoices, 0); + m_choiceFilter->SetSelection(0); + m_choiceFilter->Hide(); + + bSizer3->Add(m_choiceFilter, 0, wxALIGN_BOTTOM|wxTOP|wxRIGHT|wxLEFT, 5); + + bSizer2->Add(bSizer3, 0, wxEXPAND, 5); + + m_notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); + m_panel7 = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + wxBoxSizer* bSizer157; + bSizer157 = new wxBoxSizer(wxVERTICAL); + + m_listCtrl = new wxListCtrl(m_panel7, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT|wxLC_SORT_DESCENDING|wxALWAYS_SHOW_SB); + bSizer157->Add(m_listCtrl, 1, wxEXPAND|wxALL, 5); + + m_panel7->SetSizer(bSizer157); + m_panel7->Layout(); + bSizer157->Fit(m_panel7); + m_notebook->AddPage(m_panel7, wxT("All Transactions"), false); + + bSizer2->Add(m_notebook, 1, wxEXPAND, 5); + + wxBoxSizer* bSizer_TabsForFutureUse; + bSizer_TabsForFutureUse = new wxBoxSizer(wxVERTICAL); + + m_panel9 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel9->Hide(); + + wxBoxSizer* bSizer159; + bSizer159 = new wxBoxSizer(wxVERTICAL); + + m_listCtrlEscrows = new wxListCtrl(m_panel9, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT); + bSizer159->Add(m_listCtrlEscrows, 1, wxALL|wxEXPAND, 5); + + m_panel9->SetSizer(bSizer159); + m_panel9->Layout(); + bSizer159->Fit(m_panel9); + bSizer_TabsForFutureUse->Add(m_panel9, 1, wxEXPAND | wxALL, 5); + + m_panel8 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel8->Hide(); + + wxBoxSizer* bSizer158; + bSizer158 = new wxBoxSizer(wxVERTICAL); + + m_listCtrlOrdersSent = new wxListCtrl(m_panel8, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT); + bSizer158->Add(m_listCtrlOrdersSent, 1, wxALL|wxEXPAND, 5); + + m_panel8->SetSizer(bSizer158); + m_panel8->Layout(); + bSizer158->Fit(m_panel8); + bSizer_TabsForFutureUse->Add(m_panel8, 1, wxEXPAND | wxALL, 5); + + m_panel10 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel10->Hide(); + + wxBoxSizer* bSizer160; + bSizer160 = new wxBoxSizer(wxVERTICAL); + + m_listCtrlProductsSent = new wxListCtrl(m_panel10, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT); + bSizer160->Add(m_listCtrlProductsSent, 1, wxALL|wxEXPAND, 5); + + m_panel10->SetSizer(bSizer160); + m_panel10->Layout(); + bSizer160->Fit(m_panel10); + bSizer_TabsForFutureUse->Add(m_panel10, 1, wxEXPAND | wxALL, 5); + + m_panel11 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_panel11->Hide(); + + wxBoxSizer* bSizer161; + bSizer161 = new wxBoxSizer(wxVERTICAL); + + m_listCtrlOrdersReceived = new wxListCtrl(m_panel11, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT); + bSizer161->Add(m_listCtrlOrdersReceived, 1, wxALL|wxEXPAND, 5); + + m_panel11->SetSizer(bSizer161); + m_panel11->Layout(); + bSizer161->Fit(m_panel11); + bSizer_TabsForFutureUse->Add(m_panel11, 1, wxEXPAND | wxALL, 5); + + bSizer2->Add(bSizer_TabsForFutureUse, 1, wxEXPAND, 5); + + this->SetSizer(bSizer2); + this->Layout(); + + // Connect Events + this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CMainFrameBase::OnClose)); + this->Connect(wxEVT_IDLE, wxIdleEventHandler(CMainFrameBase::OnIdle)); + this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_MOTION, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_MIDDLE_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_RIGHT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Connect(wxEVT_PAINT, wxPaintEventHandler(CMainFrameBase::OnPaint)); + this->Connect(m_menuFileExit->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuFileExit)); + this->Connect(m_menuOptionsGenerateBitcoins->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuOptionsGenerate)); + this->Connect(m_menuOptionsOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuOptionsOptions)); + this->Connect(m_menuHelpAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuHelpAbout)); + this->Connect(wxID_BUTTONSEND, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonSend)); + this->Connect(wxID_BUTTONRECEIVE, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonAddressBook)); + m_textCtrlAddress->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CMainFrameBase::OnKeyDown), NULL, this); + m_textCtrlAddress->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_MOTION, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_MIDDLE_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_RIGHT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(CMainFrameBase::OnSetFocusAddress), NULL, this); + m_buttonCopy->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonCopy), NULL, this); + m_button91->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonChange), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, wxListEventHandler(CMainFrameBase::OnListColBeginDrag), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedAllTransactions), NULL, this); + m_listCtrl->Connect(wxEVT_PAINT, wxPaintEventHandler(CMainFrameBase::OnPaintListCtrl), NULL, this); + m_listCtrlOrdersSent->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedOrdersSent), NULL, this); + m_listCtrlProductsSent->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedProductsSent), NULL, this); + m_listCtrlOrdersReceived->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedOrdersReceived), NULL, this); +} + +CMainFrameBase::~CMainFrameBase() +{ + // Disconnect Events + this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CMainFrameBase::OnClose)); + this->Disconnect(wxEVT_IDLE, wxIdleEventHandler(CMainFrameBase::OnIdle)); + this->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_MIDDLE_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_RIGHT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_MOTION, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_MIDDLE_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_RIGHT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(CMainFrameBase::OnMouseEvents)); + this->Disconnect(wxEVT_PAINT, wxPaintEventHandler(CMainFrameBase::OnPaint)); + this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuFileExit)); + this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuOptionsGenerate)); + this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuOptionsOptions)); + this->Disconnect(wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CMainFrameBase::OnMenuHelpAbout)); + this->Disconnect(wxID_BUTTONSEND, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonSend)); + this->Disconnect(wxID_BUTTONRECEIVE, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonAddressBook)); + m_textCtrlAddress->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CMainFrameBase::OnKeyDown), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_MIDDLE_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_RIGHT_UP, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_MOTION, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_MIDDLE_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_RIGHT_DCLICK, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(CMainFrameBase::OnMouseEventsAddress), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(CMainFrameBase::OnSetFocusAddress), NULL, this); + m_buttonCopy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonCopy), NULL, this); + m_button91->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CMainFrameBase::OnButtonChange), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, wxListEventHandler(CMainFrameBase::OnListColBeginDrag), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedAllTransactions), NULL, this); + m_listCtrl->Disconnect(wxEVT_PAINT, wxPaintEventHandler(CMainFrameBase::OnPaintListCtrl), NULL, this); + m_listCtrlOrdersSent->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedOrdersSent), NULL, this); + m_listCtrlProductsSent->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedProductsSent), NULL, this); + m_listCtrlOrdersReceived->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CMainFrameBase::OnListItemActivatedOrdersReceived), NULL, this); +} + +CTxDetailsDialogBase::CTxDetailsDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer64; + bSizer64 = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* bSizer66; + bSizer66 = new wxBoxSizer(wxVERTICAL); + + m_htmlWin = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); + bSizer66->Add(m_htmlWin, 1, wxALL|wxEXPAND, 5); + + bSizer64->Add(bSizer66, 1, wxEXPAND, 5); + + wxBoxSizer* bSizer65; + bSizer65 = new wxBoxSizer(wxVERTICAL); + + m_buttonOK = new wxButton(this, wxID_OK, wxT("OK"), wxDefaultPosition, wxSize(85,25), 0); + bSizer65->Add(m_buttonOK, 0, wxALL, 5); + + bSizer64->Add(bSizer65, 0, wxALIGN_RIGHT, 5); + + this->SetSizer(bSizer64); + this->Layout(); + + // Connect Events + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CTxDetailsDialogBase::OnButtonOK), NULL, this); +} + +CTxDetailsDialogBase::~CTxDetailsDialogBase() +{ + // Disconnect Events + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CTxDetailsDialogBase::OnButtonOK), NULL, this); +} + +COptionsDialogBase::COptionsDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer55; + bSizer55 = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* bSizer57; + bSizer57 = new wxBoxSizer(wxVERTICAL); + + + bSizer57->Add(0, 20, 0, wxEXPAND, 5); + + m_staticText32 = new wxStaticText(this, wxID_ANY, wxT("Optional transaction fee you give to the nodes that process your transactions."), wxDefaultPosition, wxDefaultSize, 0); + m_staticText32->Wrap(-1); + bSizer57->Add(m_staticText32, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + wxBoxSizer* bSizer56; + bSizer56 = new wxBoxSizer(wxHORIZONTAL); + + m_staticText31 = new wxStaticText(this, wxID_ANY, wxT("Transaction fee:"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText31->Wrap(-1); + bSizer56->Add(m_staticText31, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5); + + m_textCtrlTransactionFee = new wxTextCtrl(this, wxID_TRANSACTIONFEE, wxEmptyString, wxDefaultPosition, wxSize(70,-1), 0); + bSizer56->Add(m_textCtrlTransactionFee, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + bSizer57->Add(bSizer56, 0, wxEXPAND, 5); + + bSizer55->Add(bSizer57, 1, wxEXPAND|wxLEFT, 5); + + wxBoxSizer* bSizer58; + bSizer58 = new wxBoxSizer(wxHORIZONTAL); + + m_buttonOK = new wxButton(this, wxID_OK, wxT("OK"), wxDefaultPosition, wxSize(85,25), 0); + bSizer58->Add(m_buttonOK, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer58->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer55->Add(bSizer58, 0, wxALIGN_RIGHT, 5); + + this->SetSizer(bSizer55); + this->Layout(); + + // Connect Events + m_textCtrlTransactionFee->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(COptionsDialogBase::OnKillFocusTransactionFee), NULL, this); + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(COptionsDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(COptionsDialogBase::OnButtonCancel), NULL, this); +} + +COptionsDialogBase::~COptionsDialogBase() +{ + // Disconnect Events + m_textCtrlTransactionFee->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(COptionsDialogBase::OnKillFocusTransactionFee), NULL, this); + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(COptionsDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(COptionsDialogBase::OnButtonCancel), NULL, this); +} + +CAboutDialogBase::CAboutDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer60; + bSizer60 = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* bSizer62; + bSizer62 = new wxBoxSizer(wxHORIZONTAL); + + + bSizer62->Add(60, 0, 0, wxEXPAND, 5); + + wxBoxSizer* bSizer63; + bSizer63 = new wxBoxSizer(wxVERTICAL); + + + bSizer63->Add(0, 50, 0, wxEXPAND, 5); + + wxBoxSizer* bSizer64; + bSizer64 = new wxBoxSizer(wxHORIZONTAL); + + m_staticText40 = new wxStaticText(this, wxID_ANY, wxT("Bitflash "), wxDefaultPosition, wxDefaultSize, 0); + m_staticText40->Wrap(-1); + m_staticText40->SetFont(wxFont(10, 74, 90, 92, false, wxT("Tahoma"))); + + bSizer64->Add(m_staticText40, 0, wxALIGN_BOTTOM|wxTOP|wxBOTTOM|wxLEFT, 5); + + m_staticTextVersion = new wxStaticText(this, wxID_ANY, wxT("version"), wxDefaultPosition, wxDefaultSize, 0); + m_staticTextVersion->Wrap(-1); + m_staticTextVersion->SetFont(wxFont(10, 74, 90, 90, false, wxT("Tahoma"))); + + bSizer64->Add(m_staticTextVersion, 0, wxALIGN_BOTTOM|wxTOP|wxBOTTOM|wxRIGHT, 5); + + bSizer63->Add(bSizer64, 0, wxEXPAND, 5); + + + bSizer63->Add(0, 4, 0, wxEXPAND, 5); + + m_staticTextMain = new wxStaticText(this, wxID_ANY, wxT("Bitflash (BTF) - a revival of the original Bitcoin v0.1 client.\nNo pre-mine. No ICO. One CPU, one vote.\n\nCopyright © 2009 Satoshi Nakamoto (original Bitcoin).\nCopyright © 2026 The Bitflash developers.\n\nThis is experimental software. Do not rely on it for actual financial transactions.\n\nDistributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php.\n\nThis product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com)."), wxDefaultPosition, wxDefaultSize, 0); + m_staticTextMain->Wrap(400); + bSizer63->Add(m_staticTextMain, 0, wxALL, 5); + + + bSizer63->Add(0, 0, 1, wxEXPAND, 5); + + bSizer62->Add(bSizer63, 1, wxEXPAND, 5); + + bSizer60->Add(bSizer62, 1, wxEXPAND, 5); + + wxBoxSizer* bSizer61; + bSizer61 = new wxBoxSizer(wxHORIZONTAL); + + + bSizer61->Add(0, 0, 1, wxEXPAND, 5); + + m_buttonOK = new wxButton(this, wxID_OK, wxT("OK"), wxDefaultPosition, wxSize(85,25), 0); + bSizer61->Add(m_buttonOK, 0, wxALL, 5); + + bSizer60->Add(bSizer61, 0, wxALIGN_RIGHT|wxEXPAND, 5); + + this->SetSizer(bSizer60); + this->Layout(); + // Resize the dialog to fit all the About text (avoids the bottom being cut off) + bSizer60->Fit(this); + + // Connect Events + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAboutDialogBase::OnButtonOK), NULL, this); +} + +CAboutDialogBase::~CAboutDialogBase() +{ + // Disconnect Events + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAboutDialogBase::OnButtonOK), NULL, this); +} + +CSendDialogBase::CSendDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer21; + bSizer21 = new wxBoxSizer(wxVERTICAL); + + + bSizer21->Add(0, 5, 0, wxEXPAND, 5); + + wxFlexGridSizer* fgSizer1; + fgSizer1 = new wxFlexGridSizer(3, 2, 0, 0); + fgSizer1->AddGrowableCol(1); + fgSizer1->SetFlexibleDirection(wxBOTH); + fgSizer1->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + + + fgSizer1->Add(0, 0, 0, wxEXPAND, 5); + + m_staticText14 = new wxStaticText(this, wxID_ANY, wxT("Enter the recipient's IP address (e.g. 123.45.6.7) for online transfer with comments and confirmation, \nor Bitflash address if recipient is not online."), wxDefaultPosition, wxDefaultSize, 0); + m_staticText14->Wrap(-1); + fgSizer1->Add(m_staticText14, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + wxBoxSizer* bSizer47; + bSizer47 = new wxBoxSizer(wxHORIZONTAL); + + bSizer47->SetMinSize(wxSize(70,-1)); + + bSizer47->Add(0, 0, 1, wxEXPAND, 5); + + m_bitmapCheckMark = new wxStaticBitmap(this, wxID_ANY, wxICON(check), wxDefaultPosition, wxSize(16,16), 0); + bSizer47->Add(m_bitmapCheckMark, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_staticText36 = new wxStaticText(this, wxID_ANY, wxT("Pay &To:"), wxDefaultPosition, wxSize(-1,-1), wxALIGN_RIGHT); + m_staticText36->Wrap(-1); + bSizer47->Add(m_staticText36, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5); + + fgSizer1->Add(bSizer47, 1, wxEXPAND|wxLEFT, 5); + + wxBoxSizer* bSizer19; + bSizer19 = new wxBoxSizer(wxHORIZONTAL); + + m_textCtrlAddress = new wxTextCtrl(this, wxID_TEXTCTRLPAYTO, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + bSizer19->Add(m_textCtrlAddress, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + m_buttonPaste = new wxButton(this, wxID_BUTTONPASTE, wxT("&Paste"), wxDefaultPosition, wxSize(-1,-1), wxBU_EXACTFIT); + bSizer19->Add(m_buttonPaste, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); + + m_buttonAddress = new wxButton(this, wxID_BUTTONADDRESSBOOK, wxT(" Address &Book..."), wxDefaultPosition, wxDefaultSize, 0); + bSizer19->Add(m_buttonAddress, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); + + fgSizer1->Add(bSizer19, 1, wxEXPAND|wxRIGHT, 5); + + m_staticText19 = new wxStaticText(this, wxID_ANY, wxT("&Amount:"), wxDefaultPosition, wxSize(-1,-1), wxALIGN_RIGHT); + m_staticText19->Wrap(-1); + fgSizer1->Add(m_staticText19, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_RIGHT, 5); + + m_textCtrlAmount = new wxTextCtrl(this, wxID_TEXTCTRLAMOUNT, wxEmptyString, wxDefaultPosition, wxSize(145,-1), 0); + m_textCtrlAmount->SetMaxLength(20); + m_textCtrlAmount->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString)); + + fgSizer1->Add(m_textCtrlAmount, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + m_staticText20 = new wxStaticText(this, wxID_ANY, wxT("T&ransfer:"), wxDefaultPosition, wxSize(-1,-1), wxALIGN_RIGHT); + m_staticText20->Wrap(-1); + fgSizer1->Add(m_staticText20, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5); + + wxString m_choiceTransferTypeChoices[] = { wxT(" Standard") }; + int m_choiceTransferTypeNChoices = sizeof(m_choiceTransferTypeChoices) / sizeof(wxString); + m_choiceTransferType = new wxChoice(this, wxID_CHOICETRANSFERTYPE, wxDefaultPosition, wxDefaultSize, m_choiceTransferTypeNChoices, m_choiceTransferTypeChoices, 0); + m_choiceTransferType->SetSelection(0); + fgSizer1->Add(m_choiceTransferType, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + bSizer21->Add(fgSizer1, 0, wxEXPAND|wxLEFT, 5); + + wxBoxSizer* bSizer672; + bSizer672 = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer* bSizer681; + bSizer681 = new wxBoxSizer(wxVERTICAL); + + m_staticTextFrom = new wxStaticText(this, wxID_ANY, wxT("&From:"), wxDefaultPosition, wxDefaultSize, 0); + m_staticTextFrom->Wrap(-1); + bSizer681->Add(m_staticTextFrom, 0, wxBOTTOM|wxLEFT, 5); + + m_textCtrlFrom = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + bSizer681->Add(m_textCtrlFrom, 0, wxLEFT|wxEXPAND, 5); + + bSizer672->Add(bSizer681, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5); + + bSizer21->Add(bSizer672, 0, wxEXPAND, 5); + + wxBoxSizer* bSizer67; + bSizer67 = new wxBoxSizer(wxHORIZONTAL); + + wxBoxSizer* bSizer68; + bSizer68 = new wxBoxSizer(wxVERTICAL); + + m_staticTextMessage = new wxStaticText(this, wxID_ANY, wxT("&Message:"), wxDefaultPosition, wxDefaultSize, 0); + m_staticTextMessage->Wrap(-1); + bSizer68->Add(m_staticTextMessage, 0, wxTOP|wxBOTTOM|wxLEFT, 5); + + m_textCtrlMessage = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); + bSizer68->Add(m_textCtrlMessage, 1, wxEXPAND|wxLEFT, 5); + + bSizer67->Add(bSizer68, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5); + + bSizer21->Add(bSizer67, 1, wxEXPAND, 5); + + wxBoxSizer* bSizer23; + bSizer23 = new wxBoxSizer(wxHORIZONTAL); + + + bSizer23->Add(0, 0, 1, wxEXPAND, 5); + + m_buttonSend = new wxButton(this, wxID_BUTTONSEND, wxT("&Send"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonSend->SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString)); + m_buttonSend->SetMinSize(wxSize(85,25)); + + bSizer23->Add(m_buttonSend, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer23->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer21->Add(bSizer23, 0, wxEXPAND, 5); + + this->SetSizer(bSizer21); + this->Layout(); + + // Connect Events + m_textCtrlAddress->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_textCtrlAddress->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(CSendDialogBase::OnTextAddress), NULL, this); + m_buttonPaste->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonPaste), NULL, this); + m_buttonAddress->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonAddressBook), NULL, this); + m_textCtrlAmount->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_textCtrlAmount->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(CSendDialogBase::OnKillFocusAmount), NULL, this); + m_textCtrlFrom->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_textCtrlMessage->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_buttonSend->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonSend), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonCancel), NULL, this); +} + +CSendDialogBase::~CSendDialogBase() +{ + // Disconnect Events + m_textCtrlAddress->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_textCtrlAddress->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(CSendDialogBase::OnTextAddress), NULL, this); + m_buttonPaste->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonPaste), NULL, this); + m_buttonAddress->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonAddressBook), NULL, this); + m_textCtrlAmount->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_textCtrlAmount->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(CSendDialogBase::OnKillFocusAmount), NULL, this); + m_textCtrlFrom->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_textCtrlMessage->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CSendDialogBase::OnKeyDown), NULL, this); + m_buttonSend->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonSend), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendDialogBase::OnButtonCancel), NULL, this); +} + +CSendingDialogBase::CSendingDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer68; + bSizer68 = new wxBoxSizer(wxVERTICAL); + + m_staticTextSending = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,14), 0); + m_staticTextSending->Wrap(-1); + bSizer68->Add(m_staticTextSending, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 8); + + m_textCtrlStatus = new wxTextCtrl(this, wxID_ANY, wxT("\n\nConnecting..."), wxDefaultPosition, wxDefaultSize, wxTE_CENTRE|wxTE_MULTILINE|wxTE_NO_VSCROLL|wxTE_READONLY|wxNO_BORDER); + m_textCtrlStatus->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + + bSizer68->Add(m_textCtrlStatus, 1, wxEXPAND|wxRIGHT|wxLEFT, 10); + + wxBoxSizer* bSizer69; + bSizer69 = new wxBoxSizer(wxHORIZONTAL); + + + bSizer69->Add(0, 0, 1, wxEXPAND, 5); + + m_buttonOK = new wxButton(this, wxID_ANY, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonOK->Enable(false); + m_buttonOK->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonOK, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer68->Add(bSizer69, 0, wxEXPAND, 5); + + this->SetSizer(bSizer68); + this->Layout(); + + // Connect Events + this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CSendingDialogBase::OnClose)); + this->Connect(wxEVT_PAINT, wxPaintEventHandler(CSendingDialogBase::OnPaint)); + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendingDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendingDialogBase::OnButtonCancel), NULL, this); +} + +CSendingDialogBase::~CSendingDialogBase() +{ + // Disconnect Events + this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CSendingDialogBase::OnClose)); + this->Disconnect(wxEVT_PAINT, wxPaintEventHandler(CSendingDialogBase::OnPaint)); + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendingDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CSendingDialogBase::OnButtonCancel), NULL, this); +} + +CYourAddressDialogBase::CYourAddressDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer68; + bSizer68 = new wxBoxSizer(wxVERTICAL); + + + bSizer68->Add(0, 5, 0, wxEXPAND, 5); + + m_staticText45 = new wxStaticText(this, wxID_ANY, wxT("These are your Bitflash addresses for receiving payments.\nYou may want to give a different one to each sender so you can keep track of who is paying you."), wxDefaultPosition, wxDefaultSize, 0); + m_staticText45->Wrap(600); + bSizer68->Add(m_staticText45, 0, wxALL, 5); + + m_listCtrl = new wxListCtrl(this, wxID_LISTCTRL, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT|wxLC_SORT_ASCENDING); + bSizer68->Add(m_listCtrl, 1, wxALL|wxEXPAND, 5); + + wxBoxSizer* bSizer69; + bSizer69 = new wxBoxSizer(wxHORIZONTAL); + + + bSizer69->Add(0, 0, 1, wxEXPAND, 5); + + m_buttonRename = new wxButton(this, wxID_BUTTONRENAME, wxT("&Rename..."), wxDefaultPosition, wxDefaultSize, 0); + m_buttonRename->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonRename, 0, wxALL, 5); + + m_buttonNew = new wxButton(this, wxID_BUTTONNEW, wxT("&New Address..."), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonNew->SetMinSize(wxSize(110,25)); + + bSizer69->Add(m_buttonNew, 0, wxALL, 5); + + m_buttonCopy = new wxButton(this, wxID_BUTTONCOPY, wxT("&Copy to Clipboard"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonCopy->SetMinSize(wxSize(120,25)); + + bSizer69->Add(m_buttonCopy, 0, wxALL, 5); + + m_buttonOK = new wxButton(this, wxID_OK, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonOK->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonOK, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer68->Add(bSizer69, 0, wxEXPAND, 5); + + this->SetSizer(bSizer68); + this->Layout(); + + // Connect Events + this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CYourAddressDialogBase::OnClose)); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_END_LABEL_EDIT, wxListEventHandler(CYourAddressDialogBase::OnListEndLabelEdit), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CYourAddressDialogBase::OnListItemActivated), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(CYourAddressDialogBase::OnListItemSelected), NULL, this); + m_buttonRename->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonRename), NULL, this); + m_buttonNew->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonNew), NULL, this); + m_buttonCopy->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonCopy), NULL, this); + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonCancel), NULL, this); +} + +CYourAddressDialogBase::~CYourAddressDialogBase() +{ + // Disconnect Events + this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CYourAddressDialogBase::OnClose)); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_END_LABEL_EDIT, wxListEventHandler(CYourAddressDialogBase::OnListEndLabelEdit), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CYourAddressDialogBase::OnListItemActivated), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(CYourAddressDialogBase::OnListItemSelected), NULL, this); + m_buttonRename->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonRename), NULL, this); + m_buttonNew->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonNew), NULL, this); + m_buttonCopy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonCopy), NULL, this); + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CYourAddressDialogBase::OnButtonCancel), NULL, this); +} + +CAddressBookDialogBase::CAddressBookDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer68; + bSizer68 = new wxBoxSizer(wxVERTICAL); + + + bSizer68->Add(0, 5, 0, wxEXPAND, 5); + + m_staticText55 = new wxStaticText(this, wxID_ANY, wxT("Bitflash Address"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText55->Wrap(-1); + m_staticText55->Hide(); + + bSizer68->Add(m_staticText55, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + m_listCtrl = new wxListCtrl(this, wxID_LISTCTRL, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT|wxLC_SORT_ASCENDING); + bSizer68->Add(m_listCtrl, 1, wxALL|wxEXPAND, 5); + + wxBoxSizer* bSizer69; + bSizer69 = new wxBoxSizer(wxHORIZONTAL); + + + bSizer69->Add(0, 0, 1, wxEXPAND, 5); + + m_buttonEdit = new wxButton(this, wxID_BUTTONEDIT, wxT("&Edit..."), wxDefaultPosition, wxDefaultSize, 0); + m_buttonEdit->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonEdit, 0, wxALL, 5); + + m_buttonNew = new wxButton(this, wxID_BUTTONNEW, wxT("&New Address..."), wxDefaultPosition, wxDefaultSize, 0); + m_buttonNew->SetMinSize(wxSize(110,25)); + + bSizer69->Add(m_buttonNew, 0, wxALL, 5); + + m_buttonDelete = new wxButton(this, wxID_BUTTONDELETE, wxT("&Delete"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonDelete->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonDelete, 0, wxALL, 5); + + m_buttonOK = new wxButton(this, wxID_OK, wxT("OK"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonOK->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonOK, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer69->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer68->Add(bSizer69, 0, wxEXPAND, 5); + + this->SetSizer(bSizer68); + this->Layout(); + + // Connect Events + this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CAddressBookDialogBase::OnClose)); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_END_LABEL_EDIT, wxListEventHandler(CAddressBookDialogBase::OnListEndLabelEdit), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CAddressBookDialogBase::OnListItemActivated), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(CAddressBookDialogBase::OnListItemSelected), NULL, this); + m_buttonEdit->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonEdit), NULL, this); + m_buttonNew->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonNew), NULL, this); + m_buttonDelete->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonDelete), NULL, this); + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonCancel), NULL, this); +} + +CAddressBookDialogBase::~CAddressBookDialogBase() +{ + // Disconnect Events + this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CAddressBookDialogBase::OnClose)); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_END_LABEL_EDIT, wxListEventHandler(CAddressBookDialogBase::OnListEndLabelEdit), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CAddressBookDialogBase::OnListItemActivated), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(CAddressBookDialogBase::OnListItemSelected), NULL, this); + m_buttonEdit->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonEdit), NULL, this); + m_buttonNew->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonNew), NULL, this); + m_buttonDelete->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonDelete), NULL, this); + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CAddressBookDialogBase::OnButtonCancel), NULL, this); +} + +CProductsDialogBase::CProductsDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer22; + bSizer22 = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* bSizer23; + bSizer23 = new wxBoxSizer(wxHORIZONTAL); + + m_comboBoxCategory = new wxComboBox(this, wxID_ANY, wxT("(Any Category)"), wxDefaultPosition, wxSize(150,-1), 0, NULL, 0); + m_comboBoxCategory->Append(wxT("(Any Category)")); + bSizer23->Add(m_comboBoxCategory, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlSearch = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + bSizer23->Add(m_textCtrlSearch, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonSearch = new wxButton(this, wxID_ANY, wxT("&Search"), wxDefaultPosition, wxDefaultSize, 0); + bSizer23->Add(m_buttonSearch, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + bSizer22->Add(bSizer23, 0, wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT, 5); + + m_listCtrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT); + bSizer22->Add(m_listCtrl, 1, wxALL|wxEXPAND, 5); + + this->SetSizer(bSizer22); + this->Layout(); + + // Connect Events + m_comboBoxCategory->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(CProductsDialogBase::OnCombobox), NULL, this); + m_textCtrlSearch->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CProductsDialogBase::OnKeyDown), NULL, this); + m_buttonSearch->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CProductsDialogBase::OnButtonSearch), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CProductsDialogBase::OnListItemActivated), NULL, this); +} + +CProductsDialogBase::~CProductsDialogBase() +{ + // Disconnect Events + m_comboBoxCategory->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(CProductsDialogBase::OnCombobox), NULL, this); + m_textCtrlSearch->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CProductsDialogBase::OnKeyDown), NULL, this); + m_buttonSearch->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CProductsDialogBase::OnButtonSearch), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CProductsDialogBase::OnListItemActivated), NULL, this); +} + +CEditProductDialogBase::CEditProductDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); + + wxBoxSizer* bSizer20; + bSizer20 = new wxBoxSizer(wxVERTICAL); + + m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxTAB_TRAVERSAL|wxVSCROLL); + m_scrolledWindow->SetScrollRate(5, 5); + m_scrolledWindow->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + wxBoxSizer* bSizer21; + bSizer21 = new wxBoxSizer(wxVERTICAL); + + wxFlexGridSizer* fgSizer8; + fgSizer8 = new wxFlexGridSizer(0, 2, 0, 0); + fgSizer8->AddGrowableCol(1); + fgSizer8->SetFlexibleDirection(wxBOTH); + fgSizer8->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + + m_staticText106 = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Category"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + m_staticText106->Wrap(-1); + fgSizer8->Add(m_staticText106, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5); + + m_comboBoxCategory = new wxComboBox(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0); + m_comboBoxCategory->SetMinSize(wxSize(180,-1)); + + fgSizer8->Add(m_comboBoxCategory, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_staticText108 = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Title"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + m_staticText108->Wrap(-1); + fgSizer8->Add(m_staticText108, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5); + + m_textCtrlTitle = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + fgSizer8->Add(m_textCtrlTitle, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5); + + m_staticText107 = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Price"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); + m_staticText107->Wrap(-1); + fgSizer8->Add(m_staticText107, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5); + + m_textCtrlPrice = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlPrice->SetMinSize(wxSize(105,-1)); + + fgSizer8->Add(m_textCtrlPrice, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + bSizer21->Add(fgSizer8, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5); + + m_staticText22 = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Page 1: Description"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText22->Wrap(-1); + bSizer21->Add(m_staticText22, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + m_textCtrlDescription = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); + m_textCtrlDescription->SetMinSize(wxSize(-1,170)); + + bSizer21->Add(m_textCtrlDescription, 0, wxALL|wxEXPAND, 5); + + m_staticText23 = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Page 2: Order Form"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText23->Wrap(-1); + bSizer21->Add(m_staticText23, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + m_textCtrlInstructions = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); + m_textCtrlInstructions->SetMinSize(wxSize(-1,120)); + + bSizer21->Add(m_textCtrlInstructions, 0, wxEXPAND|wxALL, 5); + + fgSizer5 = new wxFlexGridSizer(0, 3, 0, 0); + fgSizer5->AddGrowableCol(1); + fgSizer5->SetFlexibleDirection(wxBOTH); + fgSizer5->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + + m_staticText24 = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Label"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText24->Wrap(-1); + fgSizer5->Add(m_staticText24, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5); + + m_staticText25 = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Comma separated list of choices, or leave blank for text field"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText25->Wrap(-1); + fgSizer5->Add(m_staticText25, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT, 5); + + + fgSizer5->Add(0, 0, 1, wxEXPAND, 5); + + m_textCtrlLabel0 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel0->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel0, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField0 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField0, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel0 = new wxButton(m_scrolledWindow, wxID_DEL0, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel0, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlLabel1 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel1->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel1, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField1 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField1, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel1 = new wxButton(m_scrolledWindow, wxID_DEL1, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel1, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel2 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel2->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel2, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField2 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField2, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel2 = new wxButton(m_scrolledWindow, wxID_DEL2, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel2, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel3 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel3->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel3, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField3 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField3, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel3 = new wxButton(m_scrolledWindow, wxID_DEL3, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel3, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel4 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel4->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel4, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField4 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField4, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel4 = new wxButton(m_scrolledWindow, wxID_DEL4, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel4, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel5 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel5->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel5, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField5 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField5, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel5 = new wxButton(m_scrolledWindow, wxID_DEL5, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel5, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel6 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel6->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel6, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField6 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField6, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel6 = new wxButton(m_scrolledWindow, wxID_DEL6, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel6, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel7 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel7->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel7, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField7 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField7, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel7 = new wxButton(m_scrolledWindow, wxID_DEL7, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel7, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel8 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel8->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel8, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField8 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField8, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel8 = new wxButton(m_scrolledWindow, wxID_DEL8, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel8, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel9 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel9->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel9, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField9 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField9, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel9 = new wxButton(m_scrolledWindow, wxID_DEL9, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel10 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel10->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel10, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField10 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField10, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel10 = new wxButton(m_scrolledWindow, wxID_DEL10, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel11 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel11->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel11, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField11 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField11, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel11 = new wxButton(m_scrolledWindow, wxID_DEL11, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel11, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel12 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel12->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel12, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField12 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField12, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel12 = new wxButton(m_scrolledWindow, wxID_DEL12, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel13 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel13->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel13, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField13 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField13, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel13 = new wxButton(m_scrolledWindow, wxID_DEL13, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel14 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel14->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel14, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField14 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField14, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel14 = new wxButton(m_scrolledWindow, wxID_DEL14, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel14, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel15 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel15->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel15, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField15 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField15, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel15 = new wxButton(m_scrolledWindow, wxID_DEL15, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel16 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel16->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel16, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField16 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField16, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel16 = new wxButton(m_scrolledWindow, wxID_DEL16, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel16, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel17 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel17->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel17, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField17 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField17, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel17 = new wxButton(m_scrolledWindow, wxID_DEL17, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel17, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel18 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel18->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel18, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField18 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField18, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel18 = new wxButton(m_scrolledWindow, wxID_DEL18, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel18, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + m_textCtrlLabel19 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_textCtrlLabel19->SetMinSize(wxSize(150,-1)); + + fgSizer5->Add(m_textCtrlLabel19, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5); + + m_textCtrlField19 = new wxTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,-1), 0); + fgSizer5->Add(m_textCtrlField19, 0, wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5); + + m_buttonDel19 = new wxButton(m_scrolledWindow, wxID_DEL19, wxT("Delete"), wxDefaultPosition, wxSize(60,20), 0); + fgSizer5->Add(m_buttonDel19, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5); + + bSizer21->Add(fgSizer5, 0, wxEXPAND, 5); + + wxBoxSizer* bSizer25; + bSizer25 = new wxBoxSizer(wxHORIZONTAL); + + m_buttonAddField = new wxButton(m_scrolledWindow, wxID_ANY, wxT("&Add Field"), wxDefaultPosition, wxDefaultSize, 0); + bSizer25->Add(m_buttonAddField, 0, wxALL, 5); + + bSizer21->Add(bSizer25, 0, wxALIGN_CENTER_HORIZONTAL, 5); + + m_scrolledWindow->SetSizer(bSizer21); + m_scrolledWindow->Layout(); + bSizer21->Fit(m_scrolledWindow); + bSizer20->Add(m_scrolledWindow, 1, wxEXPAND|wxALL, 5); + + wxBoxSizer* bSizer26; + bSizer26 = new wxBoxSizer(wxHORIZONTAL); + + m_buttonOK = new wxButton(this, wxID_BUTTONSEND, wxT("&Send"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonOK->SetMinSize(wxSize(85,25)); + + bSizer26->Add(m_buttonOK, 0, wxALL, 5); + + m_buttonPreview = new wxButton(this, wxID_BUTTONPREVIEW, wxT("&Preview"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonPreview->SetMinSize(wxSize(85,25)); + + bSizer26->Add(m_buttonPreview, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer26->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer20->Add(bSizer26, 0, wxALIGN_RIGHT, 5); + + this->SetSizer(bSizer20); + this->Layout(); + + // Connect Events + m_textCtrlTitle->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlPrice->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlDescription->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlInstructions->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlLabel0->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField0->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel0->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel0), NULL, this); + m_textCtrlLabel1->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField1->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel1->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel1), NULL, this); + m_textCtrlLabel2->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField2->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel2->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel2), NULL, this); + m_textCtrlLabel3->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField3->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel3->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel3), NULL, this); + m_textCtrlLabel4->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField4->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel4->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel4), NULL, this); + m_textCtrlLabel5->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField5->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel5->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel5), NULL, this); + m_textCtrlLabel6->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField6->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel6->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel6), NULL, this); + m_textCtrlLabel7->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField7->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel7->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel7), NULL, this); + m_textCtrlLabel8->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField8->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel8->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel8), NULL, this); + m_textCtrlLabel9->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField9->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel9->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel9), NULL, this); + m_textCtrlLabel10->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField10->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel10->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel10), NULL, this); + m_textCtrlLabel11->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField11->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel11->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel11), NULL, this); + m_textCtrlLabel12->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField12->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel12->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel12), NULL, this); + m_textCtrlLabel13->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField13->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel13->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel13), NULL, this); + m_textCtrlLabel14->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField14->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel14->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel14), NULL, this); + m_textCtrlLabel15->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField15->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel15->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel15), NULL, this); + m_textCtrlLabel16->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField16->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel16->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel16), NULL, this); + m_textCtrlLabel17->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField17->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel17->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel17), NULL, this); + m_textCtrlLabel18->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField18->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel18->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel18), NULL, this); + m_textCtrlLabel19->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField19->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel19->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel19), NULL, this); + m_buttonAddField->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonAddField), NULL, this); + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonSend), NULL, this); + m_buttonPreview->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonPreview), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonCancel), NULL, this); +} + +CEditProductDialogBase::~CEditProductDialogBase() +{ + // Disconnect Events + m_textCtrlTitle->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlPrice->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlDescription->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlInstructions->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlLabel0->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField0->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel0->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel0), NULL, this); + m_textCtrlLabel1->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField1->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel1), NULL, this); + m_textCtrlLabel2->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField2->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel2->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel2), NULL, this); + m_textCtrlLabel3->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField3->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel3->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel3), NULL, this); + m_textCtrlLabel4->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField4->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel4->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel4), NULL, this); + m_textCtrlLabel5->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField5->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel5->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel5), NULL, this); + m_textCtrlLabel6->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField6->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel6->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel6), NULL, this); + m_textCtrlLabel7->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField7->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel7->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel7), NULL, this); + m_textCtrlLabel8->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField8->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel8->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel8), NULL, this); + m_textCtrlLabel9->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField9->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel9->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel9), NULL, this); + m_textCtrlLabel10->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField10->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel10), NULL, this); + m_textCtrlLabel11->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField11->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel11->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel11), NULL, this); + m_textCtrlLabel12->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField12->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel12->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel12), NULL, this); + m_textCtrlLabel13->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField13->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel13->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel13), NULL, this); + m_textCtrlLabel14->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField14->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel14->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel14), NULL, this); + m_textCtrlLabel15->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField15->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel15->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel15), NULL, this); + m_textCtrlLabel16->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField16->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel16->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel16), NULL, this); + m_textCtrlLabel17->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField17->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel17->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel17), NULL, this); + m_textCtrlLabel18->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField18->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel18->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel18), NULL, this); + m_textCtrlLabel19->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_textCtrlField19->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditProductDialogBase::OnKeyDown), NULL, this); + m_buttonDel19->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonDel19), NULL, this); + m_buttonAddField->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonAddField), NULL, this); + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonSend), NULL, this); + m_buttonPreview->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonPreview), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditProductDialogBase::OnButtonCancel), NULL, this); +} + +CViewProductDialogBase::CViewProductDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); + + wxBoxSizer* bSizer20; + bSizer20 = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* bSizer116; + bSizer116 = new wxBoxSizer(wxHORIZONTAL); + + m_htmlWinReviews = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); + m_htmlWinReviews->Hide(); + + bSizer116->Add(m_htmlWinReviews, 1, wxALL|wxEXPAND, 5); + + m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxTAB_TRAVERSAL|wxVSCROLL); + m_scrolledWindow->SetScrollRate(5, 5); + m_scrolledWindow->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + wxBoxSizer* bSizer21; + bSizer21 = new wxBoxSizer(wxVERTICAL); + + m_richTextHeading = new wxRichTextCtrl(m_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1,50), wxTE_READONLY|wxNO_BORDER); + bSizer21->Add(m_richTextHeading, 0, wxEXPAND, 5); + + m_staticTextInstructions = new wxStaticText(m_scrolledWindow, wxID_ANY, wxT("Order Form instructions here\nmultiple lines\n1\n2\n3\n4\n5\n6"), wxDefaultPosition, wxDefaultSize, 0); + m_staticTextInstructions->Wrap(-1); + bSizer21->Add(m_staticTextInstructions, 0, wxALL|wxEXPAND, 5); + + wxBoxSizer* bSizer25; + bSizer25 = new wxBoxSizer(wxHORIZONTAL); + + m_buttonSubmitForm = new wxButton(m_scrolledWindow, wxID_BUTTONSAMPLE, wxT("&Submit"), wxDefaultPosition, wxDefaultSize, 0); + bSizer25->Add(m_buttonSubmitForm, 0, wxALL, 5); + + m_buttonCancelForm = new wxButton(m_scrolledWindow, wxID_CANCEL2, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); + bSizer25->Add(m_buttonCancelForm, 0, wxALL, 5); + + bSizer21->Add(bSizer25, 0, wxALIGN_CENTER_HORIZONTAL, 5); + + m_scrolledWindow->SetSizer(bSizer21); + m_scrolledWindow->Layout(); + bSizer21->Fit(m_scrolledWindow); + bSizer116->Add(m_scrolledWindow, 1, wxEXPAND|wxALL, 5); + + bSizer20->Add(bSizer116, 1, wxEXPAND, 5); + + wxBoxSizer* bSizer26; + bSizer26 = new wxBoxSizer(wxHORIZONTAL); + + m_buttonBack = new wxButton(this, wxID_BUTTONBACK, wxT("< &Back "), wxDefaultPosition, wxDefaultSize, 0); + m_buttonBack->Enable(false); + m_buttonBack->SetMinSize(wxSize(85,25)); + + bSizer26->Add(m_buttonBack, 0, wxALL, 5); + + m_buttonNext = new wxButton(this, wxID_BUTTONNEXT, wxT(" &Next >"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonNext->SetMinSize(wxSize(85,25)); + + bSizer26->Add(m_buttonNext, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer26->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer20->Add(bSizer26, 0, wxALIGN_RIGHT, 5); + + this->SetSizer(bSizer20); + this->Layout(); + + // Connect Events + m_buttonSubmitForm->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonSubmitForm), NULL, this); + m_buttonCancelForm->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonCancelForm), NULL, this); + m_buttonBack->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonBack), NULL, this); + m_buttonNext->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonNext), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonCancel), NULL, this); +} + +CViewProductDialogBase::~CViewProductDialogBase() +{ + // Disconnect Events + m_buttonSubmitForm->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonSubmitForm), NULL, this); + m_buttonCancelForm->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonCancelForm), NULL, this); + m_buttonBack->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonBack), NULL, this); + m_buttonNext->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonNext), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewProductDialogBase::OnButtonCancel), NULL, this); +} + +CViewOrderDialogBase::CViewOrderDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); + + wxBoxSizer* bSizer20; + bSizer20 = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* bSizer116; + bSizer116 = new wxBoxSizer(wxHORIZONTAL); + + m_htmlWin = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); + bSizer116->Add(m_htmlWin, 1, wxALL|wxEXPAND, 5); + + bSizer20->Add(bSizer116, 1, wxEXPAND, 5); + + wxBoxSizer* bSizer26; + bSizer26 = new wxBoxSizer(wxHORIZONTAL); + + m_buttonOK = new wxButton(this, wxID_OK, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonOK->SetMinSize(wxSize(85,25)); + + bSizer26->Add(m_buttonOK, 0, wxALL, 5); + + bSizer20->Add(bSizer26, 0, wxALIGN_RIGHT, 5); + + this->SetSizer(bSizer20); + this->Layout(); + + // Connect Events + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewOrderDialogBase::OnButtonOK), NULL, this); +} + +CViewOrderDialogBase::~CViewOrderDialogBase() +{ + // Disconnect Events + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CViewOrderDialogBase::OnButtonOK), NULL, this); +} + +CEditReviewDialogBase::CEditReviewDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENU)); + + wxBoxSizer* bSizer112; + bSizer112 = new wxBoxSizer(wxVERTICAL); + + + bSizer112->Add(0, 3, 0, 0, 5); + + m_staticTextSeller = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_staticTextSeller->Wrap(-1); + bSizer112->Add(m_staticTextSeller, 0, wxALL|wxEXPAND, 5); + + + bSizer112->Add(0, 3, 0, 0, 5); + + m_staticText110 = new wxStaticText(this, wxID_ANY, wxT("Rating"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText110->Wrap(-1); + bSizer112->Add(m_staticText110, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + wxString m_choiceStarsChoices[] = { wxT(" 1 star"), wxT(" 2 stars"), wxT(" 3 stars"), wxT(" 4 stars"), wxT(" 5 stars") }; + int m_choiceStarsNChoices = sizeof(m_choiceStarsChoices) / sizeof(wxString); + m_choiceStars = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceStarsNChoices, m_choiceStarsChoices, 0); + m_choiceStars->SetSelection(0); + bSizer112->Add(m_choiceStars, 0, wxALL, 5); + + m_staticText43 = new wxStaticText(this, wxID_ANY, wxT("Review"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText43->Wrap(-1); + bSizer112->Add(m_staticText43, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + m_textCtrlReview = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); + bSizer112->Add(m_textCtrlReview, 1, wxALL|wxEXPAND, 5); + + wxBoxSizer* bSizer113; + bSizer113 = new wxBoxSizer(wxHORIZONTAL); + + m_buttonSubmit = new wxButton(this, wxID_SUBMIT, wxT("&Submit"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonSubmit->SetMinSize(wxSize(85,25)); + + bSizer113->Add(m_buttonSubmit, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer113->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer112->Add(bSizer113, 0, wxALIGN_RIGHT, 5); + + this->SetSizer(bSizer112); + this->Layout(); + + // Connect Events + m_textCtrlReview->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditReviewDialogBase::OnKeyDown), NULL, this); + m_buttonSubmit->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditReviewDialogBase::OnButtonSubmit), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditReviewDialogBase::OnButtonCancel), NULL, this); +} + +CEditReviewDialogBase::~CEditReviewDialogBase() +{ + // Disconnect Events + m_textCtrlReview->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CEditReviewDialogBase::OnKeyDown), NULL, this); + m_buttonSubmit->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditReviewDialogBase::OnButtonSubmit), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CEditReviewDialogBase::OnButtonCancel), NULL, this); +} + +CPokerLobbyDialogBase::CPokerLobbyDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + + wxBoxSizer* bSizer156; + bSizer156 = new wxBoxSizer(wxHORIZONTAL); + + m_treeCtrl = new wxTreeCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HAS_BUTTONS|wxTR_HIDE_ROOT|wxTR_LINES_AT_ROOT); + m_treeCtrl->SetMinSize(wxSize(130,-1)); + + bSizer156->Add(m_treeCtrl, 0, wxEXPAND|wxTOP|wxBOTTOM|wxLEFT, 5); + + wxBoxSizer* bSizer172; + bSizer172 = new wxBoxSizer(wxVERTICAL); + + m_listCtrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_NO_SORT_HEADER|wxLC_REPORT); + bSizer172->Add(m_listCtrl, 1, wxEXPAND|wxALL, 5); + + m_buttonNewTable = new wxButton(this, wxID_OPENNEWTABLE, wxT("&Open New Table"), wxDefaultPosition, wxDefaultSize, 0); + bSizer172->Add(m_buttonNewTable, 0, wxALL, 5); + + bSizer156->Add(bSizer172, 1, wxEXPAND, 5); + + this->SetSizer(bSizer156); + this->Layout(); + + // Connect Events + m_treeCtrl->Connect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(CPokerLobbyDialogBase::OnTreeSelChanged), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CPokerLobbyDialogBase::OnListItemActivated), NULL, this); + m_listCtrl->Connect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(CPokerLobbyDialogBase::OnListItemSelected), NULL, this); + m_buttonNewTable->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerLobbyDialogBase::OnButtonNewTable), NULL, this); +} + +CPokerLobbyDialogBase::~CPokerLobbyDialogBase() +{ + // Disconnect Events + m_treeCtrl->Disconnect(wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler(CPokerLobbyDialogBase::OnTreeSelChanged), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(CPokerLobbyDialogBase::OnListItemActivated), NULL, this); + m_listCtrl->Disconnect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(CPokerLobbyDialogBase::OnListItemSelected), NULL, this); + m_buttonNewTable->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerLobbyDialogBase::OnButtonNewTable), NULL, this); +} + +CPokerDialogBase::CPokerDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxFrame(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer174; + bSizer174 = new wxBoxSizer(wxVERTICAL); + + m_checkSitOut = new wxCheckBox(this, wxID_ANY, wxT("Deal Me Out"), wxDefaultPosition, wxDefaultSize, 0); + + bSizer174->Add(m_checkSitOut, 0, wxALL, 5); + + m_buttonDealHand = new wxButton(this, wxID_DEALHAND, wxT("&Deal Hand"), wxDefaultPosition, wxSize(150,25), 0); + bSizer174->Add(m_buttonDealHand, 0, wxALL, 5); + + m_buttonFold = new wxButton(this, wxID_FOLD, wxT("&Fold"), wxDefaultPosition, wxSize(80,25), 0); + bSizer174->Add(m_buttonFold, 0, wxALL, 5); + + m_buttonCall = new wxButton(this, wxID_CALL, wxT("&Call"), wxDefaultPosition, wxSize(80,25), 0); + bSizer174->Add(m_buttonCall, 0, wxALL, 5); + + m_buttonRaise = new wxButton(this, wxID_RAISE, wxT("&Raise"), wxDefaultPosition, wxSize(80,25), 0); + bSizer174->Add(m_buttonRaise, 0, wxALL, 5); + + m_buttonLeaveTable = new wxButton(this, wxID_LEAVETABLE, wxT("&Leave Table"), wxDefaultPosition, wxSize(90,25), 0); + bSizer174->Add(m_buttonLeaveTable, 0, wxALL, 5); + + m_textDitchPlayer = new wxTextCtrl(this, wxID_DITCHPLAYER, wxEmptyString, wxDefaultPosition, wxSize(45,-1), wxTE_PROCESS_ENTER); + bSizer174->Add(m_textDitchPlayer, 0, wxALL, 5); + + m_checkPreFold = new wxCheckBox(this, wxID_ANY, wxT("FOLD"), wxDefaultPosition, wxSize(100,-1), 0); + + bSizer174->Add(m_checkPreFold, 0, wxALL, 5); + + m_checkPreCall = new wxCheckBox(this, wxID_ANY, wxT("CALL"), wxDefaultPosition, wxSize(100,-1), 0); + + bSizer174->Add(m_checkPreCall, 0, wxALL, 5); + + m_checkPreCallAny = new wxCheckBox(this, wxID_ANY, wxT("CALL ANY"), wxDefaultPosition, wxSize(100,-1), 0); + + bSizer174->Add(m_checkPreCallAny, 0, wxALL, 5); + + m_checkPreRaise = new wxCheckBox(this, wxID_ANY, wxT("RAISE"), wxDefaultPosition, wxSize(100,-1), 0); + + bSizer174->Add(m_checkPreRaise, 0, wxALL, 5); + + m_checkPreRaiseAny = new wxCheckBox(this, wxID_ANY, wxT("RAISE ANY"), wxDefaultPosition, wxSize(100,-1), 0); + + bSizer174->Add(m_checkPreRaiseAny, 0, wxALL, 5); + + this->SetSizer(bSizer174); + this->Layout(); + m_statusBar = this->CreateStatusBar(1, wxST_SIZEGRIP, wxID_ANY); + + // Connect Events + this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CPokerDialogBase::OnClose)); + this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_MIDDLE_UP, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_MOTION, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_MIDDLE_DCLICK, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_RIGHT_DCLICK, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Connect(wxEVT_PAINT, wxPaintEventHandler(CPokerDialogBase::OnPaint)); + this->Connect(wxEVT_SIZE, wxSizeEventHandler(CPokerDialogBase::OnSize)); + m_checkSitOut->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckSitOut), NULL, this); + m_buttonDealHand->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonDealHand), NULL, this); + m_buttonFold->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonFold), NULL, this); + m_buttonCall->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonCall), NULL, this); + m_buttonRaise->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonRaise), NULL, this); + m_buttonLeaveTable->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonLeaveTable), NULL, this); + m_textDitchPlayer->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(CPokerDialogBase::OnDitchPlayer), NULL, this); + m_checkPreFold->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreFold), NULL, this); + m_checkPreCall->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreCall), NULL, this); + m_checkPreCallAny->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreCallAny), NULL, this); + m_checkPreRaise->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreRaise), NULL, this); + m_checkPreRaiseAny->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreRaiseAny), NULL, this); +} + +CPokerDialogBase::~CPokerDialogBase() +{ + // Disconnect Events + this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CPokerDialogBase::OnClose)); + this->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_LEFT_UP, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_MIDDLE_DOWN, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_MIDDLE_UP, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_RIGHT_UP, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_MOTION, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_MIDDLE_DCLICK, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_RIGHT_DCLICK, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(CPokerDialogBase::OnMouseEvents)); + this->Disconnect(wxEVT_PAINT, wxPaintEventHandler(CPokerDialogBase::OnPaint)); + this->Disconnect(wxEVT_SIZE, wxSizeEventHandler(CPokerDialogBase::OnSize)); + m_checkSitOut->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckSitOut), NULL, this); + m_buttonDealHand->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonDealHand), NULL, this); + m_buttonFold->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonFold), NULL, this); + m_buttonCall->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonCall), NULL, this); + m_buttonRaise->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonRaise), NULL, this); + m_buttonLeaveTable->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnButtonLeaveTable), NULL, this); + m_textDitchPlayer->Disconnect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(CPokerDialogBase::OnDitchPlayer), NULL, this); + m_checkPreFold->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreFold), NULL, this); + m_checkPreCall->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreCall), NULL, this); + m_checkPreCallAny->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreCallAny), NULL, this); + m_checkPreRaise->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreRaise), NULL, this); + m_checkPreRaiseAny->Disconnect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(CPokerDialogBase::OnCheckPreRaiseAny), NULL, this); +} + +CGetTextFromUserDialogBase::CGetTextFromUserDialogBase(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style) : wxDialog(parent, id, title, pos, size, style) +{ + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* bSizer79; + bSizer79 = new wxBoxSizer(wxVERTICAL); + + wxBoxSizer* bSizer81; + bSizer81 = new wxBoxSizer(wxVERTICAL); + + + bSizer81->Add(0, 0, 1, wxEXPAND, 5); + + m_staticTextMessage1 = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_staticTextMessage1->Wrap(-1); + bSizer81->Add(m_staticTextMessage1, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + m_textCtrl1 = new wxTextCtrl(this, wxID_TEXTCTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + bSizer81->Add(m_textCtrl1, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5); + + m_staticTextMessage2 = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + m_staticTextMessage2->Wrap(-1); + m_staticTextMessage2->Hide(); + + bSizer81->Add(m_staticTextMessage2, 0, wxTOP|wxRIGHT|wxLEFT, 5); + + m_textCtrl2 = new wxTextCtrl(this, wxID_TEXTCTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + m_textCtrl2->Hide(); + + bSizer81->Add(m_textCtrl2, 0, wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5); + + + bSizer81->Add(0, 0, 1, wxEXPAND, 5); + + bSizer79->Add(bSizer81, 1, wxEXPAND|wxALL, 10); + + wxBoxSizer* bSizer80; + bSizer80 = new wxBoxSizer(wxHORIZONTAL); + + + bSizer80->Add(0, 0, 1, wxEXPAND, 5); + + m_buttonOK = new wxButton(this, wxID_OK, wxT("OK"), wxDefaultPosition, wxSize(-1,-1), 0); + m_buttonOK->SetMinSize(wxSize(85,25)); + + bSizer80->Add(m_buttonOK, 0, wxALL, 5); + + m_buttonCancel = new wxButton(this, wxID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxDefaultSize, 0); + m_buttonCancel->SetMinSize(wxSize(85,25)); + + bSizer80->Add(m_buttonCancel, 0, wxALL, 5); + + bSizer79->Add(bSizer80, 0, wxEXPAND, 5); + + this->SetSizer(bSizer79); + this->Layout(); + + // Connect Events + this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CGetTextFromUserDialogBase::OnClose)); + m_textCtrl1->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CGetTextFromUserDialogBase::OnKeyDown), NULL, this); + m_textCtrl2->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(CGetTextFromUserDialogBase::OnKeyDown), NULL, this); + m_buttonOK->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CGetTextFromUserDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CGetTextFromUserDialogBase::OnButtonCancel), NULL, this); +} + +CGetTextFromUserDialogBase::~CGetTextFromUserDialogBase() +{ + // Disconnect Events + this->Disconnect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(CGetTextFromUserDialogBase::OnClose)); + m_textCtrl1->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CGetTextFromUserDialogBase::OnKeyDown), NULL, this); + m_textCtrl2->Disconnect(wxEVT_KEY_DOWN, wxKeyEventHandler(CGetTextFromUserDialogBase::OnKeyDown), NULL, this); + m_buttonOK->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CGetTextFromUserDialogBase::OnButtonOK), NULL, this); + m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(CGetTextFromUserDialogBase::OnButtonCancel), NULL, this); +} diff --git a/src/uibase.h b/src/uibase.h new file mode 100644 index 0000000..6d7799e --- /dev/null +++ b/src/uibase.h @@ -0,0 +1,720 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Apr 16 2008) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __uibase__ +#define __uibase__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +#define wxID_MAINFRAME 1000 +#define wxID_OPTIONSGENERATEBITCOINS 1001 +#define wxID_BUTTONSEND 1002 +#define wxID_BUTTONRECEIVE 1003 +#define wxID_TEXTCTRLADDRESS 1004 +#define wxID_BUTTONCOPY 1005 +#define wxID_BUTTONCHANGE 1006 +#define wxID_TRANSACTIONFEE 1007 +#define wxID_TEXTCTRLPAYTO 1008 +#define wxID_BUTTONPASTE 1009 +#define wxID_BUTTONADDRESSBOOK 1010 +#define wxID_TEXTCTRLAMOUNT 1011 +#define wxID_CHOICETRANSFERTYPE 1012 +#define wxID_LISTCTRL 1013 +#define wxID_BUTTONRENAME 1014 +#define wxID_BUTTONNEW 1015 +#define wxID_BUTTONEDIT 1016 +#define wxID_BUTTONDELETE 1017 +#define wxID_DEL0 1018 +#define wxID_DEL1 1019 +#define wxID_DEL2 1020 +#define wxID_DEL3 1021 +#define wxID_DEL4 1022 +#define wxID_DEL5 1023 +#define wxID_DEL6 1024 +#define wxID_DEL7 1025 +#define wxID_DEL8 1026 +#define wxID_DEL9 1027 +#define wxID_DEL10 1028 +#define wxID_DEL11 1029 +#define wxID_DEL12 1030 +#define wxID_DEL13 1031 +#define wxID_DEL14 1032 +#define wxID_DEL15 1033 +#define wxID_DEL16 1034 +#define wxID_DEL17 1035 +#define wxID_DEL18 1036 +#define wxID_DEL19 1037 +#define wxID_BUTTONPREVIEW 1038 +#define wxID_BUTTONSAMPLE 1039 +#define wxID_CANCEL2 1040 +#define wxID_BUTTONBACK 1041 +#define wxID_BUTTONNEXT 1042 +#define wxID_SUBMIT 1043 +#define wxID_OPENNEWTABLE 1044 +#define wxID_DEALHAND 1045 +#define wxID_FOLD 1046 +#define wxID_CALL 1047 +#define wxID_RAISE 1048 +#define wxID_LEAVETABLE 1049 +#define wxID_DITCHPLAYER 1050 +#define wxID_TEXTCTRL 1051 + +/////////////////////////////////////////////////////////////////////////////// +/// Class CMainFrameBase +/////////////////////////////////////////////////////////////////////////////// +class CMainFrameBase : public wxFrame +{ +private: + +protected: + wxMenuBar* m_menubar; + wxMenu* m_menuFile; + wxMenu* m_menuHelp; + wxToolBar* m_toolBar; + wxStatusBar* m_statusBar; + + wxStaticText* m_staticText32; + wxTextCtrl* m_textCtrlAddress; + wxButton* m_buttonCopy; + wxButton* m_button91; + + wxPanel* m_panel14; + wxStaticText* m_staticText41; + wxStaticText* m_staticTextBalance; + + wxChoice* m_choiceFilter; + wxNotebook* m_notebook; + wxPanel* m_panel7; + wxPanel* m_panel9; + wxPanel* m_panel8; + wxPanel* m_panel10; + wxPanel* m_panel11; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose(wxCloseEvent& event){ event.Skip(); } + virtual void OnIdle(wxIdleEvent& event){ event.Skip(); } + virtual void OnMouseEvents(wxMouseEvent& event){ event.Skip(); } + virtual void OnPaint(wxPaintEvent& event){ event.Skip(); } + virtual void OnMenuFileExit(wxCommandEvent& event){ event.Skip(); } + virtual void OnMenuOptionsGenerate(wxCommandEvent& event){ event.Skip(); } + virtual void OnMenuOptionsOptions(wxCommandEvent& event){ event.Skip(); } + virtual void OnMenuHelpAbout(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonSend(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonAddressBook(wxCommandEvent& event){ event.Skip(); } + virtual void OnKeyDown(wxKeyEvent& event){ event.Skip(); } + virtual void OnMouseEventsAddress(wxMouseEvent& event){ event.Skip(); } + virtual void OnSetFocusAddress(wxFocusEvent& event){ event.Skip(); } + virtual void OnButtonCopy(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonChange(wxCommandEvent& event){ event.Skip(); } + virtual void OnListColBeginDrag(wxListEvent& event){ event.Skip(); } + virtual void OnListItemActivatedAllTransactions(wxListEvent& event){ event.Skip(); } + virtual void OnPaintListCtrl(wxPaintEvent& event){ event.Skip(); } + virtual void OnListItemActivatedOrdersSent(wxListEvent& event){ event.Skip(); } + virtual void OnListItemActivatedProductsSent(wxListEvent& event){ event.Skip(); } + virtual void OnListItemActivatedOrdersReceived(wxListEvent& event){ event.Skip(); } + + +public: + wxMenu* m_menuOptions; + wxListCtrl* m_listCtrl; + wxListCtrl* m_listCtrlEscrows; + wxListCtrl* m_listCtrlOrdersSent; + wxListCtrl* m_listCtrlProductsSent; + wxListCtrl* m_listCtrlOrdersReceived; + CMainFrameBase(wxWindow* parent, wxWindowID id = wxID_MAINFRAME, const wxString& title = wxT("Bitflash"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(705,484), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL); + ~CMainFrameBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CTxDetailsDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CTxDetailsDialogBase : public wxDialog +{ +private: + +protected: + wxHtmlWindow* m_htmlWin; + wxButton* m_buttonOK; + + // Virtual event handlers, overide them in your derived class + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + + +public: + CTxDetailsDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Transaction Details"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(620,450), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); + ~CTxDetailsDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class COptionsDialogBase +/////////////////////////////////////////////////////////////////////////////// +class COptionsDialogBase : public wxDialog +{ +private: + +protected: + + wxStaticText* m_staticText32; + wxStaticText* m_staticText31; + wxTextCtrl* m_textCtrlTransactionFee; + wxButton* m_buttonOK; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnKillFocusTransactionFee(wxFocusEvent& event){ event.Skip(); } + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + COptionsDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(500,261), long style = wxDEFAULT_DIALOG_STYLE); + ~COptionsDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CAboutDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CAboutDialogBase : public wxDialog +{ +private: + +protected: + + + wxStaticText* m_staticText40; + + wxStaticText* m_staticTextMain; + + + wxButton* m_buttonOK; + + // Virtual event handlers, overide them in your derived class + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + + +public: + wxStaticText* m_staticTextVersion; + CAboutDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("About Bitflash"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(507,298), long style = wxDEFAULT_DIALOG_STYLE); + ~CAboutDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CSendDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CSendDialogBase : public wxDialog +{ +private: + +protected: + + + wxStaticText* m_staticText14; + + wxStaticBitmap* m_bitmapCheckMark; + wxStaticText* m_staticText36; + wxTextCtrl* m_textCtrlAddress; + wxButton* m_buttonPaste; + wxButton* m_buttonAddress; + wxStaticText* m_staticText19; + wxTextCtrl* m_textCtrlAmount; + wxStaticText* m_staticText20; + wxChoice* m_choiceTransferType; + wxStaticText* m_staticTextFrom; + wxTextCtrl* m_textCtrlFrom; + wxStaticText* m_staticTextMessage; + wxTextCtrl* m_textCtrlMessage; + + wxButton* m_buttonSend; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnKeyDown(wxKeyEvent& event){ event.Skip(); } + virtual void OnTextAddress(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonPaste(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonAddressBook(wxCommandEvent& event){ event.Skip(); } + virtual void OnKillFocusAmount(wxFocusEvent& event){ event.Skip(); } + virtual void OnButtonSend(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + CSendDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Send Coins"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(675,338), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); + ~CSendDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CSendingDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CSendingDialogBase : public wxDialog +{ +private: + +protected: + wxStaticText* m_staticTextSending; + wxTextCtrl* m_textCtrlStatus; + + wxButton* m_buttonOK; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose(wxCloseEvent& event){ event.Skip(); } + virtual void OnPaint(wxPaintEvent& event){ event.Skip(); } + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + CSendingDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Sending..."), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(442,151), long style = wxDEFAULT_DIALOG_STYLE); + ~CSendingDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CYourAddressDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CYourAddressDialogBase : public wxDialog +{ +private: + +protected: + + wxStaticText* m_staticText45; + wxListCtrl* m_listCtrl; + + wxButton* m_buttonRename; + wxButton* m_buttonNew; + wxButton* m_buttonCopy; + wxButton* m_buttonOK; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose(wxCloseEvent& event){ event.Skip(); } + virtual void OnListEndLabelEdit(wxListEvent& event){ event.Skip(); } + virtual void OnListItemActivated(wxListEvent& event){ event.Skip(); } + virtual void OnListItemSelected(wxListEvent& event){ event.Skip(); } + virtual void OnButtonRename(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonNew(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCopy(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + CYourAddressDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Your Bitflash Address"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(610,390), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); + ~CYourAddressDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CAddressBookDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CAddressBookDialogBase : public wxDialog +{ +private: + +protected: + + wxStaticText* m_staticText55; + wxListCtrl* m_listCtrl; + + wxButton* m_buttonEdit; + wxButton* m_buttonNew; + wxButton* m_buttonDelete; + wxButton* m_buttonOK; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose(wxCloseEvent& event){ event.Skip(); } + virtual void OnListEndLabelEdit(wxListEvent& event){ event.Skip(); } + virtual void OnListItemActivated(wxListEvent& event){ event.Skip(); } + virtual void OnListItemSelected(wxListEvent& event){ event.Skip(); } + virtual void OnButtonEdit(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonNew(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDelete(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + wxButton* m_buttonCancel; + CAddressBookDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Address Book"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(610,390), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); + ~CAddressBookDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CProductsDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CProductsDialogBase : public wxDialog +{ +private: + +protected: + wxComboBox* m_comboBoxCategory; + wxTextCtrl* m_textCtrlSearch; + wxButton* m_buttonSearch; + wxListCtrl* m_listCtrl; + + // Virtual event handlers, overide them in your derived class + virtual void OnCombobox(wxCommandEvent& event){ event.Skip(); } + virtual void OnKeyDown(wxKeyEvent& event){ event.Skip(); } + virtual void OnButtonSearch(wxCommandEvent& event){ event.Skip(); } + virtual void OnListItemActivated(wxListEvent& event){ event.Skip(); } + + +public: + CProductsDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Marketplace"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(708,535), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER); + ~CProductsDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CEditProductDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CEditProductDialogBase : public wxFrame +{ +private: + +protected: + wxScrolledWindow* m_scrolledWindow; + wxStaticText* m_staticText106; + wxComboBox* m_comboBoxCategory; + wxStaticText* m_staticText108; + wxTextCtrl* m_textCtrlTitle; + wxStaticText* m_staticText107; + wxTextCtrl* m_textCtrlPrice; + wxStaticText* m_staticText22; + wxTextCtrl* m_textCtrlDescription; + wxStaticText* m_staticText23; + wxTextCtrl* m_textCtrlInstructions; + wxStaticText* m_staticText24; + wxStaticText* m_staticText25; + + wxTextCtrl* m_textCtrlLabel0; + wxTextCtrl* m_textCtrlField0; + wxButton* m_buttonDel0; + wxTextCtrl* m_textCtrlLabel1; + wxTextCtrl* m_textCtrlField1; + wxButton* m_buttonDel1; + wxTextCtrl* m_textCtrlLabel2; + wxTextCtrl* m_textCtrlField2; + wxButton* m_buttonDel2; + wxTextCtrl* m_textCtrlLabel3; + wxTextCtrl* m_textCtrlField3; + wxButton* m_buttonDel3; + wxTextCtrl* m_textCtrlLabel4; + wxTextCtrl* m_textCtrlField4; + wxButton* m_buttonDel4; + wxTextCtrl* m_textCtrlLabel5; + wxTextCtrl* m_textCtrlField5; + wxButton* m_buttonDel5; + wxTextCtrl* m_textCtrlLabel6; + wxTextCtrl* m_textCtrlField6; + wxButton* m_buttonDel6; + wxTextCtrl* m_textCtrlLabel7; + wxTextCtrl* m_textCtrlField7; + wxButton* m_buttonDel7; + wxTextCtrl* m_textCtrlLabel8; + wxTextCtrl* m_textCtrlField8; + wxButton* m_buttonDel8; + wxTextCtrl* m_textCtrlLabel9; + wxTextCtrl* m_textCtrlField9; + wxButton* m_buttonDel9; + wxTextCtrl* m_textCtrlLabel10; + wxTextCtrl* m_textCtrlField10; + wxButton* m_buttonDel10; + wxTextCtrl* m_textCtrlLabel11; + wxTextCtrl* m_textCtrlField11; + wxButton* m_buttonDel11; + wxTextCtrl* m_textCtrlLabel12; + wxTextCtrl* m_textCtrlField12; + wxButton* m_buttonDel12; + wxTextCtrl* m_textCtrlLabel13; + wxTextCtrl* m_textCtrlField13; + wxButton* m_buttonDel13; + wxTextCtrl* m_textCtrlLabel14; + wxTextCtrl* m_textCtrlField14; + wxButton* m_buttonDel14; + wxTextCtrl* m_textCtrlLabel15; + wxTextCtrl* m_textCtrlField15; + wxButton* m_buttonDel15; + wxTextCtrl* m_textCtrlLabel16; + wxTextCtrl* m_textCtrlField16; + wxButton* m_buttonDel16; + wxTextCtrl* m_textCtrlLabel17; + wxTextCtrl* m_textCtrlField17; + wxButton* m_buttonDel17; + wxTextCtrl* m_textCtrlLabel18; + wxTextCtrl* m_textCtrlField18; + wxButton* m_buttonDel18; + wxTextCtrl* m_textCtrlLabel19; + wxTextCtrl* m_textCtrlField19; + wxButton* m_buttonDel19; + wxButton* m_buttonAddField; + wxButton* m_buttonOK; + wxButton* m_buttonPreview; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnKeyDown(wxKeyEvent& event){ event.Skip(); } + virtual void OnButtonDel0(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel1(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel2(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel3(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel4(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel5(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel6(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel7(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel8(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel9(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel10(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel11(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel12(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel13(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel14(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel15(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel16(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel17(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel18(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDel19(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonAddField(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonSend(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonPreview(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + wxFlexGridSizer* fgSizer5; + CEditProductDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Edit Product"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(660,640), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL); + ~CEditProductDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CViewProductDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CViewProductDialogBase : public wxFrame +{ +private: + +protected: + wxHtmlWindow* m_htmlWinReviews; + wxScrolledWindow* m_scrolledWindow; + wxRichTextCtrl* m_richTextHeading; + wxStaticText* m_staticTextInstructions; + wxButton* m_buttonSubmitForm; + wxButton* m_buttonCancelForm; + wxButton* m_buttonBack; + wxButton* m_buttonNext; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnButtonSubmitForm(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancelForm(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonBack(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonNext(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + CViewProductDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Order Form"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(630,520), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL); + ~CViewProductDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CViewOrderDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CViewOrderDialogBase : public wxFrame +{ +private: + +protected: + wxHtmlWindow* m_htmlWin; + wxButton* m_buttonOK; + + // Virtual event handlers, overide them in your derived class + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + + +public: + CViewOrderDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("View Order"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(630,520), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL); + ~CViewOrderDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CEditReviewDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CEditReviewDialogBase : public wxFrame +{ +private: + +protected: + + wxStaticText* m_staticTextSeller; + + wxStaticText* m_staticText110; + wxChoice* m_choiceStars; + wxStaticText* m_staticText43; + wxTextCtrl* m_textCtrlReview; + wxButton* m_buttonSubmit; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnKeyDown(wxKeyEvent& event){ event.Skip(); } + virtual void OnButtonSubmit(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + CEditReviewDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Enter Review"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(630,440), long style = wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER|wxTAB_TRAVERSAL); + ~CEditReviewDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CPokerLobbyDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CPokerLobbyDialogBase : public wxFrame +{ +private: + +protected: + wxTreeCtrl* m_treeCtrl; + wxListCtrl* m_listCtrl; + wxButton* m_buttonNewTable; + + // Virtual event handlers, overide them in your derived class + virtual void OnTreeSelChanged(wxTreeEvent& event){ event.Skip(); } + virtual void OnListItemActivated(wxListEvent& event){ event.Skip(); } + virtual void OnListItemSelected(wxListEvent& event){ event.Skip(); } + virtual void OnButtonNewTable(wxCommandEvent& event){ event.Skip(); } + + +public: + CPokerLobbyDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Poker Lobby"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(586,457), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL); + ~CPokerLobbyDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CPokerDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CPokerDialogBase : public wxFrame +{ +private: + +protected: + wxButton* m_buttonDealHand; + wxButton* m_buttonFold; + wxButton* m_buttonCall; + wxButton* m_buttonRaise; + wxButton* m_buttonLeaveTable; + wxTextCtrl* m_textDitchPlayer; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose(wxCloseEvent& event){ event.Skip(); } + virtual void OnMouseEvents(wxMouseEvent& event){ event.Skip(); } + virtual void OnPaint(wxPaintEvent& event){ event.Skip(); } + virtual void OnSize(wxSizeEvent& event){ event.Skip(); } + virtual void OnCheckSitOut(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonDealHand(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonFold(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCall(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonRaise(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonLeaveTable(wxCommandEvent& event){ event.Skip(); } + virtual void OnDitchPlayer(wxCommandEvent& event){ event.Skip(); } + virtual void OnCheckPreFold(wxCommandEvent& event){ event.Skip(); } + virtual void OnCheckPreCall(wxCommandEvent& event){ event.Skip(); } + virtual void OnCheckPreCallAny(wxCommandEvent& event){ event.Skip(); } + virtual void OnCheckPreRaise(wxCommandEvent& event){ event.Skip(); } + virtual void OnCheckPreRaiseAny(wxCommandEvent& event){ event.Skip(); } + + +public: + wxCheckBox* m_checkSitOut; + wxCheckBox* m_checkPreFold; + wxCheckBox* m_checkPreCall; + wxCheckBox* m_checkPreCallAny; + wxCheckBox* m_checkPreRaise; + wxCheckBox* m_checkPreRaiseAny; + wxStatusBar* m_statusBar; + CPokerDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Poker"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(806,550), long style = wxDEFAULT_FRAME_STYLE|wxFRAME_NO_TASKBAR|wxFULL_REPAINT_ON_RESIZE|wxTAB_TRAVERSAL); + ~CPokerDialogBase(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class CGetTextFromUserDialogBase +/////////////////////////////////////////////////////////////////////////////// +class CGetTextFromUserDialogBase : public wxDialog +{ +private: + +protected: + + wxStaticText* m_staticTextMessage1; + wxTextCtrl* m_textCtrl1; + wxStaticText* m_staticTextMessage2; + wxTextCtrl* m_textCtrl2; + + + wxButton* m_buttonOK; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose(wxCloseEvent& event){ event.Skip(); } + virtual void OnKeyDown(wxKeyEvent& event){ event.Skip(); } + virtual void OnButtonOK(wxCommandEvent& event){ event.Skip(); } + virtual void OnButtonCancel(wxCommandEvent& event){ event.Skip(); } + + +public: + CGetTextFromUserDialogBase(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(403,138), long style = wxDEFAULT_DIALOG_STYLE); + ~CGetTextFromUserDialogBase(); + +}; + +#endif //__uibase__ diff --git a/src/uint256.h b/src/uint256.h new file mode 100644 index 0000000..61e11fc --- /dev/null +++ b/src/uint256.h @@ -0,0 +1,750 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif + + +inline int Testuint256AdHoc(vector vArg); + + + +// We have to keep a separate base class without constructors +// so the compiler will let us use it in a union +template +class base_uint +{ +protected: + enum { WIDTH=BITS/32 }; + unsigned int pn[WIDTH]; +public: + + bool operator!() const + { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) + return false; + return true; + } + + const base_uint operator~() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const + { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + + base_uint& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint& operator^=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint& operator&=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint& operator|=(const base_uint& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint& operator^=(uint64 b) + { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator&=(uint64 b) + { + pn[0] &= (unsigned int)b; + pn[1] &= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator|=(uint64 b) + { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint& operator<<=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i+k+1 < WIDTH && shift != 0) + pn[i+k+1] |= (a.pn[i] >> (32-shift)); + if (i+k < WIDTH) + pn[i+k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint& operator>>=(unsigned int shift) + { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i-k-1 >= 0 && shift != 0) + pn[i-k-1] |= (a.pn[i] << (32-shift)); + if (i-k >= 0) + pn[i-k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint& operator+=(const base_uint& b) + { + uint64 carry = 0; + for (int i = 0; i < WIDTH; i++) + { + uint64 n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint& operator-=(const base_uint& b) + { + *this += -b; + return *this; + } + + base_uint& operator+=(uint64 b64) + { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint& operator-=(uint64 b64) + { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + + base_uint& operator++() + { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator++(int) + { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint& operator--() + { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH-1) + i++; + return *this; + } + + const base_uint operator--(int) + { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + + friend inline bool operator<(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint& a, const base_uint& b) + { + for (int i = base_uint::WIDTH-1; i >= 0; i--) + { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint& a, const base_uint& b) + { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) + return false; + return true; + } + + friend inline bool operator==(const base_uint& a, uint64 b) + { + if (a.pn[0] != (unsigned int)b) + return false; + if (a.pn[1] != (unsigned int)(b >> 32)) + return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) + return false; + return true; + } + + friend inline bool operator!=(const base_uint& a, const base_uint& b) + { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint& a, uint64 b) + { + return (!(a == b)); + } + + + + std::string GetHex() const + { + char psz[sizeof(pn)*2 + 1]; + for (int i = 0; i < sizeof(pn); i++) + sprintf(psz + i*2, "%02x", ((unsigned char*)pn)[sizeof(pn) - i - 1]); + return string(psz, psz + sizeof(pn)*2); + } + + void SetHex(const std::string& str) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip 0x + const char* psz = str.c_str(); + while (isspace(*psz)) + psz++; + if (psz[0] == '0' && tolower(psz[1]) == 'x') + psz += 2; + while (isspace(*psz)) + psz++; + + // hex string to uint + static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; + const char* pbegin = psz; + while (phexdigit[*psz] || *psz == '0') + psz++; + psz--; + unsigned char* p1 = (unsigned char*)pn; + unsigned char* pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) + { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) + { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + std::string ToString() const + { + return (GetHex()); + } + + unsigned char* begin() + { + return (unsigned char*)&pn[0]; + } + + unsigned char* end() + { + return (unsigned char*)&pn[WIDTH]; + } + + unsigned int size() + { + return sizeof(pn); + } + + + unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const + { + return sizeof(pn); + } + + template + void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const + { + s.write((char*)pn, sizeof(pn)); + } + + template + void Unserialize(Stream& s, int nType=0, int nVersion=VERSION) + { + s.read((char*)pn, sizeof(pn)); + } + + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + + + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// It's safe to search and replace 160 with 256 and vice versa. +// + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +class uint160 : public base_uint160 +{ +public: + typedef base_uint160 basetype; + + uint160() + { + } + + uint160(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string& str) + { + SetHex(str); + } + + explicit uint160(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } +inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } +inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } +inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; } + +inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; } +inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; } +inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; } +inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } +inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } + +inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + + + + + + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +class uint256 : public base_uint256 +{ +public: + typedef base_uint256 basetype; + + uint256() + { + } + + uint256(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256& operator=(const basetype& b) + { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256& operator=(uint64 b) + { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string& str) + { + SetHex(str); + } + + explicit uint256(const std::vector& vch) + { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } +inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } +inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } +inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; } + +inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; } +inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; } +inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; } +inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; } +inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; } + +inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; } + +inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; } +inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; } +inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; } +inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; } +inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; } +inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; } +inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } +inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } +inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; } +inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; } + + + + + + + + + + + + +inline int Testuint256AdHoc(vector vArg) +{ + uint256 g(0); + + + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g--; printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + + + + uint256 a(7); + printf("a=7\n"); + printf("%s\n", a.ToString().c_str()); + + uint256 b; + printf("b undefined\n"); + printf("%s\n", b.ToString().c_str()); + int c = 3; + + a = c; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + printf("a %s\n", a.ToString().c_str()); + + a = a | b | (uint256)0x1000; + + + printf("a %s\n", a.ToString().c_str()); + printf("b %s\n", b.ToString().c_str()); + + a = 0xfffffffe; + a.pn[4] = 9; + + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + uint256 d = a--; + printf("%s\n", d.ToString().c_str()); + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + + d = a; + + printf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); + + uint256 neg = d; + neg = ~neg; + printf("%s\n", neg.ToString().c_str()); + + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + printf("\n"); + printf("%s\n", e.ToString().c_str()); + + + printf("\n"); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1 << i; + printf("%s\n", x2.ToString().c_str()); + } + + printf("\n"); + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) + { + x2 = x1; + x2 >>= i; + printf("%s\n", x2.ToString().c_str()); + } + + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) >> i); + printf("%s\n", k.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) + { + uint256 k = (~uint256(0) << i); + printf("%s\n", k.ToString().c_str()); + } + + return (0); +} diff --git a/src/uiproject.fbp b/src/uiproject.fbp new file mode 100644 index 0000000..57a2950 --- /dev/null +++ b/src/uiproject.fbp @@ -0,0 +1,11825 @@ + + + + + + C++ + 1 + UTF-8 + connect + uibase + 1000 + none + 0 + + + . + + 1 + 0 + 0 + + wxSYS_COLOUR_BTNFACE + + + 1 + + + + 0 + wxID_MAINFRAME + + + CMainFrameBase + + 705,484 + wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER + + Bitcoin + + + + wxTAB_TRAVERSAL + 1 + + + + OnClose + + + + + OnIdle + + + + + + + + + + + + OnMouseEvents + + OnPaint + + + + + + + + 240,240,240 + + 1 + + + 0 + wxID_ANY + MyMenuBar + + + m_menubar + protected + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &File + m_menuFile + protected + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + E&xit + m_menuFileExit + none + + + OnMenuFileExit + + + + + &Options + m_menuOptions + public + + + 0 + 1 + + wxID_OPTIONSGENERATEBITCOINS + wxITEM_CHECK + &Generate Coins + m_menuOptionsGenerateBitcoins + none + + + OnMenuOptionsGenerate + + + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + &Options... + m_menuOptionsOptions + none + + + OnMenuOptionsOptions + + + + + &Help + m_menuHelp + protected + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + &About... + m_menuHelpAbout + none + + + OnMenuHelpAbout + + + + + + + 20,20 + + 1 + + ,90,90,-1,70,0 + 0 + wxID_ANY + + + + m_toolBar + 1 + protected + + 1 + -1,-1 + wxTB_FLAT|wxTB_HORZ_TEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + send20; Load From Resource + wxID_BUTTONSEND + wxITEM_NORMAL + &Send Coins + m_tool1 + + + + OnButtonSend + + + + + + addressbook20; Load From Resource + wxID_BUTTONRECEIVE + wxITEM_NORMAL + &Address Book + m_tool2 + + + + OnButtonAddressBook + + + + + + + 240,240,240 + + 1 + + 1 + + 0 + wxID_ANY + + + m_statusBar + protected + + + wxST_SIZEGRIP + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer2 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 2 + protected + 0 + + + + 5 + wxEXPAND|wxRIGHT|wxLEFT + 0 + + + bSizer85 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Your Bitcoin Address: + + + m_staticText32 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + wxSYS_COLOUR_MENU + + 1 + + + 0 + wxID_TEXTCTRLADDRESS + -1,-1 + 0 + -1,-1 + m_textCtrlAddress + protected + + 250,-1 + wxTE_READONLY + + + + + + + + + + OnKeyDown + + + + + + + + + + + OnMouseEventsAddress + + + + + + OnSetFocusAddress + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONCOPY + &Copy to Clipboard + + + m_buttonCopy + protected + + + wxBU_EXACTFIT + + + + + + OnButtonCopy + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxRIGHT + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONCHANGE + C&hange... + + + m_button91 + protected + + + + + + + + + OnButtonChange + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 0 + protected + 0 + + + + + + 5 + wxEXPAND + 0 + + + bSizer3 + wxHORIZONTAL + none + + 5 + wxEXPAND|wxALIGN_BOTTOM|wxALL + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_panel14 + protected + + + + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer66 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Balance: + + + m_staticText41 + protected + + -1,15 + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 240,240,240 + + 1 + + ,90,90,8,70,0 + 0 + wxID_ANY + + + + m_staticTextBalance + protected + + 120,15 + wxALIGN_RIGHT|wxST_NO_AUTORESIZE + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 0 + protected + 0 + + + + 5 + wxALIGN_BOTTOM|wxTOP|wxRIGHT|wxLEFT + 0 + + + " All" " Sent" " Received" " In Progress" + + 1 + + + 1 + wxID_ANY + + + m_choiceFilter + protected + + 0 + 110,-1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + + + 1 + + + 0 + wxID_ANY + + + m_notebook + protected + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + All Transactions + 0 + + + + 1 + + + 0 + wxID_ANY + + + m_panel7 + protected + + + + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer157 + wxVERTICAL + none + + 5 + wxEXPAND|wxALL + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_listCtrl + public + + + wxLC_NO_SORT_HEADER|wxLC_REPORT|wxLC_SORT_DESCENDING + + + + + wxALWAYS_SHOW_SB + + + + + + + + + + + + + + + OnListColBeginDrag + + + + + + + + + OnListItemActivatedAllTransactions + + + + + + + + + + + + + OnPaintListCtrl + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer_TabsForFutureUse + wxVERTICAL + none + + 5 + wxEXPAND | wxALL + 1 + + + + 1 + + + 1 + wxID_ANY + + + m_panel9 + protected + + + + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer159 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_listCtrlEscrows + public + + + wxLC_NO_SORT_HEADER|wxLC_REPORT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + + + 1 + + + 1 + wxID_ANY + + + m_panel8 + protected + + + + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer158 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_listCtrlOrdersSent + public + + + wxLC_NO_SORT_HEADER|wxLC_REPORT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnListItemActivatedOrdersSent + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + + + 1 + + + 1 + wxID_ANY + + + m_panel10 + protected + + + + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer160 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_listCtrlProductsSent + public + + + wxLC_NO_SORT_HEADER|wxLC_REPORT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnListItemActivatedProductsSent + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + + + 1 + + + 1 + wxID_ANY + + + m_panel11 + protected + + + + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer161 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_listCtrlOrdersReceived + public + + + wxLC_NO_SORT_HEADER|wxLC_REPORT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnListItemActivatedOrdersReceived + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CTxDetailsDialogBase + + 620,450 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Transaction Details + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer64 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + bSizer66 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_htmlWin + protected + + + wxHW_SCROLLBAR_AUTO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_RIGHT + 0 + + + bSizer65 + wxVERTICAL + none + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OK + OK + + + m_buttonOK + protected + + 85,25 + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + COptionsDialogBase + + 500,261 + wxDEFAULT_DIALOG_STYLE + + Options + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer55 + wxVERTICAL + none + + 5 + wxEXPAND|wxLEFT + 1 + + + bSizer57 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 20 + protected + 0 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + + + 1 + + + 0 + wxID_ANY + Optional transaction fee you give to the nodes that process your transactions. + + + m_staticText32 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer56 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Transaction fee: + + + m_staticText31 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_TRANSACTIONFEE + + 0 + + m_textCtrlTransactionFee + protected + + 70,-1 + + + + + + + + + + + + + OnKillFocusTransactionFee + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_RIGHT + 0 + + + bSizer58 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OK + OK + + + m_buttonOK + protected + + 85,25 + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + -1,-1 + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CAboutDialogBase + + 507,298 + wxDEFAULT_DIALOG_STYLE + + About Bitcoin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer60 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + bSizer62 + wxHORIZONTAL + none + + 5 + wxEXPAND + 0 + + 0 + protected + 60 + + + + 5 + wxEXPAND + 1 + + + bSizer63 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 50 + protected + 0 + + + + 5 + wxEXPAND + 0 + + + bSizer64 + wxHORIZONTAL + none + + 5 + wxALIGN_BOTTOM|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + Tahoma,90,92,10,74,0 + 0 + wxID_ANY + Bitcoin + + + m_staticText40 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_BOTTOM|wxTOP|wxBOTTOM|wxRIGHT + 0 + + + + 1 + + Tahoma,90,90,10,74,0 + 0 + wxID_ANY + version + + + m_staticTextVersion + public + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 4 + protected + 0 + + + + 5 + wxALL + 0 + + + + 1 + + + 0 + wxID_ANY + Copyright © 2009 Satoshi Nakamoto. This is experimental software. Do not rely on it for actual financial transactions. Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com). + + + m_staticTextMain + protected + + + + + + + + + 400 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + + + + + 5 + wxALIGN_RIGHT|wxEXPAND + 0 + + + bSizer61 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OK + OK + + + m_buttonOK + protected + + 85,25 + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CSendDialogBase + + 675,338 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Send Coins + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer21 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 5 + protected + 0 + + + + 5 + wxEXPAND|wxLEFT + 0 + + 2 + wxBOTH + 1 + + 0 + + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 3 + 0 + + 5 + wxEXPAND + 0 + + 0 + protected + 0 + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Enter the recipient's IP address (e.g. 123.45.6.7) for online transfer with comments and confirmation, or bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJED9L) if recipient is not online. + + + m_staticText14 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxLEFT + 1 + + 70,-1 + bSizer47 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + check; Load From Icon Resource [-1; -1] + + 1 + + + 0 + wxID_ANY + + + m_bitmapCheckMark + protected + + 16,16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Pay &To: + + + m_staticText36 + protected + + -1,-1 + wxALIGN_RIGHT + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxRIGHT + 1 + + + bSizer19 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 1 + + + + 1 + + + 0 + wxID_TEXTCTRLPAYTO + + 0 + + m_textCtrlAddress + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + OnTextAddress + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONPASTE + &Paste + + + m_buttonPaste + protected + + -1,-1 + wxBU_EXACTFIT + + + + + + OnButtonPaste + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONADDRESSBOOK + Address &Book... + + + m_buttonAddress + protected + + + + + + + + + OnButtonAddressBook + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT|wxALIGN_RIGHT + 0 + + + + 1 + + + 0 + wxID_ANY + &Amount: + + + m_staticText19 + protected + + -1,-1 + wxALIGN_RIGHT + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + + + 1 + + ,90,90,-1,70,0 + 0 + wxID_TEXTCTRLAMOUNT + + 20 + + m_textCtrlAmount + protected + + 145,-1 + + + + + + + + + + + OnKeyDown + + OnKillFocusAmount + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + T&ransfer: + + + m_staticText20 + protected + + -1,-1 + wxALIGN_RIGHT + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + + " Standard" + + 1 + + + 0 + wxID_CHOICETRANSFERTYPE + + + m_choiceTransferType + protected + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer672 + wxHORIZONTAL + none + + 5 + wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT + 1 + + + bSizer681 + wxVERTICAL + none + + 5 + wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + &From: + + + m_staticTextFrom + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxLEFT|wxEXPAND + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + + m_textCtrlFrom + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer67 + wxHORIZONTAL + none + + 5 + wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT + 1 + + + bSizer68 + wxVERTICAL + none + + 5 + wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + &Message: + + + m_staticTextMessage + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxLEFT + 1 + + + + 1 + + + 0 + wxID_ANY + + 0 + + m_textCtrlMessage + protected + + + wxTE_MULTILINE + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer23 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + + + 0 + 1 + + ,90,90,-1,70,0 + 0 + wxID_BUTTONSEND + &Send + + 85,25 + m_buttonSend + protected + + -1,-1 + + + + + + + OnButtonSend + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + -1,-1 + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CSendingDialogBase + + 442,151 + wxDEFAULT_DIALOG_STYLE + + Sending... + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + OnPaint + + + + + + + + + bSizer68 + wxVERTICAL + none + + 8 + wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + + + + m_staticTextSending + protected + + -1,14 + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + wxEXPAND|wxRIGHT|wxLEFT + 1 + + wxSYS_COLOUR_BTNFACE + + 1 + + + 0 + wxID_ANY + + 0 + + m_textCtrlStatus + protected + + + wxTE_CENTRE|wxTE_MULTILINE|wxTE_NO_VSCROLL|wxTE_READONLY + + + Connecting... + + + wxNO_BORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer69 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + + + 0 + 0 + + + 0 + wxID_ANY + OK + + 85,25 + m_buttonOK + protected + + + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + -1,-1 + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CYourAddressDialogBase + + 610,390 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Your Bitcoin Address + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer68 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 5 + protected + 0 + + + + 5 + wxALL + 0 + + + + 1 + + + 0 + wxID_ANY + These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you. + + + m_staticText45 + protected + + + + + + + + + 600 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_LISTCTRL + + + m_listCtrl + protected + + + wxLC_NO_SORT_HEADER|wxLC_REPORT|wxLC_SORT_ASCENDING + + + + + + + + + + + + + + + + + + + + + + + + + + + OnListEndLabelEdit + + OnListItemActivated + + + + + OnListItemSelected + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer69 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONRENAME + &Rename... + + 85,25 + m_buttonRename + protected + + + + + + + + + OnButtonRename + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONNEW + &New Address... + + 110,25 + m_buttonNew + protected + + -1,-1 + + + + + + + OnButtonNew + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONCOPY + &Copy to Clipboard + + 120,25 + m_buttonCopy + protected + + -1,-1 + + + + + + + OnButtonCopy + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OK + OK + + 85,25 + m_buttonOK + protected + + + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + -1,-1 + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CAddressBookDialogBase + + 610,390 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Address Book + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer68 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 5 + protected + 0 + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 1 + wxID_ANY + Bitcoin Address + + + m_staticText55 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_LISTCTRL + + + m_listCtrl + protected + + + wxLC_NO_SORT_HEADER|wxLC_REPORT|wxLC_SORT_ASCENDING + + + + + + + + + + + + + + + + + + + + + + + + + + + OnListEndLabelEdit + + OnListItemActivated + + + + + OnListItemSelected + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer69 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONEDIT + &Edit... + + 85,25 + m_buttonEdit + protected + + + + + + + + + OnButtonEdit + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONNEW + &New Address... + + 110,25 + m_buttonNew + protected + + + + + + + + + OnButtonNew + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONDELETE + &Delete + + 85,25 + m_buttonDelete + protected + + + + + + + + + OnButtonDelete + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OK + OK + + 85,25 + m_buttonOK + protected + + -1,-1 + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + public + + -1,-1 + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CProductsDialogBase + + 708,535 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Marketplace + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer22 + wxVERTICAL + none + + 5 + wxEXPAND|wxTOP|wxBOTTOM|wxRIGHT + 0 + + + bSizer23 + wxHORIZONTAL + none + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + "(Any Category)" + + 1 + + + 0 + wxID_ANY + + + m_comboBoxCategory + protected + + 150,-1 + + + + (Any Category) + + + + + OnCombobox + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 1 + + + + 1 + + + 0 + wxID_ANY + + 0 + + m_textCtrlSearch + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 0 + 1 + + + 0 + wxID_ANY + &Search + + + m_buttonSearch + protected + + + + + + + + + OnButtonSearch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_listCtrl + protected + + + wxLC_NO_SORT_HEADER|wxLC_REPORT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnListItemActivated + + + + + + + + + + + + + + + + + + + + + + + + + wxSYS_COLOUR_MENU + + + 1 + + + + 0 + wxID_ANY + + + CEditProductDialogBase + + 660,640 + wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER + + Edit Product + + + + wxTAB_TRAVERSAL + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer20 + wxVERTICAL + none + + 5 + wxEXPAND|wxALL + 1 + + wxSYS_COLOUR_WINDOW + + 1 + + + 0 + wxID_ANY + + + m_scrolledWindow + protected + + 5 + 5 + + + + + + wxHSCROLL|wxTAB_TRAVERSAL|wxVSCROLL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer21 + wxVERTICAL + none + + 5 + wxEXPAND|wxTOP|wxRIGHT|wxLEFT + 0 + + 2 + wxBOTH + 1 + + 0 + + fgSizer8 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Category + + + m_staticText106 + protected + + + wxALIGN_RIGHT + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + + 1 + + + 0 + wxID_ANY + + 180,-1 + m_comboBoxCategory + protected + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Title + + + m_staticText108 + protected + + + wxALIGN_RIGHT + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + 0 + + m_textCtrlTitle + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Price + + + m_staticText107 + protected + + + wxALIGN_RIGHT + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 105,-1 + m_textCtrlPrice + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Page 1: Description + + + m_staticText22 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + -1,170 + m_textCtrlDescription + protected + + + wxTE_MULTILINE + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Page 2: Order Form + + + m_staticText23 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxALL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + -1,120 + m_textCtrlInstructions + protected + + + wxTE_MULTILINE + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 3 + wxBOTH + 1 + + 0 + + fgSizer5 + wxFLEX_GROWMODE_SPECIFIED + public + 0 + 0 + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Label + + + m_staticText24 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Comma separated list of choices, or leave blank for text field + + + m_staticText25 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel0 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField0 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL + 0 + + + + 0 + 1 + + + 0 + wxID_DEL0 + Delete + + -1,-1 + m_buttonDel0 + protected + + 60,20 + + + + + + + OnButtonDel0 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel1 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField1 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL1 + Delete + + -1,-1 + m_buttonDel1 + protected + + 60,20 + + + + + + + OnButtonDel1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel2 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField2 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL2 + Delete + + -1,-1 + m_buttonDel2 + protected + + 60,20 + + + + + + + OnButtonDel2 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel3 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField3 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL3 + Delete + + -1,-1 + m_buttonDel3 + protected + + 60,20 + + + + + + + OnButtonDel3 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel4 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField4 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL4 + Delete + + -1,-1 + m_buttonDel4 + protected + + 60,20 + + + + + + + OnButtonDel4 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel5 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField5 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL5 + Delete + + -1,-1 + m_buttonDel5 + protected + + 60,20 + + + + + + + OnButtonDel5 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel6 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField6 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL6 + Delete + + -1,-1 + m_buttonDel6 + protected + + 60,20 + + + + + + + OnButtonDel6 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel7 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField7 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL7 + Delete + + -1,-1 + m_buttonDel7 + protected + + 60,20 + + + + + + + OnButtonDel7 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel8 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField8 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL8 + Delete + + -1,-1 + m_buttonDel8 + protected + + 60,20 + + + + + + + OnButtonDel8 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel9 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField9 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL9 + Delete + + -1,-1 + m_buttonDel9 + protected + + 60,20 + + + + + + + OnButtonDel9 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel10 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField10 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL10 + Delete + + -1,-1 + m_buttonDel10 + protected + + 60,20 + + + + + + + OnButtonDel10 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel11 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField11 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL11 + Delete + + -1,-1 + m_buttonDel11 + protected + + 60,20 + + + + + + + OnButtonDel11 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel12 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField12 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL12 + Delete + + -1,-1 + m_buttonDel12 + protected + + 60,20 + + + + + + + OnButtonDel12 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel13 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField13 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL13 + Delete + + -1,-1 + m_buttonDel13 + protected + + 60,20 + + + + + + + OnButtonDel13 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel14 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField14 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL14 + Delete + + -1,-1 + m_buttonDel14 + protected + + 60,20 + + + + + + + OnButtonDel14 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel15 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField15 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL15 + Delete + + -1,-1 + m_buttonDel15 + protected + + 60,20 + + + + + + + OnButtonDel15 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel16 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField16 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL16 + Delete + + -1,-1 + m_buttonDel16 + protected + + 60,20 + + + + + + + OnButtonDel16 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel17 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField17 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL17 + Delete + + -1,-1 + m_buttonDel17 + protected + + 60,20 + + + + + + + OnButtonDel17 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel18 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField18 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL18 + Delete + + -1,-1 + m_buttonDel18 + protected + + 60,20 + + + + + + + OnButtonDel18 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + + 0 + 150,-1 + m_textCtrlLabel19 + protected + + + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 0 + + + + 1 + + + 0 + wxID_ANY + -1,-1 + 0 + -1,-1 + m_textCtrlField19 + protected + + -1,-1 + + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 0 + + + + 0 + 1 + + + 0 + wxID_DEL19 + Delete + + -1,-1 + m_buttonDel19 + protected + + 60,20 + + + + + + + OnButtonDel19 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_HORIZONTAL + 0 + + + bSizer25 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_ANY + &Add Field + + + m_buttonAddField + protected + + + + + + + + + OnButtonAddField + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_RIGHT + 0 + + + bSizer26 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONSEND + &Send + + 85,25 + m_buttonOK + protected + + + + + + + + + OnButtonSend + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONPREVIEW + &Preview + + 85,25 + m_buttonPreview + protected + + + + + + + + + OnButtonPreview + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wxSYS_COLOUR_MENU + + + 1 + + + + 0 + wxID_ANY + + + CViewProductDialogBase + + 630,520 + wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER + + Order Form + + + + wxTAB_TRAVERSAL + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer20 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + bSizer116 + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 1 + wxID_ANY + + + m_htmlWinReviews + protected + + + wxHW_SCROLLBAR_AUTO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxALL + 1 + + wxSYS_COLOUR_WINDOW + + 1 + + + 0 + wxID_ANY + + + m_scrolledWindow + protected + + 5 + 5 + + + + + + wxHSCROLL|wxTAB_TRAVERSAL|wxVSCROLL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer21 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + + + 1 + + + 0 + wxID_ANY + + -1,-1 + m_richTextHeading + protected + + -1,50 + wxTE_READONLY + + + + + wxNO_BORDER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + + + 1 + + + 0 + wxID_ANY + Order Form instructions here multiple lines 1 2 3 4 5 6 + + + m_staticTextInstructions + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_HORIZONTAL + 0 + + + bSizer25 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONSAMPLE + &Submit + + -1,-1 + m_buttonSubmitForm + protected + + + + + + + + + OnButtonSubmitForm + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL2 + Cancel + + + m_buttonCancelForm + protected + + + + + + + + + OnButtonCancelForm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_RIGHT + 0 + + + bSizer26 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + + + 0 + 0 + + + 0 + wxID_BUTTONBACK + < &Back + + 85,25 + m_buttonBack + protected + + + + + + + + + OnButtonBack + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_BUTTONNEXT + &Next > + + 85,25 + m_buttonNext + protected + + + + + + + + + OnButtonNext + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wxSYS_COLOUR_MENU + + + 1 + + + + 0 + wxID_ANY + + + CViewOrderDialogBase + + 630,520 + wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER + + View Order + + + + wxTAB_TRAVERSAL + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer20 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + bSizer116 + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_htmlWin + protected + + + wxHW_SCROLLBAR_AUTO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_RIGHT + 0 + + + bSizer26 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OK + OK + + 85,25 + m_buttonOK + protected + + + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wxSYS_COLOUR_MENU + + + 1 + + + + 0 + wxID_ANY + + + CEditReviewDialogBase + + 630,440 + wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER + + Enter Review + + + + wxTAB_TRAVERSAL + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer112 + wxVERTICAL + none + + 5 + + 0 + + 3 + protected + 0 + + + + 5 + wxALL|wxEXPAND + 0 + + + + 1 + + + 0 + wxID_ANY + + + + m_staticTextSeller + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + 0 + + 3 + protected + 0 + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Rating + + + m_staticText110 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + " 1 star" " 2 stars" " 3 stars" " 4 stars" " 5 stars" + + 1 + + + 0 + wxID_ANY + + + m_choiceStars + protected + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + Review + + + m_staticText43 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + + + 0 + wxID_ANY + + 0 + + m_textCtrlReview + protected + + + wxTE_MULTILINE + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_RIGHT + 0 + + + bSizer113 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_SUBMIT + &Submit + + 85,25 + m_buttonSubmit + protected + + + + + + + + + OnButtonSubmit + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wxSYS_COLOUR_BTNFACE + + + 1 + + + + 0 + wxID_ANY + + + CPokerLobbyDialogBase + + 586,457 + wxDEFAULT_FRAME_STYLE + + Poker Lobby + + + + wxTAB_TRAVERSAL + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer156 + wxHORIZONTAL + none + + 5 + wxEXPAND|wxTOP|wxBOTTOM|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + + 130,-1 + m_treeCtrl + protected + + + wxTR_HAS_BUTTONS|wxTR_HIDE_ROOT|wxTR_LINES_AT_ROOT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnTreeSelChanged + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer172 + wxVERTICAL + none + + 5 + wxEXPAND|wxALL + 1 + + + + 1 + + + 0 + wxID_ANY + + + m_listCtrl + protected + + + wxLC_NO_SORT_HEADER|wxLC_REPORT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnListItemActivated + + + + + OnListItemSelected + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OPENNEWTABLE + &Open New Table + + + m_buttonNewTable + protected + + + + + + + + + OnButtonNewTable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CPokerDialogBase + + 806,550 + wxDEFAULT_FRAME_STYLE|wxFRAME_NO_TASKBAR + + Poker + + + + wxFULL_REPAINT_ON_RESIZE|wxTAB_TRAVERSAL + 1 + + + + OnClose + + + + + + + + + + + + + + + + + OnMouseEvents + + OnPaint + + + + + OnSize + + + + bSizer174 + wxVERTICAL + none + + 5 + wxALL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + Deal Me Out + + + m_checkSitOut + public + + + + + + + + + + OnCheckSitOut + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_DEALHAND + &Deal Hand + + + m_buttonDealHand + protected + + 150,25 + + + + + + + OnButtonDealHand + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_FOLD + &Fold + + + m_buttonFold + protected + + 80,25 + + + + + + + OnButtonFold + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CALL + &Call + + + m_buttonCall + protected + + 80,25 + + + + + + + OnButtonCall + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_RAISE + &Raise + + + m_buttonRaise + protected + + 80,25 + + + + + + + OnButtonRaise + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_LEAVETABLE + &Leave Table + + + m_buttonLeaveTable + protected + + 90,25 + + + + + + + OnButtonLeaveTable + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 1 + + + 0 + wxID_DITCHPLAYER + + 0 + + m_textDitchPlayer + protected + + 45,-1 + wxTE_PROCESS_ENTER + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OnDitchPlayer + + + + + + + 5 + wxALL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + FOLD + + + m_checkPreFold + public + + 100,-1 + + + + + + + + OnCheckPreFold + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + CALL + + + m_checkPreCall + public + + 100,-1 + + + + + + + + OnCheckPreCall + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + CALL ANY + + + m_checkPreCallAny + public + + 100,-1 + + + + + + + + OnCheckPreCallAny + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + RAISE + + + m_checkPreRaise + public + + 100,-1 + + + + + + + + OnCheckPreRaise + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + RAISE ANY + + + m_checkPreRaiseAny + public + + 100,-1 + + + + + + + + OnCheckPreRaiseAny + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + 1 + + 0 + wxID_ANY + + + m_statusBar + public + + + wxST_SIZEGRIP + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + 0 + wxID_ANY + + + CGetTextFromUserDialogBase + + 403,138 + wxDEFAULT_DIALOG_STYLE + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer79 + wxVERTICAL + none + + 10 + wxEXPAND|wxALL + 1 + + + bSizer81 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 0 + wxID_ANY + + + + m_staticTextMessage1 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL + 0 + + + + 1 + + + 0 + wxID_TEXTCTRL + + 0 + + m_textCtrl1 + protected + + + wxTE_PROCESS_ENTER + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + + + 1 + + + 1 + wxID_ANY + + + + m_staticTextMessage2 + protected + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_HORIZONTAL + 0 + + + + 1 + + + 1 + wxID_TEXTCTRL + + 0 + + m_textCtrl2 + protected + + + wxTE_PROCESS_ENTER + + + + + + + + + + OnKeyDown + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + + + 5 + wxEXPAND + 0 + + + bSizer80 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_OK + OK + + 85,25 + m_buttonOK + protected + + -1,-1 + + + + + + + OnButtonOK + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + + + 0 + 1 + + + 0 + wxID_CANCEL + Cancel + + 85,25 + m_buttonCancel + protected + + + + + + + + + OnButtonCancel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..0b794d4 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,393 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "headers.h" + + + +bool fDebug = false; + + + + +#ifdef _WIN32 +// Init openssl library multithreading support (OpenSSL < 1.1 needed callbacks; +// OpenSSL 1.1+/3 is internally thread-safe, so the Linux build skips this). +static HANDLE* lock_cs; + +void win32_locking_callback(int mode, int type, const char* file, int line) +{ + if (mode & CRYPTO_LOCK) + WaitForSingleObject(lock_cs[type], INFINITE); + else + ReleaseMutex(lock_cs[type]); +} +#endif + +// Init +class CInit +{ +public: + CInit() + { +#ifdef _WIN32 + // Init openssl library multithreading support + lock_cs = (HANDLE*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(HANDLE)); + for (int i = 0; i < CRYPTO_num_locks(); i++) + lock_cs[i] = CreateMutex(NULL,FALSE,NULL); + CRYPTO_set_locking_callback(win32_locking_callback); + + // Seed random number generator with screen scrape and other hardware sources + RAND_screen(); +#endif + // Seed random number generator with perfmon / high-res timer data + RandAddSeed(true); + } + ~CInit() + { +#ifdef _WIN32 + // Shutdown openssl library multithreading support + CRYPTO_set_locking_callback(NULL); + for (int i =0 ; i < CRYPTO_num_locks(); i++) + CloseHandle(lock_cs[i]); + OPENSSL_free(lock_cs); +#endif + } +} +instance_of_cinit; + + + + +void RandAddSeed(bool fPerfmon) +{ +#ifdef _WIN32 + // Seed with CPU performance counter + LARGE_INTEGER PerformanceCount; + QueryPerformanceCounter(&PerformanceCount); + RAND_add(&PerformanceCount, sizeof(PerformanceCount), 1.5); + memset(&PerformanceCount, 0, sizeof(PerformanceCount)); + + static int64 nLastPerfmon; + if (fPerfmon || GetTime() > nLastPerfmon + 5 * 60) + { + nLastPerfmon = GetTime(); + + // Seed with the entire set of perfmon data + unsigned char pdata[250000]; + memset(pdata, 0, sizeof(pdata)); + unsigned long nSize = sizeof(pdata); + long ret = RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize); + RegCloseKey(HKEY_PERFORMANCE_DATA); + if (ret == ERROR_SUCCESS) + { + uint256 hash; + SHA256(pdata, nSize, (unsigned char*)&hash); + RAND_add(&hash, sizeof(hash), min(nSize/500.0, (double)sizeof(hash))); + hash = 0; + memset(pdata, 0, nSize); + printf("RandAddSeed() got %d bytes of performance data\n", nSize); + } + } +#else + // Seed with a high-resolution timer; OpenSSL also seeds itself from + // /dev/urandom, so this is just extra entropy. No perfmon on Linux. + (void)fPerfmon; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + RAND_add(&ts, sizeof(ts), 1.5); + memset(&ts, 0, sizeof(ts)); +#endif +} + + + + + + + + + + +// Safer snprintf +// - prints up to limit-1 characters +// - output string is always null terminated even if limit reached +// - return value is the number of characters actually printed +int my_snprintf(char* buffer, size_t limit, const char* format, ...) +{ + if (limit == 0) + return 0; + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + return ret; +} + + +string strprintf(const char* format, ...) +{ + char buffer[50000]; + char* p = buffer; + int limit = sizeof(buffer); + int ret; + loop + { + va_list arg_ptr; + va_start(arg_ptr, format); + ret = _vsnprintf(p, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret >= 0 && ret < limit) + break; + if (p != buffer) + delete p; + limit *= 2; + p = new char[limit]; + if (p == NULL) + throw std::bad_alloc(); + } +#ifdef _MSC_VER + // msvc optimisation + if (p == buffer) + return string(p, p+ret); +#endif + string str(p, p+ret); + if (p != buffer) + delete p; + return str; +} + + +bool error(const char* format, ...) +{ + char buffer[50000]; + int limit = sizeof(buffer); + va_list arg_ptr; + va_start(arg_ptr, format); + int ret = _vsnprintf(buffer, limit, format, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + ret = limit - 1; + buffer[limit-1] = 0; + } + printf("ERROR: %s\n", buffer); + return false; +} + + +void PrintException(std::exception* pex, const char* pszThread) +{ + char pszModule[260]; + pszModule[0] = '\0'; +#ifdef _WIN32 + GetModuleFileName(NULL, pszModule, sizeof(pszModule)); + _strlwr(pszModule); +#else + strncpy(pszModule, "bitflash-node", sizeof(pszModule) - 1); +#endif + char pszMessage[1000]; + if (pex) + snprintf(pszMessage, sizeof(pszMessage), + "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); + else + snprintf(pszMessage, sizeof(pszMessage), + "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); + printf("\n\n************************\n%s", pszMessage); + if (wxTheApp) + wxMessageBox(pszMessage, "Error", wxOK | wxICON_ERROR); + throw; + //DebugBreak(); +} + + +void ParseString(const string& str, char c, vector& v) +{ + unsigned int i1 = 0; + unsigned int i2; + do + { + i2 = str.find(c, i1); + v.push_back(str.substr(i1, i2-i1)); + i1 = i2+1; + } + while (i2 != str.npos); +} + + +string FormatMoney(int64 n, bool fPlus) +{ + n /= CENT; + string str = strprintf("%I64d.%02I64d", (n > 0 ? n : -n)/100, (n > 0 ? n : -n)%100); + for (int i = 6; i < str.size(); i += 4) + if (isdigit(str[str.size() - i - 1])) + str.insert(str.size() - i, 1, ','); + if (n < 0) + str.insert((unsigned int)0, 1, '-'); + else if (fPlus && n > 0) + str.insert((unsigned int)0, 1, '+'); + return str; +} + +bool ParseMoney(const char* pszIn, int64& nRet) +{ + string strWhole; + int64 nCents = 0; + const char* p = pszIn; + while (isspace(*p)) + p++; + for (; *p; p++) + { + if (*p == ',' && p > pszIn && isdigit(p[-1]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && !isdigit(p[4])) + continue; + if (*p == '.') + { + p++; + if (!isdigit(p[0]) || !isdigit(p[1])) + return false; + nCents = atoi64(p); + if (nCents < 0 || nCents > 99) + return false; + p += 2; + break; + } + if (isspace(*p)) + break; + if (!isdigit(*p)) + return false; + strWhole.insert(strWhole.end(), *p); + } + for (; *p; p++) + if (!isspace(*p)) + return false; + if (strWhole.size() > 17) + return false; + int64 nWhole = atoi64(strWhole); + int64 nValue = nWhole * 100 + nCents; + if (nValue / 100 != nWhole) + return false; + nValue *= CENT; + nRet = nValue; + return true; +} + + + + + + + + + + +bool FileExists(const char* psz) +{ +#ifdef WIN32 + return GetFileAttributes(psz) != -1; +#else + return access(psz, 0) != -1; +#endif +} + +int GetFilesize(FILE* file) +{ + int nSavePos = ftell(file); + int nFilesize = -1; + if (fseek(file, 0, SEEK_END) == 0) + nFilesize = ftell(file); + fseek(file, nSavePos, SEEK_SET); + return nFilesize; +} + + + + + + + + +uint64 GetRand(uint64 nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64 nRange = (_UI64_MAX / nMax) * nMax; + uint64 nRand = 0; + do + RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); + while (nRand >= nRange); + return (nRand % nMax); +} + + + + + + + + + + +// +// "Never go to sea with two chronometers; take one or three." +// Our three chronometers are: +// - System clock +// - Median of other server's clocks +// - NTP servers +// +// note: NTP isn't implemented yet, so until then we just use the median +// of other nodes clocks to correct ours. +// + +int64 GetTime() +{ + return time(NULL); +} + +static int64 nTimeOffset = 0; + +int64 GetAdjustedTime() +{ + return GetTime() + nTimeOffset; +} + +void AddTimeData(unsigned int ip, int64 nTime) +{ + int64 nOffsetSample = nTime - GetTime(); + + // Ignore duplicates + static set setKnown; + if (!setKnown.insert(ip).second) + return; + + // Add data + static vector vTimeOffsets; + if (vTimeOffsets.empty()) + vTimeOffsets.push_back(0); + vTimeOffsets.push_back(nOffsetSample); + printf("Added time data, samples %d, ip %08x, offset %+I64d (%+I64d minutes)\n", vTimeOffsets.size(), ip, vTimeOffsets.back(), vTimeOffsets.back()/60); + if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) + { + sort(vTimeOffsets.begin(), vTimeOffsets.end()); + int64 nMedian = vTimeOffsets[vTimeOffsets.size()/2]; + nTimeOffset = nMedian; + if ((nMedian > 0 ? nMedian : -nMedian) > 5 * 60) + { + // Only let other nodes change our clock so far before we + // go to the NTP servers + /// todo: Get time from NTP servers, then set a flag + /// to make sure it doesn't get changed again + } + foreach(int64 n, vTimeOffsets) + printf("%+I64d ", n); + printf("| nTimeOffset = %+I64d (%+I64d minutes)\n", nTimeOffset, nTimeOffset/60); + } +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..96cb4d4 --- /dev/null +++ b/src/util.h @@ -0,0 +1,413 @@ +// Copyright (c) 2009 Satoshi Nakamoto +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false) ; else for +#endif + +#ifndef _MSC_VER +#define __forceinline inline +#endif + +#define foreach BOOST_FOREACH +#define loop for (;;) +#define BEGIN(a) ((char*)&(a)) +#define END(a) ((char*)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char*)&(a)) +#define UEND(a) ((unsigned char*)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) + +// Route printf to debug.log (and the console) on the GUI Windows build and on +// the headless Linux build alike. +#if defined(_WINDOWS) || !defined(_WIN32) +#define printf OutputDebugStringF +#endif + +#ifdef snprintf +#undef snprintf +#endif +#define snprintf my_snprintf + +#ifndef PRId64 +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MSVCRT__) +#define PRId64 "I64d" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#else +#define PRId64 "lld" +#define PRIu64 "llu" +#define PRIx64 "llx" +#endif +#endif + +// This is needed because the foreach macro can't get over the comma in pair +#define PAIRTYPE(t1, t2) pair + +// Used to bypass the rule against non-const reference to temporary +// where it makes sense with wrappers such as CFlatData or CTxDB +template +inline T& REF(const T& val) +{ + return (T&)val; +} + + + + + + + + + +extern bool fDebug; + +void RandAddSeed(bool fPerfmon=false); +int my_snprintf(char* buffer, size_t limit, const char* format, ...); +string strprintf(const char* format, ...); +bool error(const char* format, ...); +void PrintException(std::exception* pex, const char* pszThread); +void ParseString(const string& str, char c, vector& v); +string FormatMoney(int64 n, bool fPlus=false); +bool ParseMoney(const char* pszIn, int64& nRet); +bool FileExists(const char* psz); +int GetFilesize(FILE* file); +uint64 GetRand(uint64 nMax); +int64 GetTime(); +int64 GetAdjustedTime(); +void AddTimeData(unsigned int ip, int64 nTime); + + + + + + + + + + + + +// Wrapper to automatically initialize critical section +// Could use wxCriticalSection for portability, but it doesn't support TryEnterCriticalSection +class CCriticalSection +{ +protected: + CRITICAL_SECTION cs; +public: + char* pszFile; + int nLine; + explicit CCriticalSection() { InitializeCriticalSection(&cs); } + ~CCriticalSection() { DeleteCriticalSection(&cs); } + void Enter() { EnterCriticalSection(&cs); } + void Leave() { LeaveCriticalSection(&cs); } + bool TryEnter() { return TryEnterCriticalSection(&cs); } + CRITICAL_SECTION* operator&() { return &cs; } +}; + +// Automatically leave critical section when leaving block, needed for exception safety +class CCriticalBlock +{ +protected: + CRITICAL_SECTION* pcs; +public: + CCriticalBlock(CRITICAL_SECTION& csIn) { pcs = &csIn; EnterCriticalSection(pcs); } + CCriticalBlock(CCriticalSection& csIn) { pcs = &csIn; EnterCriticalSection(pcs); } + ~CCriticalBlock() { LeaveCriticalSection(pcs); } +}; + +// WARNING: This will catch continue and break! +// break is caught with an assertion, but there's no way to detect continue. +// I'd rather be careful than suffer the other more error prone syntax. +// The compiler will optimise away all this loop junk. +#define CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CCriticalBlock criticalblock(cs); fcriticalblockonce && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + +class CTryCriticalBlock +{ +protected: + CRITICAL_SECTION* pcs; +public: + CTryCriticalBlock(CRITICAL_SECTION& csIn) { pcs = (TryEnterCriticalSection(&csIn) ? &csIn : NULL); } + CTryCriticalBlock(CCriticalSection& csIn) { pcs = (TryEnterCriticalSection(&csIn) ? &csIn : NULL); } + ~CTryCriticalBlock() { if (pcs) LeaveCriticalSection(pcs); } + bool Entered() { return pcs != NULL; } +}; + +#define TRY_CRITICAL_BLOCK(cs) \ + for (bool fcriticalblockonce=true; fcriticalblockonce; assert(("break caught by TRY_CRITICAL_BLOCK!", !fcriticalblockonce)), fcriticalblockonce=false) \ + for (CTryCriticalBlock criticalblock(cs); fcriticalblockonce && (fcriticalblockonce = criticalblock.Entered()) && (cs.pszFile=__FILE__, cs.nLine=__LINE__, true); fcriticalblockonce=false, cs.pszFile=NULL, cs.nLine=0) + + + + + + + + + + + + +inline string i64tostr(int64 n) +{ + return strprintf("%" PRId64, n); +} + +inline string itostr(int n) +{ + return strprintf("%d", n); +} + +inline int64 atoi64(const char* psz) +{ +#ifdef _MSC_VER + return _atoi64(psz); +#else + return strtoll(psz, NULL, 10); +#endif +} + +inline int64 atoi64(const string& str) +{ +#ifdef _MSC_VER + return _atoi64(str.c_str()); +#else + return strtoll(str.c_str(), NULL, 10); +#endif +} + +inline int atoi(const string& str) +{ + return atoi(str.c_str()); +} + +inline int roundint(double d) +{ + return (int)(d > 0 ? d + 0.5 : d - 0.5); +} + +template +string HexStr(const T itbegin, const T itend, bool fSpaces=true) +{ + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + string str; + for (const unsigned char* p = pbegin; p != pend; p++) + str += strprintf((fSpaces && p != pend-1 ? "%02x " : "%02x"), *p); + return str; +} + +template +string HexNumStr(const T itbegin, const T itend, bool f0x=true) +{ + const unsigned char* pbegin = (const unsigned char*)&itbegin[0]; + const unsigned char* pend = pbegin + (itend - itbegin) * sizeof(itbegin[0]); + string str = (f0x ? "0x" : ""); + for (const unsigned char* p = pend-1; p >= pbegin; p--) + str += strprintf("%02X", *p); + return str; +} + +template +void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) +{ + printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); +} + + + + + + + + +inline int OutputDebugStringF(const char* pszFormat, ...) +{ +#if 1 // debug.log always enabled (was previously only under __WXDEBUG__) + // log file + FILE* fileout = fopen("debug.log", "a"); + if (fileout) + { + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + vfprintf(fileout, pszFormat, arg_ptr); + va_end(arg_ptr); + fclose(fileout); + } + +#ifndef _WIN32 + // Headless build: also echo to the console (the Windows path below returns + // early before reaching the console print, which is fine for the GUI app). + { + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + vprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + fflush(stdout); + } +#endif + + // accumulate a line at a time + static CCriticalSection cs_OutputDebugStringF; + CRITICAL_BLOCK(cs_OutputDebugStringF) + { + static char pszBuffer[50000]; + static char* pend; + if (pend == NULL) + pend = pszBuffer; + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + int limit = END(pszBuffer) - pend - 2; + int ret = _vsnprintf(pend, limit, pszFormat, arg_ptr); + va_end(arg_ptr); + if (ret < 0 || ret >= limit) + { + pend = END(pszBuffer) - 2; + *pend++ = '\n'; + } + else + pend += ret; + *pend = '\0'; + char* p1 = pszBuffer; + char* p2; + while (p2 = strchr(p1, '\n')) + { + p2++; + char c = *p2; + *p2 = '\0'; + OutputDebugStringA(p1); + *p2 = c; + p1 = p2; + } + if (p1 != pszBuffer) + memmove(pszBuffer, p1, pend - p1 + 1); + pend -= (p1 - pszBuffer); + return ret; + } +#endif + + if (!wxTheApp) + { + // print to console + va_list arg_ptr; + va_start(arg_ptr, pszFormat); + vprintf(pszFormat, arg_ptr); + va_end(arg_ptr); + } + return 0; +} + + + + + + + + + +inline void heapchk() +{ + if (_heapchk() != _HEAPOK) + DebugBreak(); +} + +// Randomize the stack to help protect against buffer overrun exploits +#define IMPLEMENT_RANDOMIZE_STACK(ThreadFn) \ + { \ + static char nLoops; \ + if (nLoops <= 0) \ + nLoops = GetRand(50) + 1; \ + if (nLoops-- > 1) \ + { \ + ThreadFn; \ + return; \ + } \ + } + +#define CATCH_PRINT_EXCEPTION(pszFn) \ + catch (std::exception& e) { \ + PrintException(&e, (pszFn)); \ + } catch (...) { \ + PrintException(NULL, (pszFn)); \ + } + + + + + + + + + +template +inline uint256 Hash(const T1 pbegin, const T1 pend) +{ + uint256 hash1; + SHA256((unsigned char*)&pbegin[0], (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end) +{ + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +inline uint256 Hash(const T1 p1begin, const T1 p1end, + const T2 p2begin, const T2 p2end, + const T3 p3begin, const T3 p3end) +{ + uint256 hash1; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, (unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])); + SHA256_Update(&ctx, (unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])); + SHA256_Update(&ctx, (unsigned char*)&p3begin[0], (p3end - p3begin) * sizeof(p3begin[0])); + SHA256_Final((unsigned char*)&hash1, &ctx); + uint256 hash2; + SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} + +template +uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=VERSION) +{ + // Most of the time is spent allocating and deallocating CDataStream's + // buffer. If this ever needs to be optimized further, make a CStaticStream + // class with its buffer on the stack. + CDataStream ss(nType, nVersion); + ss.reserve(10000); + ss << obj; + return Hash(ss.begin(), ss.end()); +} + +inline uint160 Hash160(const vector& vch) +{ + uint256 hash1; + SHA256(&vch[0], vch.size(), (unsigned char*)&hash1); + uint160 hash2; + RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); + return hash2; +} -- cgit v1.2.3