refactor: move brother_node development artifact to dev/test-nodes subdirectory

Development Artifact Cleanup:
 BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location
- dev/test-nodes/brother_node/: Moved from root directory for better organization
- Contains development configuration, test logs, and test chain data
- No impact on production systems - purely development/testing artifact

 DEVELOPMENT ARTIFACTS IDENTIFIED:
- Chain ID: aitbc-brother-chain (test/development chain)
- Ports: 8010 (P2P) and 8011 (RPC) - different from production
- Environment: .env file with test configuration
- Logs: rpc.log and node.log from development testing session (March 15, 2026)

 ROOT DIRECTORY CLEANUP: Removed development clutter from production directory
- brother_node/ moved to dev/test-nodes/brother_node/
- Root directory now contains only production-ready components
- Development artifacts properly organized in dev/ subdirectory

DIRECTORY STRUCTURE IMPROVEMENT:
📁 dev/test-nodes/: Development and testing node configurations
🏗️ Root Directory: Clean production structure with only essential components
🧪 Development Isolation: Test environments separated from production

BENEFITS:
 Clean Production Directory: No development artifacts in root
 Better Organization: Development nodes grouped in dev/ subdirectory
 Clear Separation: Production vs development environments clearly distinguished
 Maintainability: Easier to identify and manage development components

RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
2026-03-30 17:09:06 +02:00
parent bf730dcb4a
commit 816e258d4c
11734 changed files with 2001707 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/AbstractSigner.sol)
pragma solidity ^0.8.20;
/**
* @dev Abstract contract for signature validation.
*
* Developers must implement {_rawSignatureValidation} and use it as the lowest-level signature validation mechanism.
*
* @custom:stateless
*/
abstract contract AbstractSigner {
/**
* @dev Signature validation algorithm.
*
* WARNING: Implementing a signature validation algorithm is a security-sensitive operation as it involves
* cryptographic verification. It is important to review and test thoroughly before deployment. Consider
* using one of the signature verification libraries (xref:api:utils/cryptography#ECDSA[ECDSA],
* xref:api:utils/cryptography#P256[P256] or xref:api:utils/cryptography#RSA[RSA]).
*/
function _rawSignatureValidation(bytes32 hash, bytes calldata signature) internal view virtual returns (bool);
}

View File

