Add smsgbroadcast RPC command for messaging all known peers

New command: smsgbroadcast <addrFrom> <message>
Iterates all public keys in smsgDB and sends an encrypted message to each.
Skips the sender's own address. Reports sent/failed counts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 17:03:51 -07:00
parent ae5e97ad6e
commit 50713cc87b
4 changed files with 101 additions and 4 deletions
+9 -3
View File
@@ -47,7 +47,7 @@ public:
void refreshMessageTable()
{
cachedMessageTable.clear();
if (parent->getWalletModel()->getEncryptionStatus() == WalletModel::Locked)
{
// -- messages are stored encrypted, can't load them without the private keys
@@ -84,7 +84,7 @@ public:
sent_datetime .setTime_t(msg.timestamp);
received_datetime.setTime_t(smsgStored.timeReceived);
memcpy(&vchKey[0], chKey, 18);
addMessageEntry(MessageTableEntry(vchKey,
@@ -112,7 +112,7 @@ public:
sent_datetime .setTime_t(msg.timestamp);
received_datetime.setTime_t(smsgStored.timeReceived);
memcpy(&vchKey[0], chKey, 18);
addMessageEntry(MessageTableEntry(vchKey,
@@ -124,10 +124,16 @@ public:
received_datetime,
(char*)&msg.vchMessage[0]),
true);
} else
{
printf("refreshMessageTable: outbox decrypt failed (err %d) for addr %s\n", decryptResult, smsgStored.sAddrOutbox.c_str());
}
};
delete it;
printf("refreshMessageTable: inbox %u found, %u decrypted; outbox %u found, %u decrypted\n",
nInbox, nInboxDecrypted, nOutbox, nOutboxDecrypted);
}
}
+90 -1
View File
@@ -889,7 +889,96 @@ Value smsgbuckets(const Array& params, bool fHelp)
result.push_back(Pair("result", "Unknown Mode."));
result.push_back(Pair("expected", "[stats|dump]."));
};
return result;
};
Value smsgbroadcast(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 2)
throw runtime_error(
"smsgbroadcast <addrFrom> <message>\n"
"Send an encrypted message to all known public keys in the smsgDB.\n"
"Returns the number of recipients and any failures.");
if (!fSecMsgEnabled)
throw runtime_error("Secure messaging is disabled.");
if (pwalletMain->IsLocked())
throw runtime_error("Wallet is locked.");
std::string addrFrom = params[0].get_str();
std::string msg = params[1].get_str();
// Validate sender address
CTrianglesAddress coinAddrFrom(addrFrom);
if (!coinAddrFrom.IsValid())
throw runtime_error("Invalid from address.");
Object result;
uint32_t nSent = 0;
uint32_t nFailed = 0;
Array failures;
{
LOCK(cs_smsgDB);
SecMsgDB dbPub;
if (!dbPub.Open("r"))
throw runtime_error("Could not open smsgDB.");
// Iterate all "pk" entries
std::string sPrefix("pk");
leveldb::Iterator* it = dbPub.pdb->NewIterator(leveldb::ReadOptions());
for (it->Seek(sPrefix); it->Valid(); it->Next())
{
std::string key = it->key().ToString();
if (key.size() < 2 || key[0] != 'p' || key[1] != 'k')
break;
// Deserialize the CKeyID from the key (after "pk" prefix)
CDataStream ssKey(key.data(), key.data() + key.size(), SER_DISK, CLIENT_VERSION);
char prefix[2];
ssKey >> prefix[0];
ssKey >> prefix[1];
CKeyID ckidTo;
ssKey >> ckidTo;
// Convert CKeyID to address string
CTrianglesAddress addrTo(ckidTo);
if (!addrTo.IsValid())
continue;
std::string sAddrTo = addrTo.ToString();
// Skip sending to self
if (sAddrTo == addrFrom)
continue;
std::string sError;
if (SecureMsgSend(addrFrom, sAddrTo, msg, sError) != 0)
{
nFailed++;
Object objFail;
objFail.push_back(Pair("address", sAddrTo));
objFail.push_back(Pair("error", sError));
failures.push_back(objFail);
} else
{
nSent++;
}
}
delete it;
}
char cbuf[256];
snprintf(cbuf, sizeof(cbuf), "Broadcast sent to %u recipients, %u failed.", nSent, nFailed);
result.push_back(Pair("result", std::string(cbuf)));
result.push_back(Pair("sent", (int)nSent));
result.push_back(Pair("failed", (int)nFailed));
if (nFailed > 0)
result.push_back(Pair("failures", failures));
return result;
};
+1
View File
@@ -336,6 +336,7 @@ static const CRPCCommand vRPCCommands[] =
{ "smsginbox", &smsginbox, false, false},
{ "smsgoutbox", &smsgoutbox, false, false},
{ "smsgbuckets", &smsgbuckets, false, false},
{ "smsgbroadcast", &smsgbroadcast, false, false},
+1
View File
@@ -247,6 +247,7 @@ extern json_spirit::Value smsgsendanon(const json_spirit::Array& params, bool fH
extern json_spirit::Value smsginbox(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value smsgoutbox(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value smsgbuckets(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value smsgbroadcast(const json_spirit::Array& params, bool fHelp);
#endif