require the policy line and check the declared cosigner count when importing coldcard format multisig wallets

This commit is contained in:
Craig Raw
2026-09-09 10:17:06 +02:00
parent b7902f5405
commit 5aa262ee24
4 changed files with 53 additions and 4 deletions
@@ -121,7 +121,8 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
Wallet wallet = new Wallet();
wallet.setPolicyType(PolicyType.MULTI_HD);
int threshold = 2;
int threshold = 0;
int cosigners = 0;
ScriptType scriptType = ScriptType.P2SH;
String derivation = null;
@@ -143,7 +144,12 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
wallet.setName(value.trim());
break;
case "Policy":
threshold = Integer.parseInt(value.split(" ")[0]);
String[] policy = value.split("\\s+");
if(policy.length != 3) {
throw new IllegalStateException("Could not determine the multisig policy from \"" + line + "\"");
}
threshold = Integer.parseInt(policy[0]);
cosigners = Integer.parseInt(policy[2]);
break;
case "Derivation":
case "# derivation":
@@ -167,8 +173,16 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
}
Policy policy = Policy.getPolicy(PolicyType.MULTI_HD, scriptType, wallet.getKeystores(), threshold);
wallet.setDefaultPolicy(policy);
if(threshold == 0) {
throw new IllegalStateException("This file does not specify the multisig policy");
}
if(cosigners != wallet.getKeystores().size()) {
throw new IllegalStateException("This file specifies a policy of " + threshold + " of " + cosigners + ", but contains " + wallet.getKeystores().size() + " cosigner key" + (wallet.getKeystores().size() == 1 ? "" : "s"));
}
Policy walletPolicy = Policy.getPolicy(PolicyType.MULTI_HD, scriptType, wallet.getKeystores(), threshold);
wallet.setDefaultPolicy(walletPolicy);
wallet.setScriptType(scriptType);
try {
@@ -99,6 +99,18 @@ public class ColdcardMultisigTest extends IoTest {
Assertions.assertTrue(wallet.isValid());
}
@Test
public void importWalletMissingPolicy() {
ColdcardMultisig ccMultisig = new ColdcardMultisig();
Assertions.assertThrows(ImportException.class, () -> ccMultisig.importWallet(getInputStream("cc-multisig-export-nopolicy.txt"), null));
}
@Test
public void importWalletMissingCosigner() {
ColdcardMultisig ccMultisig = new ColdcardMultisig();
Assertions.assertThrows(ImportException.class, () -> ccMultisig.importWallet(getInputStream("cc-multisig-export-missingkey.txt"), null));
}
@Test
public void exportWallet1() throws ImportException, ExportException, IOException {
ColdcardMultisig ccMultisig = new ColdcardMultisig();
@@ -0,0 +1,10 @@
# Coldcard Multisig setup file (created on 0F056943)
#
Name: CC-2-of-4
Policy: 2 of 4
Derivation: m/48'/0'/0'/2'
Format: P2WSH
0F056943: xpub6EfEGa5isJbQFSswM5Uptw5BSq2Td1ZDJr3QUNUcMySpC7itZ3ccypVHtLPnvMzKQ2qxrAgH49vhVxRcaQLFbixAVRR8RACrYTp88Uv9h8Z
6BA6CFD0: xpub6FFEQVG6QR28giDuML74Y7EMPwqEiKftNjScLzg5WKM41bf6LMP2XspjBgNp28tvkNUZdokmTY4TcRbuGZBSMvNoUECrKW1y3TBPeQJVmAg
747B698E: xpub6Eb6Z1xtmWRiWKgRpHf6dHiEagGd6FLiBXrnma1nFK4PGRYqSVqVyJaxna5Mb8etSP4ATKVAvKnXG1a9HZauoAawuSDJT5RgH2HqEVHZVHY
@@ -0,0 +1,13 @@
# Exported from Electrum
Name: el-CC-3-of-3-sb-2
Format: P2WSH
Derivation: m/48'/0'/0'/2'
06B57041: xpub6EfEGa5isJbQFSswM5Uptw5BSq2Td1ZDJr3QUNUcMySpC7itZ3ccypVHtLPnvMzKQ2qxrAgH49vhVxRcaQLFbixAVRR8RACrYTp88Uv9h8Z
Derivation: m/48'/0'/0'/2'
4B569672: xpub6FFEQVG6QR28giDuML74Y7EMPwqEiKftNjScLzg5WKM41bf6LMP2XspjBgNp28tvkNUZdokmTY4TcRbuGZBSMvNoUECrKW1y3TBPeQJVmAg
Derivation: m/47'/0'/0'/1'
CA9A2B19: xpub6Eb6Z1xtmWRiWKgRpHf6dHiEagGd6FLiBXrnma1nFK4PGRYqSVqVyJaxna5Mb8etSP4ATKVAvKnXG1a9HZauoAawuSDJT5RgH2HqEVHZVHY