@@ -0,0 +1,252 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/MultiSignerERC7913.sol)
pragma solidity ^0.8.26;
import {AbstractSigner} from "./AbstractSigner.sol";
import {SignatureChecker} from "../SignatureChecker.sol";
import {EnumerableSet} from "../../structs/EnumerableSet.sol";
/**
* @dev Implementation of {AbstractSigner} using multiple ERC-7913 signers with a threshold-based
* signature verification system.
*
* This contract allows managing a set of authorized signers and requires a minimum number of
* signatures (threshold) to approve operations. It uses ERC-7913 formatted signers, which
* makes it natively compatible with ECDSA and ERC-1271 signers.
*
* Example of usage:
*
* ```solidity
* contract MyMultiSignerAccount is Account, MultiSignerERC7913, Initializable {
* function initialize(bytes[] memory signers, uint64 threshold) public initializer {
* _addSigners(signers);
* _setThreshold(threshold);
* }
*
* function addSigners(bytes[] memory signers) public onlyEntryPointOrSelf {
* _addSigners(signers);
* }
*
* function removeSigners(bytes[] memory signers) public onlyEntryPointOrSelf {
* _removeSigners(signers);
* }
*
* function setThreshold(uint64 threshold) public onlyEntryPointOrSelf {
* _setThreshold(threshold);
* }
* }
* ```
*
* IMPORTANT: Failing to properly initialize the signers and threshold either during construction
* (if used standalone) or during initialization (if used as a clone) may leave the contract
* either front-runnable or unusable.
*/
abstract contract MultiSignerERC7913 is AbstractSigner {
using EnumerableSet for EnumerableSet.BytesSet;
using SignatureChecker for *;
EnumerableSet.BytesSet private _signers;
uint64 private _threshold;
/// @dev Emitted when a signer is added.
event ERC7913SignerAdded(bytes indexed signers);
/// @dev Emitted when a signers is removed.
event ERC7913SignerRemoved(bytes indexed signers);
/// @dev Emitted when the threshold is updated.
event ERC7913ThresholdSet(uint64 threshold);
/// @dev The `signer` already exists.
error MultiSignerERC7913AlreadyExists(bytes signer);
/// @dev The `signer` does not exist.
error MultiSignerERC7913NonexistentSigner(bytes signer);
/// @dev The `signer` is less than 20 bytes long.
error MultiSignerERC7913InvalidSigner(bytes signer);
/// @dev The `threshold` is zero.
error MultiSignerERC7913ZeroThreshold();
/// @dev The `threshold` is unreachable given the number of `signers`.
error MultiSignerERC7913UnreachableThreshold(uint64 signers, uint64 threshold);
constructor(bytes[] memory signers_, uint64 threshold_) {
_addSigners(signers_);
_setThreshold(threshold_);
}
/**
* @dev Returns a slice of the set of authorized signers.
*
* Using `start = 0` and `end = type(uint64).max` will return the entire set of signers.
*
* WARNING: Depending on the `start` and `end`, this operation can copy a large amount of data to memory, which
* can be expensive. This is designed for view accessors queried without gas fees. Using it in state-changing
* functions may become uncallable if the slice grows too large.
*/
function getSigners(uint64 start, uint64 end) public view virtual returns (bytes[] memory) {
return _signers.values(start, end);
}
/// @dev Returns the number of authorized signers
function getSignerCount() public view virtual returns (uint256) {
return _signers.length();
}
/// @dev Returns whether the `signer` is an authorized signer.
function isSigner(bytes memory signer) public view virtual returns (bool) {
return _signers.contains(signer);
}
/// @dev Returns the minimum number of signers required to approve a multisignature operation.
function threshold() public view virtual returns (uint64) {
return _threshold;
}
/**
* @dev Adds the `newSigners` to those allowed to sign on behalf of this contract.
* Internal version without access control.
*
* Requirements:
*
* * Each of `newSigners` must be at least 20 bytes long. Reverts with {MultiSignerERC7913InvalidSigner} if not.
* * Each of `newSigners` must not be authorized. See {isSigner}. Reverts with {MultiSignerERC7913AlreadyExists} if so.
*/
function _addSigners(bytes[] memory newSigners) internal virtual {
for (uint256 i = 0; i < newSigners.length; ++i) {
bytes memory signer = newSigners[i];
require(signer.length >= 20, MultiSignerERC7913InvalidSigner(signer));
require(_signers.add(signer), MultiSignerERC7913AlreadyExists(signer));
emit ERC7913SignerAdded(signer);
}
}
/**
* @dev Removes the `oldSigners` from the authorized signers. Internal version without access control.
*
* Requirements:
*
* * Each of `oldSigners` must be authorized. See {isSigner}. Otherwise {MultiSignerERC7913NonexistentSigner} is thrown.
* * See {_validateReachableThreshold} for the threshold validation.
*/
function _removeSigners(bytes[] memory oldSigners) internal virtual {
for (uint256 i = 0; i < oldSigners.length; ++i) {
bytes memory signer = oldSigners[i];
require(_signers.remove(signer), MultiSignerERC7913NonexistentSigner(signer));
emit ERC7913SignerRemoved(signer);
}
_validateReachableThreshold();
}
/**
* @dev Sets the signatures `threshold` required to approve a multisignature operation.
* Internal version without access control.
*
* Requirements:
*
* * See {_validateReachableThreshold} for the threshold validation.
*/
function _setThreshold(uint64 newThreshold) internal virtual {
require(newThreshold > 0, MultiSignerERC7913ZeroThreshold());
_threshold = newThreshold;
_validateReachableThreshold();
emit ERC7913ThresholdSet(newThreshold);
}
/**
* @dev Validates the current threshold is reachable.
*
* Requirements:
*
* * The {getSignerCount} must be greater or equal than to the {threshold}. Throws
* {MultiSignerERC7913UnreachableThreshold} if not.
*/
function _validateReachableThreshold() internal view virtual {
uint256 signersLength = _signers.length();
uint64 currentThreshold = threshold();
require(
signersLength >= currentThreshold,
MultiSignerERC7913UnreachableThreshold(
uint64(signersLength), // Safe cast. Economically impossible to overflow.
currentThreshold
)
);
}
/**
* @dev Decodes, validates the signature and checks the signers are authorized.
* See {_validateSignatures} and {_validateThreshold} for more details.
*
* Example of signature encoding:
*
* ```solidity
* // Encode signers (verifier || key)
* bytes memory signer1 = abi.encodePacked(verifier1, key1);
* bytes memory signer2 = abi.encodePacked(verifier2, key2);
*
* // Order signers by their id
* if (keccak256(signer1) > keccak256(signer2)) {
* (signer1, signer2) = (signer2, signer1);
* (signature1, signature2) = (signature2, signature1);
* }
*
* // Assign ordered signers and signatures
* bytes[] memory signers = new bytes[](2);
* bytes[] memory signatures = new bytes[](2);
* signers[0] = signer1;
* signatures[0] = signature1;
* signers[1] = signer2;
* signatures[1] = signature2;
*
* // Encode the multi signature
* bytes memory signature = abi.encode(signers, signatures);
* ```
*
* Requirements:
*
* * The `signature` must be encoded as `abi.encode(signers, signatures)`.
*/
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
if (signature.length == 0) return false; // For ERC-7739 compatibility
(bytes[] memory signers, bytes[] memory signatures) = abi.decode(signature, (bytes[], bytes[]));
return _validateThreshold(signers) && _validateSignatures(hash, signers, signatures);
}
/**
* @dev Validates the signatures using the signers and their corresponding signatures.
* Returns whether the signers are authorized and the signatures are valid for the given hash.
*
* IMPORTANT: Sorting the signers by their `keccak256` hash will improve the gas efficiency of this function.
* See {SignatureChecker-areValidSignaturesNow-bytes32-bytes[]-bytes[]} for more details.
*
* Requirements:
*
* * The `signatures` and `signers` arrays must be equal in length. Returns false otherwise.
*/
function _validateSignatures(
bytes32 hash,
bytes[] memory signers,
bytes[] memory signatures
) internal view virtual returns (bool valid) {
for (uint256 i = 0; i < signers.length; ++i) {
if (!isSigner(signers[i])) {
return false;
}
}
return hash.areValidSignaturesNow(signers, signatures);
}
/**
* @dev Validates that the number of signers meets the {threshold} requirement.
* Assumes the signers were already validated. See {_validateSignatures} for more details.
*/
function _validateThreshold(bytes[] memory validatingSigners) internal view virtual returns (bool) {
return validatingSigners.length >= threshold();
}
}

