Контракты с подписью

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

К примеру, пусть имеется контракт перевода денег MoneyTransfer:

contract MoneyTransfer {
    data {
      Recipient int
      Amount    money
    }
    ...
}

Если в некотором контракте, запущенном пользователем, будет вписана строка MoneyTransfer(«Recipient,Amount», 12345, 100), то будет осуществлен перевод 100 монет на кошелек 12345. При этом пользователь, подписывающий внешний контракт, останется не в курсе осуществленной транзакции. Исключить такую ситуацию возможно, если контракт MoneyTransfer будет требовать получения дополнительной подписи пользователя при вызове его из других контрактов. Для этого необходимо:

  1. Добавить в секцию data контракта MoneyTransfer поле с именем Signature с параметрами optional и hidden, которые позволяют не требовать дополнительной подписи при прямом вызове контракта, поскольку в поле Signature уже будет подпись.
contract MoneyTransfer {
    data {
      Recipient int
      Amount    money
      Signature string "optional hidden"
    }
    ...
}
  1. Добавить в таблицу Signatures запись содержащую:
  • имя контракта MoneyTransfer,
  • имена полей, значения которых будут показываться пользователю, и их текстовое описание,
  • текст, который будет выводиться при подтверждении.

В текущем примере достаточно указать два поля Receipient и Amount:

  • Title: Are you agree to send money this recipient?
  • Parameter: Receipient Text: Wallet ID
  • Parameter: Amount Text: Amount (qEGS)

Теперь если вставить вызов контракта MoneyTransfer(«Recipient, Amount», 12345, 100), то будет получена системная ошибка «Signature is not defined». Если же контракт будет вызван следующим образом MoneyTransfer(«Recipient, Amount, Signature», 12345, 100, «xxx…xxxxx»), то возникнет ошибка при проверке подписи. При вызове контракта проверяется подпись следующих данных: «»время оригинальной транзакции, id пользователя, значение полей указанных в таблице signatures»«, и подделать эту подпись невозможно.

Для того, чтобы пользователь при вызове контракта MoneyTransfer увидел подтверждение на перевод денег, во внешний контракт необходимо добавить поле с произвольным названием и типом string и дополнительным параметром signature:contractname. При вызове вложенного контракта MoneyTransfer необходимо просто передать этот параметр. Также следует иметь в виду, что параметры для вызова защищенного контракта должны также быть описаны в секции data внешнего контракта (они могут быть скрытыми, но они все равно будут отображаться при подтверждении). Например,

contract MyTest {
  data {
      Recipient int "hidden"
      Amount    money
      Signature string "signature:send_money"
  }
  func action {
      MoneyTransfer("Recipient,Amount,Signature",$Recipient,$Amount,$Signature)
  }
}

При отправке контракта MyTest, у пользователя будет запрошено дополнительное подтверждение для перевода суммы на указанный кошелек. Если во вложенном контракте будут указаны другие значения, например MoneyTransfer(«Recipient,Amount,Signature»,$Recipient, $Amount+10, $Signature), то будет получена ошибку, что подпись неверна.