ОРИГИНАЛЬНАЯ СТАТЬЯ
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro плюс немного отсебятины для нашего развития
$10 на gas для Jolah Molivski ---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09
Децентрализованный мир растет в геометрической прогрессии с точки зрения принятия пользователями. Компании ежегодно создают сотни инновационных приложений dApp на Ethereum для удовлетворения различных требований рынка. В основе этих dApps лежат смарт-контракты, совместимые с EVM, в основном написанные на Solidity. Хотя изучение синтаксиса и мотодов разработки на solidity несложно, но для создания масштабируемого, удобного и безопасного контракта необходимо понимать правильные шаблоны проектирования.
Одно из основных различий между программистом-новичком и профессиональным разработчиком заключается в понимании соответствующих шаблонов проектирования. Эти шаблоны являются проверенными решениями для распространенных проблем и могут стать вашим самым мощным инструментом.
По сравнению с другими направлениями разработки программного обеспечения, разработка надежных смарт-контрактов является сложной задачей. Распределенный конечный автомат запускает вашу развернутую программу 24/7, предоставляя доступ ко всему миру, это палка о двух концах. Чтобы убедиться, что ваша бизнес-логика работает безопасным, надежным и детерминированным образом, необходимо следовать лучшим практикам проектирования.
На платформе, где самая маленькая ошибка может стоить вам миллионы долларов, вы бы рискнули?
Неспособность правильно справиться с этим может привести к непредсказуемым вариантам использования.
Некоторые цели, которые могут быть достигнуты шаблонами авторизации:
Концепция:
Перед выполнением логики функции вызывающая сторона должна выполнить определенные условия. Эти условия могут быть связаны с личностью вызывающего абонента, входными параметрами, состоянием контракта и т. д. Функция запускается после выполнения всех требований. Если требования не выполняются, EVM обрабатывает ошибку, возвращая состояние, не внося изменений в вызов функции. Если подобное ограничение необходимо для нескольких функций, можно использовать защитную проверку с модификаторами. Кроме того, если вы не хотите, чтобы переменные и функции были доступны публично, защитите данные их состоянии, выбрав частную видимость.
Пример кода:
Концепция:
Бывают ситуации, когда транзакция или вызов функции могут потребовать подтверждения нескольких пользователей. Таким образом, если пул авторизаторов имеет размер X и нам требуется подмножество Y, нет авторизаций (где X≥Y) для выполнения транзакции. Это полезный подход, когда задействованы платежные транзакции на основе мультиподписи. Проблема в этом подходе заключается в том, что члены в группе авторизации должны быть определены заранее, а минимальное количество авторизаторов должно быть доступно во время выполнения для подписи. Также в случае компрометации/утери ключа учетная запись авторизатора становится бесполезной.
Взята отсюда
Концепция:
Развернутый контракт может иметь выделенную работу и доступ на основе ролей, как и в любой традиционной системе. Распределение ролей и администрирование могут быть выполнены путем применения правил управления доступом на основе прав собственности и ролей. Доступ на основе ролей может помочь разработать модификаторы для конкретных функций, которые разрешают доступ только выделенной группе.
Стандартная реализация этого паттерна доступна в библиотеке .
Концепция:
В отличие от предварительно авторизованных групп и членов на основе ролей, могут быть ситуации, когда сторона авторизации неизвестна для данной ситуации. Динамическая привязка адреса не выполняется по умолчанию, их необходимо авторизовать при первом развертывании транзакции/контракта, поскольку обычно списки контроля доступа предопределены.
В такой ситуации мы можем сгенерировать секрет вне сети со стороны клиента для выделенной функции. Этот секрет будет хеширован (SHA-256) и зарегистрирован в контракте. Будет выполнена рабочая привязка к секрету, так что любой пользователь, который отправит транзакцию вместе с секретом в качестве параметра, будет авторизован. Эти секреты могут использоваться совместно со стандартным протоколом обмена ключами с предполагаемыми адресами.
Хэшлоки полезны для каналов состояния платежей, условного депонирования атомарных свопов. Кроме того, секреты лучше всего подходят для одной транзакции, так как проверка в цепочке раскроет их.
Это простая секретная регистрация, которую можно использовать вместе с любыми хэшлоками. Секрет передается в контракт для раскрытия и ссылки. Для событий временной блокировки можно сравнить блок раскрытия и блок истечения срока действия.
Авторизация в сети с помощью ролей и управления собственностью стала необходимостью для управления активами на основе контрактов. Кроме того, будьте предельно осторожны с секретами вне сети и тем, как вы ими делитесь. Попробуйте использовать один из этих шаблонов в своем следующем децентрализованном приложении
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro плюс немного отсебятины для нашего развития
$10 на gas для Jolah Molivski ---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09
Децентрализованный мир растет в геометрической прогрессии с точки зрения принятия пользователями. Компании ежегодно создают сотни инновационных приложений dApp на Ethereum для удовлетворения различных требований рынка. В основе этих dApps лежат смарт-контракты, совместимые с EVM, в основном написанные на Solidity. Хотя изучение синтаксиса и мотодов разработки на solidity несложно, но для создания масштабируемого, удобного и безопасного контракта необходимо понимать правильные шаблоны проектирования.
Зачем заботиться о шаблонах проектирования?
Понимать — значит воспринимать закономерности
- Исайя Берлин
Одно из основных различий между программистом-новичком и профессиональным разработчиком заключается в понимании соответствующих шаблонов проектирования. Эти шаблоны являются проверенными решениями для распространенных проблем и могут стать вашим самым мощным инструментом.
По сравнению с другими направлениями разработки программного обеспечения, разработка надежных смарт-контрактов является сложной задачей. Распределенный конечный автомат запускает вашу развернутую программу 24/7, предоставляя доступ ко всему миру, это палка о двух концах. Чтобы убедиться, что ваша бизнес-логика работает безопасным, надежным и детерминированным образом, необходимо следовать лучшим практикам проектирования.
На платформе, где самая маленькая ошибка может стоить вам миллионы долларов, вы бы рискнули?
Шаблоны авторизации Solidity
Хотя смарт-контракты легко доступны по сети любому, это может не быть желаемой целью программы. Могут существовать определенные функции, которые должны использоваться только определенными группами пользователей.Неспособность правильно справиться с этим может привести к непредсказуемым вариантам использования.
Некоторые цели, которые могут быть достигнуты шаблонами авторизации:
- Разрешить выполнение функции поставторизации группой пользователей
- Разрешить выполнение транзакции после аутентификации вызывающего абонента.
- Ограничить возможности контракта создателем
1. Ограничение доступа
Концепция: Перед выполнением логики функции вызывающая сторона должна выполнить определенные условия. Эти условия могут быть связаны с личностью вызывающего абонента, входными параметрами, состоянием контракта и т. д. Функция запускается после выполнения всех требований. Если требования не выполняются, EVM обрабатывает ошибку, возвращая состояние, не внося изменений в вызов функции. Если подобное ограничение необходимо для нескольких функций, можно использовать защитную проверку с модификаторами. Кроме того, если вы не хотите, чтобы переменные и функции были доступны публично, защитите данные их состоянии, выбрав частную видимость.
Пример кода:
Код:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title An incremental time-bound donation receiver
*/
contract AccessRestriction {
/**
* @dev public variables in global, visible to everyone
*/
address public treasury = msg.sender;
uint256 public creationTime = block.timestamp;
uint256 public minimumDonation;
/**
* @dev private visibility of winner address
*/
address private winner;
/**********Modifier Blocks********/
/**
* @dev check if donation period has started
*/
modifier onlyBefore(uint256 _time) {
require(block.timestamp < _time);
_;
}
/**
* @dev check if donation period has ended
*/
modifier onlyAfter(uint256 _time) {
require(block.timestamp > _time);
_;
}
modifier isHigherDonation() {
require(msg.value > minimumDonation, "Please send higher amount");
winner = msg.sender;
minimumDonation = msg.value;
_;
}
function sendDonation()
external
payable
onlyBefore(creationTime + 1 weeks)
isHigherDonation
{
payable(treasury).transfer(msg.value);
}
function revealHighestDonor()
external
view
onlyAfter(creationTime + 1 weeks)
returns (address)
{
return winner;
}
}
2. Мульти авторизация
Концепция: Бывают ситуации, когда транзакция или вызов функции могут потребовать подтверждения нескольких пользователей. Таким образом, если пул авторизаторов имеет размер X и нам требуется подмножество Y, нет авторизаций (где X≥Y) для выполнения транзакции. Это полезный подход, когда задействованы платежные транзакции на основе мультиподписи. Проблема в этом подходе заключается в том, что члены в группе авторизации должны быть определены заранее, а минимальное количество авторизаторов должно быть доступно во время выполнения для подписи. Также в случае компрометации/утери ключа учетная запись авторизатора становится бесполезной.
Код:
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "./base/ModuleManager.sol";
import "./base/OwnerManager.sol";
import "./base/FallbackManager.sol";
import "./base/GuardManager.sol";
import "./common/EtherPaymentFallback.sol";
import "./common/Singleton.sol";
import "./common/SignatureDecoder.sol";
import "./common/SecuredTokenTransfer.sol";
import "./common/StorageAccessible.sol";
import "./interfaces/ISignatureValidator.sol";
import "./external/GnosisSafeMath.sol";
/// @title Gnosis Safe - A multisignature wallet with support for confirmations using signed messages based on ERC191.
/// @author Stefan George - <stefan@gnosis.io>
/// @author Richard Meissner - <richard@gnosis.io>
contract GnosisSafe is
EtherPaymentFallback,
Singleton,
ModuleManager,
OwnerManager,
SignatureDecoder,
SecuredTokenTransfer,
ISignatureValidatorConstants,
FallbackManager,
StorageAccessible,
GuardManager
{
using GnosisSafeMath for uint256;
string public constant VERSION = "1.3.0";
// keccak256(
// "EIP712Domain(uint256 chainId,address verifyingContract)"
// );
bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
// keccak256(
// "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
// );
bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
event SafeSetup(address indexed initiator, address[] owners, uint256 threshold, address initializer, address fallbackHandler);
event ApproveHash(bytes32 indexed approvedHash, address indexed owner);
event SignMsg(bytes32 indexed msgHash);
event ExecutionFailure(bytes32 txHash, uint256 payment);
event ExecutionSuccess(bytes32 txHash, uint256 payment);
uint256 public nonce;
bytes32 private _deprecatedDomainSeparator;
// Mapping to keep track of all message hashes that have been approved by ALL REQUIRED owners
mapping(bytes32 => uint256) public signedMessages;
// Mapping to keep track of all hashes (message or transaction) that have been approved by ANY owners
mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
// This constructor ensures that this contract can only be used as a master copy for Proxy contracts
constructor() {
// By setting the threshold it is not possible to call setup anymore,
// so we create a Safe with 0 owners and threshold 1.
// This is an unusable Safe, perfect for the singleton
threshold = 1;
}
/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
/// @param to Contract address for optional delegate call.
/// @param data Data payload for optional delegate call.
/// @param fallbackHandler Handler for fallback calls to this contract
/// @param paymentToken Token that should be used for the payment (0 is ETH)
/// @param payment Value that should be paid
/// @param paymentReceiver Address that should receive the payment (or 0 if tx.origin)
function setup(
address[] calldata _owners,
uint256 _threshold,
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver
) external {
// setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
setupOwners(_owners, _threshold);
if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
// As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
setupModules(to, data);
if (payment > 0) {
// To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
// baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
}
emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);
}
/// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.
/// Note: The fees are always transferred, even if the user transaction fails.
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @param safeTxGas Gas that should be used for the Safe transaction.
/// @param baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
/// @param gasPrice Gas price that should be used for the payment calculation.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures
) public payable virtual returns (bool success) {
bytes32 txHash;
// Use scope here to limit variable lifetime and prevent `stack too deep` errors
{
bytes memory txHashData =
encodeTransactionData(
// Transaction info
to,
value,
data,
operation,
safeTxGas,
// Payment info
baseGas,
gasPrice,
gasToken,
refundReceiver,
// Signature info
nonce
);
// Increase nonce and execute transaction.
nonce++;
txHash = keccak256(txHashData);
checkSignatures(txHash, txHashData, signatures);
}
address guard = getGuard();
{
if (guard != address(0)) {
Guard(guard).checkTransaction(
// Transaction info
to,
value,
data,
operation,
safeTxGas,
// Payment info
baseGas,
gasPrice,
gasToken,
refundReceiver,
// Signature info
signatures,
msg.sender
);
}
}
// We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
// We also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150
require(gasleft() >= ((safeTxGas * 64) / 63).max(safeTxGas + 2500) + 500, "GS010");
// Use scope here to limit variable lifetime and prevent `stack too deep` errors
{
uint256 gasUsed = gasleft();
// If the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than safeTxGas)
// We only substract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than safeTxGas
success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas);
gasUsed = gasUsed.sub(gasleft());
// If no safeTxGas and no gasPrice was set (e.g. both are 0), then the internal tx is required to be successful
// This makes it possible to use `estimateGas` without issues, as it searches for the minimum gas where the tx doesn't revert
require(success || safeTxGas != 0 || gasPrice != 0, "GS013");
// We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
uint256 payment = 0;
if (gasPrice > 0) {
payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
}
if (success) emit ExecutionSuccess(txHash, payment);
else emit ExecutionFailure(txHash, payment);
}
{
if (guard != address(0)) {
Guard(guard).checkAfterExecution(txHash, success);
}
}
}
function handlePayment(
uint256 gasUsed,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver
) private returns (uint256 payment) {
// solhint-disable-next-line avoid-tx-origin
address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
if (gasToken == address(0)) {
// For ETH we will only adjust the gas price to not be higher than the actual used gas price
payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
require(receiver.send(payment), "GS011");
} else {
payment = gasUsed.add(baseGas).mul(gasPrice);
require(transferToken(gasToken, receiver, payment), "GS012");
}
}
/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
* @param dataHash Hash of the data (could be either a message hash or transaction hash)
* @param data That should be signed (this is passed to an external validator contract)
* @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
*/
function checkSignatures(
bytes32 dataHash,
bytes memory data,
bytes memory signatures
) public view {
// Load threshold to avoid multiple storage loads
uint256 _threshold = threshold;
// Check that a threshold is set
require(_threshold > 0, "GS001");
checkNSignatures(dataHash, data, signatures, _threshold);
}
/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
* @param dataHash Hash of the data (could be either a message hash or transaction hash)
* @param data That should be signed (this is passed to an external validator contract)
* @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
* @param requiredSignatures Amount of required valid signatures.
*/
function checkNSignatures(
bytes32 dataHash,
bytes memory data,
bytes memory signatures,
uint256 requiredSignatures
) public view {
// Check that the provided signature data is not too short
require(signatures.length >= requiredSignatures.mul(65), "GS020");
// There cannot be an owner with address 0.
address lastOwner = address(0);
address currentOwner;
uint8 v;
bytes32 r;
bytes32 s;
uint256 i;
for (i = 0; i < requiredSignatures; i++) {
(v, r, s) = signatureSplit(signatures, i);
if (v == 0) {
// If v is 0 then it is a contract signature
// When handling contract signatures the address of the contract is encoded into r
currentOwner = address(uint160(uint256(r)));
// Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes
// This check is not completely accurate, since it is possible that more signatures than the threshold are send.
// Here we only check that the pointer is not pointing inside the part that is being processed
require(uint256(s) >= requiredSignatures.mul(65), "GS021");
// Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
require(uint256(s).add(32) <= signatures.length, "GS022");
// Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
uint256 contractSignatureLen;
// solhint-disable-next-line no-inline-assembly
assembly {
contractSignatureLen := mload(add(add(signatures, s), 0x20))
}
require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "GS023");
// Check signature
bytes memory contractSignature;
// solhint-disable-next-line no-inline-assembly
assembly {
// The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s
contractSignature := add(add(signatures, s), 0x20)
}
require(ISignatureValidator(currentOwner).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "GS024");
} else if (v == 1) {
// If v is 1 then it is an approved hash
// When handling approved hashes the address of the approver is encoded into r
currentOwner = address(uint160(uint256(r)));
// Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction
require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "GS025");
} else if (v > 30) {
// If v > 30 then default va (27,28) has been adjusted for eth_sign flow
// To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before applying ecrecover
currentOwner = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
} else {
// Default is the ecrecover flow with the provided data hash
// Use ecrecover with the messageHash for EOA signatures
currentOwner = ecrecover(dataHash, v, r, s);
}
require(currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS, "GS026");
lastOwner = currentOwner;
}
}
/// @dev Allows to estimate a Safe transaction.
/// This method is only meant for estimation purpose, therefore the call will always revert and encode the result in the revert data.
/// Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
/// @notice Deprecated in favor of common/StorageAccessible.sol and will be removed in next version.
function requiredTxGas(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation
) external returns (uint256) {
uint256 startGas = gasleft();
// We don't provide an error message here, as we use it to return the estimate
require(execute(to, value, data, operation, gasleft()));
uint256 requiredGas = startGas - gasleft();
// Convert response to string and return via error message
revert(string(abi.encodePacked(requiredGas)));
}
/**
* @dev Marks a hash as approved. This can be used to validate a hash that is used by a signature.
* @param hashToApprove The hash that should be marked as approved for signatures that are verified by this contract.
*/
function approveHash(bytes32 hashToApprove) external {
require(owners[msg.sender] != address(0), "GS030");
approvedHashes[msg.sender][hashToApprove] = 1;
emit ApproveHash(hashToApprove, msg.sender);
}
/// @dev Returns the chain id used by this contract.
function getChainId() public view returns (uint256) {
uint256 id;
// solhint-disable-next-line no-inline-assembly
assembly {
id := chainid()
}
return id;
}
function domainSeparator() public view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));
}
/// @dev Returns the bytes that are hashed to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Gas that should be used for the safe transaction.
/// @param baseGas Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash bytes.
function encodeTransactionData(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) public view returns (bytes memory) {
bytes32 safeTxHash =
keccak256(
abi.encode(
SAFE_TX_TYPEHASH,
to,
value,
keccak256(data),
operation,
safeTxGas,
baseGas,
gasPrice,
gasToken,
refundReceiver,
_nonce
)
);
return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
}
/// @dev Returns hash to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Fas that should be used for the safe transaction.
/// @param baseGas Gas costs for data used to trigger the safe transaction.
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash.
function getTransactionHash(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) public view returns (bytes32) {
return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
}
}
Взята отсюда
3. Владение и управление доступом на основе ролей
Концепция: Развернутый контракт может иметь выделенную работу и доступ на основе ролей, как и в любой традиционной системе. Распределение ролей и администрирование могут быть выполнены путем применения правил управления доступом на основе прав собственности и ролей. Доступ на основе ролей может помочь разработать модификаторы для конкретных функций, которые разрешают доступ только выделенной группе.
Стандартная реализация этого паттерна доступна в библиотеке .
4. Динамическая авторизация с использованием секрета вне сети
Концепция: В отличие от предварительно авторизованных групп и членов на основе ролей, могут быть ситуации, когда сторона авторизации неизвестна для данной ситуации. Динамическая привязка адреса не выполняется по умолчанию, их необходимо авторизовать при первом развертывании транзакции/контракта, поскольку обычно списки контроля доступа предопределены.
В такой ситуации мы можем сгенерировать секрет вне сети со стороны клиента для выделенной функции. Этот секрет будет хеширован (SHA-256) и зарегистрирован в контракте. Будет выполнена рабочая привязка к секрету, так что любой пользователь, который отправит транзакцию вместе с секретом в качестве параметра, будет авторизован. Эти секреты могут использоваться совместно со стандартным протоколом обмена ключами с предполагаемыми адресами.
Хэшлоки полезны для каналов состояния платежей, условного депонирования атомарных свопов. Кроме того, секреты лучше всего подходят для одной транзакции, так как проверка в цепочке раскроет их.
Это простая секретная регистрация, которую можно использовать вместе с любыми хэшлоками. Секрет передается в контракт для раскрытия и ссылки. Для событий временной блокировки можно сравнить блок раскрытия и блок истечения срока действия.
Код:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
contract SecretRegistry {
mapping(bytes32 => uint256) private secretToBlock;
event SecretRevealed(bytes32 indexed secrethash, bytes32 secret);
/**
* @dev Register the secret that's been used for validation
*/
function registerSecret(bytes32 secret) public returns (bool) {
bytes32 secrethash = sha256(abi.encodePacked(secret));
if (secretToBlock[secrethash] > 0) {
return false;
}
secretToBlock[secrethash] = block.number;
emit SecretRevealed(secrethash, secret);
return true;
}
function getRevealedSecretBlockHeight(bytes32 secrethash) public view returns (uint256) {
return secretToBlock[secrethash];
}
}
Авторизация в сети с помощью ролей и управления собственностью стала необходимостью для управления активами на основе контрактов. Кроме того, будьте предельно осторожны с секретами вне сети и тем, как вы ими делитесь. Попробуйте использовать один из этих шаблонов в своем следующем децентрализованном приложении