View File

@@ -0,0 +1,208 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/MultiSignerERC7913Weighted.sol)
pragma solidity ^0.8.26;
import {SafeCast} from "../../math/SafeCast.sol";
import {MultiSignerERC7913} from "./MultiSignerERC7913.sol";
/**
* @dev Extension of {MultiSignerERC7913} that supports weighted signatures.
*
* This contract allows assigning different weights to each signer, enabling more
* flexible governance schemes. For example, some signers could have higher weight
* than others, allowing for weighted voting or prioritized authorization.
*
* Example of usage:
*
* ```solidity
* contract MyWeightedMultiSignerAccount is Account, MultiSignerERC7913Weighted, Initializable {
* function initialize(bytes[] memory signers, uint64[] memory weights, uint64 threshold) public initializer {
* _addSigners(signers);
* _setSignerWeights(signers, weights);
* _setThreshold(threshold);
* }
*
* function addSigners(bytes[] memory signers) public onlyEntryPointOrSelf {
* _addSigners(signers);
* }
*
* function removeSigners(bytes[] memory signers) public onlyEntryPointOrSelf {
* _removeSigners(signers);
* }
*
* function setThreshold(uint64 threshold) public onlyEntryPointOrSelf {
* _setThreshold(threshold);
* }
*
* function setSignerWeights(bytes[] memory signers, uint64[] memory weights) public onlyEntryPointOrSelf {
* _setSignerWeights(signers, weights);
* }
* }
* ```
*
* IMPORTANT: When setting a threshold value, ensure it matches the scale used for signer weights.
* For example, if signers have weights like 1, 2, or 3, then a threshold of 4 would require at
* least two signers (e.g., one with weight 1 and one with weight 3). See {signerWeight}.
*/
abstract contract MultiSignerERC7913Weighted is MultiSignerERC7913 {
using SafeCast for *;
// Sum of all the extra weights of all signers. Storage packed with `MultiSignerERC7913._threshold`
uint64 private _totalExtraWeight;
// Mapping from signer to extraWeight (in addition to all authorized signers having weight 1)
mapping(bytes signer => uint64) private _extraWeights;
/**
* @dev Emitted when a signer's weight is changed.
*
* NOTE: Not emitted in {_addSigners} or {_removeSigners}. Indexers must rely on {ERC7913SignerAdded}
* and {ERC7913SignerRemoved} to index a default weight of 1. See {signerWeight}.
*/
event ERC7913SignerWeightChanged(bytes indexed signer, uint64 weight);
/// @dev Thrown when a signer's weight is invalid.
error MultiSignerERC7913WeightedInvalidWeight(bytes signer, uint64 weight);
/// @dev Thrown when the arrays lengths don't match. See {_setSignerWeights}.
error MultiSignerERC7913WeightedMismatchedLength();
constructor(bytes[] memory signers_, uint64[] memory weights_, uint64 threshold_) MultiSignerERC7913(signers_, 1) {
_setSignerWeights(signers_, weights_);
_setThreshold(threshold_);
}
/// @dev Gets the weight of a signer. Returns 0 if the signer is not authorized.
function signerWeight(bytes memory signer) public view virtual returns (uint64) {
unchecked {
// Safe cast, _setSignerWeights guarantees 1+_extraWeights is a uint64
return uint64(isSigner(signer).toUint() * (1 + _extraWeights[signer]));
}
}
/// @dev Gets the total weight of all signers.
function totalWeight() public view virtual returns (uint64) {
return (getSignerCount() + _totalExtraWeight).toUint64();
}
/**
* @dev Sets weights for multiple signers at once. Internal version without access control.
*
* Requirements:
*
* * `signers` and `weights` arrays must have the same length. Reverts with {MultiSignerERC7913WeightedMismatchedLength} on mismatch.
* * Each signer must exist in the set of authorized signers. Otherwise reverts with {MultiSignerERC7913NonexistentSigner}
* * Each weight must be greater than 0. Otherwise reverts with {MultiSignerERC7913WeightedInvalidWeight}
* * See {_validateReachableThreshold} for the threshold validation.
*
* Emits {ERC7913SignerWeightChanged} for each signer.
*/
function _setSignerWeights(bytes[] memory signers, uint64[] memory weights) internal virtual {
require(signers.length == weights.length, MultiSignerERC7913WeightedMismatchedLength());
uint256 extraWeightAdded = 0;
uint256 extraWeightRemoved = 0;
for (uint256 i = 0; i < signers.length; ++i) {
bytes memory signer = signers[i];
require(isSigner(signer), MultiSignerERC7913NonexistentSigner(signer));
uint64 weight = weights[i];
require(weight > 0, MultiSignerERC7913WeightedInvalidWeight(signer, weight));
unchecked {
uint64 oldExtraWeight = _extraWeights[signer];
uint64 newExtraWeight = weight - 1;
if (oldExtraWeight != newExtraWeight) {
// Overflow impossible: weight values are bounded by uint64 and economic constraints
extraWeightRemoved += oldExtraWeight;
extraWeightAdded += _extraWeights[signer] = newExtraWeight;
emit ERC7913SignerWeightChanged(signer, weight);
}
}
}
unchecked {
// Safe from underflow: `extraWeightRemoved` is bounded by `_totalExtraWeight` by construction
// and weight values are bounded by uint64 and economic constraints
_totalExtraWeight = (uint256(_totalExtraWeight) + extraWeightAdded - extraWeightRemoved).toUint64();
}
_validateReachableThreshold();
}
/**
* @dev See {MultiSignerERC7913-_addSigners}.
*
* In cases where {totalWeight} is almost `type(uint64).max` (due to a large `_totalExtraWeight`), adding new
* signers could cause the {totalWeight} computation to overflow. Adding a {totalWeight} calls after the new
* signers are added ensures no such overflow happens.
*/
function _addSigners(bytes[] memory newSigners) internal virtual override {
super._addSigners(newSigners);
// This will revert if the new signers cause an overflow
_validateReachableThreshold();
}
/**
* @dev See {MultiSignerERC7913-_removeSigners}.
*
* Just like {_addSigners}, this function does not emit {ERC7913SignerWeightChanged} events. The
* {ERC7913SignerRemoved} event emitted by {MultiSignerERC7913-_removeSigners} is enough to track weights here.
*/
function _removeSigners(bytes[] memory signers) internal virtual override {
// Clean up weights for removed signers
//
// The `extraWeightRemoved` is bounded by `_totalExtraWeight`. The `super._removeSigners` function will revert
// if the signers array contains any duplicates, ensuring each signer's weight is only counted once. Since
// `_totalExtraWeight` is stored as a `uint64`, the final subtraction operation is also safe.
unchecked {
uint64 extraWeightRemoved = 0;
for (uint256 i = 0; i < signers.length; ++i) {
bytes memory signer = signers[i];
extraWeightRemoved += _extraWeights[signer];
delete _extraWeights[signer];
}
_totalExtraWeight -= extraWeightRemoved;
}
super._removeSigners(signers);
}
/**
* @dev Sets the threshold for the multisignature operation. Internal version without access control.
*
* Requirements:
*
* * The {totalWeight} must be `>=` the {threshold}. Otherwise reverts with {MultiSignerERC7913UnreachableThreshold}
*
* NOTE: This function intentionally does not call `super._validateReachableThreshold` because the base implementation
* assumes each signer has a weight of 1, which is a subset of this weighted implementation. Consider that multiple
* implementations of this function may exist in the contract, so important side effects may be missed
* depending on the linearization order.
*/
function _validateReachableThreshold() internal view virtual override {
uint64 weight = totalWeight();
uint64 currentThreshold = threshold();
require(weight >= currentThreshold, MultiSignerERC7913UnreachableThreshold(weight, currentThreshold));
}
/**
* @dev Validates that the total weight of signers meets the threshold requirement.
*
* NOTE: This function intentionally does not call `super._validateThreshold` because the base implementation
* assumes each signer has a weight of 1, which is a subset of this weighted implementation. Consider that multiple
* implementations of this function may exist in the contract, so important side effects may be missed
* depending on the linearization order.
*/
function _validateThreshold(bytes[] memory signers) internal view virtual override returns (bool) {
unchecked {
uint64 weight = 0;
for (uint256 i = 0; i < signers.length; ++i) {
// Overflow impossible: weight values are bounded by uint64 and economic constraints
weight += signerWeight(signers[i]);
}
return weight >= threshold();
}
}
}

