Использование мультиподписей (multisig) в Komodo

Decker
7 min readApr 30, 2019

--

Как создать и использовать multisig кошелек в Komodo?

Для начала давайте разберемся что такое multisig “кошельки” или скрипты с мультиподписями (multisignature scripts) в сети Komodo, а также случаи в которых нам может быть необходимо их использовать. Сразу отмечу, что сильно углубляться в теорию в этой статье я не буду, желающие могут ознакомиться с ней самостоятельно, например, в книге Андреаса М. Антонопулоса “Осваиваем биткойн”. Здесь мы лишь рассмотрим применение данной технологии на примерах. Руководствуясь этой статьей вы сможете изучить и использовать multisig на практике.

Представим что у Alice и Bob есть собственные средства накопленные в KMD и они договорились их потратить, например, на покупку нового ПК или для участия в грядущем ICO. Предварительно они договорились, что распоряжаться накопленными средствами они могут только вместе. Т.е. никто из участников договоренности не имеет права потратить ни сатоши из накопленной суммы без согласия другой стороны. При этом и та и другая сторона договоренности могут “пополнять” общий вклад, т.е. общую накопленную сумму без каких либо ограничений. Как Alice быть уверенной что Bob не потратит совместно накопленные средства без ее согласия? И как Bob может доверять Alice?

Именно для этой цели и были созданы мультиподписи (multisig). Alice и Bob могут создать “общий адрес”, который они могут пополнять без каких-либо ограничений, однако, для расходования средств с этого адреса потребуется чтобы и Alice и Bob подписали транзакцию своей уникальной подписью, т.е. “без согласия” другой стороны расходование средств будет невозможно. Существует множество других вариантов мультиподписи, например, M-of-N, где N - общее количество ключей, а M - минимальное количество подписей, требующихся для проверки и подтверждения. Например в варианте 2-of-3 всего имеется три ключа (3 участника), но для расходования средств достаточно 2-х подписей, т.е. согласия двух сторон. В нашем же случае с Alice и Bob’ом будет использоваться схема 2-of-2 как вы уже поняли. Т.к. всего у нас два участника и для распоряжения средствами будут требоваться подписи обеих сторон.

Перейдем к практике. На ПК Alice и Bob’а уже установлены и синхронизированы кошельки Komodo (в качестве кошельков можно использовать komodod, Agama, Komodo-Qt и т.п., главное условие, чтобы в кошельке так или иначе была возможность общения с демоном по RPC или с помощью консольных команд). Для наглядности, ниже, я приведу адреса Alice и Bob’а вместе с их pubkey (публичными ключами) и privkey (приватными ключами). Естественно, что в реальной жизни разглашать свои приватные ключи нельзя нигде и не при каких условиях, но т.к. наша статья носит обучающий характер, а по окончании ее написания никаких средств на адресах соответствующих этим приватным ключам не будет - то в данном случае пренебрежение этим правилом будет допустимым.

Bob

Address (Share)
RTF5FYE83Sn4Jkj4gti3DYXX72E9fCvRuU
Public key (Share)
0292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d3
Private key (WIF key)
UuGLCEfsqh8efWpCcgmEZfMz2hXjgmR381fBKow62hZDPC6Mjd1T

Address (Share)
RTRQMTj8kW8BKGSKiZ4hzXUFDQ6ZreC1Xw
Public key (Share)
026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e02
Private key (WIF key)
Uw9Kiujx45xFGevuFn9YxfuDvphFkMUsaZCpe9cmcavLLfJBorBR

Как можно увидеть в Explorer’е, изначально и на адресе Bob’а и на адресе Alice было по 0.5 KMD. Для того чтобы получить публичный ключ (public key) от адреса, достаточно использовать команду validateaddress в кошельке, для получения приватного ключа используется команда dumpprivkey. Теперь Alice и Bob должны создать multisig адрес, делается это следующим образом:

createmultisig 2 ‘[“0292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d3”, “026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e02”]’

Результатом выполнения команды является multisig адрес bEEdJPcdzDVFfAkzNKJJhEdGaXx5176qQ3 и redeemScript:

52210292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d321026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e0252ae

Адрес является публичным, т.е. переводить средства на него может кто угодно (!), а вот потратить средства могут только Alice и Bob, причем “договорившись” заранее, т.е. подписав транзакцию расходования средств своими приватными ключами по очереди. Для формирования транзакции расходования и Alice и Bob должны обладать reedem скриптом. Т.е. после создания multisig адреса, предполагается что они обменялись между собой этой информацией (т.е. что redeem скрипт известен каждому, либо Alice создала его и поделилась с Bob’ом, либо наооборот, либо каждый из них выполнил команду createmultisig у себя в кошельке и получил аналогичный результат).

Как мы видели, для создания multisig адреса нам были нужны только pubkey “подписантов”. Теперь Alice и Bob после того как они создали multisig адрес переводят на него свои 0.5 KMD:

