Skip to content

Latest commit

 

History

History
972 lines (876 loc) · 31 KB

http.md

File metadata and controls

972 lines (876 loc) · 31 KB

Multisig HTTP

Multisig HTTP Server adds some API methods and disables several existing ones.

Authentication Configs

It's recommended, that you setup API_KEY for HTTP in general and turn on walletAuth. This will make sure no route can be called without authentication.

API_KEY

You will still need to set API_KEY for http server, which is same as Node HTTP or Wallet HTTP in bcoin.

Authentication

Multisig wallet uses cosigner tokens to authenticate cosigners, that are configured by cosigners when joining. (NOTE: Admin token wont be able to access cosigner APIs. So don't use admin token as cosigner token.)
There are some routes that do not require authentication or only admin token can query.

Some endpoints need signatures and public keys, you can check recent updates: signing This document will also include some steps to reproduce signatures.

Some examples:

  • PUT /multisig/:id* - used for wallet creation does not need any authentication (you should use API_KEY to control access to the API itself).
  • GET /multisig/:id/proposal/ - can be requested using cosigner token.
  • GET /multisig* and DEL /multisig/:id can be requested only with admin token.
  • POST /multisig/join* - will check joinSignature.
  • POST /multisig/:id/proposal - needs signature (using authPubKey).

Signing

All endpoint definitions contain examples what is data and how to sign. For more details check signing.

Additional API Endpoints

Note: this examples will use regtest network serializations.

GET /multisig (Admin Only)

This will return list of the available multisig wallets

await client.getWallets(); // returns [ 'mswallet1', 'mswallet2' ]

HTTP Response

{ wallets: [ 'mswallet1', 'mswallet2' ]

PUT /multisig/:id

Create multisig wallet.

Before creating multisig wallet, client needs to generate secret key, that will be used for joining by other cosigners. Client also needs to pass cosigner data with creation. (Author of the wallet joins on creation.)

Data to sign for joinSignature:

Data to sign for accountKeyProof and authPubKey

  • description: walletName || cosignerName || authPubKey || accountKey
  • example:
    • walletName: test (74657374)
    • cosignerName: cosigner1 (636f7369676e657231)
    • authPubKey: 033faf1f3e2328c37addb474ef8326312a96f698bd7e18f74f75066bdcef1dd180
    • raw account key: eab4fa05032dca132f80000000acdbf8120f8d5ff1c8b40c10d8a32216746f775f695b7a55219204921f6e98ff035679212b890015d137186010db38ef4a6171da6e5549740ad7486a4896f65700aa02b755
    • result: 74657374636f7369676e657231033faf1f3e2328c37addb474ef8326312a96f698bd7e18f74f75066bdcef1dd180eab4fa05032dca132f80000000acdbf8120f8d5ff1c8b40c10d8a32216746f775f695b7a55219204921f6e98ff035679212b890015d137186010db38ef4a6171da6e5549740ad7486a4896f65700aa02b755
    • Now we can use signmessage rpc or similar apis to get the hash to sign.

Keys (secp256k1):

  • joinPrivateKey - key generated by the author to be shared with other cosigners.
  • accountKey - account key used for generating addresses. (e.g. derived as m/44'/0'/0')
  • accountKeyProof - This is signature of the above message, using derivation accountKey/0x7fffffff/0
  • authPubKey - This key will be used to sign data related to proposals (creationg, rejection).
  • joinSignature

Params (example):

// 2-of-2 multisig wallet
// wallet name: test.
{
  // general wallet configs.
  "id": "test",
  "witness": true,
  "m": 2,
  "n": 2,

  "cosigner": {
    // this token will be used for authentication.
    "token": "0101010101010101010101010101010101010101010101010101010101010101",

    "name": "cosigner1",
    "purpose": 44,
    // master key fingerprint (not xpub)
    "fingerPrint": 2654134519,
    // you can put additional data up to 100 bytes.
    "data": "63636363636363636363",
    // base58 encoded account key. (for example, derived as m/44'/0'/0')
    "accountKey": "rpubKBBSxApoKWuxYnNo3YUULJxV1LVT3nawafxrfch37rUn8v1xyt6m6METTUVRy5gMSFnUoheCe6Mba2J1iqxrb1hnDo8ndNVLG7wvc4juT42Y",
    // check signing document: Wallet Joining.
    "accountKeyProof": "20a304942c61b3bb7e357a18485ed210a291d8c76354f84b6ef191a421fe2800a362e24c0b9b7184ba098e9eaa6cdb9c72eeecac37d0e46bb124f55be24d521e37",
    // public key that will be used for signing and verifying: proposal rejection and creation signatures.
    "authPubKey": "03fff6ec2e4bb4d2f99b8e9c9bfdc6a72126424714e79a5a5ff42948bee3324099",
  },

  // signature of the data mentioned above using joinPrivateKey.
  "joinSignature": "1fd52de8fe2ebcebdd630035702e3b6d719222b999fe750c84a92faa9c4460494f442051d0bc071205f24dad6a68d766216269af00f23967a3d767cc6d4e0c6c82",
  // public key of joinPrivateKey.
  "joinPubKey": "02cac403562ce31e3399d004dc6a742048598f3e75fae80f73d1d322c9b7635204"
}
await client.createWallet('name-of-wallet', { ...options });

HTTP Response:

{
  "network": "regtest",
  "wid": 1,
  "id": "test",
  "watchOnly": true,
  "accountDepth": 1,
  "token": null,
  "tokenDepth": 0,
  "master": {
    "encrypted": false
  },
  "balance": null,
  // This will become true when all cosigners join, before that cosigners can't use the wallet.
  "initialized": false,
  "joinPubKey": "02c16fe174fc1ceb8e25ebb244dad9c3663d408570cfaaa7b4305f7c5279772852",
  "cosigners": [
    {
      "id": 0,
      "name": "cosigner1",
      "authPubKey": "0388f1eb6e232e98c6354383cb5130a56015fab1fb43eef2bd17f6511ccc0d9a9e",
      "joinSignature": "20f6e7874b0977a5e148ccbbf39453772b5765a3aadfa1f6212bb1620e4fc1795337f965ff9b0417da473e617855b3ae432582f66b0d725acba90825a6da4cc71a",
      "accountKey": "rpubKBB83sbxiyok5kWNmvHiMmkRpHXWh39WYLuU1zfYwjdhZVwasc4CuHnMrqoUpjAppNyVAw2MUzACiGpge8ZRuaED9QJsbh9fgHdbXLbDHsPe",
      "purpose": 44,
      "fingerPrint": 3664892090,
      "data": "63636363636363636363",
      "tokenDepth": 0,
      "token": "0101010101010101010101010101010101010101010101010101010101010101"
    }
  ]
}

POST /multisig/:id/join

Join the wallet, you will need joinPrivKey and walletName from the wallet author. For details check wallet creation endpoint.

Params:

{
  "cosigner": {
    "name": "cosigner2",
    "purpose": 44,
    "fingerPrint": 2698755706,
    "data": "",
    "token": "0202020202020202020202020202020202020202020202020202020202020202",
    "accountKey": "rpubKBAS32yftCYdYvWgXxEEH7upohWFsXP3dwp1eCNA9R7vYwVoes8bSv4NxPwsuJ4puBRCJWRSUxe1MPqYV78UtdkpGdeue5mG66yM2QGGbXmJ",
    "accountKeyProof": "20d8baaec98fe57054e2c131c01a5f83774ff7acdbb2966ffc0e186dd3595a461a52d217bbeff61d6107dfc30a8dcc9099dfd7c9ff9da288d0f326af863e40a296",
    "authPubKey": "03bdccfe18655a3fcba674227d0b9f383e49ade32791b140a6181b596e378ec4bf",
  },

  "joinSignature": "1fbd7825512a1352a461552ddc9349ad98ffbc0c543d4aac40e85b0eb38ac795967034472a1fd3b2578e3ff953a1852c90c4e7313d30c4d70185cfd36b6980c442"
}
await client.joinWallet('name-of-wallet', { ...options });

HTTP Response

{
  "network": "regtest",
  "wid": 1,
  "id": "test",
  "watchOnly": true,
  "accountDepth": 1,
  "token": null,
  "tokenDepth": 0,
  "master": {
    "encrypted": false
  },
  "balance": null,
  "initialized": true,
  "joinPubKey": "031dbf621b0a5fb8a29d8144682c75e035b3f1b01aa39287d45ae15f7aaa1bf72c",
  "cosigners": [
    {
      "id": 0,
      "name": "cosigner1",
      "authPubKey": "02348755bfeae058de77918f09c7f31397333715b83303014eeb36be0ac57f2310",
      "joinSignature": "2077152bbab4ad2743af8619f085f3856258b255786b2083532cbe829855d9cbc551cab29777108f23d9385e4821d7e48c66a14eac6462469e06bb3e92995e9712",
      "accountKey": "rpubKBAsBfPVMBBeBfD8F4EA5Dk1NAZ2dK7dc9JsE9dq9tDDF73NecpNP4z3gfMWuRm9rLVdSaeTj8BHZbVyHeRtV6UhciDz26pPDEntqCJvqKg2",
      "purpose": 44,
      "fingerPrint": 3664892090,
      "data": "63636363636363636363",
    },
    {
      "id": 1,
      "name": "cosigner2",
      "authPubKey": "0325e5fb974a2e12f954edb3dd6d851a43d6744784f2a41e12c1060aefb783a702",
      "joinSignature": "1fb393639c0f8279ef3377ff0c1bb9067474f41a4b4b7301a2814d64c6abd1fb446b48f361ec91d42d4c9dd589337e98cedc24762618bebed82c2d32e8244587f4",
      "accountKey":  "rpubKBB7QfFrX68MzrYd7FdcjSnZmg7sPRQzYKQuEfkJGF4BWV68qu23S4wvjwvo8b4sqRwUf7uNCWmDwghKFv6tPnGFjXaY4TXQw9QQTy5zcjfu",
      "purpose": 44,
      "fingerPrint": 358396470,
      "data": "",
      "tokenDepth": 0,
      "token": "0202020202020202020202020202020202020202020202020202020202020202"
    }
  ]
}

GET /multisig/:id/name-of-wallet

Cosigner authentication.

Get the wallet info.

Query Params:

{
  // if we want account details, e.g. address
  // NOTE: there are separate account endpoints available as well,
  // same as bcoin wallet.
  details: true
}
await client.getInfo('name-of-wallet', true);

HTTP Response:

{
  "network": "regtest",
  "wid": 1,
  "id": "test",
  "watchOnly": true,
  "accountDepth": 1,
  "token": null,
  "tokenDepth": 0,
  "master": {
    "encrypted": false
  },
  "balance": {
    "tx": 0,
    "coin": 0,
    "unconfirmed": 0,
    "confirmed": 0
  },
  "initialized": true,
  "joinPubKey": "03fba95156992e6955e16ea7f887c2f158044b73573eb90f6428f3b6e61a6b1836",
  "cosigners": [
    {
      "id": 0,
      "name": "cosigner1",
      "authPubKey": "02348755bfeae058de77918f09c7f31397333715b83303014eeb36be0ac57f2310",
      "joinSignature": "2077152bbab4ad2743af8619f085f3856258b255786b2083532cbe829855d9cbc551cab29777108f23d9385e4821d7e48c66a14eac6462469e06bb3e92995e9712",
      "accountKey": "rpubKBAsBfPVMBBeBfD8F4EA5Dk1NAZ2dK7dc9JsE9dq9tDDF73NecpNP4z3gfMWuRm9rLVdSaeTj8BHZbVyHeRtV6UhciDz26pPDEntqCJvqKg2",
      "purpose": 44,
      "fingerPrint": 3664892090,
      "data": "63636363636363636363"
    },
    {
      "id": 1,
      "name": "cosigner2",
      "authPubKey": "0325e5fb974a2e12f954edb3dd6d851a43d6744784f2a41e12c1060aefb783a702",
      "joinSignature": "1fb393639c0f8279ef3377ff0c1bb9067474f41a4b4b7301a2814d64c6abd1fb446b48f361ec91d42d4c9dd589337e98cedc24762618bebed82c2d32e8244587f4",
      "accountKey":  "rpubKBB7QfFrX68MzrYd7FdcjSnZmg7sPRQzYKQuEfkJGF4BWV68qu23S4wvjwvo8b4sqRwUf7uNCWmDwghKFv6tPnGFjXaY4TXQw9QQTy5zcjfu",
      "purpose": 44,
      "fingerPrint": 358396470,
      "data": ""
    }
  ],
  "proposalStats": {
    "lockedOwnCoins": 0,
    "lockedOwnBalance": 0,
    "proposals": 0,
    "pending": 0,
    "approved": 0,
    "rejected": 0
  }
}

DELETE /multisig/:id/name-of-wallet

Delete wallet. Admin Only

await client.removeWallet('name-of-wallet'); // true/false
{
  success: true
}

POST /multisig/:id/token

Cosigner authentication.

Change your current token with new one.

Params:

{
  // 32 bytes (encoded as hex string)
  "newToken": "0303030303030303030303030303030303030303030303030303030303030303"
}
await client.setToken(id, { newToken: ... });

HTTP Response:

{
  "id": 0,
  "name": "cosigner1",
  "authPubKey": "033a0107dd9ecbc720d26dacca1a16d1e3678f03633a9d27542266e50dced1d657",
  "joinSignature": "20c32ef0653db6369f66d20ce72250807a19dcd955affa0f4c815e4d229ba8d0105c2f4ba3fa0771d8dd9418da02206cead4cbc0ea0ee51bb68a2d14b699b4bcb0",
  "accountKey": "rpubKBAVZYL2jVQnKHyvWXDji7ZFtK7T5V5qJ3KhZZnRNLASNwamupMFM6VkWzxaAFGTUYQkQjsVKMbm1s2NQD19yejH6AJFDWuAss9B6B9REtJG",
  "purpose": 44,
  "fingerPrint": 516910651,
  "data": "63636363636363636363",
  "tokenDepth": 1,
  "token": "0303030303030303030303030303030303030303030303030303030303030303"
}

POST /multisig/:id/create

Create transaction without signing and locking coins.

See TXOptioins in bcoin docs.

await client.createTX(id, options)

GET /multisig/:id/proposal

Cosigner authentication.

List existing proposals.

Query Params:

{
  pending: true // when true, this will only list pending proposals [default=true]
}
await client.getProposals(id, true);

HTTP Response:

{
  "proposals": [{
    "id": 0,
    "memo": "proposal1",
    "tx": null,
    // you can check `cosignerDetails` map to check author details.
    "author": 1,
    "approvals": {},
    "rejections": {},
    // This is the proposal creation signature that clients need to verify.
    "signature": "1f5fb8c6d9df6b13c3d634d28f68398101800ad46ebbd768e72b4fcd38c016e28d42ab3d94d12eb542052e4ba0e3d9f0b8fe44a0cb006c7540cf1bbdfd198f5015",
    // These are the HTTP options that were used for proposal creation.
    "options": {
      "memo": "proposal1",
      "timestamp": 1554548312,
      "txoptions": {
        "subtractFee": true,
        "outputs": [
          {
            "address": "RHTE2sM5byAhYGxc6pnvNN955qAAUvQ5FQ",
            "value": 100000000
          }
        ]
      }
    },
    // author sets timestamp
    "timestamp": 1554548312,
    // server sets createdAt
    "createdAt": 1554548312,
    "rejectedAt": null,
    "approvedAt": null,
    "m": 2,
    "n": 2,
    "statusCode": 0,
    "statusMessage": "Proposal is in progress.",
    "cosignerDetails": {
      "0": {
        "id": 0,
        "name": "cosigner1",
        "authPubKey": "02348755bfeae058de77918f09c7f31397333715b83303014eeb36be0ac57f2310",
        "joinSignature": "2077152bbab4ad2743af8619f085f3856258b255786b2083532cbe829855d9cbc551cab29777108f23d9385e4821d7e48c66a14eac6462469e06bb3e92995e9712",
        "accountKey": "rpubKBAsBfPVMBBeBfD8F4EA5Dk1NAZ2dK7dc9JsE9dq9tDDF73NecpNP4z3gfMWuRm9rLVdSaeTj8BHZbVyHeRtV6UhciDz26pPDEntqCJvqKg2",
        "purpose": 44,
        "fingerPrint": 3664892090,
        "data": "63636363636363636363"
      },
      "1": {
        "id": 1,
        "name": "cosigner2",
        "authPubKey": "0325e5fb974a2e12f954edb3dd6d851a43d6744784f2a41e12c1060aefb783a702",
        "joinSignature": "1fb393639c0f8279ef3377ff0c1bb9067474f41a4b4b7301a2814d64c6abd1fb446b48f361ec91d42d4c9dd589337e98cedc24762618bebed82c2d32e8244587f4",
        "accountKey":  "rpubKBB7QfFrX68MzrYd7FdcjSnZmg7sPRQzYKQuEfkJGF4BWV68qu23S4wvjwvo8b4sqRwUf7uNCWmDwghKFv6tPnGFjXaY4TXQw9QQTy5zcjfu",
        "purpose": 44,
        "fingerPrint": 358396470,
        "data": ""
      }
    }
  }]
}

POST /multisig/:id/proposal

Cosigner authentication.

Create proposal.

Proposal also needs signature to verify that author was the one who created the proposal. See signing

Data to sign:

  • description: payload type with JSON serialized proposal options.
  • example:
    • Wallet name: test (hex: 74657374)
    • Payload type is create: 0 (hex: 00)
    • {"memo":"proposal1","timestamp":1554549769,"txoptions":{"subtractFee":true,"outputs":[{"address":"RSdnT2yZvda2j8ff3cYQzcZB4oqqEmFZDL","value":100000000}]}}
    • encoded: 007b226d656d6f223a2270726f706f73616c31222c2274696d657374616d70223a313535343534393736392c2274786f7074696f6e73223a7b227375627472616374466565223a747275652c226f757470757473223a5b7b2261646472657373223a225253646e5432795a766461326a386666336359517a635a42346f7171456d465a444c222c2276616c7565223a3130303030303030307d5d7d7d
    • signed using signmessage rpc method or similar api.

After creating proposal, you still need to approve it.
This will lock coins, so these coins won't be used for create TX or another proposal creation.

Params:

{
  "proposal": {
    // some description of the proposal. (up to 100 bytes)
    "memo": "proposal1",
    // client also sends timestamp for verification purposes. See signing doc.
    "timestamp": 1555065103,
    // you can check bcoin docs for details
    // what are accepted in `txoptions`.
    "txoptions": {
      "subtractFee": true,
      "outputs": [
        {
          "address": "RWgVixJfGyTCvhmJzEBD6tsBvv1btcTSXJ",
          "value": 100000000
        }
      ]
    }
  },
  // signature of data to sign using `authPrivKey`.
  "signature": "20b4740aeedee457c4df314d35dc4484bb6c2c1edb525158cabfa7619effd1a0c40154a72d060ad0fb256d2f83aac0ce78ba1d07472cfeda72e1d1fcba2d1001ff"
}
await client.createProposal(id, {
  proposal: proposalOptions,
  signature: signature
});

HTTP Response:

{
  "id": 0,
  "memo": "proposal1",
  "tx": "010000000001011fde4f1c4fe35700e76c71f4fe2ae301cad918a7d2b6e204a49feb0810ae75770000000000ffffffff01b4d2f505000000001976a9147ce53b17719f95a49b290424f4d03279abdf91e388ac0400000047522103101caa304ee27baf13475d09116235756b22e3d98fd26aa356992244c3bf1c0b2103930cc331ebea62cb6e0094a4e0baeabfd7f9c6575bdffee6169905af0c7e55c352ae00000000",
  "author": 1,
  "approvals": {},
  "rejections": {},
  "signature": "208b715dcb5e3b45d1eb6a159af63047534d501e7deb7206ee0aa1b6bfc05db5d73f95587f81e1eb0d7eaee20b57ccbc024f3fac7257735ba2703c754b77feac58",
  "options": {
    "memo": "proposal1",
    "timestamp": 1555065102,
    "txoptions": {
      "subtractFee": true,
      "outputs": [
        {
          "address": "RLfaUYH5wWJYf9EUTFg25zFvmdzcGgSYH5",
          "value": 100000000
        }
      ]
    }
  },
  "timestamp": 1555065102,
  "createdAt": 1555065102,
  "rejectedAt": null,
  "approvedAt": null,
  "m": 2,
  "n": 2,
  "statusCode": 0,
  "statusMessage": "Proposal is in progress.",
  "cosignerDetails": {
    "0": {
      "id": 0,
      "name": "cosigner1",
      "authPubKey": "02348755bfeae058de77918f09c7f31397333715b83303014eeb36be0ac57f2310",
      "joinSignature": "2077152bbab4ad2743af8619f085f3856258b255786b2083532cbe829855d9cbc551cab29777108f23d9385e4821d7e48c66a14eac6462469e06bb3e92995e9712",
      "accountKey": "rpubKBAsBfPVMBBeBfD8F4EA5Dk1NAZ2dK7dc9JsE9dq9tDDF73NecpNP4z3gfMWuRm9rLVdSaeTj8BHZbVyHeRtV6UhciDz26pPDEntqCJvqKg2",
      "purpose": 44,
      "fingerPrint": 3664892090,
      "data": "63636363636363636363"
    },
    "1": {
      "id": 1,
      "name": "cosigner2",
      "authPubKey": "0325e5fb974a2e12f954edb3dd6d851a43d6744784f2a41e12c1060aefb783a702",
      "joinSignature": "1fb393639c0f8279ef3377ff0c1bb9067474f41a4b4b7301a2814d64c6abd1fb446b48f361ec91d42d4c9dd589337e98cedc24762618bebed82c2d32e8244587f4",
      "accountKey":  "rpubKBB7QfFrX68MzrYd7FdcjSnZmg7sPRQzYKQuEfkJGF4BWV68qu23S4wvjwvo8b4sqRwUf7uNCWmDwghKFv6tPnGFjXaY4TXQw9QQTy5zcjfu",
      "purpose": 44,
      "fingerPrint": 358396470,
      "data": ""
    }
  }
}

GET /multisig/:id/proposal/:pid

Cosigner authentication.

Get proposal by proposal id.

Params:

{
  // return transaction with proposal info.
  tx: true
}
await client.getProposalInfo(id, pid, true);

HTTP Response:

{
  "id": 0,
  "memo": "proposal1",
  "tx": "01000000000101cc5a323d7b52f1ef798254ddacb448da68439255a4a08fb9f4339a2e118fe3810000000000ffffffff01b4d2f505000000001976a914668a3ad48214fa364735b58d391ed15ec584aa9988ac0400000047522103621bd4659c36110df64219bb504db00e02d5d6871565c3c7c6008d91d8d41d4d2103dc953d900278c468603eace16ef7c09b777c8d8e463c40d41ecc18d02c958d8b52ae00000000",
  "author": 1,
  "approvals": {},
  "rejections": {},
  "signature": "20b22409672ef0986a18008a30b9e2a1132abfadbc30d3c6aa41373baf2c6f509b2e587249e4b2a8644722f25cc759c14bae5f14220c0367835f338d37556edaec",
  "options": {
    "memo": "proposal1",
    "timestamp": 1554551309,
    "txoptions": {
      "subtractFee": true,
      "outputs": [
        {
          "address": "RUQcLbkzLUv7FqoCcc8zQqyxAAdBKoSftr",
          "value": 100000000
        }
      ]
    }
  },
  "timestamp": 1554551309,
  "createdAt": 1554551309,
  "rejectedAt": null,
  "approvedAt": null,
  "m": 2,
  "n": 2,
  "statusCode": 0,
  "statusMessage": "Proposal is in progress.",
  "cosignerDetails": {
    "0": {
      "id": 0,
      "name": "cosigner1",
      "authPubKey": "02348755bfeae058de77918f09c7f31397333715b83303014eeb36be0ac57f2310",
      "joinSignature": "2077152bbab4ad2743af8619f085f3856258b255786b2083532cbe829855d9cbc551cab29777108f23d9385e4821d7e48c66a14eac6462469e06bb3e92995e9712",
      "accountKey": "rpubKBAsBfPVMBBeBfD8F4EA5Dk1NAZ2dK7dc9JsE9dq9tDDF73NecpNP4z3gfMWuRm9rLVdSaeTj8BHZbVyHeRtV6UhciDz26pPDEntqCJvqKg2",
      "purpose": 44,
      "fingerPrint": 3664892090,
      "data": "63636363636363636363"
    },
    "1": {
      "id": 1,
      "name": "cosigner2",
      "authPubKey": "02348755bfeae058de77918f09c7f31397333715b83303014eeb36be0ac57f2310",
      "joinSignature": "2077152bbab4ad2743af8619f085f3856258b255786b2083532cbe829855d9cbc551cab29777108f23d9385e4821d7e48c66a14eac6462469e06bb3e92995e9712",
      "accountKey": "rpubKBAsBfPVMBBeBfD8F4EA5Dk1NAZ2dK7dc9JsE9dq9tDDF73NecpNP4z3gfMWuRm9rLVdSaeTj8BHZbVyHeRtV6UhciDz26pPDEntqCJvqKg2",
      "purpose": 44,
      "fingerPrint": 3664892090,
      "data": ""
    }
  }
}

GET /multisig/:id/proposal/:name/tx

Get transaction details of the proposal. Cosigner authentication.

Query Params:

{
  paths: true, // return paths used for each input
  scripts: true // return pubScripts
}
await client.getProposalMTX(id, name, options);

HTTP Response:

{
  "tx": {
    "hash": "fe8fb96918ffd9e67925429b040700024b36e293106de6f57246fee045515302",
    "witnessHash": "fe8fb96918ffd9e67925429b040700024b36e293106de6f57246fee045515302",
    "fee": 6800,
    "rate": 42500,
    "mtime": 1554552579,
    "version": 1,
    "inputs": [
      {
        "prevout": {
          "hash": "5aecd3a218052027f29c8621f941000bee183667e38152e05c9340919bea2e43",
          "index": 0
        },
        "script": "0000004752210211ac31efebfe955f5c3a9b97aef12e876a22f366c6e9b12afdb1902b8e58dac1210352810849cd21a522ec55ae0dc14e16164b85ae4e789f8e1befb5422159fde79452ae",
        "witness": "00",
        "sequence": 4294967295,
        "coin": {
          "version": 1,
          "height": 1,
          "value": 100000000,
          "script": "a914cf1d47ba5ac5c07af7cd8945794fd2203aba793287",
          "address": "Gcj3NFyv5PH7GgVhDqUsdH9sKFSV1KBMgL",
          "coinbase": false
        }
      }
    ],
    "outputs": [
      {
        "value": 99993200,
        "script": "76a9145b6608d5a175e01fce73f11c957d8182be933a1788ac",
        "address": "RHcToTKiDp5ZdiayeXGxyjQZMx5PkYN91j"
      }
    ],
    "locktime": 0,
    "hex": "0100000001432eea9b9140935ce05281e3673618ee0b0041f921869cf227200518a2d3ec5a000000004b0000004752210211ac31efebfe955f5c3a9b97aef12e876a22f366c6e9b12afdb1902b8e58dac1210352810849cd21a522ec55ae0dc14e16164b85ae4e789f8e1befb5422159fde79452aeffffffff0170c6f505000000001976a9145b6608d5a175e01fce73f11c957d8182be933a1788ac00000000"
  },
  // maps to inputs
  "txs": [
    "01000000012908863f569b257d62b273e1a1d247e35a1da0ce2bc399240c4c414ed67178770000000000ffffffff0100e1f5050000000017a914cf1d47ba5ac5c07af7cd8945794fd2203aba79328700000000"
  ],
  "paths": [
    {
      "branch": 0,
      "index": 2,
      "receive": true,
      "change": false,
      "nested": false
    }
  ],
  "scripts": null
}

POST /multisig/:id/proposal/:name/approve

Cosigner authentication.

Approve with signatures, this will verify signatures when submitted. NOTE: This endpoint does not return cosigner details.

Params:

{
  // signatures for inputs (can be null), maps to inputs.
  "signatures": [
    "3044022060c8b70234e946814df040a021f34c6468b7d7cbc2aa070363b6ad86177a793802200b540cc622fed09625bc8ea1ba6e7fa70ca702afa3677e966f2138917b78395e01"
  ],
  // Do we want to broadcast transaction(if our approval was last once)
  "broadcast": true
}
await client.approveProposal(id, name, {
  signatures: [
    "3044022060c8b70234e946814df040a021f34c6468b7d7cbc2aa070363b6ad86177a793802200b540cc622fed09625bc8ea1ba6e7fa70ca702afa3677e966f2138917b78395e01"
  ],
  broadcast: true
});

HTTP Response:

{
  "broadcasted": true,
  // if you wanted to broadcast and it failed, this will contain error message.
  "broadcastError": null,
  "proposal": {
    "id": 1,
    "memo": "proposal2",
    "tx": "01000000000101bb9a9ba497d7b092c807b29ee1b4666e2a95a7d549c7ca258c4e916bd7d4e11c0000000000ffffffff01b4d2f505000000001976a914dce85ae1e11c2044aec3cf3fcfdc54fe796a2ad588ac0400483045022100e23caa92fdca32e9c50a186c651e7f54c150f329693a260e18d720148de7d687022041d740178ca29089450cc0b36b5503e28e6825dd1b4f92ba7304cf8d575a4bcb01473044022060c8b70234e946814df040a021f34c6468b7d7cbc2aa070363b6ad86177a793802200b540cc622fed09625bc8ea1ba6e7fa70ca702afa3677e966f2138917b78395e0147522102b7eaa9cbc9494db518f387a8e996abd83291a9ff3ad7d4b74ef7e8f43e67f275210302049c65a18d53a04bfa18672b3cb0de4214de806b36fc939a503fe02fa0743852ae00000000",
    "author": 0,
    "approvals": {
      "0": [
        "3045022100e23caa92fdca32e9c50a186c651e7f54c150f329693a260e18d720148de7d687022041d740178ca29089450cc0b36b5503e28e6825dd1b4f92ba7304cf8d575a4bcb01"
      ],
      "1": [
        "3044022060c8b70234e946814df040a021f34c6468b7d7cbc2aa070363b6ad86177a793802200b540cc622fed09625bc8ea1ba6e7fa70ca702afa3677e966f2138917b78395e01"
      ]
    },
    "rejections": {},
    "signature": "20264892b65f9c7d9540e76b0e9d59afc81d4f34f21612122d53d3fa7aa8d2587d04ea73efaa730fec489f77a06476cfa251eca476dc2a3b5d099addc84fdd6a47",
    "options": {
      "memo": "proposal2",
      "timestamp": 1555069077,
      "txoptions": {
        "subtractFee": true,
        "outputs": [
          {
            "address": "RVRF62ZqRutUHCy5b553ZjNWaQR6TUcb4p",
            "value": 100000000
          }
        ]
      }
    },
    "timestamp": 1555069077,
    "createdAt": 1555069077,
    "rejectedAt": null,
    "approvedAt": 1555069077,
    "m": 2,
    "n": 2,
    "statusCode": 1,
    "statusMessage": "Proposal has been approved.",
    "cosignerDetails": {}
  }
}

POST /multisig/:id/proposal/:name/reject

Cosigner or Admin authentication.

Reject proposal. In order to reject proposal, cosigner needs to sign proposalOptions that where used for creating proposal and are cached in proposal (and signed by author.) Cosigners will need local authPrivKeys to sign and signing process is same as the creation, with one difference:

  • walletName: test (hex: 74657374)
  • payload type will be rejection: 0x01.
  • so encoded version will be: 017b226d656d6f223a2270726f706f73616c31222c2274696d657374616d70223a313535343534393736392c2274786f7074696f6e73223a7b227375627472616374466565223a747275652c226f757470757473223a5b7b2261646472657373223a225253646e5432795a766461326a386666336359517a635a42346f7171456d465a444c222c2276616c7565223a3130303030303030307d5d7d7d.

When there are sufficient rejections (Not enough cosigners left to approve transaction) proposal will get rejected and locked coins will be released.

Params:

{
  // signed using `authPrivKey` of the cosigner.
  "signature":"2030a42cdd77ddefe0ff44012c75d710f990d764b10f4dce43c084e02a62aa871b3b00d1561e5be1632f723dfd3f87cfb25ff56ce033f47e595a1697810d52a5ee"
}

or Admin:

{
  "force": true
}
await client.rejectProposal(id, name, {
  signature: signature
});

// admin
await adminClient.rejectProposal(id, name, {
  force: true
});

HTTP Response:

{
  "id": 0,
  "memo": "proposal1",
  "tx": null,
  "author": 1,
  "approvals": {},
  "rejections": {
    "0": "2030a42cdd77ddefe0ff44012c75d710f990d764b10f4dce43c084e02a62aa871b3b00d1561e5be1632f723dfd3f87cfb25ff56ce033f47e595a1697810d52a5ee"
  },
  "signature": "1fbb47423e669edafaa48f1f05ae4367933d068b39514e30db1e4189b6b66a8be17f533d221206f7158a2bc88502da220cc9bf926ba7a50c9ea4e5c5d6e34cbb4f",
  "options": {
    "memo": "proposal1",
    "timestamp": 1555069254,
    "txoptions": {
      "subtractFee": true,
      "outputs": [
        {
          "address": "RP9MnZsGon9ue7ymmdZrmPypuPuWK9K75x",
          "value": 100000000
        }
      ]
    }
  },
  "timestamp": 1555069254,
  "createdAt": 1555069254,
  "rejectedAt": 1555069254,
  "approvedAt": null,
  "m": 2,
  "n": 2,
  "statusCode": 2,
  "statusMessage": "Cosigners rejected the proposal.",
  "cosignerDetails": {}
}

GET /multisig/:id/proposal/coin/:hash/:index

Cosigner or admin auth

Get proposal by coin (UTXO hash and index).

No parameters.

await client.getProposalByCoin(id, hash, index);

HTTP Reponse:

{
  "id": 1,
  "memo": "proposal1",
  "tx": null,
  "author": 1,
  "approvals": {},
  "rejections": {},
  "signature": "20106466afbc9d95f5f3e37290534f0cb6f6464189d24bf89b2cb40c566c3945e371045d9ab7630b20ad7548bcce0ee83c25bea954e82684cafc1772b49e768d2f",
  "options": {
    "memo": "proposal1",
    "timestamp": 1565901901,
    "txoptions": {
      "subtractFee": true,
      "outputs": [
        {
          "address": "RSuiCPBrELanmdXeLHoA6VqnHEm5XFsR7h",
          "value": 500000000
        }
      ]
    }
  },
  "timestamp": 1565901901,
  "createdAt": 1565901901,
  "rejectedAt": null,
  "approvedAt": null,
  "m": 2,
  "n": 2,
  "statusCode": 0,
  "statusMessage": "Proposal is in progress.",
  "cosignerDetails": {}
}

Import/Export

You can import and export wallets from the multisigdb with admin token. HTTP Endpoints return JSON serialized object, but they can be serialized in Binary format as well, using serializers from /lib/export.js.

GET /multisig/:id/export

Admin authorization.

await client.export(id);

HTTP Response:

{
  "watchOnly": true,
  "accountDepth": 1,
  "tokenDepth": 0,
  "token": "1111111111111111111111111111111111111111111111111111111111111111",
  "master": "003e04334be01bfa16e233a073d39172060f41f9c60ff4c9cd76b84a80aa74c8847bdb11e514aae4ac284a9b4f8544dfa61a2bcac17f536383122796ec464a438f018000028a85e0467678fc2499e375a76ecbc3dc",
  "joinPubKey": "0308c749ece3230fca2a324623963c6ec20c4be9056b0d0089ecb95f1bfbb29396",
  "timestamp": 1564595915,
  "accounts": [
    {
      "name": "default",
      "witness": true,
      "initialized": true,
      "watchOnly": true,
      "type": "multisig",
      "m": 2,
      "n": 2,
      "accountIndex": 0,
      "receiveDepth": 1,
      "changeDepth": 1,
      "nestedDepth": 1,
      "lookahead": 11,
      "accountKey": "rpubKBBV6Y2asgWUBVPmPfaBdufC4RxMoB2HYBxi1bN9bW9Pyodf5TWAJeog7NBsradD5MHe9M7RWkK6p5ZGir6iVHUiQdVSVrKVSU19xYmVQMLV",
      "keys": [
        "rpubKBBvERPndYY5wx6ibRr7JpsABertsCGLPDkk55TvMpKqtJku89WngaNickRxy6hrRmmzVmByFg1ocKiHmNas4m6RpeTmH7Uzwh9HdKC5fxqW"
      ]
    }
  ],
  "cosigners": [
    {
      "id": 0,
      "name": "cosigner1",
      "data": "63636363636363636363",
      "purpose": 44,
      "fingerPrint": 891844849,
      "accountKey": "rpubKBBV6Y2asgWUBVPmPfaBdufC4RxMoB2HYBxi1bN9bW9Pyodf5TWAJeog7NBsradD5MHe9M7RWkK6p5ZGir6iVHUiQdVSVrKVSU19xYmVQMLV",
      "authPubKey": "02bad879395e171121982a397d0a8fafb3063eea61d6984824798c08bc85add085",
      "joinSignature": "2043d1dfd38115082cd78a7de23d6a2e9a5ccc437022ebc07c8e81e57d8f1797be292683aeea5d43c3f25ff0976718ae0b080f4e132f7084630c4b5b7a3d74a99a",
      "token": "0101010101010101010101010101010101010101010101010101010101010101",
      "tokenDepth": 0
    },
    {
      "id": 1,
      "name": "cosigner2",
      "data": "",
      "purpose": 44,
      "fingerPrint": 2264922574,
      "accountKey": "rpubKBBvERPndYY5wx6ibRr7JpsABertsCGLPDkk55TvMpKqtJku89WngaNickRxy6hrRmmzVmByFg1ocKiHmNas4m6RpeTmH7Uzwh9HdKC5fxqW",
      "authPubKey": "0203c12bbb10cd45afde168b548765106f8255e43142d1250a1551496d31e23b17",
      "joinSignature": "2020342880c3db62e49b6450ce1f55f7334ce5185d6e49e3ae9c2ebe078c82b7f0365e4097fc68ec413606cf3158005daa935941b4a3f36bffee25936b2e25aa15",
      "token": "0202020202020202020202020202020202020202020202020202020202020202",
      "tokenDepth": 0
    }
  ]
}

POST /multisig/import

Admin authorization.

Params:

{
  "id": "wallet-name",
  "importOptions": {
    // Same object as returned by export.
  }
}

HTTP Response:

  // Same response as Create Wallet.