1Before you start
A wallet's sendtoaddress does four things for you: picks inputs, builds the transaction, signs it, and broadcasts it. This page does the four by hand, because that is the only way to do anything a plain wallet cannot: spend from a multisig address, sign on an offline machine, or build a payment for someone else to sign.
8 or 9 (mainnet D)You need a testnet node with a funded wallet. Everything here was run on two: Divi Core divid 3.0.0 (the C++ node, called divi-cli below) and IronDivi 0.2.6 (the Rust node, irondivi-cli). One signer lives in each. If you have only one node, every step still works; the second signature just comes from the same wallet. Get Testnet DIVI explains how to start a testnet node and fill its wallet; JSON-RPC from Python and JavaScript shows the same calls from code.
A raw transaction has no fee field. The fee is the difference between what the inputs hold and what the outputs pay out. Spend a 25,000 DIVI input, pay out 100, and forget the change output: the miner keeps 24,900. Always add a change output, and read the decoded transaction before signing. sendrawtransaction refuses a fee above 100 DIVI unless you pass allowhighfees, and that is the only safety net.
Do all of this on testnet until the numbers in the decoded transaction match what you expect every time. Mainnet mistakes are final.
2A transaction by hand: create, decode, sign, send
Four RPC calls. The first and second touch no keys and run anywhere, even on the public endpoint; the third needs the wallet that holds the key; the fourth needs any node.
Pick an input. listunspent lists what your wallet can spend. Each entry is one output of an earlier transaction, named by txid and vout:
divi-cli listunspent
[ { "txid": "bac42bf898fbed9f3b1ca4909f8dd3d36f153f61c0f16c14903550470a1bb9ff",
"vout": 0, "address": "xybfC8XGp89W5TQCfk8dBLjDvbWdE81DNe",
"amount": 25000.00000000, "confirmations": 12, "spendable": true } ]
Create. Inputs are a list of {txid, vout}; outputs are a map of address to amount. This one pays 100 DIVI to the multisig address made in section 3 and sends the rest back to a fresh change address of the same wallet, leaving 0.01 DIVI as the fee:
divi-cli createrawtransaction \
'[{"txid":"bac42bf898fbed9f3b1ca4909f8dd3d36f153f61c0f16c14903550470a1bb9ff","vout":0}]' \
'{"8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv":100,"xwAmrbDQG8PJuuT2VMFLTbULC4ZRoj8o6D":24899.99}'
0100000001ffb91b0a47503590146cf1c0613f156fd3d38d9f90a41c3b9fedfb98f82bc4ba0000000000ffffffff0200e40b540200000017a914122fa2dad22700b0019c3804593cb1938dafb24a87c08181bf430200001976a91402113ab33e6bdbbfc41b9de52cefc1e60fc029a988ac00000000
The result is the unsigned transaction as hex. Nothing has been stored or sent; it is just a string.
Decode and read it. Never sign what you have not read:
divi-cli decoderawtransaction 0100000001ffb91b…00000000
{ "txid": "1ad9d6c0ea405417f1029a82fb7e4be2f6dbcee725144d8600c7bf19f3ac2291",
"vin": [ { "txid": "bac42bf8…b9ff", "vout": 0, "scriptSig": { "asm": "", "hex": "" } } ],
"vout": [ { "value": 100.00000000, "n": 0, "scriptPubKey": { "type": "scripthash",
"addresses": [ "8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv" ] } },
{ "value": 24899.99000000, "n": 1, "scriptPubKey": { "type": "pubkeyhash",
"addresses": [ "xwAmrbDQG8PJuuT2VMFLTbULC4ZRoj8o6D" ] } } ] }
Check three things: the input is the one you meant, the outputs add up to the input minus the fee you intended (25,000 − 100 − 24,899.99 = 0.01), and the scriptSig is empty, which is what unsigned looks like. The txid shown here will change once the input is signed.
Sign. The wallet looks up the input, finds it owns the key, and fills in the scriptSig:
divi-cli signrawtransaction 0100000001ffb91b…00000000
{ "hex": "0100000001ffb91b0a…006a473044…ffffffff02…00000000",
"complete": true }
complete: true means every input has all the signatures it needs. The hex is longer now because the signature and public key are inside it.
Send. Broadcast the signed hex. The answer is the final transaction id:
divi-cli sendrawtransaction 0100000001ffb91b0a…00000000
6113a245794ab8172cff1a5ebf910671ddc67c5c36f36e54124fe8c886b87a15
It appeared in getrawmempool at once and had one confirmation about a minute later: 6113a245…7a15 in Explorer 2.0, block 359943.
IronDivi answers createrawtransaction, decoderawtransaction, signrawtransaction and sendrawtransaction with the same arguments (its signrawtransaction takes only the hex and an optional prevtxs list, no private keys; see section 5 for what that means for multisig). The public endpoint https://services.divi.domains/api/testnet/rpc/ allows createrawtransaction, decoderawtransaction and sendrawtransaction but refuses signrawtransaction with Method not allowed, because signing belongs on your own machine.
3Make a 2-of-2 multisig address
A multisig address is a script that says "any n of these m public keys must sign". Divi wraps it in pay-to-script-hash, so the address is the hash of that script and starts with 8 or 9 on testnet. The two signers here are address A in the IronDivi wallet and address B in the divid wallet.
Collect the public keys. Each signer runs validateaddress on one of its own addresses and shares the pubkey field (public keys are safe to share; private keys are not):
irondivi-cli validateaddress xxwaTGZFoB48cm6MYvvhfpXWP2mCwMHoXT # signer A { "ismine": true, "pubkey": "03b9e7f374712c1f85b242eb3da39e36c305291b43c719078d146e1b4bc6c025c1", … } divi-cli validateaddress xybfC8XGp89W5TQCfk8dBLjDvbWdE81DNe # signer B { "ismine": true, "pubkey": "02ac5c3fe394ed7c11b7ee26aa5226c5d76424aeff6a0e3bee4e235012a585b38b", … }
Make the address. createmultisig needs no wallet and no keys of its own, so anyone with both public keys gets the same answer. The key order matters: a different order is a different address.
divi-cli createmultisig 2 '["03b9e7f374712c1f85b242eb3da39e36c305291b43c719078d146e1b4bc6c025c1","02ac5c3fe394ed7c11b7ee26aa5226c5d76424aeff6a0e3bee4e235012a585b38b"]'
{ "address": "8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv",
"redeemScript": "522103b9e7f374712c1f85b242eb3da39e36c305291b43c719078d146e1b4bc6c025c12102ac5c3fe394ed7c11b7ee26aa5226c5d76424aeff6a0e3bee4e235012a585b38b52ae" }
IronDivi's createmultisig and the public endpoint's both returned exactly this. Keep the redeemScript. The address is only its hash; to spend, every signer needs the script itself. decodescript shows what is inside it:
divi-cli decodescript 522103b9e7…52ae
{ "asm": "2 03b9e7f3…025c1 02ac5c3f…b38b 2 OP_CHECKMULTISIG",
"reqSigs": 2, "type": "multisig",
"addresses": [ "xxwaTGZFoB48cm6MYvvhfpXWP2mCwMHoXT", "xybfC8XGp89W5TQCfk8dBLjDvbWdE81DNe" ],
"p2sh": "8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv" }
Tell each wallet about it. addmultisigaddress takes the same arguments and stores the script in the wallet, so the wallet recognises payments to it and can sign for its own key later without being handed the script again. Run it on every signer's node:
divi-cli addmultisigaddress 2 '["03b9e7f3…025c1","02ac5c3f…b38b"]' 8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv irondivi-cli addmultisigaddress 2 '["03b9e7f3…025c1","02ac5c3f…b38b"]' { "address": "8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv", "redeemScript": "5221…52ae" }
Afterwards divi-cli validateaddress 8g5cwLNM… reports "isscript": true, "script": "multisig", "sigsrequired": 2, and "ismine": false, "iswatchonly": true: the wallet watches the address but cannot spend from it alone, which is the whole point.
Lose the redeem script and a signer who has only the private key cannot spend. It is not secret, so keep a copy with each signer's wallet backup and in the same password manager as the recovery phrase (Proton Pass is free; 1Password, Bitwarden, Dashlane, Keeper and Apple Passwords all work).
4Fund it
The transaction in section 2 is the funding transaction: output 0 pays 100 DIVI to 8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv. A plain sendtoaddress 8g5cwLNM… 100 does the same in one call. Once it is mined, confirm the coins are there from three places:
divi-cli gettxout 6113a245794ab8172cff1a5ebf910671ddc67c5c36f36e54124fe8c886b87a15 0 { "confirmations": 2, "value": 100.00000000, "scriptPubKey": { "type": "scripthash", "hex": "a914122fa2dad22700b0019c3804593cb1938dafb24a87", "addresses": [ "8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv" ] } } curl -s -X POST https://services.divi.domains/api/testnet/rpc/ -H 'Content-Type: application/json' \ -d '{"method":"getaddressutxos","params":["8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv"]}' {"result":[{"address":"8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv","txid":"6113a245…7a15","outputIndex":0, "script":"a914122fa2dad22700b0019c3804593cb1938dafb24a87","satoshis":10000000000,"height":359943}],…}
and the address in Explorer 2.0. Note down three values from gettxout for the next step: the txid, the output index (0) and the scriptPubKey.hex.
divi-cli listunspent 0 9999999 '["8g5cwLNM…"]' returned [] even after the payment confirmed, because the wallet only lists outputs it can spend on its own and a 2-of-2 needs the other signer. Use gettxout, getaddressutxos or the explorer instead.
5Spend it with two signatures
Spending is the same create, sign, send, but the signing happens twice, on two machines, and the half-signed hex travels between them. This spend pays 50 DIVI to a customer address and 49.99 back to signer A, fee 0.01.
Create (either signer, or anyone):
divi-cli createrawtransaction \
'[{"txid":"6113a245794ab8172cff1a5ebf910671ddc67c5c36f36e54124fe8c886b87a15","vout":0}]' \
'{"yFA5y6f2pKUSeUMT7Lk7da3pydX2sG1WqF":50,"xxwaTGZFoB48cm6MYvvhfpXWP2mCwMHoXT":49.99}'
0100000001157ab886c8e84f12546ef3365c7cc6dd710691bf5e1aff2c17b84a7945a213610000000000ffffffff0200f2052a010000001976a914c762325e05f8f55a3561d5c6d2709363cbb9ffcb88acc0aff629010000001976a91415828e2cb633851b692393a20adebc5392d847cb88ac00000000
Describe the input. For a pay-to-script-hash input the signer must be told the scriptPubKey of the output being spent and the redeemScript behind it. That is the second argument of signrawtransaction; the help text marks redeemScript "required for P2SH":
PREV='[{"txid":"6113a245794ab8172cff1a5ebf910671ddc67c5c36f36e54124fe8c886b87a15","vout":0,
"scriptPubKey":"a914122fa2dad22700b0019c3804593cb1938dafb24a87",
"redeemScript":"522103b9e7f374712c1f85b242eb3da39e36c305291b43c719078d146e1b4bc6c025c12102ac5c3fe394ed7c11b7ee26aa5226c5d76424aeff6a0e3bee4e235012a585b38b52ae"}]'
First signature (signer B, on divid):
divi-cli signrawtransaction 0100000001157ab8…00000000 "$PREV"
{ "hex": "0100000001157ab886…9200483045022100eccb8b…0147522103b9e7…52aeffffffff02…00000000",
"complete": false }
complete: false is correct: one of two signatures is in. Decode the hex and look at the input's scriptSig.asm: it now reads 0 <signature B> <redeemScript>. The leading 0 is a placeholder the multisig opcode needs; the empty slot is where A's signature will go.
Try to send it now and the node refuses, with a code and no message:
divi-cli sendrawtransaction 0100000001157ab886…(half signed)
error: {"code":-25,"message":""}
Second signature (signer A). Hand the half-signed hex and the same PREV list to the second signer. On a second divid wallet that holds key A this is exactly the first call again, and it returns complete: true. In this test key A lives in IronDivi 0.2.6, whose signrawtransaction accepts prevtxs but ignores the redeemScript inside them, so it returned complete: false with the hex unchanged. The workaround used here is the third argument of divid's signrawtransaction: a list of private keys that "will be the only keys used to sign". Export A's key from IronDivi and hand it over inside one shell, never on the screen and never in a file:
K=$(irondivi-cli dumpprivkey xxwaTGZFoB48cm6MYvvhfpXWP2mCwMHoXT | tr -d '"\n')
divi-cli signrawtransaction 0100000001157ab886…(half signed) "$PREV" "[\"$K\"]"
unset K
{ "hex": "0100000001157ab886…db00483045022100de5dec…01483045022100eccb8b…0147522103b9e7…52aeffffffff02…00000000",
"complete": true }
The existing signature B was kept and A's was added in front of it: the decoded scriptSig.asm is 0 <signature A> <signature B> <redeemScript>, in the order of the keys in the script.
Moving a private key from one wallet to another defeats the point of multisig: whoever runs the second node now holds both keys. On mainnet each signer signs on its own machine and only the hex changes hands. The shortcut is on this page because it is what works with IronDivi 0.2.6 today, and because showing a half-signed hex refused and then accepted is the clearest proof that the second signature matters.
Send the complete hex from any node:
divi-cli sendrawtransaction 0100000001157ab886…(fully signed)
36a1bb249bec76e38e5b4d67bcc13131b60952b782c705aea8f09df024c0f5d5
Mined one block later: 36a1bb24…f5d5 in Explorer 2.0, block 359948. Sending the same hex a second time answers {"code":-27,"message":"transaction already in block chain"}, and gettxout 6113a245…7a15 0 now returns nothing, because the output is spent.
6Check it from outside
Anyone can verify the spend without a wallet. The public endpoint's getrawtransaction with verbosity 1 shows the input with its two signatures and the script, and which output spent what:
curl -s -X POST https://services.divi.domains/api/testnet/rpc/ -H 'Content-Type: application/json' \
-d '{"method":"getrawtransaction","params":["36a1bb249bec76e38e5b4d67bcc13131b60952b782c705aea8f09df024c0f5d5",1]}'
{ "txid": "36a1bb24…f5d5", "blockhash": "cd497a17…5512", "confirmations": 275,
"vin": [ { "txid": "6113a245…7a15", "vout": 0, "address": "8g5cwLNMtqTobZjzvxiBVWGogSENRz6bnv", "value": 100.0,
"scriptSig": { "asm": "0 3045…8b01 3045…7801 5221…52ae" } } ],
"vout": [ { "n": 0, "value": 50.0, "scriptPubKey": { "addresses": [ "yFA5y6f2pKUSeUMT7Lk7da3pydX2sG1WqF" ] },
"spentTxId": "39f86883…81c6", "spentHeight": 359949 },
{ "n": 1, "value": 49.99, "scriptPubKey": { "addresses": [ "xxwaTGZFoB48cm6MYvvhfpXWP2mCwMHoXT" ] } } ] }
The funding transaction's output 0 likewise carries "spentTxId": "36a1bb24…f5d5" once the spend is mined. A node with ZMQ on saw the spend twice: a hashtx when it entered the mempool and the same hash again when block cd497a17… arrived (ZMQ for developers).
7Send the coins back
Faucet coins are shared. When you are done, send what you have left to the faucet's return address, from each wallet, leaving a little for the fee:
divi-cli sendtoaddress yJehBfdK9KbrwPRjYrzkrPES9uG5nFLjY4 24949.9 irondivi-cli sendtoaddress yJehBfdK9KbrwPRjYrzkrPES9uG5nFLjY4 25049.9
Anything still sitting in a multisig address has to leave the way section 5 shows, with both signatures.
If something goes wrong
| You see | What it means |
|---|---|
"complete": false after signing a single-key input | The wallet does not hold the key for that input, or the input is not in its view yet. Check validateaddress says "ismine": true for the input's address, and pass prevtxs if the funding transaction is unconfirmed. |
"complete": false after the first multisig signature | Expected. One of two signatures is in; pass the hex to the next signer. |
"complete": false and the hex did not change | The signer added nothing. Either it holds none of the keys in the redeem script, or it is IronDivi 0.2.6 signing a P2SH input (it ignores redeemScript). Sign on divid, or use the private-key argument as in section 5. |
{"code":-25,"message":""} from sendrawtransaction | The node rejected the transaction's scripts. Usually a missing signature on a multisig input; also a wrong redeemScript or a key that is not in the script. |
{"code":-26,"message":"Unknown inputs being spent"} | An input's txid/vout does not exist, was already spent, or the node has not seen it yet. |
{"code":-26,"message":"Fees being paid are in excess of maximum fee, but fee override is not provided"} | Inputs minus outputs is above 100 DIVI. You almost certainly forgot the change output. Rebuild the transaction; do not pass allowhighfees unless you mean it. |
{"code":-27,"message":"transaction already in block chain"} | Already mined. Nothing to do. |
Invalid private key from signrawtransaction | A key in the third argument is not a valid base58 private key for this network (testnet keys come from a testnet wallet's dumpprivkey). |
Method not allowed: signrawtransaction | You sent it to the public endpoint, which never signs. Sign on your own node. |
listunspent shows nothing for the multisig address | Normal for a 2-of-2. Use gettxout, the public getaddressutxos, or the explorer. |
| A different multisig address than your co-signer got | The public keys were given in a different order, or one of you used an address where the other used a public key. Agree on the exact key list. |
For the node itself (ports, config, syncing) see Get Testnet DIVI. Still stuck? Ask the Divi chatbot, or see all tutorials.