Таким образом после совершения этих двух транзакций на multisig адресе bEEdJPcdzDVFfAkzNKJJhEdGaXx5176qQ3 у нас находится ровно 1 KMD:

Предположим, что пришло время потратить эти средства. Например, Alice с Bob’ом договорились о том, что они хотят перевести мне 1 KMD на адрес RDECKERmQJXWRN3n7o6gKy4vJrLqG16ufE в благодарность за эту статью. Что ж, давайте поможем им в этом.

Для начала давайте создадим “шаблон” транзакции для перевода, т.е. неподписанную raw транзакцию, которая отражает намерения Alice и Bob. Для этого нам потребуется знать vin (входы), которые они хотят потратить и vout (выходы), т.е. адрес получателя средств. Как видно из explorer’а адрес bEEdJPcdzDVFfAkzNKJJhEdGaXx5176qQ3 имеет две входящие транзакции:

  • txid: 47ea0630c827d77c3796030af1081d226d09202272caa1936158519b20c23106, vout: 0
  • txid: 75981a2a59a142b41e8be19945b4088f9a653b27ece8574005eba9c8f1b6316d, vout: 0

Где vout, это номер выхода в транзакции зачисления средств на multisig адрес. Воспользуемся командой createrawtransaction для создания неподписанной транзакции:

