mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
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:
co-authored by
Hector Chu
OmarHatem
parent
f0524bcef1
commit
77812016ac
@@ -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,
|
||||
|
||||
@@ -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!,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,3 +28,4 @@ migrate_working_dir/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
||||
jniLibs/
|
||||
|
||||
+13
-10
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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?,
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user