Skip to content

Commit 8208689

Browse files
authored
Update contractsignatures.rst
1 parent 9be8a47 commit 8208689

File tree

1 file changed

+43
-40
lines changed

1 file changed

+43
-40
lines changed

introduction/contractsignatures.rst

+43-40
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,63 @@
22
Контракты с подписью
33
################################################################################
44

5-
При формировании транзакции с контрактом, мы всегда имеем подпись отправившего лица. Эта подпись автоматически поверяется при разборе транзакции. Кроме этого, имеются функция **CallContract** и возможность вызова контракта в виде **ContractName(parameters)**. Разберем такую ситуацию, пусть имеется контракт перевода денег *send_money*.
5+
Поскольку язык написания контрактов позволяет выполнять вложенные контракты, то существует возможность выполнения такого вложенного контракта без ведома пользователя запустившего внешний контракт, что может привести к подписи пользователем несанкционированных им транзакций, скажем перевода денег со своего счета.
6+
7+
К примеру, пусть имеется контракт перевода денег *MoneyTransfer*:
68

79
.. code:: js
810
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+
}
1818
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** уже будет подпись.
2022

2123
.. code:: js
2224
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+
}
3333
34-
**Title**: Are you agree to send money this recipient?
3534
36-
**Parameter**: Receipient **Text**: Wallet ID
35+
2. Добавить в таблицу *Signatures* запись содержащую:
36+
* имя контракта *MoneyTransfer*,
37+
* имена полей, значения которых будут показываться пользователю, и их текстовое описание,
38+
* текст, который будет выводиться при подтверждении.
39+
40+
В текущем примере достаточно указать два поля **Receipient** и **Amount**:
41+
3742

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)
3946

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”", и подделать эту подпись невозможно.
4148
42-
Теперь рассмотрим, что нужно сделать, чтобы пользователь увидел при отправке нашего контракта подтверждение на перевод денег и контракт смог без проблем вызвать *send_money* с подписанными данными. Для этого в наш контракт необходимо добавить поле с типом *string* и дополнительной опцией **signature:contractname**. Имя поля может быть любое. И при вызове необходимо просто передать этот параметр. Также следует иметь в виду, что параметры для вызова защищенного контракта должны также присутствовать в нашем контракте, Они могут быть скрытые, но они все равно будут отображаться при подтверждении. Например,
49+
Для того, чтобы пользователь при вызове контракта *MoneyTransfer* увидел подтверждение на перевод денег, во внешний контракт необходимо добавить поле с произвольным названием и типом *string* и дополнительным параметром *signature:contractname*. При вызове вложенного контракта *MoneyTransfer* необходимо просто передать этот параметр. Также следует иметь в виду, что параметры для вызова защищенного контракта должны также быть описаны в секции *data* внешнего контракта (они могут быть скрытыми, но они все равно будут отображаться при подтверждении). Например,
4350

4451
.. code:: js
4552
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+
}
5462
}
55-
}
56-
57-
В данном случае, при отправке контракта *MyTest*, у пользователя будет запрошено дополнительное подтверждение для перевода определенной суммы на определенный кошелек. Если мы в контракте попытаемся указать другие параметры *send_money("Recipient,Amount,Signature",$Recipient,$Amount+10,$Signature)*, то получим ошибку, что подпись неверна.
58-
59-
60-
6163
64+
При отправке контракта *MyTest*, у пользователя будет запрошено дополнительное подтверждение для перевода суммы на указанный кошелек. Если во вложенном контракте будут указаны другие значения, например *MoneyTransfer(“Recipient,Amount,Signature”,$Recipient, $Amount+10, $Signature)*, то будет получена ошибку, что подпись неверна.

0 commit comments

Comments
 (0)