View File

@@ -0,0 +1,56 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/SignerECDSA.sol)
pragma solidity ^0.8.20;
import {AbstractSigner} from "./AbstractSigner.sol";
import {ECDSA} from "../ECDSA.sol";
/**
* @dev Implementation of {AbstractSigner} using xref:api:utils/cryptography#ECDSA[ECDSA] signatures.
*
* For {Account} usage, a {_setSigner} function is provided to set the {signer} address.
* Doing so is easier for a factory, who is likely to use initializable clones of this contract.
*
* Example of usage:
*
* ```solidity
* contract MyAccountECDSA is Account, SignerECDSA, Initializable {
* function initialize(address signerAddr) public initializer {
* _setSigner(signerAddr);
* }
* }
* ```
*
* IMPORTANT: Failing to call {_setSigner} either during construction (if used standalone)
* or during initialization (if used as a clone) may leave the signer either front-runnable or unusable.
*/
abstract contract SignerECDSA is AbstractSigner {
address private _signer;
constructor(address signerAddr) {
_setSigner(signerAddr);
}
/**
* @dev Sets the signer with the address of the native signer. This function should be called during construction
* or through an initializer.
*/
function _setSigner(address signerAddr) internal {
_signer = signerAddr;
}
/// @dev Return the signer's address.
function signer() public view virtual returns (address) {
return _signer;
}
/// @inheritdoc AbstractSigner
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return signer() == recovered && err == ECDSA.RecoverError.NoError;
}
}

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/SignerERC7702.sol)
pragma solidity ^0.8.20;
import {AbstractSigner} from "./AbstractSigner.sol";
import {ECDSA} from "../ECDSA.sol";
/**
* @dev Implementation of {AbstractSigner} for implementation for an EOA. Useful for ERC-7702 accounts.
*
* @custom:stateless
*/
abstract contract SignerERC7702 is AbstractSigner {
/**
* @dev Validates the signature using the EOA's address (i.e. `address(this)`).
*/
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return address(this) == recovered && err == ECDSA.RecoverError.NoError;
}
}

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/SignerERC7913.sol)
pragma solidity ^0.8.24;
import {AbstractSigner} from "./AbstractSigner.sol";
import {SignatureChecker} from "../SignatureChecker.sol";
/**
* @dev Implementation of {AbstractSigner} using
* https://eips.ethereum.org/EIPS/eip-7913[ERC-7913] signature verification.
*
* For {Account} usage, a {_setSigner} function is provided to set the ERC-7913 formatted {signer}.
* Doing so is easier for a factory, who is likely to use initializable clones of this contract.
*
* The signer is a `bytes` object that concatenates a verifier address and a key: `verifier || key`.
*
* Example of usage:
*
* ```solidity
* contract MyAccountERC7913 is Account, SignerERC7913, Initializable {
* function initialize(bytes memory signer_) public initializer {
* _setSigner(signer_);
* }
*
* function setSigner(bytes memory signer_) public onlyEntryPointOrSelf {
* _setSigner(signer_);
* }
* }
* ```
*
* IMPORTANT: Failing to call {_setSigner} either during construction (if used standalone)
* or during initialization (if used as a clone) may leave the signer either front-runnable or unusable.
*/
abstract contract SignerERC7913 is AbstractSigner {
bytes private _signer;
constructor(bytes memory signer_) {
_setSigner(signer_);
}
/// @dev Return the ERC-7913 signer (i.e. `verifier || key`).
function signer() public view virtual returns (bytes memory) {
return _signer;
}
/// @dev Sets the signer (i.e. `verifier || key`) with an ERC-7913 formatted signer.
function _setSigner(bytes memory signer_) internal {
_signer = signer_;
}
/**
* @dev Verifies a signature using {SignatureChecker-isValidSignatureNow-bytes-bytes32-bytes-}
* with {signer}, `hash` and `signature`.
*/
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
return SignatureChecker.isValidSignatureNow(signer(), hash, signature);
}
}

