Use a multi-signature wallet
Sending multi-signature transactions that require M-of-N signatures to be spent.
Before executing a multisig transaction on the Hathor blockchain, you must first follow some preparation steps:
- The public keys of all participants have to be collected
- Each party's multisig wallets should be configured and started
- A user must create a multisig transaction proposal and share it (its
txHex
code) among all participants. - All users will be able to decode the
txHex
to validate transaction data and decide whether to send back the signatures or not. - With all signatures and the initial
txHex
, any participant can assemble the multisig transaction and push it.
The following sections will detail using the APIs to perform all these steps.
1. Collect Public Keys
Each participant can start an instance of the wallet headless service (npm start
) and invoke this /multisig-pubkey
endpoint to obtain their respective public key. Once a participant has their own pubkey, they must send it to the other participants so that everyone can start the multisig wallet.
Parameters
- seedKey: Parameter to define which seed (from the object
seeds
in theconfig.js
file) will be used to obtain the public key. - passphrase: Optional parameter to generate the public key with a passphrase. If not sent an empty string is used.
Request
curl -X POST --data "seedKey=seedId0" http://localhost:8000/multisig-pubkey
Response
{
"success":true,
"xpubkey":"xpub6Cz2jqhx7TVwyb1JvBWsoXrLwemjDpeCTEszWQbimHPDVM9ZZ8ibgtN8gZBxXjq9UgVMtk4cE98ou8RPfU3xyuo5bahpuVwdY1dY46QJMoC"
}
2. Configure MultiSig Wallets
Any seed to be started as MultiSig must have a setting in the multisig
key of the src/config.js
file. The following is a 2-of-2 MultiSig example.
In an M-of-N wallet, N and M must be at most 16.
...
seeds: {
seedId0: '24-words seed',
},
multisig: {
seedId0: {
total: 2,
numSignatures: 2,
pubkeys: [
'xpub6Cz2jqhx7TVwyb1JvBWsoXrLwemjDpeCTEszWQbimHPDVM9ZZ8ibgtN8gZBxXjq9UgVMtk4cE98ou8RPfU3xyuo5bahpuVwdY1dY46QJMoC',
'xpub6BsQ6vsaUK3AixC8G3dT2CaFFFtCx5fzMcUrTQpBgJPuSkF65yDRNMrPCSCDok9QbsYQeyWinNQvA4SZzNGL5HV5GTk1Xj46wvPpAMfW5Aw'
]
}
},
...
MultiSig addresses are determined by the participants' public keys (xpubs) and the minimum number of signatures. Changing the minimum number of signatures will generate a different MultiSig wallet. The order of public keys order is not relevant.
3. Start MultiSig Wallets
Start each MultiSig wallet as described in the Starting Wallet section. Include the parameter multisig=true
in the HTTP request body.
The headless wallet needs to be restarted after the `src/config.js` has been changed to include the multisig parameters (previous step).
Parameters
- x-wallet-id: Wallet identifier.
- passphrase: Optional parameter to start the wallet with a passphrase. If the user sets a passphrase when getting the public key (step 1), he must also use it when starting the wallet.
- seedKey: Parameter to define which seed (from the object
seeds
in theconfig.js
file) will be used to generate the wallet. - multisig: Parameter to start the wallet as a MultiSig wallet. Must be set to
**true**
.
Request
curl -X POST --data "wallet-id=walletId0" --data "seedKey=seedId0" \
--data "multisig=true" http://localhost:8000/start
4. Create a MultiSig Transaction
A transaction proposal is created. It is a proposal because no value is transferred to the destination address. In other words, although the transaction has been created, it has not yet been executed. Instead, the multisig transaction has been prepared so that all participants can verify its details before it is executed. The /wallet/p2sh/tx-proposal
endpoint returns the transaction txHex
that must be sent to all parties so they can access the transaction proposal.
Parameters
- x-wallet-id: Wallet identifier.
- outputs: Array of JSON objects.
- address: Destination address
- token: Token UID if the output is not for HTR
- value: Value to be transferred. Must be an integer with the value in cents, i.e., 123 means 1.23 HTR.
Request
curl -X POST -H "X-Wallet-Id: walletId0" -H "Content-type: application/json" \
--data '{"outputs": [{"address":"WVUM5x6zfxc11we7zhNb2jVVyxadWDevpv","value":1}]}' \
http://localhost:8000/wallet/p2sh/tx-proposal
Response
{
"success":true,
"txHex":"000100010200d0f99478ebe87a2b1a82b587e670e7ca89be28d409c7ede80393e4d019805a000000000001f300001976a914dc6c192478a5b3c439b9289f447cedd736b5823d88ac0000000100001976a9144a93166ef1d958a80cb66e3af261058ff097363988ac7ff8000000000000000000000000000000"
}
5. Check the MultiSig Transaction
Before agreeing to the created multisig transaction proposal, each participant may use the txHex
of that transaction to verify its details, such as destination address, token UID (if the output is not for HTR), and value to be transferred. The multisig transaction details are retrieved from the /wallet/decode
endpoint.
Parameters
- x-wallet-id: Wallet identifier.
- txHex: Transaction hexadecimal code
Request
curl -X POST -H "X-Wallet-Id: walletId0" -H "Content-type: application/json" \
--data '{"txHex":"000100010200d0f99478ebe87a2b1a82b587e670e7ca89be28d409c7ede80393e4d019805a000000000001f300001976a914dc6c192478a5b3c439b9289f447cedd736b5823d88ac0000000100001976a9144a93166ef1d958a80cb66e3af261058ff097363988ac7ff8000000000000000000000000000000"}' \
http://localhost:8000/wallet/decode
Response
{
"success":true,
"tx":{
"tokens":[
],
"inputs":[
{
"txId":"00d0f99478ebe87a2b1a82b587e670e7ca89be28d409c7ede80393e4d019805a",
"index":0
}
],
"outputs":[
{
"value":499,
"tokenData":0,
"script":"dqkU3GwZJHils8Q5uSifRHzt1za1gj2IrA==",
"type":"p2pkh",
"decoded":{
"address":"WimX2vewFTkZpYA6UtpsXtLSZpDdBR9feL",
"timelock":null
}
},
{
"value":1,
"tokenData":0,
"script":"dqkUSpMWbvHZWKgMtm468mEFj/CXNjmIrA==",
"type":"p2pkh",
"decoded":{
"address":"WVUM5x6zfxc11we7zhNb2jVVyxadWDevpv",
"timelock":null
}
}
]
}
}
6. Get Participant Signatures
Suppose participants approve the transaction after verifying their details in the previous step. In this case, each participant must use txHex
to sign the transaction via the /wallet/p2sh/tx-proposal/get-my-signatures
endpoint.
Parameters
- x-wallet-id: Wallet identifier.
- txHex: Transaction hexadecimal code
Request
curl -X POST -H "X-Wallet-Id: walletId0" -H "Content-type: application/json" \
--data '{"txHex":"000100010200d0f99478ebe87a2b1a82b587e670e7ca89be28d409c7ede80393e4d019805a000000000001f300001976a914dc6c192478a5b3c439b9289f447cedd736b5823d88ac0000000100001976a9144a93166ef1d958a80cb66e3af261058ff097363988ac7ff8000000000000000000000000000000"}' \
http://localhost:8000/wallet/p2sh/tx-proposal/get-my-signatures
Response
{
"success":true,
"signatures":"023eb05452d1fd0568a101da19d5da45725f4621d15ed80eae364372fb61dfd6f5|0:3045022100c95a6c1f0c119e7e2162e3d1c65ff726e7fb61c3a1dafdc7e260d90fbd81674202204ea877de40ed052d47a25521e42fffac37c8942e835e765010cb78c69924c434"
}
7. Send the MultiSig Transaction
Once enough signatures have been collected, any participant can use the /wallet/p2sh/tx-proposal/sign-and-push
to send the transaction.
Parameters
- x-wallet-id: Wallet identifier.
- txHex: Transaction hexadecimal code
- signatures: Array where each position contains the signature of a participant in the MultiSig transaction.
Request
curl -X POST -H "X-Wallet-Id: walledId0" -H "Content-type: application/json" \
--data '{"txHex": "000100010200d0f99478ebe87a2b1a82b587e670e7ca89be28d409c7ede80393e4d019805a000000000001f300001976a914dc6c192478a5b3c439b9289f447cedd736b5823d88ac0000000100001976a9144a93166ef1d958a80cb66e3af261058ff097363988ac7ff8000000000000000000000000000000", "signatures":["023eb05452d1fd0568a101da19d5da45725f4621d15ed80eae364372fb61dfd6f5|0:3045022100c95a6c1f0c119e7e2162e3d1c65ff726e7fb61c3a1dafdc7e260d90fbd81674202204ea877de40ed052d47a25521e42fffac37c8942e835e765010cb78c69924c434","021ddcac9112297360daf3c555eb69b483e45d31d331ebdf0dd5362fa347e4f315|0:30440220570cb8494b358781f9804ad5ee5bfc3d3b197589ec91efc0c607ffdfd7b34baf022006ecdd8c0d7247a527596581ef7f9d6eaaf82922faf969b1df08cce67fe8f45f"]}' \
http://localhost:8000/wallet/p2sh/tx-proposal/sign-and-push
Response
{
"success":true,
"inputs":[
{
"hash":"00d0f99478ebe87a2b1a82b587e670e7ca89be28d409c7ede80393e4d019805a",
"index":0,
"data":{
"type":"Buffer",
"data":[+]
},
"tx_id":"00d0f99478ebe87a2b1a82b587e670e7ca89be28d409c7ede80393e4d019805a"
}
],
"outputs":[
{
"value":499,
"script":{
"type":"Buffer",
"data":[+]
},
"tokenData":0,
"decodedScript":{
"address":{
"base58":"WimX2vewFTkZpYA6UtpsXtLSZpDdBR9feL",
"network":{
"name":"testnet",
"versionBytes":{
"p2pkh":73,
"p2sh":135,
"xpriv":70568132,
"xpub":76067358
},
"bitcoreNetwork":{
"name":"htr-testnet",
"alias":"test",
"pubkeyhash":73,
"privatekey":128,
"scripthash":135,
"bech32prefix":"tn",
"xpubkey":76067358,
"xprivkey":70568132,
"networkMagic":{
"type":"Buffer",
"data":[+]
},
"port":8333,
"dnsSeeds":[
]
}
}
},
"timelock":null
},
"token_data":0
},
{
"value":1,
"script":{
"type":"Buffer",
"data":[+]
},
"tokenData":0,
"decodedScript":{
"address":{
"base58":"WVUM5x6zfxc11we7zhNb2jVVyxadWDevpv",
"network":{
"name":"testnet",
"versionBytes":{
"p2pkh":73,
"p2sh":135,
"xpriv":70568132,
"xpub":76067358
},
"bitcoreNetwork":{
"name":"htr-testnet",
"alias":"test",
"pubkeyhash":73,
"privatekey":128,
"scripthash":135,
"bech32prefix":"tn",
"xpubkey":76067358,
"xprivkey":70568132,
"networkMagic":{
"type":"Buffer",
"data":[+]
},
"port":8333,
"dnsSeeds":[
]
}
}
},
"timelock":null
},
"token_data":0
}
],
"version":1,
"weight":18.020647094115752,
"nonce":100826,
"timestamp":1655224964,
"parents":[
"008be839a95b792912e3668ceb4fa46df897a29d069ef9898f6e6b415e661ce3",
"00fe3983367b32d1ce0e4aee8152795e75e079758f71809155a9c71d3d6df92f"
],
"tokens":[
],
"hash":"000024dd6fbb9a9bd9021ede5e706bcf0f7a09bc0649d727ce5afd77a2ca2e48",
"_dataToSignCache":null
}