upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/selftest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/selftest.cpp')
-rw-r--r--src/selftest.cpp332
1 files changed, 325 insertions, 7 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp
index d12c96b..eed6779 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -7,9 +7,13 @@
7 7
8#include "headers_core.h" 8#include "headers_core.h"
9#include "bip32.h" 9#include "bip32.h"
10#include "proxy.h"
10#include "selftest.h" 11#include "selftest.h"
12#include "sockcount.h"
11#include "walletcmd.h" 13#include "walletcmd.h"
12 14
15extern int RunPoolStratumSelfTest();
16
13// Test results go to the terminal, not to debug.log. 17// Test results go to the terminal, not to debug.log.
14// 18//
15// printf in this tree is OutputDebugStringF, which writes into the data 19// printf in this tree is OutputDebugStringF, which writes into the data
@@ -348,17 +352,17 @@ static int RunWalletHDSelfTest()
348 nFail += Check(HaveHDSeed(), "the wallet reports having a seed") ? 0 : 1; 352 nFail += Check(HaveHDSeed(), "the wallet reports having a seed") ? 0 : 1;
349 nFail += Check(nHDKeySchema == HD_SCHEMA_BIP44, 353 nFail += Check(nHDKeySchema == HD_SCHEMA_BIP44,
350 "a new recovery phrase records the BIP44 HD schema") ? 0 : 1; 354 "a new recovery phrase records the BIP44 HD schema") ? 0 : 1;
351 nFail += Check(nHDCoinType == HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL, 355 nFail += Check(nHDCoinType == HD_BIP44_COIN_TYPE_BITFLASH,
352 "a new recovery phrase records the provisional BIP44 coin type") ? 0 : 1; 356 "a new recovery phrase records the SLIP-0044 BIP44 coin type") ? 0 : 1;
353 nFail += Check(nHDReceiveNext == 1 && nHDChangeNext == 0, 357 nFail += Check(nHDReceiveNext == 1 && nHDChangeNext == 0,
354 "BIP44 receive/change counters reserve the default receive key") ? 0 : 1; 358 "BIP44 receive/change counters reserve the default receive key") ? 0 : 1;
355 std::vector<unsigned int> vBIP44Path = HDBIP44Path(HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL, 359 std::vector<unsigned int> vBIP44Path = HDBIP44Path(HD_BIP44_COIN_TYPE_BITFLASH,
356 HD_BIP44_ACCOUNT, 360 HD_BIP44_ACCOUNT,
357 HD_BIP44_CHAIN_RECEIVE, 361 HD_BIP44_CHAIN_RECEIVE,
358 0); 362 0);
359 nFail += Check(vBIP44Path.size() == 5 && 363 nFail += Check(vBIP44Path.size() == 5 &&
360 vBIP44Path[0] == (HD_BIP44_PURPOSE | bitflash::BIP32_HARDENED) && 364 vBIP44Path[0] == (HD_BIP44_PURPOSE | bitflash::BIP32_HARDENED) &&
361 vBIP44Path[1] == (HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL | bitflash::BIP32_HARDENED) && 365 vBIP44Path[1] == (HD_BIP44_COIN_TYPE_BITFLASH | bitflash::BIP32_HARDENED) &&
362 vBIP44Path[2] == (HD_BIP44_ACCOUNT | bitflash::BIP32_HARDENED) && 366 vBIP44Path[2] == (HD_BIP44_ACCOUNT | bitflash::BIP32_HARDENED) &&
363 vBIP44Path[3] == HD_BIP44_CHAIN_RECEIVE && 367 vBIP44Path[3] == HD_BIP44_CHAIN_RECEIVE &&
364 vBIP44Path[4] == 0, 368 vBIP44Path[4] == 0,
@@ -380,7 +384,7 @@ static int RunWalletHDSelfTest()
380 hdParent.chainCode = vchHDChainCode; 384 hdParent.chainCode = vchHDChainCode;
381 bitflash::BIP32PrivateNode hdBIP44ReceiveChild; 385 bitflash::BIP32PrivateNode hdBIP44ReceiveChild;
382 if (!bitflash::BIP32DerivePath(hdParent, 386 if (!bitflash::BIP32DerivePath(hdParent,
383 HDBIP44Path(HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL, 387 HDBIP44Path(HD_BIP44_COIN_TYPE_BITFLASH,
384 HD_BIP44_ACCOUNT, 388 HD_BIP44_ACCOUNT,
385 HD_BIP44_CHAIN_RECEIVE, 389 HD_BIP44_CHAIN_RECEIVE,
386 0), 390 0),
@@ -568,7 +572,7 @@ static int RunWalletHDSelfTest()
568 nFail += Check(audit.fHaveSeed, "the recovery audit reports the phrase") ? 0 : 1; 572 nFail += Check(audit.fHaveSeed, "the recovery audit reports the phrase") ? 0 : 1;
569 nFail += Check(audit.nSchema == HD_SCHEMA_BIP44, 573 nFail += Check(audit.nSchema == HD_SCHEMA_BIP44,
570 "the recovery audit reports the derivation schema") ? 0 : 1; 574 "the recovery audit reports the derivation schema") ? 0 : 1;
571 nFail += Check(audit.nCoinType == HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL, 575 nFail += Check(audit.nCoinType == HD_BIP44_COIN_TYPE_BITFLASH,
572 "the recovery audit reports the BIP44 coin type") ? 0 : 1; 576 "the recovery audit reports the BIP44 coin type") ? 0 : 1;
573 nFail += Check(audit.nReceiveNext == nHDReceiveNext && audit.nChangeNext == 1, 577 nFail += Check(audit.nReceiveNext == nHDReceiveNext && audit.nChangeNext == 1,
574 "the recovery audit reports receive/change counters") ? 0 : 1; 578 "the recovery audit reports receive/change counters") ? 0 : 1;
@@ -1241,6 +1245,314 @@ static int RunConsensusLimitsSelfTest()
1241 return nFail == 0 ? 0 : 1; 1245 return nFail == 0 ? 0 : 1;
1242} 1246}
1243 1247
1248static int RunParseMoneySelfTest()
1249{
1250 printf("parse-money self-test\n");
1251 int nFail = 0;
1252 struct Case { const char* in; bool ok; int64 val; };
1253 Case cases[] = {
1254 {"0.5", true, 50000000LL},
1255 {"1", true, 100000000LL},
1256 {"1.23456789", true, 123456789LL},
1257 {"0.00000001", true, 1LL},
1258 {"10.00000000", true, 1000000000LL},
1259 {"1,000", true, 100000000000LL},
1260 {"1.5 ", true, 150000000LL},
1261 {"0.123456789", false, 0LL},
1262 {"1.2.3", false, 0LL},
1263 {"abc", false, 0LL},
1264 {"-1", false, 0LL},
1265 {"1e5", false, 0LL},
1266 };
1267 for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++)
1268 {
1269 int64 v = -1;
1270 bool r = ParseMoney(cases[i].in, v);
1271 bool pass = (r == cases[i].ok) && (!r || v == cases[i].val);
1272 char desc[160];
1273 snprintf(desc, sizeof(desc), "ParseMoney(\"%s\") %s and value matches",
1274 cases[i].in, cases[i].ok ? "accepts" : "rejects");
1275 nFail += Check(pass, desc) ? 0 : 1;
1276 }
1277 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
1278 nFail, nFail == 1 ? "" : "s");
1279 fflush(stdout);
1280 return nFail == 0 ? 0 : 1;
1281}
1282
1283static bool SelfTestReadN(SOCKET s, void* buf, int n)
1284{
1285 char* p = (char*)buf;
1286 int off = 0;
1287 while (off < n)
1288 {
1289 int r = recv(s, p + off, n - off, 0);
1290 if (r <= 0)
1291 return false;
1292 off += r;
1293 }
1294 return true;
1295}
1296
1297static bool SelfTestWriteN(SOCKET s, const void* buf, int n)
1298{
1299 const char* p = (const char*)buf;
1300 int off = 0;
1301 while (off < n)
1302 {
1303 int r = send(s, p + off, n - off, 0);
1304 if (r <= 0)
1305 return false;
1306 off += r;
1307 }
1308 return true;
1309}
1310
1311static void SelfTestSetSocketTimeout(SOCKET s, int nTimeoutSecs)
1312{
1313#ifdef _WIN32
1314 DWORD tv = (DWORD)nTimeoutSecs * 1000;
1315 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
1316 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
1317#else
1318 struct timeval tv;
1319 tv.tv_sec = nTimeoutSecs;
1320 tv.tv_usec = 0;
1321 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
1322 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
1323#endif
1324}
1325
1326#ifdef _WIN32
1327struct SelfTestWinsock
1328{
1329 bool fStarted;
1330 SelfTestWinsock() : fStarted(false)
1331 {
1332 WSADATA wsadata;
1333 fStarted = (WSAStartup(MAKEWORD(2,2), &wsadata) == 0);
1334 }
1335 ~SelfTestWinsock()
1336 {
1337 if (fStarted)
1338 WSACleanup();
1339 }
1340};
1341#endif
1342
1343static bool RunSocks5HandshakeProbe(std::string& errOut)
1344{
1345 errOut.clear();
1346#ifdef _WIN32
1347 SelfTestWinsock winsock;
1348 if (!winsock.fStarted)
1349 {
1350 errOut = "could not start Winsock";
1351 return false;
1352 }
1353#endif
1354
1355 SOCKET listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1356 if (listener == INVALID_SOCKET)
1357 {
1358 errOut = "could not create listener";
1359 return false;
1360 }
1361 SelfTestSetSocketTimeout(listener, 5);
1362
1363 struct sockaddr_in addr;
1364 memset(&addr, 0, sizeof(addr));
1365 addr.sin_family = AF_INET;
1366 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1367 addr.sin_port = 0;
1368 if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) != 0 ||
1369 listen(listener, 1) != 0)
1370 {
1371 closesocket(listener);
1372 errOut = "could not bind/listen on loopback";
1373 return false;
1374 }
1375
1376#ifdef _WIN32
1377 int addrLen = sizeof(addr);
1378#else
1379 socklen_t addrLen = sizeof(addr);
1380#endif
1381 if (getsockname(listener, (struct sockaddr*)&addr, &addrLen) != 0)
1382 {
1383 closesocket(listener);
1384 errOut = "could not read listener port";
1385 return false;
1386 }
1387 unsigned short proxyPort = ntohs(addr.sin_port);
1388
1389 bool fServerOk = false;
1390 std::string serverErr;
1391 std::string requestedHost;
1392 unsigned short requestedPort = 0;
1393 std::thread server([&]() {
1394 SOCKET s = accept(listener, NULL, NULL);
1395 if (s == INVALID_SOCKET)
1396 {
1397 serverErr = "accept failed";
1398 return;
1399 }
1400 SelfTestSetSocketTimeout(s, 5);
1401
1402 unsigned char hello[3] = {0,0,0};
1403 if (!SelfTestReadN(s, hello, sizeof(hello)) ||
1404 hello[0] != 0x05 || hello[1] != 0x01 || hello[2] != 0x00)
1405 {
1406 serverErr = "bad SOCKS5 greeting";
1407 closesocket(s);
1408 return;
1409 }
1410 unsigned char choice[2] = {0x05, 0x00};
1411 if (!SelfTestWriteN(s, choice, sizeof(choice)))
1412 {
1413 serverErr = "could not write method choice";
1414 closesocket(s);
1415 return;
1416 }
1417
1418 unsigned char hdr[5] = {0,0,0,0,0};
1419 if (!SelfTestReadN(s, hdr, sizeof(hdr)) ||
1420 hdr[0] != 0x05 || hdr[1] != 0x01 || hdr[2] != 0x00 || hdr[3] != 0x03)
1421 {
1422 serverErr = "CONNECT did not use domain-name address type";
1423 closesocket(s);
1424 return;
1425 }
1426
1427 unsigned char len = hdr[4];
1428 std::vector<unsigned char> rest((size_t)len + 2);
1429 if (!SelfTestReadN(s, &rest[0], (int)rest.size()))
1430 {
1431 serverErr = "could not read CONNECT target";
1432 closesocket(s);
1433 return;
1434 }
1435 requestedHost.assign((const char*)&rest[0], (size_t)len);
1436 requestedPort = ((unsigned short)rest[len] << 8) | rest[len + 1];
1437
1438 unsigned char reply[10] = {0x05, 0x00, 0x00, 0x01, 0,0,0,0, 0,0};
1439 if (!SelfTestWriteN(s, reply, sizeof(reply)))
1440 {
1441 serverErr = "could not write CONNECT reply";
1442 closesocket(s);
1443 return;
1444 }
1445 fServerOk = true;
1446 closesocket(s);
1447 });
1448
1449 std::string err;
1450 BtfClearSocks5Proxy();
1451 bool fClientOk = BtfSetSocks5Proxy(strprintf("127.0.0.1:%u", (unsigned)proxyPort), err);
1452 SOCKET client = INVALID_SOCKET;
1453 if (fClientOk)
1454 client = BtfConnectSocket("relay.example", 443, SOCK_NOSTR, 5);
1455 if (client != INVALID_SOCKET)
1456 BtfCloseSocket(client);
1457 BtfClearSocks5Proxy();
1458 closesocket(listener);
1459 server.join();
1460
1461 if (!fClientOk)
1462 errOut = strprintf("could not enable proxy: %s", err.c_str());
1463 else if (client == INVALID_SOCKET)
1464 errOut = "client SOCKS5 connect failed";
1465 else if (!fServerOk)
1466 errOut = serverErr.empty() ? "mock server failed" : serverErr;
1467 else if (requestedHost != "relay.example" || requestedPort != 443)
1468 errOut = strprintf("unexpected CONNECT target %s:%u", requestedHost.c_str(), (unsigned)requestedPort);
1469 else
1470 return true;
1471 return false;
1472}
1473
1474static int RunSocks5ProxySelfTest()
1475{
1476 printf("socks5-proxy self-test\n");
1477 int nFail = 0;
1478 std::string host, err;
1479 unsigned short port = 0;
1480
1481 nFail += Check(BtfParseSocks5Proxy("127.0.0.1:9050", host, port, err) &&
1482 host == "127.0.0.1" && port == 9050,
1483 "parses local Tor proxy endpoint") ? 0 : 1;
1484 nFail += Check(BtfParseSocks5Proxy("localhost:9050", host, port, err) &&
1485 host == "localhost" && port == 9050,
1486 "parses hostname proxy endpoint") ? 0 : 1;
1487 nFail += Check(BtfParseSocks5Proxy("[::1]:9050", host, port, err) &&
1488 host == "::1" && port == 9050,
1489 "parses bracketed IPv6 proxy endpoint") ? 0 : 1;
1490 nFail += Check(!BtfParseSocks5Proxy("::1:9050", host, port, err),
1491 "rejects unbracketed IPv6 proxy endpoint") ? 0 : 1;
1492 nFail += Check(!BtfParseSocks5Proxy("localhost", host, port, err),
1493 "rejects missing port") ? 0 : 1;
1494 nFail += Check(!BtfParseSocks5Proxy("localhost:0", host, port, err),
1495 "rejects zero port") ? 0 : 1;
1496 nFail += Check(!BtfParseSocks5Proxy("localhost:70000", host, port, err),
1497 "rejects out-of-range port") ? 0 : 1;
1498 nFail += Check(!BtfParseSocks5Proxy("localhost:999999999999999999999999999999", host, port, err),
1499 "rejects overflowing port") ? 0 : 1;
1500 nFail += Check(!BtfParseSocks5Proxy("user:pass@localhost:9050", host, port, err),
1501 "rejects unsupported authenticated proxy syntax") ? 0 : 1;
1502 nFail += Check(BtfIsTorOnionHost("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd.onion") &&
1503 BtfIsTorOnionHost("Relay.Example.ONION.") &&
1504 !BtfIsTorOnionHost("relay.example") &&
1505 !BtfIsTorOnionHost(".onion"),
1506 "detects .onion hosts without accepting lookalikes") ? 0 : 1;
1507
1508 BtfClearSocks5Proxy();
1509 nFail += Check(!BtfSocks5ProxyEnabled(), "starts disabled after clear") ? 0 : 1;
1510 nFail += Check(BtfSetSocks5Proxy("127.0.0.1:9050", err) &&
1511 BtfSocks5ProxyEnabled() &&
1512 BtfSocks5ProxyName() == "127.0.0.1:9050",
1513 "enables parsed SOCKS5 proxy") ? 0 : 1;
1514 BtfClearSocks5Proxy();
1515 nFail += Check(!BtfSocks5ProxyEnabled(), "clear disables proxy") ? 0 : 1;
1516
1517 nFail += Check(BtfSetSocks5Proxy("[::1]:9050", err) &&
1518 BtfSocks5ProxyEnabled() &&
1519 BtfSocks5ProxyName() == "[::1]:9050",
1520 "enables bracketed IPv6 proxy endpoint") ? 0 : 1;
1521 BtfClearSocks5Proxy();
1522
1523 nFail += Check(BtfEnableTorProxy("", err) &&
1524 BtfSocks5ProxyEnabled() &&
1525 BtfTorProxyEnabled() &&
1526 BtfSocks5ProxyName() == "127.0.0.1:9050",
1527 "enables Tor mode on the default local SOCKS5 endpoint") ? 0 : 1;
1528 BtfClearSocks5Proxy();
1529
1530 nFail += Check(BtfEnableTorProxy("[::1]:9050", err) &&
1531 BtfSocks5ProxyEnabled() &&
1532 BtfTorProxyEnabled() &&
1533 BtfSocks5ProxyName() == "[::1]:9050",
1534 "enables Tor mode on a custom IPv6 endpoint") ? 0 : 1;
1535 BtfClearSocks5Proxy();
1536
1537 nFail += Check(BtfSetSocks5Proxy("127.0.0.1:9051", err) &&
1538 BtfSocks5ProxyEnabled() &&
1539 !BtfTorProxyEnabled(),
1540 "plain SOCKS5 mode is distinct from Tor mode") ? 0 : 1;
1541 BtfClearSocks5Proxy();
1542
1543 std::string handshakeErr;
1544 bool fHandshakeOk = RunSocks5HandshakeProbe(handshakeErr);
1545 nFail += Check(fHandshakeOk, handshakeErr.empty() ?
1546 "completes SOCKS5 domain-name CONNECT handshake" :
1547 strprintf("completes SOCKS5 domain-name CONNECT handshake (%s)",
1548 handshakeErr.c_str()).c_str()) ? 0 : 1;
1549
1550 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
1551 nFail, nFail == 1 ? "" : "s");
1552 fflush(stdout);
1553 return nFail == 0 ? 0 : 1;
1554}
1555
1244int RunSelfTest(const std::string& name) 1556int RunSelfTest(const std::string& name)
1245{ 1557{
1246 AttachTerminal(); 1558 AttachTerminal();
@@ -1259,8 +1571,14 @@ int RunSelfTest(const std::string& name)
1259 return RunNetMessageSelfTest(); 1571 return RunNetMessageSelfTest();
1260 if (name == "consensus-limits") 1572 if (name == "consensus-limits")
1261 return RunConsensusLimitsSelfTest(); 1573 return RunConsensusLimitsSelfTest();
1574 if (name == "pool-stratum")
1575 return RunPoolStratumSelfTest();
1576 if (name == "parse-money")
1577 return RunParseMoneySelfTest();
1578 if (name == "socks5-proxy")
1579 return RunSocks5ProxySelfTest();
1262 1580
1263 printf("Unknown self-test '%s'\n", name.c_str()); 1581 printf("Unknown self-test '%s'\n", name.c_str());
1264 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, net-message, consensus-limits\n"); 1582 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy\n");
1265 return 1; 1583 return 1;
1266} 1584}