View File

@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/SignerP256.sol)
pragma solidity ^0.8.20;
import {AbstractSigner} from "./AbstractSigner.sol";
import {P256} from "../P256.sol";
/**
* @dev Implementation of {AbstractSigner} using xref:api:utils/cryptography#P256[P256] signatures.
*
* For {Account} usage, a {_setSigner} function is provided to set the {signer} public key.
* Doing so is easier for a factory, who is likely to use initializable clones of this contract.
*
* Example of usage:
*
* ```solidity
* contract MyAccountP256 is Account, SignerP256, Initializable {
* function initialize(bytes32 qx, bytes32 qy) public initializer {
* _setSigner(qx, qy);
* }
* }
* ```
*
* IMPORTANT: Failing to call {_setSigner} either during construction (if used standalone)
* or during initialization (if used as a clone) may leave the signer either front-runnable or unusable.
*/
abstract contract SignerP256 is AbstractSigner {
bytes32 private _qx;
bytes32 private _qy;
error SignerP256InvalidPublicKey(bytes32 qx, bytes32 qy);
constructor(bytes32 qx, bytes32 qy) {
_setSigner(qx, qy);
}
/**
* @dev Sets the signer with a P256 public key. This function should be called during construction
* or through an initializer.
*/
function _setSigner(bytes32 qx, bytes32 qy) internal {
if (!P256.isValidPublicKey(qx, qy)) revert SignerP256InvalidPublicKey(qx, qy);
_qx = qx;
_qy = qy;
}
/// @dev Return the signer's P256 public key.
function signer() public view virtual returns (bytes32 qx, bytes32 qy) {
return (_qx, _qy);
}
/// @inheritdoc AbstractSigner
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
if (signature.length < 0x40) return false;
bytes32 r = bytes32(signature[0x00:0x20]);
bytes32 s = bytes32(signature[0x20:0x40]);
(bytes32 qx, bytes32 qy) = signer();
return P256.verify(hash, r, s, qx, qy);
}
}

