// Minimal Carrot coinbase derivation checker for p2pool (test utility) // Usage (built in p2pool-salvium/build after enabling tests manually if needed): // ./carrot_debug // Hardcoded wallet keys below (testnet SC1Touh... provided by user). #include "common.h" #include "salvium_carrot.h" #include "wallet.h" #include // User-provided testnet wallet (SC1TouhH...): static const char* SPEND_SEC_HEX = "3e7620c2f3ec905091cb6bc29bf842659fcfe810b459b1dc8ab53382a9718401"; static const char* VIEW_SEC_HEX = "d1f69ec092ea9b24d62cd84c2abefd29ac08e8e1725297bdecb71c79b0b01105"; using namespace p2pool; using namespace p2pool::salvium_carrot; static bool parse_hex(const std::string& hex, std::vector& out) { out.clear(); out.reserve(hex.size() / 2); auto cvt = [](char c) -> int { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; }; if (hex.size() % 2) return false; for (size_t i = 0; i < hex.size(); i += 2) { int hi = cvt(hex[i]); int lo = cvt(hex[i+1]); if (hi < 0 || lo < 0) return false; out.push_back(static_cast((hi << 4) | lo)); } return true; } int main(int argc, char** argv) { if (argc < 4) { std::cerr << "usage: " << argv[0] << " \n"; return 1; } uint64_t height = std::strtoull(argv[1], nullptr, 10); std::vector ko_hex, vt_hex; if (!parse_hex(argv[2], ko_hex) || ko_hex.size() != HASH_SIZE) { std::cerr << "bad ko hex\n"; return 1; } if (!parse_hex(argv[3], vt_hex)) { std::cerr << "bad vt hex\n"; return 1; } hash spend_sec{}, view_sec{}; { std::vector buf; if (!parse_hex(SPEND_SEC_HEX, buf) || buf.size() != HASH_SIZE) { std::cerr << "bad spend_sec\n"; return 1; } memcpy(spend_sec.h, buf.data(), HASH_SIZE); if (!parse_hex(VIEW_SEC_HEX, buf) || buf.size() != HASH_SIZE) { std::cerr << "bad view_sec\n"; return 1; } memcpy(view_sec.h, buf.data(), HASH_SIZE); } // Build a dummy share set with one miner share for this wallet Wallet w{}; w.assign(spend_sec, view_sec, NetworkType::Testnet, false); std::vector shares; shares.emplace_back(difficulty_type{1,0}, &w); // Rewards: use on-chain amount for comparison; we only need one output std::vector amounts; amounts.push_back(0); // placeholder; will set below std::vector enotes; amounts[0] = 0; // will be filled by caller; not used to match Ko/vt logic directly // Try deriving with the provided amount from Ko (not required to derive Ko) if (!build_coinbase_enotes(shares, amounts, height, enotes) || enotes.empty()) { std::cerr << "build_coinbase_enotes failed\n"; return 1; } const CoinbaseEnote& e = enotes.front(); std::cout << "derived Ko=" << e.onetime_address << "\n"; std::cout << "derived vt="; for (auto b : e.view_tag) printf("%02x", b); std::cout << "\n"; bool ko_match = (memcmp(e.onetime_address.h, ko_hex.data(), HASH_SIZE) == 0); bool vt_match = (vt_hex.size() == e.view_tag.size()) && (memcmp(vt_hex.data(), e.view_tag.data(), e.view_tag.size()) == 0); std::cout << "ko_match=" << ko_match << " vt_match=" << vt_match << "\n"; return (ko_match && vt_match) ? 0 : 1; }