Создание и использование

Генерация специализированного кода контракта с несколькими подписями

Прежде чем продолжить, убедитесь, что вы уже выполнили все шаги по настройке.

Чтобы увидеть список поддерживаемых контрактов и действий, запустите: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

Создание начального хранилища

Обратите внимание: в этом разделе используются определенные здесь функции bash.

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"

Администрирование второго контракта с помощью Specialized Multisig

Создание контракта Admin42

Контракт 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. Однако, в нашей попытке взять под контроль контракт в качестве Фреда, мы заменили ее открытый ключ и подпись. Однако контракт видит это и не принимает вызов.

Материалы разработаны TQ Tezos переведены на русский язык Tezos Ukraine

Last updated