View File

@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/SignerRSA.sol)
pragma solidity ^0.8.20;
import {AbstractSigner} from "./AbstractSigner.sol";
import {RSA} from "../RSA.sol";
/**
* @dev Implementation of {AbstractSigner} using xref:api:utils/cryptography#RSA[RSA] signatures.
*
* For {Account} usage, a {_setSigner} function is provided to set the {signer} public key.
* Doing so is easier for a factory, who is likely to use initializable clones of this contract.
*
* Example of usage:
*
* ```solidity
* contract MyAccountRSA is Account, SignerRSA, Initializable {
* function initialize(bytes memory e, bytes memory n) public initializer {
* _setSigner(e, n);
* }
* }
* ```
*
* IMPORTANT: Failing to call {_setSigner} either during construction (if used standalone)
* or during initialization (if used as a clone) may leave the signer either front-runnable or unusable.
*/
abstract contract SignerRSA is AbstractSigner {
bytes private _e;
bytes private _n;
constructor(bytes memory e, bytes memory n) {
_setSigner(e, n);
}
/**
* @dev Sets the signer with a RSA public key. This function should be called during construction
* or through an initializer.
*/
function _setSigner(bytes memory e, bytes memory n) internal {
_e = e;
_n = n;
}
/// @dev Return the signer's RSA public key.
function signer() public view virtual returns (bytes memory e, bytes memory n) {
return (_e, _n);
}
/**
* @dev See {AbstractSigner-_rawSignatureValidation}. Verifies a PKCSv1.5 signature by calling
* xref:api:utils/cryptography.adoc#RSA-pkcs1Sha256-bytes-bytes-bytes-bytes-[RSA.pkcs1Sha256].
*
* IMPORTANT: Following the RSASSA-PKCS1-V1_5-VERIFY procedure outlined in RFC8017 (section 8.2.2), the
* provided `hash` is used as the `M` (message) and rehashed using SHA256 according to EMSA-PKCS1-v1_5
* encoding as per section 9.2 (step 1) of the RFC.
*/
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
(bytes memory e, bytes memory n) = signer();
return RSA.pkcs1Sha256(abi.encodePacked(hash), signature, e, n);
}
}