createrawtransaction ‘[{“txid”:”47ea0630c827d77c3796030af1081d226d09202272caa1936158519b20c23106",”vout”:0},{“txid”:”75981a2a59a142b41e8be19945b4088f9a653b27ece8574005eba9c8f1b6316d”,”vout”:0}]’ ‘{“RDECKERmQJXWRN3n7o6gKy4vJrLqG16ufE”:1.0}’ 0 0

Результатом выполнения команды является неподписанная транзакция:

0400008085202f89020631c2209b51586193a1ca722220096d221d08f10a0396377cd727c83006ea470000000000ffffffff6d31b6f1c8a9eb054057e8ec273b659a8f08b44599e18b1eb442a1592a1a98750000000000ffffffff0100e1f505000000001976a9142b4f73e17a4ef9613a979f2cc0b17dda83c5112088ac00000000000000000000000000000000000000

Теперь Alice и Bob по очереди подписывают ее своими приватными ключами следующим образом. На стороне Alice:

signrawtransaction “0400008085202f89020631c2209b51586193a1ca722220096d221d08f10a0396377cd727c83006ea470000000000ffffffff6d31b6f1c8a9eb054057e8ec273b659a8f08b44599e18b1eb442a1592a1a98750000000000ffffffff0100e1f505000000001976a9142b4f73e17a4ef9613a979f2cc0b17dda83c5112088ac00000000000000000000000000000000000000” ‘[{“txid”:”47ea0630c827d77c3796030af1081d226d09202272caa1936158519b20c23106",”vout”:0,”scriptPubKey”:”a914108563ce8e645a16745938f7f9114c634760c66b87",”redeemScript”: “52210292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d321026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e0252ae”,”amount”:0.5},{“txid”:”75981a2a59a142b41e8be19945b4088f9a653b27ece8574005eba9c8f1b6316d”,”vout”:0,”scriptPubKey”:”a914108563ce8e645a16745938f7f9114c634760c66b87",”redeemScript”: “52210292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d321026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e0252ae”,”amount”:0.5}]’ ‘[“Uw9Kiujx45xFGevuFn9YxfuDvphFkMUsaZCpe9cmcavLLfJBorBR”]’

В ответ мы получим что-то вроде этого:

Как видно, транзакция имеет статус complete = false, это потому что для подписи транзакции мы использовали только одну подпись Alice. Что ж, давайте скопируем значение частично подписанной транзакции из поля hex и подпишем ее подписью Bob (естественно что Alice и Bob подписывают транзакцию каждый на своем ПК):

signrawtransaction “0400008085202f89020631c2209b51586193a1ca722220096d221d08f10a0396377cd727c83006ea47000000009200483045022100a16791cfcd1d0a82f08ef61645d2cae6a7db0b44ecacc5f11b5572fb67683cdd02204a79c3377a154874225685025b374ca8d37e25f41171a65e36fedf6f93d8f717014752210292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d321026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e0252aeffffffff6d31b6f1c8a9eb054057e8ec273b659a8f08b44599e18b1eb442a1592a1a98750000000091004730440220279876756dde509a3e64827cab438000ad8d6ed4b384c94124117b33f3ca034802200c2fc9de9cb200a2ca7f7b4005a3378dde86bef32cbb90932dd09666e8929d17014752210292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d321026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e0252aeffffffff0100e1f505000000001976a9142b4f73e17a4ef9613a979f2cc0b17dda83c5112088ac00000000000000000000000000000000000000” ‘[{“txid”:”47ea0630c827d77c3796030af1081d226d09202272caa1936158519b20c23106",”vout”:0,”scriptPubKey”:”a914108563ce8e645a16745938f7f9114c634760c66b87",”redeemScript”: “52210292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d321026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e0252ae”,”amount”:0.5},{“txid”:”75981a2a59a142b41e8be19945b4088f9a653b27ece8574005eba9c8f1b6316d”,”vout”:0,”scriptPubKey”:”a914108563ce8e645a16745938f7f9114c634760c66b87",”redeemScript”: “52210292882183114bb1001eac9707e0b7437fa202dd9ad91112a339a387031fe9b1d321026d34dd8c87fed8b869d54cc117b9bdf1205c0104e89e60eefa5468f3fc9b6e0252ae”,”amount”:0.5}]’ ‘[“UuGLCEfsqh8efWpCcgmEZfMz2hXjgmR381fBKow62hZDPC6Mjd1T”]’

Как видно, статус comple = true. Отправим полученную транзакцию в сеть с помощью sendrawtransaction (это можно сделать с любого ПК или с помощью опции Broadcast Transaction в explorer’е):

Давайте посмотрим на результирующую транзакцию dab1a98c5a54f9dc30406d84ad749b964d6492f2f01f95c91c400bc2f7b40e78 :

Транзакция берет два входа по 0.5 KMD с нашего P2SH multisig адреса и объединяет их в 1 вход размером в 1 KMD на адрес RDECKERmQJXWRN3n7o6gKy4vJrLqG16ufE . При этом поле scriptSig каждого из vin’ов содержит подписи Alice и Bob’а а также redeem скрипт:

который выглядит вот так:

Отлично. Все получилось. Теперь давайте чуть более подробно остановимся на параметрах которые использовались нами при подписи транзакции:

  • hexstring - здесь думаю все понятно, это неподписанная raw транзакция в hex, которую мы получили с помощью createrawtransaction в случае Alice, и частично подписанная транзакция Alice в случае Bob.
  • prevtxs - это массив vin’ов, т.е. предыдущих транзакций, входы которых используются в подписываемой транзации. С полями txid и vout каждого vin’а я думаю все понятно, а вот scriptpubkey можно получить либо из vout’а предыдущих транзакций в которых мы переводили 0.5 KMD на multisig адрес, либо просто с помощью validateaddress bEEdJPcdzDVFfAkzNKJJhEdGaXx5176qQ3, он будет равен a914108563ce8e645a16745938f7f9114c634760c66b87 в нашем случае. redeemScript - это наш redeemScript, который мы получили при создании multisig адреса, ну а amount - это сумма vin’а (или prevout, что одно и то же), которая тратится. В данном случае мы тратим два входа по 0.5 KMD, поэтому и amount в каждом случае - 0.5.

Возможно вышеописанная схема с использованием консольных команд или RPC для оперирования с multisig адресами покажется вам не совсем удобной, но в то же время, она дает наилучшее представление о том как это работает изнутри.

Подведем несколько некоторые итоги:

  • В рассмотренном примере мы создали multisig адрес 2-of-2, на основе имеющихся публичных ключей от обоих адресов.
  • Адрес такого типа может использоваться например для сбора пожертвований, т.к. кто угодно может переводить средства на данный адрес, однако, чтобы потратить средства с него нужны именно две подписи. Так, например, Alice не сможет воспользоваться собранными средствами без подписи Bob’а и наоборот. В этом же заключается и еще одна “сложность”, если например один из участников потеряет свой приватный ключ, то воспользоваться собранными средствами не сможет никто.
  • Multisig адрес 2-of-2 является подмножеством P2SH (Pay-To-Script-Hash) адресов. В сети Komodo (KMD) эти адреса начинаются с строчной английской буквы b . Все multisig адреса так или иначе являются P2SH адресами, однако, не все P2SH обязательно являются multisig. Это означает что redeem скрипт может быть более сложным и предусматривать более сложные алгоритмы для подтверждения права расходования средств (в рассматриваемом примере для получения права расходования средств были необходимы подписи двух владельцев адреса, однако, при желании можно создать и более сложный скрипт, включающий в себя и другие условия, однако это уже выходит за рамки данной статьи).
  • Если вы впервые имеете дело с multisig адресами и хотите просто попробовать как это работает или повторить пример из статьи - рекомендую делать это с мелкими суммами, например 0.0001 KMD, которые не жалко потерять, в случае если вы где-то допустите ошибку. Помните, что если вы отправите средства на p2sh адрес reedem скрипта от которого у вас нет, или который создан неправильно, или в процессе выяснится что вы потеряли один из приватных ключей и т.п. - вы уже не сможете вернуть средства отправленные на этот адрес. Перед тем как проводить операции с основными средствами - несколько раз потренируйтесь. Понимание того что вы делаете - ключевой фактор, ну и, естественно, внимательность и отсутствие ошибок.

На этом пока все …

--

--