LCW-1144 cupcake litecoin (#2557)

* Add psbt rpcs

* Only build arm64

* Rebuild protos

* Update proto

* Add psbt create rpc

* Restore wallet stuff

* more stuff

* more stuff

* more stuff

* Fix open wallet

* Fix mweb enabled

* Start of buildPsbt

* Update proto

* Psbt add recipients

* fixes

* slightly better

* more fixes

* decode ur

* add more commit code

* Update proto

* use new proto

* Don't eat psbt rpc errors

* Use unix domain socket for mwebd

* add gitignore

* Undo build only arm64

* fix pure mweb txns

* fix mweb txids

* fix ci

* fix blank pegin address

* chore: update cw_mweb dependencies
chore: remove cw_core as dependency from cw_mweb

* fix: Ltub restore

* fix: address review crashes, fix non-mweb view only wallets

* minor fixes

---------

Co-authored-by: Hector Chu <hectorchu@gmail.com>
Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
cyan
2025-11-24 17:58:12 +01:00
committed by GitHub
co-authored by Hector Chu OmarHatem
parent f0524bcef1
commit 77812016ac
26 changed files with 1875 additions and 115 deletions
+9 -2
View File
@@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:mobx/mobx.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
@@ -65,8 +66,14 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
required super.type,
String? scriptHash,
required super.network,
}) : scriptHash = scriptHash ??
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network) : null);
}) {
try {
this.scriptHash = scriptHash ??
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null);
} catch (e) {
printV(e);
}
}
factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) {
final decoded = json.decode(jsonSource) as Map;
@@ -70,6 +70,21 @@ class BitcoinWalletFromKeysCredentials extends WalletCredentials {
final String xpub;
}
class LitecoinWalletFromKeysCredentials extends WalletCredentials {
LitecoinWalletFromKeysCredentials({
required String name,
required String password,
required this.xpub,
required this.scanSecret,
required this.spendPubkey,
WalletInfo? walletInfo,
}) : super(name: name, password: password, walletInfo: walletInfo);
final String xpub;
final String scanSecret;
final String spendPubkey;
}
class BitcoinRestoreWalletFromHardware extends WalletCredentials {
BitcoinRestoreWalletFromHardware({
required String name,
+2 -2
View File
@@ -165,7 +165,7 @@ class BitcoinWalletService extends WalletService<
derivationInfo.derivationPath =
credentials.hwAccountData.derivationPath;
final xpub = convertZpubToXpub(credentials.hwAccountData.xpub!);
final xpub = convertAnyToXpub(credentials.hwAccountData.xpub!);
await credentials.walletInfo!.save();
final wallet = await BitcoinWallet(
@@ -189,7 +189,7 @@ class BitcoinWalletService extends WalletService<
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
final xpub = convertZpubToXpub(credentials.xpub);
final xpub = convertAnyToXpub(credentials.xpub);
final wallet = await BitcoinWallet(
password: credentials.password!,
+216 -55
View File
@@ -1,7 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:cw_bitcoin/address_from_output.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/mweb_utxo.dart';
import 'package:cw_core/unspent_coin_type.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/node.dart';
import 'package:cw_mweb/mwebd.pbgrpc.dart';
import 'package:fixnum/fixnum.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:bitcoin_base/src/crypto/keypair/sign_utils.dart';
@@ -51,6 +61,9 @@ import 'package:mobx/mobx.dart';
import 'package:pointycastle/ecc/api.dart';
import 'package:pointycastle/ecc/curves/secp256k1.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:ur/cbor_lite.dart';
import 'package:ur/ur.dart';
import 'package:ur/ur_decoder.dart';
part 'litecoin_wallet.g.dart';
@@ -66,6 +79,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
Uint8List? seedBytes,
String? mnemonic,
String? xpub,
this.scanSecretOverride,
this.spendPubkeyOverride,
String? passphrase,
String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses,
@@ -95,6 +110,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
mwebHd =
Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/1000'") as Bip32Slip10Secp256k1;
mwebEnabled = alwaysScan ?? false;
} else if (scanSecretOverride != null && spendPubkeyOverride != null) {
mwebHd = null;
mwebEnabled = alwaysScan ?? false;
} else {
mwebHd = null;
mwebEnabled = false;
@@ -110,6 +128,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
network: network,
mwebHd: mwebHd,
mwebEnabled: mwebEnabled,
scanSecretOverride: scanSecretOverride,
spendPubkeyOverride: spendPubkeyOverride,
isHardwareWallet: walletInfo.isHardwareWallet,
);
autorun((_) {
@@ -162,9 +182,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
@override
bool get hasRescan => true;
List<int> get scanSecret => mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
List<int> get spendSecret => mwebHd!.childKey(Bip32KeyIndex(0x80000001)).privateKey.privKey.raw;
final String? scanSecretOverride;
final String? spendPubkeyOverride;
List<int> get scanSecret => scanSecretOverride != null
? hex.decode(scanSecretOverride!)
: mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
List<int> get spendSecret =>
mwebHd?.childKey(Bip32KeyIndex(0x80000001)).privateKey.privKey.raw ?? List.filled(32, 0);
static Future<LitecoinWallet> create(
{required String mnemonic,
@@ -212,6 +238,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
);
}
@override
WalletKeysData get walletKeysData =>
WalletKeysData(mnemonic: seed, xPub: xpub, passphrase: passphrase, scanSecret: scanSecretOverride, spendPubkey: spendPubkeyOverride);
static Future<LitecoinWallet> open({
required String name,
required WalletInfo walletInfo,
@@ -277,6 +307,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
return LitecoinWallet(
mnemonic: keysData.mnemonic,
xpub: keysData.xPub,
scanSecretOverride: keysData.scanSecret,
spendPubkeyOverride: keysData.spendPubkey,
password: password,
walletInfo: walletInfo,
derivationInfo: derivationInfo,
@@ -1064,6 +1096,47 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
return fee.toInt() + feeIncrease;
}
Future<Uint8List> buildPsbt(PendingBitcoinTransaction transaction, bool isMweb) async {
final List<TxInput> inputs = [];
final List<TxOut> txouts = [];
for (final utxo in transaction.utxos) {
if (utxo.utxo.scriptType != SegwitAddresType.mweb) {
inputs.add(utxo.utxo.toInput());
txouts.add(TxOut(value: Int64(utxo.utxo.value.toInt()),
pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes()));
}
}
var resp = await CwMweb.psbtCreate(PsbtCreateRequest(
rawTx: inputs.isEmpty ? null : BtcTransaction(
inputs: inputs,
outputs: isMweb ? [] : transaction.outputs,
).toBytes(),
witnessUtxo: txouts,
));
for (final utxo in transaction.utxos) {
if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
resp = await CwMweb.psbtAddInput(PsbtAddInputRequest(
psbtB64: resp.psbtB64,
scanSecret: scanSecret,
outputId: utxo.utxo.txHash,
addressIndex: utxo.utxo.vout,
));
}
}
if (isMweb) for (final output in transaction.outputs) {
var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet);
if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) {
address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes());
}
resp = await CwMweb.psbtAddRecipient(PsbtAddRecipientRequest(
psbtB64: resp.psbtB64,
recipient: PsbtRecipient(address: address, value: Int64(output.amount.toInt())),
feeRatePerKb: Int64.parseInt(transaction.feeRate) * 1000,
));
}
return base64.decode(resp.psbtB64);
}
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
try {
@@ -1074,18 +1147,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
.getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
.address;
if (tx.shouldCommitUR()) {
tx.unsignedPsbt = await buildPsbt(tx, false);
}
return tx;
}
await waitForMwebAddresses();
final resp = await CwMweb.create(CreateRequest(
rawTx: hex.decode(tx.hex),
scanSecret: scanSecret,
spendSecret: spendSecret,
feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000,
));
final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx));
// check if the transaction doesn't contain any mweb inputs or outputs:
final transactionCredentials = credentials as BitcoinTransactionCredentials;
@@ -1117,6 +1185,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
for (final utxo in tx.utxos) {
if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
hasMwebInput = true;
} else {
// check if any of the inputs of this transaction are hog-ex:
// this list is only non-mweb inputs:
bool isHogEx = true;
final coin = unspentCoins
.firstWhere((coin) => coin.hash == utxo.utxo.txHash && coin.vout == utxo.utxo.vout);
// TODO: detect actual hog-ex inputs
if (!isHogEx) {
continue;
}
int confirmations = coin.confirmations ?? 0;
if (confirmations < 6) {
throw Exception(
"A transaction input has less than 6 confirmations, please try again later.");
}
}
}
@@ -1132,29 +1220,24 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
.address;
if (isRegular) {
tx.isMweb = false;
if (tx.shouldCommitUR()) {
tx.unsignedPsbt = await buildPsbt(tx, false);
}
return tx;
}
// check if any of the inputs of this transaction are hog-ex:
// this list is only non-mweb inputs:
tx2.inputs.forEach((txInput) {
bool isHogEx = true;
if (tx.shouldCommitUR()) {
tx.unsignedPsbt = await buildPsbt(tx, true);
return tx;
}
final utxo = unspentCoins
.firstWhere((utxo) => utxo.hash == txInput.txId && utxo.vout == txInput.txIndex);
// TODO: detect actual hog-ex inputs
if (!isHogEx) {
return;
}
int confirmations = utxo.confirmations ?? 0;
if (confirmations < 6) {
throw Exception(
"A transaction input has less than 6 confirmations, please try again later.");
}
});
final resp = await CwMweb.create(CreateRequest(
rawTx: hex.decode(tx.hex),
scanSecret: scanSecret,
spendSecret: spendSecret,
feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000,
));
final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx));
tx.hexOverride = tx2
.copyWith(
@@ -1177,31 +1260,109 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
.toHex();
tx.outputAddresses = resp.outputId;
return tx
..addListener((transaction) async {
final addresses = <String>{};
transaction.inputAddresses?.forEach((id) async {
final utxo = mwebUtxosBox.get(id);
// await mwebUtxosBox.delete(id); // gets deleted in checkMwebUtxosSpent
if (utxo == null) return;
// mark utxo as spent so we add it to the unconfirmed balance (as negative):
utxo.spent = true;
await mwebUtxosBox.put(id, utxo);
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
if (!addresses.contains(utxo.address)) {
addresses.add(utxo.address);
}
addressRecord.balance -= utxo.value.toInt();
});
transaction.inputAddresses?.addAll(addresses);
printV("isPegIn: $isPegIn, isPegOut: $isPegOut");
transaction.additionalInfo["isPegIn"] = isPegIn;
transaction.additionalInfo["isPegOut"] = isPegOut;
transactionHistory.addOne(transaction);
await updateUnspent();
await updateBalance();
});
addTransactionListener(tx, [], isPegIn, isPegOut);
return tx;
} catch (e, s) {
printV(e);
printV(s);
if (e.toString().contains("commit failed")) {
printV(e);
throw Exception("Transaction commit failed (no peers responded), please try again.");
}
rethrow;
}
}
void addTransactionListener(PendingBitcoinTransaction tx,
List<String> inputAddresses, bool isPegIn, bool isPegOut) {
tx.addListener((transaction) async {
final addresses = <String>{};
transaction.inputAddresses?.addAll(inputAddresses);
transaction.inputAddresses?.forEach((id) async {
final utxo = mwebUtxosBox.get(id);
// await mwebUtxosBox.delete(id); // gets deleted in checkMwebUtxosSpent
if (utxo == null) return;
// mark utxo as spent so we add it to the unconfirmed balance (as negative):
utxo.spent = true;
await mwebUtxosBox.put(id, utxo);
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);
if (!addresses.contains(utxo.address)) {
addresses.add(utxo.address);
}
addressRecord.balance -= utxo.value.toInt();
});
transaction.inputAddresses?.addAll(addresses);
printV("isPegIn: $isPegIn, isPegOut: $isPegOut");
transaction.additionalInfo["isPegIn"] = isPegIn;
transaction.additionalInfo["isPegOut"] = isPegOut;
transactionHistory.addOne(transaction);
await updateUnspent();
await updateBalance();
});
}
Future<void> commitPsbtUR(List<String> urCodes) async {
if (urCodes.isEmpty) throw Exception("No QR code got scanned");
bool isUr = urCodes.any((str) {
return str.startsWith("ur:psbt/");
});
if (!isUr) return;
final ur = URDecoder();
for (final inp in urCodes) {
ur.receivePart(inp);
}
final result = ur.result as UR;
final psbtB64 = base64Encode(CBORDecoder(result.cbor).decodeBytes().$1);
final resp = await CwMweb.psbtGetRecipients(PsbtGetRecipientsRequest(psbtB64: psbtB64));
bool hasMwebInput = false;
bool hasMwebOutput = false;
bool hasRegularOutput = false;
for (final recipient in resp.recipient) {
if (recipient.address.contains("mweb")) {
hasMwebOutput = true;
} else {
hasRegularOutput = true;
}
}
for (final address in resp.inputAddress) {
try {
LitecoinAddress(address);
} catch (_) {
hasMwebInput = true;
}
}
bool isPegIn = !hasMwebInput && hasMwebOutput;
bool isPegOut = hasMwebInput && hasRegularOutput;
bool isRegular = !hasMwebInput && !hasMwebOutput;
final resp2 = await CwMweb.psbtExtract(PsbtExtractRequest(psbtB64: psbtB64));
final btcTx = BtcTransaction.fromRaw(hex.encode(resp2.rawTx));
final tx = PendingBitcoinTransaction(
btcTx,
type,
electrumClient: electrumClient,
amount: btcTx.outputs.reduce((a, b) => TxOutput(amount: a.amount + b.amount, scriptPubKey: a.scriptPubKey)).amount.toInt(),
fee: resp.fee.toInt(),
feeRate: "",
network: network,
hasChange: resp.recipient.length > 1,
isMweb: !isRegular,
isViewOnly: false,
);
tx.outputAddresses = resp2.outputId;
addTransactionListener(tx, resp.inputAddress, isPegIn, isPegOut);
try {
await tx.commit();
} catch (e, s) {
printV(e);
printV(s);
+19 -4
View File
@@ -28,6 +28,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required super.isHardwareWallet,
required this.mwebHd,
required this.mwebEnabled,
required this.scanSecretOverride,
required this.spendPubkeyOverride,
super.initialAddresses,
super.initialMwebAddresses,
super.initialRegularAddressIndex,
@@ -45,9 +47,15 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
List<String> mwebAddrs = [];
bool generating = false;
List<int> get scanSecret => mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
List<int> get spendPubkey =>
mwebHd!.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed;
final String? scanSecretOverride;
final String? spendPubkeyOverride;
List<int> get scanSecret => scanSecretOverride != null
? hex.decode(scanSecretOverride!)
: mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
List<int> get spendPubkey => spendPubkeyOverride != null
? hex.decode(spendPubkeyOverride!)
: mwebHd!.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed;
@override
Future<void> init() async {
@@ -65,6 +73,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
return null;
}
if ((scanSecret.length < 1 || scanSecret.reduce((a, b) => a + b) == 0) ||
(spendPubkey.length < 1 || spendPubkey.reduce((a, b) => a + b) == 0)) {
return null;
}
Uint8List scan = Uint8List.fromList(scanSecret);
Uint8List spend = Uint8List.fromList(spendPubkey);
@@ -95,7 +107,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
// ensure mweb addresses are up to date:
// This is the Case if the Litecoin Wallet is a hardware Wallet
if (mwebHd == null) return;
if (mwebHd == null && scanSecretOverride == null) return;
if (mwebAddresses.length < mwebAddrs.length) {
List<BitcoinAddressRecord> addressRecords = mwebAddrs
@@ -127,6 +139,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
BitcoinAddressType? addressType,
}) {
if (addressType == SegwitAddresType.mweb) {
if (mwebAddrs.length == 0) {
return "null";
}
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
}
return generateP2WPKHAddress(hd: hd, index: index, network: network);
+21 -4
View File
@@ -20,7 +20,7 @@ import 'package:path_provider/path_provider.dart';
class LitecoinWalletService extends WalletService<
BitcoinNewWalletCredentials,
BitcoinRestoreWalletFromSeedCredentials,
BitcoinRestoreWalletFromWIFCredentials,
LitecoinWalletFromKeysCredentials,
BitcoinRestoreWalletFromHardware> {
LitecoinWalletService(this.unspentCoinsInfoSource, this.isDirect);
@@ -186,9 +186,26 @@ class LitecoinWalletService extends WalletService<
}
@override
Future<LitecoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
{bool? isTestnet}) async =>
throw UnimplementedError();
Future<LitecoinWallet> restoreFromKeys(LitecoinWalletFromKeysCredentials credentials,
{bool? isTestnet}) async {
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
credentials.walletInfo?.network = network.value;
final wallet = await LitecoinWallet(
password: credentials.password!,
xpub: credentials.xpub,
scanSecretOverride: credentials.scanSecret,
spendPubkeyOverride: credentials.spendPubkey,
walletInfo: credentials.walletInfo!,
derivationInfo: await credentials.walletInfo!.getDerivationInfo(),
unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
);
await wallet.save();
await wallet.init();
return wallet;
}
@override
Future<LitecoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
+39
View File
@@ -3,6 +3,15 @@ import 'dart:typed_data';
import 'package:blockchain_utils/blockchain_utils.dart';
String convertAnyToXpub(String any) {
if (any.toLowerCase().startsWith("zpub")) {
return convertZpubToXpub(any);
} else if (any.toLowerCase().startsWith("ltub")) {
return convertLtubToXpub(any);
}
return any;
}
String convertZpubToXpub(String zpub) {
try {
final decoded = Base58Decoder.checkDecode(zpub);
@@ -37,6 +46,36 @@ String convertZpubToXpub(String zpub) {
}
}
String convertLtubToXpub(String ltub) {
try {
final decoded = Base58Decoder.checkDecode(ltub);
if (decoded.length < 4) {
throw ArgumentError('Invalid extended public key length');
}
final versionBytes = decoded.sublist(0, 4);
final ltubVersionBytes = [0x01, 0x9D, 0xA4, 0x62]; // Ltub mainnet version
bool isLtub = listEquals(versionBytes, ltubVersionBytes);
if (!isLtub) {
return ltub;
}
final xpubVersionBytes = [0x04, 0x88, 0xb2, 0x1e];
final newExtendedKey = Uint8List.fromList([
...xpubVersionBytes,
...decoded.sublist(4),
]);
return Base58Encoder.checkEncode(newExtendedKey);
} catch (e) {
throw ArgumentError('Failed to convert ltub to xpub: $e');
}
}
bool listEquals<T>(List<T> a, List<T> b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; i++) {
+8 -2
View File
@@ -98,15 +98,19 @@ class WalletKeysData {
final String? altMnemonic;
final String? passphrase;
final String? xPub;
final String? scanSecret;
final String? spendPubkey;
WalletKeysData({this.privateKey, this.mnemonic, this.altMnemonic, this.passphrase, this.xPub});
WalletKeysData({this.privateKey, this.mnemonic, this.altMnemonic, this.passphrase, this.xPub, this.scanSecret, this.spendPubkey});
String toJSON() => jsonEncode({
"privateKey": privateKey,
"mnemonic": mnemonic,
if (altMnemonic != null) "altMnemonic": altMnemonic,
if (passphrase != null) "passphrase": passphrase,
if (xPub != null) "xPub": xPub
if (xPub != null) "xPub": xPub,
if (scanSecret != null) "scanSecret": scanSecret,
if (spendPubkey != null) "spendPubkey": spendPubkey,
});
static WalletKeysData fromJSON(Map<String, dynamic> json) => WalletKeysData(
@@ -115,5 +119,7 @@ class WalletKeysData {
altMnemonic: json["altMnemonic"] as String?,
passphrase: json["passphrase"] as String?,
xPub: json["xPub"] as String?,
scanSecret: json["scanSecret"] as String?,
spendPubkey: json["spendPubkey"] as String?,
);
}
+1
View File
@@ -28,3 +28,4 @@ migrate_working_dir/
.dart_tool/
.packages
build/
jniLibs/
+13 -10
View File
@@ -1,6 +1,10 @@
module github.com/cake-tech/cake_wallet/cw_mweb/go
go 1.24.2
go 1.24.1
require github.com/ltcmweb/mwebd v0.1.16
require github.com/ltcmweb/mwebd v0.1.16
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -23,10 +27,9 @@ require (
github.com/kkdai/bstream v1.0.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/ltcmweb/coinswapd v0.1.0 // indirect
github.com/ltcmweb/ltcd v0.25.6 // indirect
github.com/ltcmweb/ltcd v0.25.11 // indirect
github.com/ltcmweb/ltcd/btcec/v2 v2.3.3 // indirect
github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3 // indirect
github.com/ltcmweb/mwebd v0.1.8 // indirect
github.com/ltcmweb/neutrino v0.17.4 // indirect
github.com/ltcmweb/neutrino/cache v1.1.0 // indirect
github.com/ltcmweb/secp256k1 v0.1.1 // indirect
@@ -37,14 +40,14 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
go.etcd.io/bbolt v1.3.10 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.34.2 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
)
+92 -18
View File
@@ -6,12 +6,22 @@ github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alitto/pond/v2 v2.2.0 h1:hX3B1Lu4b5PjSHR+IWNRDKD0Jfw2ew8V25J7Vu5j7RM=
github.com/alitto/pond/v2 v2.2.0/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE=
github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88=
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
@@ -22,11 +32,25 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnN
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY=
github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8=
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ1VTIig=
github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
@@ -39,14 +63,14 @@ github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuV
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/ltcmweb/coinswapd v0.1.0 h1:M6eoz4g7fDpsQeNA8wWbBh6PY1xykrihyLU44SgljeI=
github.com/ltcmweb/coinswapd v0.1.0/go.mod h1:CY5cVSympaLrMO8fHWXO+e/iuPLs2L0U9daMCRTstEM=
github.com/ltcmweb/ltcd v0.25.6 h1:96aC38ZEyNVztylrBBqYnVZJ6cuF7IUvL2knhbMxs3Y=
github.com/ltcmweb/ltcd v0.25.6/go.mod h1:jQbvPfnT4bBXJKRwU5SdD9ZUopwmSvhngdjeopv2cIU=
github.com/ltcmweb/ltcd v0.25.11 h1:MI0n/9I0P2Ig+myofegrXXEvh00lY3TUqPk1akYHBxA=
github.com/ltcmweb/ltcd v0.25.11/go.mod h1:jQbvPfnT4bBXJKRwU5SdD9ZUopwmSvhngdjeopv2cIU=
github.com/ltcmweb/ltcd/btcec/v2 v2.3.3 h1:gJc1ljDPCBtwBKFcC4SW44BFoJqoZzkeagndMYdqKKE=
github.com/ltcmweb/ltcd/btcec/v2 v2.3.3/go.mod h1:NRr2WtpiSiSO29TkdZhbGNRA/Q16DV2eNbORf+M2ykI=
github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3 h1:CAPyzHI3bCFRrrVHZkDUR2i3Awj6l/aqAZIEi0E/nfM=
github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3/go.mod h1:zB+HhI2IbIwTGpdAhdzpm1GVX4ShcWTgN42+ar9HXrg=
github.com/ltcmweb/mwebd v0.1.8 h1:5TJgrM76H3s6kWUJ6CsU1L+DaafcAZ1edDlkyJ+GWt0=
github.com/ltcmweb/mwebd v0.1.8/go.mod h1:KPf+lXEvEMuG0NaHume0imWMb9Cvr15SNPujZO6EAv4=
github.com/ltcmweb/mwebd v0.1.16 h1:DaOIp/5TDhcDUxjvnWKHXkr6hF4LHe2bbY8I0zo7m9w=
github.com/ltcmweb/mwebd v0.1.16/go.mod h1:GEhTe4RTG4ImmzsyqCzO2NKK11U2Zgb5+leQnwY+u+4=
github.com/ltcmweb/neutrino v0.17.4 h1:ZsguBvicTM5CtZDsKg+OBFuPUCKjfEJ3lRSGXPjF2oM=
github.com/ltcmweb/neutrino v0.17.4/go.mod h1:KktZ+McIwHuTHe0K8JK9Wv4qgv8orkA21a0ZOU4SwJ4=
github.com/ltcmweb/neutrino/cache v1.1.0 h1:C0Qn2p8ogcskRPfrBKPUayjn3m/CUJqI7otopNScvhk=
@@ -58,10 +82,38 @@ github.com/ltcsuite/lnd/queue v1.1.0/go.mod h1:DJrxK2gPC2FjJAVYxPOcnY2CplI3rhL2P
github.com/ltcsuite/lnd/ticker v1.0.1/go.mod h1:WZKpekfDVAVv7Gsrr0GAWC/U1XURfGesFg9sQYJbeL4=
github.com/ltcsuite/lnd/ticker v1.1.0 h1:3zYM/JlKNqq+DotW8jiEdHVeY5Yl7n7cUb2bTof1yXM=
github.com/ltcsuite/lnd/ticker v1.1.0/go.mod h1:K2qQ3EPe8enztYvn/VAbLafCPy13XHMuCRPsySoAbt8=
github.com/ltcsuite/ltcd v0.22.0-beta h1:jYVHOeg2oBDvsduV26LtCZnzwifMDWoVbkdHTjGvHTk=
github.com/ltcsuite/ltcd v0.22.0-beta/go.mod h1:/BXtm50r591uMfXf8XgSpL5er32HCvheJtBSPYK5bFM=
github.com/ltcsuite/ltcd/btcec/v2 v2.1.0 h1:0DMWBjQDb0V1+4kCLOJlNdHs7ewwYturuUfLHq8mosY=
github.com/ltcsuite/ltcd/btcec/v2 v2.1.0/go.mod h1:Vc9ZYXMcl5D6bA0VwMvGRDJYggO3YZ7/BuIri02Lq0E=
github.com/ltcsuite/ltcd/ltcutil v1.1.0 h1:btwbdHO9cEr22zW/vgCLiF6ghh+IDngJdJsyhJ6mntU=
github.com/ltcsuite/ltcd/ltcutil v1.1.0/go.mod h1:VbZlcopVgQteiCC5KRjIuxXH5wi1CtzhsvoYZ3K7FaE=
github.com/ltcsuite/ltcwallet v0.13.1 h1:XMyrDHn0BmgUgkNbR/Lzg36vjRsup3xdiPLSD471UMg=
github.com/ltcsuite/ltcwallet v0.13.1/go.mod h1:e6pIWRM9gsd5JnMsI9SgCJM0wi7awWdr20F1C1KUPiw=
github.com/ltcsuite/ltcwallet/wallet/txauthor v1.1.0 h1:MaSgMq7LCB+6dVm9oLzNCVp0lmY1WKmevmMK/t2c6To=
github.com/ltcsuite/ltcwallet/wallet/txauthor v1.1.0/go.mod h1:I53YELeELfA+dVporL+t44O8ArpuF8AjdJbbWIQaE2Q=
github.com/ltcsuite/ltcwallet/wallet/txrules v1.2.0 h1:P6H9zsMpBBuGOsp9lnil7XfPaPujDqrbcmkqvDdiSiI=
github.com/ltcsuite/ltcwallet/wallet/txrules v1.2.0/go.mod h1:lmA2Ozxvbr2M8Mqb6ugOv5/FQT6x2Qnwg3yT/NiWEks=
github.com/ltcsuite/ltcwallet/wallet/txsizes v1.1.0 h1:W884jMwG3K3Hu8FEMnV7KX1bd4HQd/4yvaevisFo9s8=
github.com/ltcsuite/ltcwallet/wallet/txsizes v1.1.0/go.mod h1:G9+XTWnE0xaXzHRzTuP+SOIXFPXFMfYF+w/wxPHb0K8=
github.com/ltcsuite/ltcwallet/walletdb v1.3.5 h1:WymVw0FBQ8KJgH7B88ujRqBOJ9R0en9K9urpJW4atAE=
github.com/ltcsuite/ltcwallet/walletdb v1.3.5/go.mod h1:29SBzxA55wNxY3ctFw6t5PgsULwf3NMwg2MiGQgtrJE=
github.com/ltcsuite/neutrino v0.13.2 h1:SDbRn4zt4e6z/4uVkVzVeKYpgXyoAh8ksA3m/uCIBaU=
github.com/ltcsuite/neutrino v0.13.2/go.mod h1:eTkaETZBeu3es/FisfjY8Cp3M2fC4s+2V2VUeS8O1Ic=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
@@ -69,29 +121,51 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f
go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 h1:umK/Ey0QEzurTNlsV3R+MfxHAb78HCEX/IkuR+zH4WQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
+5 -3
View File
@@ -10,7 +10,7 @@ import (
var server *mwebd.Server
//export StartServer
func StartServer(chain *C.char, dataDir *C.char, nodeUri *C.char) C.int {
func StartServer(chain *C.char, dataDir *C.char, nodeUri *C.char, errMsg **C.char) C.int {
if server != nil {
server.Stop()
}
@@ -21,15 +21,17 @@ func StartServer(chain *C.char, dataDir *C.char, nodeUri *C.char) C.int {
var err error
server, err = mwebd.NewServer(goChain, goDataDir, goNodeUri)
if err != nil {
*errMsg = C.CString(err.Error())
return 0
}
start, err := server.Start(0)
err = server.StartUnix(goDataDir + "/mwebd.sock")
if err != nil {
*errMsg = C.CString(err.Error())
return 0
}
return C.int(start)
return 1
}
//export StopServer
+44 -5
View File
@@ -4,8 +4,8 @@ import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_mweb/mweb_ffi.dart';
import 'package:cw_mweb/print_verbose.dart';
import 'package:grpc/grpc.dart';
import 'package:path_provider/path_provider.dart';
import 'mwebd.pbgrpc.dart';
@@ -64,10 +64,8 @@ class CwMweb {
}
printV("Attempting to connect to server on port: $_port");
// wait for the server to finish starting up before we try to connect to it:
await Future.delayed(const Duration(seconds: 8));
_clientChannel = ClientChannel('127.0.0.1', port: _port!, channelShutdownHandler: () {
final address = InternetAddress("${appDir.path}/mwebd.sock", type: InternetAddressType.unix);
_clientChannel = ClientChannel(address, channelShutdownHandler: () {
_rpcClient = null;
printV("Channel is shutting down!");
},
@@ -219,4 +217,45 @@ class CwMweb {
rethrow;
}
}
static Future<PsbtResponse> psbtCreate(PsbtCreateRequest request) async {
log("mweb.psbtCreate() called");
_rpcClient = await stub();
return await _rpcClient!.psbtCreate(request, options: CallOptions(timeout: TIMEOUT_DURATION));
}
static Future<PsbtResponse> psbtAddInput(PsbtAddInputRequest request) async {
log("mweb.psbtAddInput() called");
_rpcClient = await stub();
return await _rpcClient!.psbtAddInput(request, options: CallOptions(timeout: TIMEOUT_DURATION));
}
static Future<PsbtResponse> psbtAddRecipient(PsbtAddRecipientRequest request) async {
log("mweb.psbtAddRecipient() called");
_rpcClient = await stub();
return await _rpcClient!.psbtAddRecipient(request, options: CallOptions(timeout: TIMEOUT_DURATION));
}
static Future<PsbtGetRecipientsResponse> psbtGetRecipients(PsbtGetRecipientsRequest request) async {
log("mweb.psbtGetRecipients() called");
_rpcClient = await stub();
return await _rpcClient!.psbtGetRecipients(request, options: CallOptions(timeout: TIMEOUT_DURATION));
}
static Future<CreateResponse> psbtExtract(PsbtExtractRequest request) async {
log("mweb.psbtExtract() called");
_rpcClient = await stub();
return await _rpcClient!.psbtExtract(request, options: CallOptions(timeout: TIMEOUT_DURATION));
}
static Future<PsbtResponse> psbtSign(PsbtSignRequest request) async {
printV("mweb.psbtSign() called");
_rpcClient = await stub();
return await _rpcClient!.psbtSign(request, options: CallOptions(timeout: TIMEOUT_DURATION));
}
static Future<PsbtResponse> psbtSignNonMweb(PsbtSignNonMwebRequest request) async {
printV("mweb.psbtSignNonMweb() called");
_rpcClient = await stub();
return await _rpcClient!.psbtSignNonMweb(request, options: CallOptions(timeout: TIMEOUT_DURATION));
}
}
+9 -1
View File
@@ -3,6 +3,7 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:cw_mweb/generated_bindings.g.dart';
import 'package:cw_mweb/print_verbose.dart';
import 'package:ffi/ffi.dart';
String libPath = (() {
@@ -24,12 +25,19 @@ class MWebFfi {
final chain = "".toNativeUtf8().cast<Char>();
final dataDir_ = dataDir.toNativeUtf8().cast<Char>();
final nodeUri_ = nodeUri.toNativeUtf8().cast<Char>();
final errMsgPtr = calloc<Pointer<Char>>();
final port = lib.StartServer(chain, dataDir_, nodeUri_);
final port = lib.StartServer(chain, dataDir_, nodeUri_, errMsgPtr);
if (port == 0) {
final errMsg = errMsgPtr.value.cast<Utf8>().toDartString();
printV('Error starting server: $errMsg');
calloc.free(errMsgPtr.value);
}
calloc.free(chain);
calloc.free(dataDir_);
calloc.free(nodeUri_);
calloc.free(errMsgPtr);
return port;
}
+1019 -1
View File
File diff suppressed because it is too large Load Diff
+181 -1
View File
@@ -41,10 +41,46 @@ class RpcClient extends $grpc.Client {
'/Rpc/Create',
($0.CreateRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.CreateResponse.fromBuffer(value));
static final _$psbtCreate = $grpc.ClientMethod<$0.PsbtCreateRequest, $0.PsbtResponse>(
'/Rpc/PsbtCreate',
($0.PsbtCreateRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.PsbtResponse.fromBuffer(value));
static final _$psbtAddInput = $grpc.ClientMethod<$0.PsbtAddInputRequest, $0.PsbtResponse>(
'/Rpc/PsbtAddInput',
($0.PsbtAddInputRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.PsbtResponse.fromBuffer(value));
static final _$psbtAddRecipient = $grpc.ClientMethod<$0.PsbtAddRecipientRequest, $0.PsbtResponse>(
'/Rpc/PsbtAddRecipient',
($0.PsbtAddRecipientRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.PsbtResponse.fromBuffer(value));
static final _$psbtGetRecipients = $grpc.ClientMethod<$0.PsbtGetRecipientsRequest, $0.PsbtGetRecipientsResponse>(
'/Rpc/PsbtGetRecipients',
($0.PsbtGetRecipientsRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.PsbtGetRecipientsResponse.fromBuffer(value));
static final _$psbtSign = $grpc.ClientMethod<$0.PsbtSignRequest, $0.PsbtResponse>(
'/Rpc/PsbtSign',
($0.PsbtSignRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.PsbtResponse.fromBuffer(value));
static final _$psbtSignNonMweb = $grpc.ClientMethod<$0.PsbtSignNonMwebRequest, $0.PsbtResponse>(
'/Rpc/PsbtSignNonMweb',
($0.PsbtSignNonMwebRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.PsbtResponse.fromBuffer(value));
static final _$psbtExtract = $grpc.ClientMethod<$0.PsbtExtractRequest, $0.CreateResponse>(
'/Rpc/PsbtExtract',
($0.PsbtExtractRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.CreateResponse.fromBuffer(value));
static final _$ledgerExchange = $grpc.ClientMethod<$0.LedgerApdu, $0.LedgerApdu>(
'/Rpc/LedgerExchange',
($0.LedgerApdu value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.LedgerApdu.fromBuffer(value));
static final _$broadcast = $grpc.ClientMethod<$0.BroadcastRequest, $0.BroadcastResponse>(
'/Rpc/Broadcast',
($0.BroadcastRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.BroadcastResponse.fromBuffer(value));
static final _$coinswap = $grpc.ClientMethod<$0.CoinswapRequest, $0.CoinswapResponse>(
'/Rpc/Coinswap',
($0.CoinswapRequest value) => value.writeToBuffer(),
($core.List<$core.int> value) => $0.CoinswapResponse.fromBuffer(value));
RpcClient($grpc.ClientChannel channel,
{$grpc.CallOptions? options,
@@ -72,9 +108,45 @@ class RpcClient extends $grpc.Client {
return $createUnaryCall(_$create, request, options: options);
}
$grpc.ResponseFuture<$0.PsbtResponse> psbtCreate($0.PsbtCreateRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$psbtCreate, request, options: options);
}
$grpc.ResponseFuture<$0.PsbtResponse> psbtAddInput($0.PsbtAddInputRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$psbtAddInput, request, options: options);
}
$grpc.ResponseFuture<$0.PsbtResponse> psbtAddRecipient($0.PsbtAddRecipientRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$psbtAddRecipient, request, options: options);
}
$grpc.ResponseFuture<$0.PsbtGetRecipientsResponse> psbtGetRecipients($0.PsbtGetRecipientsRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$psbtGetRecipients, request, options: options);
}
$grpc.ResponseFuture<$0.PsbtResponse> psbtSign($0.PsbtSignRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$psbtSign, request, options: options);
}
$grpc.ResponseFuture<$0.PsbtResponse> psbtSignNonMweb($0.PsbtSignNonMwebRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$psbtSignNonMweb, request, options: options);
}
$grpc.ResponseFuture<$0.CreateResponse> psbtExtract($0.PsbtExtractRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$psbtExtract, request, options: options);
}
$grpc.ResponseFuture<$0.LedgerApdu> ledgerExchange($0.LedgerApdu request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$ledgerExchange, request, options: options);
}
$grpc.ResponseFuture<$0.BroadcastResponse> broadcast($0.BroadcastRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$broadcast, request, options: options);
}
$grpc.ResponseFuture<$0.CoinswapResponse> coinswap($0.CoinswapRequest request, {$grpc.CallOptions? options}) {
return $createUnaryCall(_$coinswap, request, options: options);
}
}
@$pb.GrpcServiceName('Rpc')
@@ -117,6 +189,62 @@ abstract class RpcServiceBase extends $grpc.Service {
false,
($core.List<$core.int> value) => $0.CreateRequest.fromBuffer(value),
($0.CreateResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.PsbtCreateRequest, $0.PsbtResponse>(
'PsbtCreate',
psbtCreate_Pre,
false,
false,
($core.List<$core.int> value) => $0.PsbtCreateRequest.fromBuffer(value),
($0.PsbtResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.PsbtAddInputRequest, $0.PsbtResponse>(
'PsbtAddInput',
psbtAddInput_Pre,
false,
false,
($core.List<$core.int> value) => $0.PsbtAddInputRequest.fromBuffer(value),
($0.PsbtResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.PsbtAddRecipientRequest, $0.PsbtResponse>(
'PsbtAddRecipient',
psbtAddRecipient_Pre,
false,
false,
($core.List<$core.int> value) => $0.PsbtAddRecipientRequest.fromBuffer(value),
($0.PsbtResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.PsbtGetRecipientsRequest, $0.PsbtGetRecipientsResponse>(
'PsbtGetRecipients',
psbtGetRecipients_Pre,
false,
false,
($core.List<$core.int> value) => $0.PsbtGetRecipientsRequest.fromBuffer(value),
($0.PsbtGetRecipientsResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.PsbtSignRequest, $0.PsbtResponse>(
'PsbtSign',
psbtSign_Pre,
false,
false,
($core.List<$core.int> value) => $0.PsbtSignRequest.fromBuffer(value),
($0.PsbtResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.PsbtSignNonMwebRequest, $0.PsbtResponse>(
'PsbtSignNonMweb',
psbtSignNonMweb_Pre,
false,
false,
($core.List<$core.int> value) => $0.PsbtSignNonMwebRequest.fromBuffer(value),
($0.PsbtResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.PsbtExtractRequest, $0.CreateResponse>(
'PsbtExtract',
psbtExtract_Pre,
false,
false,
($core.List<$core.int> value) => $0.PsbtExtractRequest.fromBuffer(value),
($0.CreateResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.LedgerApdu, $0.LedgerApdu>(
'LedgerExchange',
ledgerExchange_Pre,
false,
false,
($core.List<$core.int> value) => $0.LedgerApdu.fromBuffer(value),
($0.LedgerApdu value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.BroadcastRequest, $0.BroadcastResponse>(
'Broadcast',
broadcast_Pre,
@@ -124,6 +252,13 @@ abstract class RpcServiceBase extends $grpc.Service {
false,
($core.List<$core.int> value) => $0.BroadcastRequest.fromBuffer(value),
($0.BroadcastResponse value) => value.writeToBuffer()));
$addMethod($grpc.ServiceMethod<$0.CoinswapRequest, $0.CoinswapResponse>(
'Coinswap',
coinswap_Pre,
false,
false,
($core.List<$core.int> value) => $0.CoinswapRequest.fromBuffer(value),
($0.CoinswapResponse value) => value.writeToBuffer()));
}
$async.Future<$0.StatusResponse> status_Pre($grpc.ServiceCall call, $async.Future<$0.StatusRequest> request) async {
@@ -146,14 +281,59 @@ abstract class RpcServiceBase extends $grpc.Service {
return create(call, await request);
}
$async.Future<$0.PsbtResponse> psbtCreate_Pre($grpc.ServiceCall call, $async.Future<$0.PsbtCreateRequest> request) async {
return psbtCreate(call, await request);
}
$async.Future<$0.PsbtResponse> psbtAddInput_Pre($grpc.ServiceCall call, $async.Future<$0.PsbtAddInputRequest> request) async {
return psbtAddInput(call, await request);
}
$async.Future<$0.PsbtResponse> psbtAddRecipient_Pre($grpc.ServiceCall call, $async.Future<$0.PsbtAddRecipientRequest> request) async {
return psbtAddRecipient(call, await request);
}
$async.Future<$0.PsbtGetRecipientsResponse> psbtGetRecipients_Pre($grpc.ServiceCall call, $async.Future<$0.PsbtGetRecipientsRequest> request) async {
return psbtGetRecipients(call, await request);
}
$async.Future<$0.PsbtResponse> psbtSign_Pre($grpc.ServiceCall call, $async.Future<$0.PsbtSignRequest> request) async {
return psbtSign(call, await request);
}
$async.Future<$0.PsbtResponse> psbtSignNonMweb_Pre($grpc.ServiceCall call, $async.Future<$0.PsbtSignNonMwebRequest> request) async {
return psbtSignNonMweb(call, await request);
}
$async.Future<$0.CreateResponse> psbtExtract_Pre($grpc.ServiceCall call, $async.Future<$0.PsbtExtractRequest> request) async {
return psbtExtract(call, await request);
}
$async.Future<$0.LedgerApdu> ledgerExchange_Pre($grpc.ServiceCall call, $async.Future<$0.LedgerApdu> request) async {
return ledgerExchange(call, await request);
}
$async.Future<$0.BroadcastResponse> broadcast_Pre($grpc.ServiceCall call, $async.Future<$0.BroadcastRequest> request) async {
return broadcast(call, await request);
}
$async.Future<$0.CoinswapResponse> coinswap_Pre($grpc.ServiceCall call, $async.Future<$0.CoinswapRequest> request) async {
return coinswap(call, await request);
}
$async.Future<$0.StatusResponse> status($grpc.ServiceCall call, $0.StatusRequest request);
$async.Stream<$0.Utxo> utxos($grpc.ServiceCall call, $0.UtxosRequest request);
$async.Future<$0.AddressResponse> addresses($grpc.ServiceCall call, $0.AddressRequest request);
$async.Future<$0.SpentResponse> spent($grpc.ServiceCall call, $0.SpentRequest request);
$async.Future<$0.CreateResponse> create($grpc.ServiceCall call, $0.CreateRequest request);
$async.Future<$0.PsbtResponse> psbtCreate($grpc.ServiceCall call, $0.PsbtCreateRequest request);
$async.Future<$0.PsbtResponse> psbtAddInput($grpc.ServiceCall call, $0.PsbtAddInputRequest request);
$async.Future<$0.PsbtResponse> psbtAddRecipient($grpc.ServiceCall call, $0.PsbtAddRecipientRequest request);
$async.Future<$0.PsbtGetRecipientsResponse> psbtGetRecipients($grpc.ServiceCall call, $0.PsbtGetRecipientsRequest request);
$async.Future<$0.PsbtResponse> psbtSign($grpc.ServiceCall call, $0.PsbtSignRequest request);
$async.Future<$0.PsbtResponse> psbtSignNonMweb($grpc.ServiceCall call, $0.PsbtSignNonMwebRequest request);
$async.Future<$0.CreateResponse> psbtExtract($grpc.ServiceCall call, $0.PsbtExtractRequest request);
$async.Future<$0.LedgerApdu> ledgerExchange($grpc.ServiceCall call, $0.LedgerApdu request);
$async.Future<$0.BroadcastResponse> broadcast($grpc.ServiceCall call, $0.BroadcastRequest request);
}
$async.Future<$0.CoinswapResponse> coinswap($grpc.ServiceCall call, $0.CoinswapRequest request);
}
+107
View File
@@ -0,0 +1,107 @@
import 'dart:io';
import 'dart:math';
import 'package:flutter/foundation.dart';
enum LogLevel { info, debug, warn, error }
/// Pass an optional [file] to also write the log to a file.
void printV(
dynamic content, {
String? file,
LogLevel level = LogLevel.info,
}) {
final programInfo = CustomTrace(StackTrace.current);
final logLine =
"[${level.name.toUpperCase()}] ${programInfo.fileName}#${programInfo.lineNumber}:${programInfo.columnNumber} ${programInfo.callerFunctionName}: $content";
print(logLine);
if (file != null) {
final logFile = File(file);
if (!logFile.existsSync()) {
logFile.createSync(recursive: true);
}
logFile.writeAsStringSync("$logLine\n", mode: FileMode.append, flush: true);
}
}
// https://stackoverflow.com/a/59386101
class CustomTrace {
final StackTrace _trace;
String? fileName;
String? functionName;
String? callerFunctionName;
int? lineNumber;
int? columnNumber;
CustomTrace(this._trace) {
try {
_parseTrace();
} catch (e) {
if (kDebugMode) print("Unable to parse trace (printV): $e");
}
}
String _getFunctionNameFromFrame(String frame) {
/* Just giving another nickname to the frame */
var currentTrace = frame;
/* To get rid off the #number thing, get the index of the first whitespace */
var indexOfWhiteSpace = currentTrace.indexOf(' ');
/* Create a substring from the first whitespace index till the end of the string */
var subStr = currentTrace.substring(max(0, indexOfWhiteSpace));
/* Grab the function name using reg expr */
var indexOfFunction = subStr.indexOf(RegExp(r'[A-Za-z0-9_]'));
/* Create a new substring from the function name index till the end of string */
subStr = subStr.substring(indexOfFunction);
indexOfWhiteSpace = subStr.indexOf(RegExp(r'[ .]'));
/* Create a new substring from start to the first index of a whitespace. This substring gives us the function name */
subStr = subStr.substring(0, max(0, indexOfWhiteSpace));
return subStr;
}
void _parseTrace() {
/* The trace comes with multiple lines of strings, (each line is also known as a frame), so split the trace's string by lines to get all the frames */
var frames = this._trace.toString().split("\n");
/* The first frame is the current function */
this.functionName = _getFunctionNameFromFrame(frames[0]);
/* The second frame is the caller function */
this.callerFunctionName = _getFunctionNameFromFrame(frames[1]);
/* The first frame has all the information we need */
var traceString = frames[1];
/* Search through the string and find the index of the file name by looking for the '.dart' regex */
var indexOfFileName = traceString.indexOf(
RegExp(r'[/A-Za-z_]+.dart'), 1); // 1 to offest and not print the printV function name
var fileInfo = traceString.substring(max(0, indexOfFileName));
var listOfInfos = fileInfo.split(":");
/* Splitting fileInfo by the character ":" separates the file name, the line number and the column counter nicely.
Example: main.dart:5:12
To get the file name, we split with ":" and get the first index
To get the line number, we would have to get the second index
To get the column number, we would have to get the third index
*/
try {
this.fileName = listOfInfos[0];
this.lineNumber = int.tryParse(listOfInfos[1]);
var columnStr = listOfInfos[2];
columnStr = columnStr.replaceFirst(")", "");
this.columnNumber = int.tryParse(columnStr);
} catch (e) {
if (kDebugMode) print("Unable to parse trace (printV): $e");
}
}
}
-2
View File
@@ -13,8 +13,6 @@ dependencies:
grpc: 4.0.1
path_provider: ^2.1.2
plugin_platform_interface: ^2.0.2
cw_core:
path: ../cw_core
ffi: ^2.1.0
dev_dependencies:
+17
View File
@@ -28,6 +28,22 @@ class CWBitcoin extends Bitcoin {
BitcoinWalletFromKeysCredentials(
name: name, password: password, xpub: xpub, hardwareWalletType: hardwareWalletType);
@override
WalletCredentials createLitecoinWalletFromKeys({
required String name,
required String password,
required String xpub,
required String scanSecret,
required String spendPubkey,
}) =>
LitecoinWalletFromKeysCredentials(
name: name,
password: password,
xpub: xpub,
scanSecret: scanSecret,
spendPubkey: spendPubkey,
);
@override
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
{required String name,
@@ -766,6 +782,7 @@ class CWBitcoin extends Bitcoin {
@override
Future<void> commitPsbtUR(Object wallet, List<String> urCodes) {
if (wallet is LitecoinWallet) return wallet.commitPsbtUR(urCodes);
final _wallet = wallet as BitcoinWalletBase;
return _wallet.commitPsbtUR(urCodes);
}
@@ -56,6 +56,12 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
spendKeyController = restoredWallet != null
? TextEditingController(text: restoredWallet.spendKey)
: TextEditingController(),
scanSecretController = restoredWallet != null
? TextEditingController(text: restoredWallet.scanSecret)
: TextEditingController(),
spendPubkeyController = restoredWallet != null
? TextEditingController(text: restoredWallet.spendPubkey)
: TextEditingController(),
privateKeyController = restoredWallet != null
? TextEditingController(text: restoredWallet.privateKey)
: TextEditingController(),
@@ -72,6 +78,8 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
final TextEditingController addressController;
final TextEditingController viewKeyController;
final TextEditingController spendKeyController;
final TextEditingController scanSecretController;
final TextEditingController spendPubkeyController;
final TextEditingController nameTextEditingController;
final TextEditingController privateKeyController;
final TextEditingController? passwordTextEditingController;
@@ -119,6 +127,8 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
viewKeyController.dispose();
privateKeyController.dispose();
spendKeyController.dispose();
scanSecretController.dispose();
spendPubkeyController.dispose();
passwordTextEditingController?.dispose();
if (passwordListener != null) {
passwordTextEditingController?.removeListener(passwordListener!);
@@ -257,6 +267,22 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
maxLines: null,
),
),
Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: scanSecretController,
hintText: "Scan secret",
maxLines: null,
),
),
Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: spendPubkeyController,
hintText: "Spend public key",
maxLines: null,
),
),
if (widget.walletRestoreViewModel.hasBlockchainHeightSelector)
BlockchainHeightWidget(
key: blockchainHeightKey,
@@ -222,8 +222,12 @@ class WalletRestorePage extends BasePage {
walletRestoreFromKeysFormKey.currentState!.addressController.text;
credentials['spendKey'] =
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
credentials['scanSecret'] =
walletRestoreFromKeysFormKey.currentState!.scanSecretController.text;
credentials['spendPubkey'] =
walletRestoreFromKeysFormKey.currentState!.spendPubkeyController.text;
credentials['height'] =
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState?.height;
}
}
}
@@ -517,7 +521,8 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
}
},
onViewKeyEntered: (bool entered) {
if (widget.walletRestoreViewModel.onlyViewKeyRestore) {
if (widget.walletRestoreViewModel.onlyViewKeyRestore ||
walletRestoreViewModel.type == WalletType.litecoin) {
walletRestoreViewModel.isButtonEnabled = entered;
}
},
@@ -13,6 +13,8 @@ class RestoredWallet {
this.txId,
this.spendKey,
this.viewKey,
this.scanSecret,
this.spendPubkey,
this.mnemonicSeed,
this.passphrase,
this.txAmount,
@@ -28,6 +30,8 @@ class RestoredWallet {
final String? txId;
final String? spendKey;
final String? viewKey;
final String? scanSecret;
final String? spendPubkey;
final String? mnemonicSeed;
final String? passphrase;
final String? txAmount;
@@ -48,9 +52,13 @@ class RestoredWallet {
} catch (e) {
// fine, we don't care, it is only for monero anyway
}
if (json['ltub'] != null) {
json['xpub'] = convertLtubToXpub(json['ltub'] as String);
}
if (json['zpub'] != null) {
json['xpub'] = convertZpubToXpub(json['zpub'] as String);
}
json['xpub'] = convertAnyToXpub(json['xpub'] as String);
json['view_key'] ??= json['xpub'];
final height = json['height'] as String?;
return RestoredWallet(
@@ -60,6 +68,8 @@ class RestoredWallet {
address: json['address'] as String?,
spendKey: json['spend_key'] as String?,
viewKey: json['view_key'] as String?,
scanSecret: json['scan_secret'] as String?,
spendPubkey: json['spend_pubkey'] as String?,
height: height != null ? int.tryParse(height) ?? 0 : 0,
privateKey: json['private_key'] as String?,
);
+12 -1
View File
@@ -56,9 +56,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
case WalletType.arbitrum:
case WalletType.decred:
case WalletType.bitcoin:
case WalletType.litecoin:
availableModes = [WalletRestoreMode.seed, WalletRestoreMode.keys];
break;
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.zano:
case WalletType.none:
@@ -238,6 +238,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
if (mode == WalletRestoreMode.keys) {
final viewKey = options['viewKey'] as String?;
final spendKey = options['spendKey'] as String?;
final scanSecret = options['scanSecret'] as String?;
final spendPubkey = options['spendPubkey'] as String?;
final address = options['address'] as String?;
switch (type) {
@@ -249,6 +251,15 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
hardwareWalletType: hardwareWalletType,
);
case WalletType.litecoin:
return bitcoin!.createLitecoinWalletFromKeys(
name: name,
password: password,
xpub: viewKey!,
scanSecret: scanSecret!,
spendPubkey: spendPubkey!,
);
case WalletType.monero:
return monero!.createMoneroRestoreWalletFromKeysCredentials(
name: name,
+1 -1
View File
@@ -47,4 +47,4 @@ GOARCH=arm \
CC=${NDK_BIN}/armv7a-linux-androideabi21-clang \
go build -v -buildmode=c-shared -o ${ANDROID_OUT}/armeabi-v7a/libmweb.so .
cd ../
dart run ffigen --config ffigen_config.yaml
dart run ffigen --config ffigen_config.yaml
+1 -1
View File
@@ -6,4 +6,4 @@ cd "$(dirname "$0")"
cd ../torch_dart
./build.sh aarch64-linux-android armv7a-linux-androideabi x86_64-linux-android
./build.sh aarch64-linux-android armv7a-linux-androideabi x86_64-linux-android
+1
View File
@@ -184,6 +184,7 @@ abstract class Bitcoin {
});
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo});
WalletCredentials createBitcoinWalletFromKeys({required String name, required String password, required String xpub, HardwareWalletType? hardwareWalletType});
WalletCredentials createLitecoinWalletFromKeys({required String name, required String password, required String xpub, required String scanSecret, required String spendPubkey});
WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password, String? passphrase, String? mnemonic});
WalletCredentials createBitcoinHardwareWalletCredentials({required String name, required HardwareAccountData accountData, WalletInfo? walletInfo});
List<String> getWordList();