View File

@@ -0,0 +1,99 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/signers/draft-ERC7739.sol)
pragma solidity ^0.8.20;
import {AbstractSigner} from "./AbstractSigner.sol";
import {EIP712} from "../EIP712.sol";
import {ERC7739Utils} from "../draft-ERC7739Utils.sol";
import {IERC1271} from "../../../interfaces/IERC1271.sol";
import {MessageHashUtils} from "../MessageHashUtils.sol";
import {ShortStrings} from "../../ShortStrings.sol";
/**
* @dev Validates signatures wrapping the message hash in a nested EIP712 type. See {ERC7739Utils}.
*
* Linking the signature to the EIP-712 domain separator is a security measure to prevent signature replay across different
* EIP-712 domains (e.g. a single offchain owner of multiple contracts).
*
* This contract requires implementing the {_rawSignatureValidation} function, which passes the wrapped message hash,
* which may be either an typed data or a personal sign nested type.
*
* NOTE: xref:api:utils/cryptography#EIP712[EIP-712] uses xref:api:utils/cryptography#ShortStrings[ShortStrings] to
* optimize gas costs for short strings (up to 31 characters). Consider that strings longer than that will use storage,
* which may limit the ability of the signer to be used within the ERC-4337 validation phase (due to
* https://eips.ethereum.org/EIPS/eip-7562#storage-rules[ERC-7562 storage access rules]).
*/
abstract contract ERC7739 is AbstractSigner, EIP712, IERC1271 {
using ERC7739Utils for *;
using MessageHashUtils for bytes32;
/**
* @dev Attempts validating the signature in a nested EIP-712 type.
*
* A nested EIP-712 type might be presented in 2 different ways:
*
* - As a nested EIP-712 typed data
* - As a _personal_ signature (an EIP-712 mimic of the `eth_personalSign` for a smart contract)
*/
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4 result) {
// For the hash `0x7739773977397739773977397739773977397739773977397739773977397739` and an empty signature,
// we return the magic value `0x77390001` as it's assumed impossible to find a preimage for it that can be used
// maliciously. Useful for simulation purposes and to validate whether the contract supports ERC-7739.
return
(_isValidNestedTypedDataSignature(hash, signature) || _isValidNestedPersonalSignSignature(hash, signature))
? IERC1271.isValidSignature.selector
: (hash == 0x7739773977397739773977397739773977397739773977397739773977397739 && signature.length == 0)
? bytes4(0x77390001)
: bytes4(0xffffffff);
}
/**
* @dev Nested personal signature verification.
*/
function _isValidNestedPersonalSignSignature(bytes32 hash, bytes calldata signature) private view returns (bool) {
return _rawSignatureValidation(_domainSeparatorV4().toTypedDataHash(hash.personalSignStructHash()), signature);
}
/**
* @dev Nested EIP-712 typed data verification.
*/
function _isValidNestedTypedDataSignature(
bytes32 hash,
bytes calldata encodedSignature
) private view returns (bool) {
// decode signature
(
bytes calldata signature,
bytes32 appSeparator,
bytes32 contentsHash,
string calldata contentsDescr
) = encodedSignature.decodeTypedDataSig();
(
,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
) = eip712Domain();
// Check that contentHash and separator are correct
// Rebuild nested hash
return
hash == appSeparator.toTypedDataHash(contentsHash) &&
bytes(contentsDescr).length != 0 &&
_rawSignatureValidation(
appSeparator.toTypedDataHash(
ERC7739Utils.typedDataSignStructHash(
contentsDescr,
contentsHash,
abi.encode(keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract, salt)
)
),
signature
);
}
}