|
2 | 2 | Контракты с подписью
|
3 | 3 | ################################################################################
|
4 | 4 |
|
5 |
| -При формировании транзакции с контрактом, мы всегда имеем подпись отправившего лица. Эта подпись автоматически поверяется при разборе транзакции. Кроме этого, имеются функция **CallContract** и возможность вызова контракта в виде **ContractName(parameters)**. Разберем такую ситуацию, пусть имеется контракт перевода денег *send_money*. |
| 5 | +Поскольку язык написания контрактов позволяет выполнять вложенные контракты, то существует возможность выполнения такого вложенного контракта без ведома пользователя запустившего внешний контракт, что может привести к подписи пользователем несанкционированных им транзакций, скажем перевода денег со своего счета. |
| 6 | + |
| 7 | +К примеру, пусть имеется контракт перевода денег *MoneyTransfer*: |
6 | 8 |
|
7 | 9 | .. code:: js
|
8 | 10 |
|
9 |
| - contract send_money { |
10 |
| - data { |
11 |
| - Recipient int |
12 |
| - Amount money |
13 |
| - } |
14 |
| - ... |
15 |
| - } |
16 |
| - |
17 |
| -Если мы в каком-то контракте вызовем его как *send_money("Recipient,Amount", 12345, 777)*, то у нас будет осуществлен перевод 777 монет на кошелек 12345. Проблема в том, что пользователь подписывающий первичный контракт мог быть вообще не курсе, что с него будут списаны деньги. Предположим, мы хотим сделать так, чтобы контракт send_money требовал дополнительной подписи пользователя при вызове из других контрактов. Что для этого нужно сделать? |
| 11 | + contract MoneyTransfer { |
| 12 | + data { |
| 13 | + Recipient int |
| 14 | + Amount money |
| 15 | + } |
| 16 | + ... |
| 17 | + } |
18 | 18 |
|
19 |
| -1. Нужно добавить в *send_money* поле с именем **Signature** и опциями *optional* и *hidden*. Эти опции позволят не требовать дополнительной подписи при вызове контракта напрямую, так как там уже будет подпись. |
| 19 | +Если в некотором контракте, запущенном пользователем, будет вписана строка MoneyTransfer(“Recipient,Amount”, 12345, 100), то будет осуществлен перевод 100 монет на кошелек 12345. При этом пользователь, подписывающий внешний контракт, останется не в курсе осуществленной транзакции. Исключить такую ситуацию возможно, если контракт MoneyTransfer будет требовать получения дополнительной подписи пользователя при вызове его из других контрактов. Для этого необходимо: |
| 20 | + |
| 21 | +1. Добавить в секцию *data* контракта *MoneyTransfer* поле с именем **Signature** с параметрами *optional* и *hidden*, которые позволяют не требовать дополнительной подписи при прямом вызове контракта, поскольку в поле **Signature** уже будет подпись. |
20 | 22 |
|
21 | 23 | .. code:: js
|
22 | 24 |
|
23 |
| - contract send_money { |
24 |
| - data { |
25 |
| - Recipient int |
26 |
| - Amount money |
27 |
| - Signature string "optional hidden" |
28 |
| - } |
29 |
| - ... |
30 |
| - } |
31 |
| - |
32 |
| -2. Нужно добавить в таблицу **Signatures** запись с именем нашего контракта *send_money*. Также там мы указываем текст, который будет показываться при подтверждении и указываем пары значений. В качестве параметра указываем имена полей, значения которыx будут показываться пользователю, а в качестве текста указываем понятное имя для данного параметра. Необязательно добавлять туда все поля, которые есть в контракте, но в нашем случае у нас всего два важных поля, поэтому укажем оба. Например, мы можем указать следующее: |
| 25 | + contract MoneyTransfer { |
| 26 | + data { |
| 27 | + Recipient int |
| 28 | + Amount money |
| 29 | + Signature string "optional hidden" |
| 30 | + } |
| 31 | + ... |
| 32 | + } |
33 | 33 |
|
34 |
| -**Title**: Are you agree to send money this recipient? |
35 | 34 |
|
36 |
| -**Parameter**: Receipient **Text**: Wallet ID |
| 35 | +2. Добавить в таблицу *Signatures* запись содержащую: |
| 36 | + * имя контракта *MoneyTransfer*, |
| 37 | + * имена полей, значения которых будут показываться пользователю, и их текстовое описание, |
| 38 | + * текст, который будет выводиться при подтверждении. |
| 39 | + |
| 40 | +В текущем примере достаточно указать два поля **Receipient** и **Amount**: |
| 41 | + |
37 | 42 |
|
38 |
| -**Parameter**: Amount **Text**: Amount (qEGS) |
| 43 | +* **Title**: Are you agree to send money this recipient? |
| 44 | +* **Parameter**: Receipient Text: Wallet ID |
| 45 | +* **Parameter**: Amount Text: Amount (qEGS) |
39 | 46 |
|
40 |
| -Этих двух шагов достаточно, чтобы защитить наш контракт от несанкционированных вызовов из других контрактов. Если вставить вызов *send_money("Recipient,Amount",123466,12)*, то мы получим ошибку *Signature is not defined*. Если мы вызовем так *send_money("Recipient,Amount,Signature",123466,12,"xxx...xxxxx")*, то мы получим ошибку при проверке подписи, так как нам не подделать подпись пользователя. При отправке будут подписываться следующие данные: *"время оригинальной транзакции,id пользователя,значение полей указанных в таблице signatures"*. Соответственно при вызове контракта будет проверяться подпись этих данных. |
| 47 | +Теперь если вставить вызов контракта *MoneyTransfer(“Recipient, Amount”, 12345, 100)*, то будет получена системная ошибка *“Signature is not defined”*. Если же контракт будет вызван следующим образом *MoneyTransfer(“Recipient, Amount, Signature”, 12345, 100, ”xxx...xxxxx”), то возникнет ошибка при проверке подписи. При вызове контракта проверяется подпись следующих данных: "“время оригинальной транзакции, id пользователя, значение полей указанных в таблице signatures”", и подделать эту подпись невозможно. |
41 | 48 |
|
42 |
| -Теперь рассмотрим, что нужно сделать, чтобы пользователь увидел при отправке нашего контракта подтверждение на перевод денег и контракт смог без проблем вызвать *send_money* с подписанными данными. Для этого в наш контракт необходимо добавить поле с типом *string* и дополнительной опцией **signature:contractname**. Имя поля может быть любое. И при вызове необходимо просто передать этот параметр. Также следует иметь в виду, что параметры для вызова защищенного контракта должны также присутствовать в нашем контракте, Они могут быть скрытые, но они все равно будут отображаться при подтверждении. Например, |
| 49 | +Для того, чтобы пользователь при вызове контракта *MoneyTransfer* увидел подтверждение на перевод денег, во внешний контракт необходимо добавить поле с произвольным названием и типом *string* и дополнительным параметром *signature:contractname*. При вызове вложенного контракта *MoneyTransfer* необходимо просто передать этот параметр. Также следует иметь в виду, что параметры для вызова защищенного контракта должны также быть описаны в секции *data* внешнего контракта (они могут быть скрытыми, но они все равно будут отображаться при подтверждении). Например, |
43 | 50 |
|
44 | 51 | .. code:: js
|
45 | 52 |
|
46 |
| - contract MyTest { |
47 |
| - data { |
48 |
| - Recipient int "hidden" |
49 |
| - Amount money |
50 |
| - Signature string "signature:send_money" |
51 |
| - } |
52 |
| - func action { |
53 |
| - send_money("Recipient,Amount,Signature",$Recipient,$Amount,$Signature) |
| 53 | + contract MyTest { |
| 54 | + data { |
| 55 | + Recipient int "hidden" |
| 56 | + Amount money |
| 57 | + Signature string "signature:send_money" |
| 58 | + } |
| 59 | + func action { |
| 60 | + MoneyTransfer("Recipient,Amount,Signature",$Recipient,$Amount,$Signature) |
| 61 | + } |
54 | 62 | }
|
55 |
| - } |
56 |
| -
|
57 |
| -В данном случае, при отправке контракта *MyTest*, у пользователя будет запрошено дополнительное подтверждение для перевода определенной суммы на определенный кошелек. Если мы в контракте попытаемся указать другие параметры *send_money("Recipient,Amount,Signature",$Recipient,$Amount+10,$Signature)*, то получим ошибку, что подпись неверна. |
58 |
| - |
59 |
| - |
60 |
| - |
61 | 63 |
|
| 64 | +При отправке контракта *MyTest*, у пользователя будет запрошено дополнительное подтверждение для перевода суммы на указанный кошелек. Если во вложенном контракте будут указаны другие значения, например *MoneyTransfer(“Recipient,Amount,Signature”,$Recipient, $Amount+10, $Signature)*, то будет получена ошибку, что подпись неверна. |
0 commit comments