Make deposit work

This commit is contained in:
Daniel 2026-06-19 19:49:29 +02:00
parent 4ab800bf4a
commit 9a8f8875b1
5 changed files with 88 additions and 9 deletions

View File

@ -17,6 +17,8 @@
#include <QUrl>
#include <QVariant>
#include <algorithm>
const QString BlockchainBackend::BLOCKCHAIN_MODULE_NAME =
QStringLiteral("liblogos_blockchain_module");
@ -57,6 +59,44 @@ static QString toDisplayMessage(const LogosResult& result)
return result.success ? result.value.toString() : toErrorMessage(result);
}
// Decode a base58 (Bitcoin alphabet) string to raw bytes. On an invalid
// character *ok is set to false and an empty array is returned.
static QByteArray decodeBase58(const QString& input, bool* ok)
{
static const QByteArray kAlphabet =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
const QByteArray s = input.trimmed().toLatin1();
QByteArray bytes; // little-endian while building, reversed at the end
bytes.append('\0');
for (const char c : s) {
const int value = kAlphabet.indexOf(c);
if (value < 0) {
if (ok) *ok = false;
return {};
}
int carry = value;
for (int j = 0; j < bytes.size(); ++j) {
carry += static_cast<unsigned char>(bytes[j]) * 58;
bytes[j] = static_cast<char>(carry & 0xff);
carry >>= 8;
}
while (carry > 0) {
bytes.append(static_cast<char>(carry & 0xff));
carry >>= 8;
}
}
// Each leading '1' maps to a leading zero byte.
for (int i = 0; i < s.size() && s[i] == '1'; ++i)
bytes.append('\0');
std::reverse(bytes.begin(), bytes.end());
if (ok) *ok = true;
return bytes;
}
BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent)
: BlockchainBackendSimpleSource(parent)
, m_logosAPI(logosAPI)
@ -341,13 +381,24 @@ QString BlockchainBackend::getNotes(QString walletAddressHex, QString optionalTi
}
QString BlockchainBackend::channelDepositWithNotes(
QString channelIdHex, QStringList inputNoteIdHexes, QString metadataHex,
QString channelIdHex, QStringList inputNoteIdHexes, QString metadataBase58,
QString changePublicKeyHex, QStringList fundingPublicKeyHexes,
QString maxTxFee, QString optionalTipHex)
{
if (!m_blockchainClient)
return QStringLiteral("Error: Module not initialized.");
// The metadata arrives base58-encoded; the module expects metadata_hex, so
// decode to bytes and hex-encode. Empty stays empty (metadata is optional).
QString metadataHex;
if (!metadataBase58.trimmed().isEmpty()) {
bool ok = false;
const QByteArray bytes = decodeBase58(metadataBase58, &ok);
if (!ok)
return QStringLiteral("Error: Invalid base58 metadata.");
metadataHex = QString::fromLatin1(bytes.toHex());
}
// 7 positional args exceed the variadic invokeRemoteMethod overloads
// (max 5), so pass them through the QVariantList form.
QVariantList args;

View File

@ -51,7 +51,7 @@ public slots:
QString getNotes(QString walletAddressHex, QString optionalTipHex) override;
QString channelDepositWithNotes(QString channelIdHex,
QStringList inputNoteIdHexes,
QString metadataHex,
QString metadataBase58,
QString changePublicKeyHex,
QStringList fundingPublicKeyHexes,
QString maxTxFee,

View File

@ -17,7 +17,7 @@ class BlockchainBackend
SLOT(QString claimLeaderRewards())
SLOT(int generateConfig(QString outputPath, QStringList initialPeers, int netPort, int blendPort, QString httpAddr, QString externalAddress, bool noPublicIpCheck, int deploymentMode, QString deploymentConfigPath, QString statePath))
SLOT(QString getNotes(QString walletAddressHex, QString optionalTipHex))
SLOT(QString channelDepositWithNotes(QString channelIdHex, QStringList inputNoteIdHexes, QString metadataHex, QString changePublicKeyHex, QStringList fundingPublicKeyHexes, QString maxTxFee, QString optionalTipHex))
SLOT(QString channelDepositWithNotes(QString channelIdHex, QStringList inputNoteIdHexes, QString metadataBase58, QString changePublicKeyHex, QStringList fundingPublicKeyHexes, QString maxTxFee, QString optionalTipHex))
SLOT(void clearLogs())
SLOT(void copyToClipboard(QString text))
}

View File

@ -298,11 +298,11 @@ Rectangle {
function(error) { channelDepositView.setNotesResult("Error: " + error) }
)
}
onSubmitRequested: function(channelIdHex, inputNoteIdHexes, metadataHex, changePublicKeyHex, fundingPublicKeyHexes, maxTxFee, optionalTipHex) {
onSubmitRequested: function(channelIdHex, inputNoteIdHexes, metadataBase58, changePublicKeyHex, fundingPublicKeyHexes, maxTxFee, optionalTipHex) {
if (!root.backend) return
logos.watch(
root.backend.channelDepositWithNotes(
channelIdHex, inputNoteIdHexes, metadataHex,
channelIdHex, inputNoteIdHexes, metadataBase58,
changePublicKeyHex, fundingPublicKeyHexes, maxTxFee, optionalTipHex),
function(result) { channelDepositView.setSubmitResult(result) },
function(error) { channelDepositView.setSubmitResult("Error: " + error) }

View File

@ -21,7 +21,7 @@ ColumnLayout {
property bool nodeRunning: false
signal getNotesRequested(string addressHex, string optionalTipHex)
signal submitRequested(string channelIdHex, var inputNoteIdHexes, string metadataHex, string changePublicKeyHex, var fundingPublicKeyHexes, string maxTxFee, string optionalTipHex)
signal submitRequested(string channelIdHex, var inputNoteIdHexes, string metadataBase58, string changePublicKeyHex, var fundingPublicKeyHexes, string maxTxFee, string optionalTipHex)
signal copyToClipboard(string text)
// --- Called by the parent after async backend calls return ---
@ -77,6 +77,18 @@ ColumnLayout {
.filter(function(s) { return s.length > 0 })
}
// --- Metadata (base58-encoded bytes) ---
// The actual base58 bytes decoding happens in the C++ backend; here we
// only check the input uses the base58 (Bitcoin) alphabet. Plain base58
// has no checksum, so a valid-alphabet string always decodes.
function metadataIsValid() {
var s = metadataField.text.trim()
if (s === "")
return true // optional
return /^[1-9A-HJ-NP-Za-km-z]+$/.test(s)
}
function canAdvance() {
switch (step) {
case 0:
@ -86,6 +98,7 @@ ColumnLayout {
&& changeKeyField.text.trim().length > 0
&& fundingKeyList().length > 0
&& maxFeeField.text.trim().length > 0
&& metadataIsValid()
default:
return true
}
@ -151,7 +164,7 @@ ColumnLayout {
{ k: qsTr("Change public key"), v: changeKeyField.text.trim() },
{ k: qsTr("Funding public keys"), v: fundingKeyList().join("\n") },
{ k: qsTr("Max tx fee"), v: maxFeeField.text.trim() },
{ k: qsTr("Metadata hex"), v: metadataField.text.trim() || qsTr("(none)") },
{ k: qsTr("Metadata (base58)"), v: metadataField.text.trim() || qsTr("(none)") },
{ k: qsTr("Optional tip hex"), v: tipField.text.trim() || qsTr("(current tip)") }
]
}
@ -326,13 +339,28 @@ ColumnLayout {
}
LogosText {
text: qsTr("Metadata hex (optional)")
text: qsTr("Metadata (base58, optional)")
font.pixelSize: Theme.typography.secondaryText
}
LogosTextField {
id: metadataField
Layout.fillWidth: true
placeholderText: qsTr("Metadata hex")
placeholderText: qsTr("Base58-encoded metadata bytes")
}
LogosText {
Layout.fillWidth: true
text: qsTr("Input must be base58-encoded; it is decoded to bytes before submission.")
color: Theme.palette.textSecondary
font.pixelSize: Theme.typography.secondaryText
wrapMode: Text.WordWrap
}
LogosText {
Layout.fillWidth: true
visible: metadataField.text.trim() !== "" && !d.metadataIsValid()
text: qsTr("Invalid base58 input")
color: Theme.palette.error
font.pixelSize: Theme.typography.secondaryText
wrapMode: Text.WordWrap
}
LogosText {