Finally I was able to sort this out.
You can use this library to create the multisig: https://github.com/tango-crypto/cardano-wallet-js
The code I ran was the following:
multisig.js
const { WalletServer , Seed } = require('cardano-wallet-js');
const data = {
"type": "atLeast", //requires at least 4 keys to sign the tx
"require": 4,
"scripts":
[
{
"type": "sig"
},
{
"type": "sig"
},
{
"type": "sig"
},
{
"type": "sig"
},
{
"type": "sig"
},
{
"type": "sig"
},
]
};
// generate the native script
const script = Seed.buildScript(data);
const jsonScript = Seed.scriptToJson(script);
console.log(jsonScript);
// get native script private keys (all them will be needed to sign the final tx)
const keys = Seed.getScriptKeys(script).map(k => k.to_bech32());
console.log("keys:", keys);
//Generate script address
const address = Seed.getScriptAddress(script, 'testnet').to_bech32();
console.log("address:", address);
The code to sign the transaction is this one (you need to replace every 'XXXX' on scriptKeys
with your signing keys, and every 'YYYY' on jsonScript.scripts
with the key hashes of each signature - all this is output of multisig.js
):
tx-sign.js
const { WalletServer , Seed , Config, Bip32PrivateKey } = require('cardano-wallet-js');
const scriptKeys = [
'XXXX',
'XXXX',
'XXXX',
'XXXX',
'XXXX',
'XXXX'
];
const signingKeys = scriptKeys.map(key => Bip32PrivateKey.from_bech32(key).to_raw_key());
// get native script (this is the SAME SCRIPT, we're just "loading" it back)
const jsonScript = {
type: 'atLeast',
require: 4,
scripts: [
{
type: 'sig',
keyHash: 'YYYY'
},
{
type: 'sig',
keyHash: 'YYYY'
},
{
type: 'sig',
keyHash: 'YYYY'
},
{
type: 'sig',
keyHash: 'YYYY'
},
{
type: 'sig',
keyHash: 'YYYY'
},
{
type: 'sig',
keyHash: 'YYYY'
}
]
};
// set network configuration
let buildOpts = {
startSlot: 0,
config: Config.Testnet,
};
const ttl = 52399446; // slot before the tx should be processed
const CoinSelectionWallet = {
"withdrawals": [],
"inputs": [
{
"amount": {
"quantity": 10000000,
"unit": "lovelace"
},
"address": "addr_test1xr20j5t970ssz0323tzqqvp3pp02s96yf64zmzj83kksyvx5l9gktulpqylz4zkyqqcrzzz74qt5gn429k9y0rddqgcq2yupvs", // script address
"id": "d48f256aea384066756be0c11c6e6b8baef8f7ad25ba92b76d1ae3c294443400",
"assets": [],
"index": 0
},
],
"deposits": [],
"change": [
{
"amount": {
"quantity": 8800000, // fee of 200000 initially
"unit": "lovelace"
},
"address": "addr_test1xr20j5t970ssz0323tzqqvp3pp02s96yf64zmzj83kksyvx5l9gktulpqylz4zkyqqcrzzz74qt5gn429k9y0rddqgcq2yupvs",
"assets": []
}
],
"outputs": [
{
"amount": {
"quantity": 1000000,
"unit": "lovelace"
},
"address": "addr_test1qpypxf9jma2lkmzwn6nhypmynqpmus7x0ekwp2jfdrxqgm8vecdcssszlmywlyn7h0nkqp9jyk8p7dhjecglhu9m75xq4mwl9v",
"assets": []
}
]
};
const script = Seed.buildScript(jsonScript);
// for the script witnesses we only need to specify the native script root
const scripts = [script.root];
// build the tx (you can include scripts/signingkeys here, eg: let tx = Seed.buildTransactionMultisig(selection, ttl, scripts, null, sigingKeys, buildOpts);)
let tx = Seed.buildTransactionMultisig(CoinSelectionWallet, ttl, [], null, [], buildOpts);
// add script
tx.addScriptWitness(...scripts);
// add witness
tx.addKeyWitnesses(signingKeys[0]);
tx.addKeyWitnesses(signingKeys[1]);
tx.addKeyWitnesses(signingKeys[2]);
tx.addKeyWitnesses(signingKeys[3]);
tx.addKeyWitnesses(signingKeys[4]);
tx.addKeyWitnesses(signingKeys[5]);
const signed = tx.build();
console.log(signed)
When you do :
tx.addKeyWitnesses(signingKeys[0]);
tx.addKeyWitnesses(signingKeys[1]);
tx.addKeyWitnesses(signingKeys[2]);
tx.addKeyWitnesses(signingKeys[3]);
tx.addKeyWitnesses(signingKeys[4]);
tx.addKeyWitnesses(signingKeys[5]);
You are signing with all 6 signatures. You can sign with any 4 you want and the UTxO will be consumed as well. On the contrary, if you provide less than 4 signatures, then the validation will fail.