Создание и использование
Чтобы увидеть список поддерживаемых конт рактов и действий, запустите:
stack exec -- lorentz-contract-multisig --help
Вы можете использовать инструмент
lorentz-contract-multisig
для генерации действительного Майкельсона для контракта с несколькими подписями, который вы хотите создать, специализированного для приема только указанного вами параметра.Предположим, мы хотим сгенерировать контракт с несколькими подписями, который принимает только
nat
в качестве допустимого аргумента. Мы можем сделать это с помощью специальной print-specialized
команды следующим образом:$ stack exec -- lorentz-contract-multisig GenericMultisig print-specialized --parameterType 'nat' --oneline
parameter (pair (pair (nat :counter) (or :action (pair nat (contract nat)) (pair (nat :threshold) (list :keys key)))) (list :sigs (option signature)));storage (pair nat (pair nat (list key)));code { CAST (pair (pair (pair nat (or (pair nat (contract nat)) (pair nat (list key)))) (list (option signature))) (pair nat (pair nat (list key))));DUP;CAR;DIP { CDR };DIP { };PUSH mutez 0;AMOUNT;COMPARE;EQ;IF { } { PUSH string "Some tokens were sent to this contract outside of a unit entry point.";FAILWITH };SWAP;DUP;DIP { SWAP };DIP { DUP;CAR;DIP { CDR };DUP;SELF;ADDRESS;CHAIN_ID;PAIR;PAIR;PACK;DIP { DUP;CAR;DIP { CDR };DIP { SWAP } };SWAP };DUP;CAR;DIP { CDR };DIP { SWAP };COMPARE;EQ;IF { } { PUSH string "Counters do not match.";FAILWITH };DIP { SWAP };DUP;CAR;DIP { CDR };DIP { PUSH nat 0;SWAP;ITER { DIP { SWAP };SWAP;IF_CONS { IF_NONE { SWAP;DROP } { SWAP;DIP { SWAP;DIP { DIP { DIP { DUP };SWAP } };DIP 2 { DUP };DIG 2;DIP { CHECK_SIGNATURE };SWAP;IF { DROP } { FAILWITH };PUSH nat 1;ADD } } } { FAILWITH };SWAP } };COMPARE;LE;IF { } { PUSH string "Quorum not present";FAILWITH };IF_CONS { FAILWITH } { };DROP;DIP { DUP;CAR;DIP { CDR };PUSH nat 1;ADD;PAIR };IF_LEFT { DIP { };SWAP;DIP { DUP;CAR;DIP { CDR };DIP { DIP { NIL operation };PUSH mutez 0 };TRANSFER_TOKENS;CONS };SWAP } { DIP { CAR };SWAP;PAIR;NIL operation };PAIR };
Скрипт
lorentz-contract-multisig
сгенерировал для нас действующий контракт Майкельсона. Мы можем сделать то же самое с любым допустимым типом Майкельсона.Если мы сгенерируем с картой от string до int, это будет выглядеть следующим образом:
$ stack exec -- lorentz-contract-multisig GenericMultisig print-specialized \
--parameterType 'map string int' --oneline
parameter (pair (pair (nat :counter) (or :action (pair (map string int) (contract (map string int))) (pair (nat :threshold) (list :keys key)))) (list :sigs (option signature)));storage (pair nat (pair nat (list key)));code { CAST (pair (pair (pair nat (or (pair (map string int) (contract (map string int))) (pair nat (list key)))) (list (option signature))) (pair nat (pair nat (list key))));DUP;CAR;DIP { CDR };DIP { };PUSH mutez 0;AMOUNT;COMPARE;EQ;IF { } { PUSH string "Some tokens were sent to this contract outside of a unit entry point.";FAILWITH };SWAP;DUP;DIP { SWAP };DIP { DUP;CAR;DIP { CDR };DUP;SELF;ADDRESS;CHAIN_ID;PAIR;PAIR;PACK;DIP { DUP;CAR;DIP { CDR };DIP { SWAP } };SWAP };DUP;CAR;DIP { CDR };DIP { SWAP };COMPARE;EQ;IF { } { PUSH string "Counters do not match.";FAILWITH };DIP { SWAP };DUP;CAR;DIP { CDR };DIP { PUSH nat 0;SWAP;ITER { DIP { SWAP };SWAP;IF_CONS { IF_NONE { SWAP;DROP } { SWAP;DIP { SWAP;DIP { DIP { DIP { DUP };SWAP } };DIP 2 { DUP };DIG 2;DIP { CHECK_SIGNATURE };SWAP;IF { DROP } { FAILWITH };PUSH nat 1;ADD } } } { FAILWITH };SWAP } };COMPARE;LE;IF { } { PUSH string "Quorum not present";FAILWITH };IF_CONS { FAILWITH } { };DROP;DIP { DUP;CAR;DIP { CDR };PUSH nat 1;ADD;PAIR };IF_LEFT { DIP { };SWAP;DIP { DUP;CAR;DIP { CDR };DIP { DIP { NIL operation };PUSH mutez 0 };TRANSFER_TOKENS;CONS };SWAP } { DIP { CAR };SWAP;PAIR;NIL operation };PAIR };
Вы можете сравнить два вывода выше и увидеть, где был вставлен новый тип.
Если бы мы попытались создать контракт с использованием недопустимого типа, мы бы получили ошибку синтаксического анализа. Инструмент
lorentz-contract-multisig
проверяет типы за вас.$ stack exec -- lorentz-contract-multisig GenericMultisig print-specialized \
--parameterType 'not-a-type' --oneline
ParseErrorBundle {bundleErrors = FancyError 0 (fromList [ErrorCustom UnknownTypeException]) :| [], bundlePosState = PosState {pstateInput = "not-a-type", pstateOffset = 0, pstateSourcePos = SourcePos {sourceName = "parameter", sourceLine = Pos 1, sourceColumn = Pos 1}, pstateTabWidth = Pos 8, pstateLinePrefix = ""}}
CallStack (from HasCallStack):
error, called at src/Lorentz/Contracts/GenericMultisig/Parsers.hs:246:29 in lorentz-contract-multisig-0.1.0.1-7nDq5hWdrcbHFov64Ydo4r:Lorentz.Contracts.GenericMultisig.Parsers
Generic Multisig позволяет нам установить администраторов контракта (
signerKeys
) и количество этих администраторов, которое необходимо для подписи (threshold
). На момент написания, инструмент командной строки разрешает делать администраторами только неявные учетные записи tz1
, хотя контракт разрешает также учетные записи, созданные KT1
.Чтобы сгенерировать Майкельсон для начального хранения, например для двух админов:
$ stack exec -- lorentz-contract-multisig GenericMultisig init-specialized \
--threshold 1 --signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key alice)\"]"
Pair 0 (Pair 1 { "edpkuPTVBFtbYd6gZWryXypSYYq6g7FvyucwphoU78T1vmGkbhj6qb"; "edpkvCHgVArnZo9RTP4P6euLTyhE89u73CYjBgsP4wEJbj4quao9oR" })
Обратите внимание, что для правильного анализа списка
signerKeys
вы должны избегать кавычек и разделять их запятыми и без пробелов.В приведенной выше команде мы добавили двух возможных подписывающих лиц и установили порог 1, поэтому, если мы инициализируем контракт с несколькими подписями с этим хранилищем, все транзакции в нем должны быть подписаны хотя бы одним из двух администраторов, чтобы список для транзакции стал действительным.
Если
threshold
установлен выше, чем количество администраторов, которые могут подписывать, выдается ошибка, поскольку невозможно иметь больше подписей администраторов, чем указано в списке signerKeys
:$ stack exec -- lorentz-contract-multisig GenericMultisig init-specialized \
--threshold 2 --signerKeys "[\"$(get_public_key bob)\"]"
threshold is greater than the number of signer keys
CallStack (from HasCallStack):
error, called at src/Lorentz/Contracts/GenericMultisig/CmdLnArgs.hs:302:13 in lorentz-contract-multisig-0.1.0.1-7nDq5hWdrcbHFov64Ydo4r:Lorentz.Contracts.GenericMultisig.CmdLnArgs
Используя команды, которые мы видели выше, теперь мы можем создать наш контракт, используя
tezos-client
, а также контракт и начальное хранилище, которые мы сгенерировали. Мы создадим контракт MultisigNat
с двумя администраторами, Бобом и Алисой, и разрешающий только тип параметров nat
.$ tezos-client --wait none originate contract MultisigNat transferring 0 from \
$BOB_ADDRESS running "$(stack exec -- lorentz-contract-multisig GenericMultisig \
print-specialized --parameterType 'nat' --oneline)" \
--init "$(stack exec -- lorentz-contract-multisig GenericMultisig \
init-specialized --threshold 1 \
--signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key alice)\"]")" \
--burn-cap 1.14
Waiting for the node to be bootstrapped before injection...
Current head: BLzWwsRQcRsp (timestamp: 2020-07-09T16:31:42-00:00, validation: 2020-07-09T16:32:10-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 31880 units (will add 100 for safety)
Estimated storage: 1140 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'oo9o8JHvRceyiUZ8DA1U3fTi8C6Nvc5mUBHxYGKTAXQa6BAddwT'
NOT waiting for the operation to be included.
Use command
tezos-client wait for oo9o8JHvRceyiUZ8DA1U3fTi8C6Nvc5mUBHxYGKTAXQa6BAddwT to be included --confirmations 30 --branch BLzWwsRQcRspFLWG5cWZF9No5g3VSZ5jgxnuDF3omYWg7ZVbHHR
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
Fee to the baker: ꜩ0.004353
Expected counter: 623986
Gas limit: 31980
Storage limit: 1160 bytes
Balance updates:
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ............. -ꜩ0.004353
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,269) ... +ꜩ0.004353
Origination:
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
Credit: ꜩ0
Script:
{ ... }
Initial storage:
(Pair 0
(Pair 1
{ "edpkuPTVBFtbYd6gZWryXypSYYq6g7FvyucwphoU78T1vmGkbhj6qb" ;
"edpkvCHgVArnZo9RTP4P6euLTyhE89u73CYjBgsP4wEJbj4quao9oR" }))
No delegate for this contract
This origination was successfully applied
Originated contracts:
KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3
Storage size: 883 bytes
Paid storage size diff: 883 bytes
Consumed gas: 31880
Balance updates:
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ... -ꜩ0.883
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ... -ꜩ0.257
New contract KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3 originated.
Contract memorized as MultisigNat.
Теперь мы можем сохранить адрес контракта в переменной bash:
$ MULTISIG_NAT_ADDRESS="KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3"
Контракт
MultisigNat
теперь можно использовать для управления контрактами, которые принимают в качестве параметров nat
. В этом руководстве мы собираемся использовать пример контракта, найденный в репозитории lorentz-contract-multisig
, который называется Admin42. Этот контракт требует, чтобы администратор взаимодействовал с ним, и принимает nat
в качестве параметра (точнее, только параметр 42
!), Поэтому мы можем использовать контракт MultisigNat
для его администрирования.(Дополнительную информацию о самом контракте Admin42 можно найти в репозитории
lorentz-contract-multisig
)Сначала мы создадим контракт Admin42, сделав наш контракт
MultisigNat
администратором.$ tezos-client --wait none originate contract MultisigAdmin42 transferring 0 \
from $BOB_ADDRESS running "$(cat admin_42.tz | tr '\n' ' ')" \
--init "\"$MULTISIG_NAT_ADDRESS\"" --burn-cap 0.406
Waiting for the node to be bootstrapped before injection...
Current head: BLGqGwqVRJEd (timestamp: 2020-07-09T16:35:12-00:00, validation: 2020-07-09T16:35:38-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 13516 units (will add 100 for safety)
Estimated storage: 406 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'op8Swwt9bYvQ58gbGPoZijJfx94tB2pbByVSco44PXuWme8oMwT'
NOT waiting for the operation to be included.
Use command
tezos-client wait for op8Swwt9bYvQ58gbGPoZijJfx94tB2pbByVSco44PXuWme8oMwT to be included --confirmations 30 --branch BLGqGwqVRJEdkEF9oborwD3BQkLKVuuWRxni6FwP155s5aqxKqQ
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
Fee to the baker: ꜩ0.001754
Expected counter: 623987
Gas limit: 13616
Storage limit: 426 bytes
Balance updates:
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ............. -ꜩ0.001754
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,269) ... +ꜩ0.001754
Origination:
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
Credit: ꜩ0
Script:
{ parameter nat ;
storage address ;
code { DUP ;
CDR ;
SENDER ;
ASSERT_CMPEQ ;
DUP ;
CAR ;
PUSH nat 42 ;
ASSERT_CMPEQ ;
CDR ;
NIL operation ;
PAIR } }
Initial storage: "KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3"
No delegate for this contract
This origination was successfully applied
Originated contracts:
KT1U2qHuZrKm7EwwRZ9j6XpZyd9y6VDhpQVo
Storage size: 149 bytes
Paid storage size diff: 149 bytes
Consumed gas: 13516
Balance updates:
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ... -ꜩ0.149
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ... -ꜩ0.257
New contract KT1U2qHuZrKm7EwwRZ9j6XpZyd9y6VDhpQVo originated.
Contract memorized as MultisigAdmin42.
И сохраним адрес контракта в переменной:
$ MULTISIG_ADMIN42_ADDRESS="KT1U2qHuZrKm7EwwRZ9j6XpZyd9y6VDhpQVo"
Когда мы взаимодействуем с нашим контрактом
MultisigNat
, мы должны включать достаточное количество подписывающих ключей администратора, чтобы соответствовать нашему пороговому значению. В этом случае у нас есть два администратора и порог 1, поэтому Боб или Алиса должны подписать любую транзакцию, которая в ызывает этот контракт, в противном случае она будет отклонена.Шаги для подписания:
- 1.Генерация байтов
- 2.Подпись байтов
- 3.Сохранение подписи
- 4.Запуск транзакции с помощью подписи
Давайте для начала подпишем операцию как Алиса.
Нам понадобится идентификатор цепочки, который мы можем получить из RPC:
$ tezos-client rpc get /chains/main/chain_id
"NetXjD3HPJJjmcd"
Установим bash переменную для идентификатора цепочки:
$ CHAIN_ID="NetXjD3HPJJjmcd"
Сначала мы сгенерируем байты для подписи с помощью инструмента
orentz-contract-multisig
.$ stack exec -- lorentz-contract-multisig GenericMultisig run-multisig \
--target-parameterType 'nat' --target-parameter '42' \
--target-contract "\"$MULTISIG_ADMIN42_ADDRESS\"" \
--multisig-contract "$MULTISIG_NAT_ADDRESS" --counter 0 --signatures "Nothing" \
--signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key alice)\"]" --chainId $CHAIN_ID
"0x05070707070a000000049caecab90a000000160198716bd8c37c014c606841e5f7398ed70d99a6a9000707000005050707002a0a0000001601d55850d12efa417a6d53f8290f027c2650390aef00"
Давайте разберемся с этим.
target-parameterType
: Мы уже знаем, что нашMultisigNat
иMultisigAdmin42
контракты принимают параметр типаnat
.target-parameter
: MultisigAdmin42 позволяет отправлять только значение параметра42
, поэтому мы передадим это как наш целевой параметр.target-contract
: Наша конечная цель - отправить параметр в$MULTISIG_ADMIN42_ADDRESS
, так что это наша цель.multisig-contract
: Контракт с несколькими подписями, управляющий нашим целевым контрактом - это$MULTISIG_NAT_ADDRESS
.counter
: Поскольку это первая транзакция, которую мы отправляем через нашMultisigNat
контракт, нашcounter
равен 0. Если вы не знаете правильного значения счетчика, вы можете использовать следующую команду для получения значения.stack exec -- lorentz-contract-multisig GenericMultisig get-counter-specialized --storageText "$(tezos-client get contract storage for $MULTISIG_NAT_ADDRESS)" --signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key alice)\"]"signatures
: Сейчас мы ничего не подписываем, поэтому аргумент"Nothing"
. Еще через пару шагов мы передадим сюда список подписей.signerKeys
: Это тот же самый список ключей, с которыми вы создали контракт (в том же порядке!). В качестве бонуса функцияget-counter-specialized
, описанная в маркереcounter
, подтвердит, что список открытых ключей, которые у вас есть, соответствует списку ключей в хранилище.chainId
: Это идентификатор цепочки, в которую мы отправляем. Включение его в сигнатуру предотвращает атаки повторного воспроизведения в разных цепочках.
Теперь возьмем байты, сгенерированные с помощью последней команды, и подпишем их как Алиса.
$ tezos-client sign bytes \
"0x05070707070a000000049caecab90a000000160198716bd8c37c014c606841e5f7398ed70d99a6a9000707000005050707002a0a0000001601d55850d12efa417a6d53f8290f027c2650390aef00" \
for alice
Signature: edsigtczKcLhrkFsRKhp9cUPdbv19z9g9ZUmKkHXLA42UJMJDJ2nQypNK2iwzbrGM2HHDQR5cGYSpyVPctSkM8jAC1zHGEKyVGc
И сохраним подпись
$ OPERATION_SIGNATURE="edsigtczKcLhrkFsRKhp9cUPdbv19z9g9ZUmKkHXLA42UJMJDJ2nQypNK2iwzbrGM2HHDQR5cGYSpyVPctSkM8jAC1zHGEKyVGc"
Теперь мы собираемся снова использовать инструмент
lorentz-contract-multisig
, но теперь мы собираемся добавить нашу подпись. Здесь следует отметить несколько моментов:- Несмотря на то, что я отправляю
$OPERATION_SIGNATURE
от Алисы, я все равно могу создать транзакцию как перевод с$BOB_ADDRESS
на$MULTISIG_NAT_ADDRESS
. Фактически, мы могли отправить эту транзакцию из$FRED_ADDRESS
, и пока у нас есть правильные подписи, он будет работать. - Следует отметить синтаксис аргумента
signatures
. Структура должна быть в виде списка, который соответствуетsignerKeys
, то есть для каждогоsignerKey
, в подписном листе должна быть соответствующая запись. В этом случае мы имеем только подпись Алисы. Алиса - второй открытый ключ в спискеsignerKeys
, когда мы структурируем наш список подписей, мы добавим$OPERATION_SIGNATURE
, которую она подписала в качестве второй записи в списке. Поскольку Боб не подписывает, мы ничего не помещаем в его индекс в списке. Так что у нас будет"Just[Nothing,Just\"$OPERATION_SIGNATURE\"]"
. Если бы Боб подписывал, а Алисы не было бы, у нас было бы"Just[Just\"$OPERATION_SIGNATURE\",Nothing]"
. А, если бы оба подписывали, у нас было бы"Just[Just\"$BOB_SIGNATURE\",Just\"$ALICE_SIGNATURE\"]"
. Далее мы покажем полный пример с несколькими подписывающими сторонами. - Мы должны указать нашу точку входа как
'mainParameter'
$ tezos-client --wait none transfer 0 from $BOB_ADDRESS to $MULTISIG_NAT_ADDRESS \
--arg "$(stack exec -- lorentz-contract-multisig \
GenericMultisig run-multisig --target-parameterType 'nat' \
--target-parameter '42' --target-contract "\"$MULTISIG_ADMIN42_ADDRESS\"" \
--multisig-contract "$MULTISIG_NAT_ADDRESS" --counter 0 \
--signatures "Just[Nothing,Just\"$OPERATION_SIGNATURE\"]" \
--signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key alice)\"]" \
--chainId $CHAIN_ID)" --burn-cap 0.000001
Waiting for the node to be bootstrapped before injection...
Current head: BMUPZBFduaBY (timestamp: 2020-07-09T16:37:12-00:00, validation: 2020-07-09T16:37:28-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 46362 units (will add 100 for safety)
Estimated storage: no bytes added
Operation successfully injected in the node.
Operation hash is 'ooqtqmu4MyKzS5Hw7TzBjvcS9B7KMVwQ8x846tPrLsqoXwq3BJM'
NOT waiting for the operation to be included.
Use command
tezos-client wait for ooqtqmu4MyKzS5Hw7TzBjvcS9B7KMVwQ8x846tPrLsqoXwq3BJM to be included --confirmations 30 --branch BMUPZBFduaBYE2SfjHkUK8NqfX3UVrKwCR3Q6ffMMmC3puVM5Fy
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
Fee to the baker: ꜩ0.005069
Expected counter: 623988
Gas limit: 46462
Storage limit: 0 bytes
Balance updates:
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ............. -ꜩ0.005069
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,269) ... +ꜩ0.005069
Transaction:
Amount: ꜩ0
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
To: KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3
Parameter: (Pair (Pair 0 (Left (Pair 42 "KT1U2qHuZrKm7EwwRZ9j6XpZyd9y6VDhpQVo")))
{ None ;
Some "edsigtczKcLhrkFsRKhp9cUPdbv19z9g9ZUmKkHXLA42UJMJDJ2nQypNK2iwzbrGM2HHDQR5cGYSpyVPctSkM8jAC1zHGEKyVGc" })
This transaction was successfully applied
Updated storage:
(Pair 1
(Pair 1
{ 0x00622ace8f1d06165b951d0362624033e6f6eb5650c45290ff0ddbff6055d2caa1 ;
0x00cc80ab168b04973d9e1f9d4d2248b077a9250d3bce750b2735b4818a7b9bb7d3 }))
Storage size: 883 bytes
Consumed gas: 32580
Internal operations:
Transaction:
Amount: ꜩ0
From: KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3
To: KT1U2qHuZrKm7EwwRZ9j6XpZyd9y6VDhpQVo
Parameter: 42
This transaction was successfully applied
Updated storage: 0x0198716bd8c37c014c606841e5f7398ed70d99a6a900
Storage size: 149 bytes
Consumed gas: 13782
Теперь, когда мы увидели, как выглядит подписание транзакции от имени Алисы, давайте посмотрим, как это выглядит, когда у нас есть оба подписывающих лица.
Шаги очень похожи на подписание с одной подписывающей стороной, но повторяются для каждой подписывающей стороны.
Сначала мы генерируем новые байты. Мы не можем использовать байты, сгенерированные на последнем шаге, потому что теперь счетчик увеличился, поскольку мы уже отправили транзакцию в контракт
MultisigNat
.$ stack exec -- lorentz-contract-multisig GenericMultisig run-multisig \
--target-parameterType 'nat' --target-parameter '42' \
--target-contract "\"$MULTISIG_ADMIN42_ADDRESS\"" \
--multisig-contract "$MULTISIG_NAT_ADDRESS" --counter 1 --signatures "Nothing" \
--signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key alice)\"]" --chainId $CHAIN_ID
"0x05070707070a000000049caecab90a000000160198716bd8c37c014c606841e5f7398ed70d99a6a9000707000105050707002a0a0000001601d55850d12efa417a6d53f8290f027c2650390aef00"
Теперь Алиса подпишет эти новые байты, и мы сохраним подпись как
ALICE_SIGNATURE
.$ tezos-client sign bytes \
"0x05070707070a000000049caecab90a000000160198716bd8c37c014c606841e5f7398ed70d99a6a9000707000105050707002a0a0000001601d55850d12efa417a6d53f8290f027c2650390aef00" \
for alice
Signature: edsigu2ay8M6gXj2LqBmc3fJ7tMDuNP8ExiCBMFjBsi3sPSFWR8SWERNaymMqAivm9aWVAWLwiqu4qYMtBKp7B7oujM8DLxC5BM
$ ALICE_SIGNATURE="edsigu2ay8M6gXj2LqBmc3fJ7tMDuNP8ExiCBMFjBsi3sPSFWR8SWERNaymMqAivm9aWVAWLwiqu4qYMtBKp7B7oujM8DLxC5BM"
И Боб сделает то же самое. Мы сохраним его подпись как
BOB_SIGNATURE
$ tezos-client sign bytes \
"0x05070707070a000000049caecab90a000000160198716bd8c37c014c606841e5f7398ed70d99a6a9000707000105050707002a0a0000001601d55850d12efa417a6d53f8290f027c2650390aef00" \
for bob
Signature: edsigtvraShNmfMppX65BDsJuaWjasWPie9qfNyTBHwGD2kyZMe7atTvm5CULseNSD4uKY2bzzGvQDq7Jks8yFp4DRdb9t5FKX4
$ BOB_SIGNATURE="edsigtvraShNmfMppX65BDsJuaWjasWPie9qfNyTBHwGD2kyZMe7atTvm5CULseNSD4uKY2bzzGvQDq7Jks8yFp4DRdb9t5FKX4"
$ tezos-client --wait none transfer 0 from $BOB_ADDRESS to $MULTISIG_NAT_ADDRESS \
--arg "$(stack exec -- lorentz-contract-multisig \
GenericMultisig run-multisig --target-parameterType 'nat' \
--target-parameter '42' --target-contract "\"$MULTISIG_ADMIN42_ADDRESS\"" \
--multisig-contract "$MULTISIG_NAT_ADDRESS" --counter 1 \
--signatures "Just[Just\"$BOB_SIGNATURE\",Just\"$ALICE_SIGNATURE\"]" \
--signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key alice)\"]" --chainId $CHAIN_ID)" \
--burn-cap 0.000001
Waiting for the node to be bootstrapped before injection...
Current head: BLkZFogRt69q (timestamp: 2020-07-09T16:39:12-00:00, validation: 2020-07-09T16:39:32-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 47004 units (will add 100 for safety)
Estimated storage: no bytes added
Operation successfully injected in the node.
Operation hash is 'op3zyuj5wFPxyyrLAicv99DBWxHdAWU9obAwmGPEULj5gy3bEUq'
NOT waiting for the operation to be included.
Use command
tezos-client wait for op3zyuj5wFPxyyrLAicv99DBWxHdAWU9obAwmGPEULj5gy3bEUq to be included --confirmations 30 --branch BLkZFogRt69qpkNRupifySnJfiPZTpoPTRn2SfgEDwsaK4xQCoX
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
Fee to the baker: ꜩ0.005237
Expected counter: 623989
Gas limit: 47104
Storage limit: 0 bytes
Balance updates:
tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm ............. -ꜩ0.005237
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,269) ... +ꜩ0.005237
Transaction:
Amount: ꜩ0
From: tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm
To: KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3
Parameter: (Pair (Pair 1 (Left (Pair 42 "KT1U2qHuZrKm7EwwRZ9j6XpZyd9y6VDhpQVo")))
{ Some "edsigtvraShNmfMppX65BDsJuaWjasWPie9qfNyTBHwGD2kyZMe7atTvm5CULseNSD4uKY2bzzGvQDq7Jks8yFp4DRdb9t5FKX4" ;
Some "edsigu2ay8M6gXj2LqBmc3fJ7tMDuNP8ExiCBMFjBsi3sPSFWR8SWERNaymMqAivm9aWVAWLwiqu4qYMtBKp7B7oujM8DLxC5BM" })
This transaction was successfully applied
Updated storage:
(Pair 2
(Pair 1
{ 0x00622ace8f1d06165b951d0362624033e6f6eb5650c45290ff0ddbff6055d2caa1 ;
0x00cc80ab168b04973d9e1f9d4d2248b077a9250d3bce750b2735b4818a7b9bb7d3 }))
Storage size: 883 bytes
Consumed gas: 33222
Internal operations:
Transaction:
Amount: ꜩ0
From: KT1NUp9a8gC5xQJDi4E9hr2WRZpUxx8BQrT3
To: KT1U2qHuZrKm7EwwRZ9j6XpZyd9y6VDhpQVo
Parameter: 42
This transaction was successfully applied
Updated storage: 0x0198716bd8c37c014c606841e5f7398ed70d99a6a900
Storage size: 149 bytes
Consumed gas: 13782
Мы успешно подписали контракт с несколькими администраторами!
Если кто-то, не являющийся администратором, попытается подписаться, мы получим ошибку моделирования трансфера. Давайте попробуем использовать этот контракт с несколькими подписями, чтобы подписаться от имени Фреда.
$ stack exec -- lorentz-contract-multisig GenericMultisig run-multisig \
--target-parameterType 'nat' --target-parameter '42' \
--target-contract "\"$MULTISIG_ADMIN42_ADDRESS\"" \
--multisig-contract "$MULTISIG_NAT_ADDRESS" --counter 4 --signatures "Nothing" \
--signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key fred)\"]" --chainId $CHAIN_ID
"0x05070707070a000000049caecab90a0000001601e434087b4651ce2e3132592129765f42427bde9d000707000405050707002a0a000000160122d24b94f44833a82467937ddf66a5a8960a28b200"
$ tezos-client sign bytes \
"0x05070707070a000000049caecab90a0000001601e434087b4651ce2e3132592129765f42427bde9d000707000405050707002a0a000000160122d24b94f44833a82467937ddf66a5a8960a28b200" \
for fred
Signature: edsigu3vWQjfgAH63MbXDx2Tv6j1DQ4x73Zebhvfcqg3Vw9rzaG1j6LUFPPVr9dFVySS9BPwYf5323ECRETLBcgFWyqazDcJN6z
$ FRED_SIGNATURE="edsigu3vWQjfgAH63MbXDx2Tv6j1DQ4x73Zebhvfcqg3Vw9rzaG1j6LUFPPVr9dFVySS9BPwYf5323ECRETLBcgFWyqazDcJN6z"
$ tezos-client --wait none transfer 0 from $BOB_ADDRESS to $MULTISIG_NAT_ADDRESS \
--arg "$(stack exec -- lorentz-contract-multisig \
GenericMultisig run-multisig --target-parameterType 'nat' \
--target-parameter '42' --target-contract "\"$MULTISIG_ADMIN42_ADDRESS\"" \
--multisig-contract "$MULTISIG_NAT_ADDRESS" --counter 4 \
--signatures "Just[Just\"$BOB_SIGNATURE\",Just\"$FRED_SIGNATURE\"]" \
--signerKeys "[\"$(get_public_key bob)\",\"$(get_public_key fred)\"]" --chainId $CHAIN_ID)" \
--burn-cap 0.000001
CallStack (from HasCallStack):
error, called at src/Lorentz/Contracts/GenericMultisig/CmdLnArgs.hs:397:25 in lorentz-contract-multisig-0.1.0.1-7nDq5hWdrcbHFov64Ydo4r:Lorentz.Contracts.GenericMultisig.CmdLnArgs
empty expression
Fatal error:
transfer simulation failed
Обратите внимание, что передача не удалась из-за «предоставленной недействительной подписи». Контракт предполагает, что открытый ключ Алисы будет находиться в
signerKeys
с индексом 1, а ее подпись (или Nothing
) будет предоставлена в списке подписей с индексом 1. Однако, в нашей попытке взять под контроль контракт в качестве Фреда, мы заменили ее открытый ключ и подпись. Однако контракт видит это и не принимает вызов.