Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21,126 changes: 21,126 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/flock/testnet-training-node-quickstart
Submodule testnet-training-node-quickstart added at 67a41e
147 changes: 147 additions & 0 deletions packages/foundry/contracts/BridgeAnalyzer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// BridgeAnalyzer.sol
pragma solidity ^0.8.24;

contract BridgeAnalyzer {
struct VulnerabilityCheck {
string functionName;
bool hasVulnerability;
uint8 riskLevel;
string description;
}

function analyzeBridgeContract(
address bridge
) public view returns (VulnerabilityCheck[] memory) {
VulnerabilityCheck[] memory checks = new VulnerabilityCheck[](3);
checks[0] = analyzeMintSecurity(bridge);
checks[1] = analyzeWithdrawSecurity(bridge);
checks[2] = analyzeOwnershipSecurity(bridge);
return checks;
}

function analyzeMintSecurity(
address bridge
) public view returns (VulnerabilityCheck memory) {
bool hasMultiSig;
bool hasLimits;

try IBridge(bridge).requiredSigners() returns (uint256 signers) {
hasMultiSig = signers > 2; // Require at least 3 signers
} catch {
hasMultiSig = false;
}

try IBridge(bridge).mintLimit() returns (uint256 limit) {
hasLimits = limit > 0;
} catch {
hasLimits = false;
}

uint8 riskLevel;
string memory description;

if (!hasMultiSig) {
riskLevel = 5;
description = "No multi-signature requirement for minting";
} else if (!hasLimits) {
riskLevel = 4;
description = "Unrestricted minting capability";
} else {
riskLevel = 1;
description = "No vulnerabilities detected";
}

return
VulnerabilityCheck({
functionName: "mint()",
hasVulnerability: !hasMultiSig || !hasLimits,
riskLevel: riskLevel,
description: description
});
}

function analyzeWithdrawSecurity(
address bridge
) public view returns (VulnerabilityCheck memory) {
bool hasTimelock;
bool hasWithdrawLimit;

try IBridge(bridge).withdrawalDelay() returns (uint256 delay) {
hasTimelock = delay >= 1 days;
} catch {
hasTimelock = false;
}

try IBridge(bridge).withdrawalLimit() returns (uint256 limit) {
hasWithdrawLimit = limit > 0;
} catch {
hasWithdrawLimit = false;
}

uint8 riskLevel;
string memory description;

if (!hasTimelock) {
riskLevel = 5;
description = "No timelock on withdrawals";
} else if (!hasWithdrawLimit) {
riskLevel = 4;
description = "No withdrawal limits";
} else {
riskLevel = 1;
description = "No vulnerabilities detected";
}

return
VulnerabilityCheck({
functionName: "withdraw()",
hasVulnerability: !hasTimelock || !hasWithdrawLimit,
riskLevel: riskLevel,
description: description
});
}

function analyzeOwnershipSecurity(
address bridge
) public view returns (VulnerabilityCheck memory) {
bool hasTwoStepTransfer;
bool hasTimelock;

try IBridge(bridge).pendingOwner() returns (address pending) {
hasTwoStepTransfer = pending != address(0); // Check if there's a pending owner
} catch {
hasTwoStepTransfer = false;
}

try IBridge(bridge).ownershipTransferDelay() returns (uint256 delay) {
hasTimelock = delay >= 1 days;
} catch {
hasTimelock = false;
}

string memory description = !hasTwoStepTransfer
? "Single-step ownership transfer"
: (
!hasTimelock
? "No timelock on ownership transfer"
: "No vulnerabilities detected"
);

return
VulnerabilityCheck({
functionName: "transferOwnership()",
hasVulnerability: !hasTwoStepTransfer || !hasTimelock,
riskLevel: !hasTwoStepTransfer || !hasTimelock ? 3 : 1,
description: description
});
}
}

interface IBridge {
function requiredSigners() external view returns (uint256);
function mintLimit() external view returns (uint256);
function withdrawalDelay() external view returns (uint256);
function withdrawalLimit() external view returns (uint256);
function pendingOwner() external view returns (address);
function ownershipTransferDelay() external view returns (uint256);
}
159 changes: 159 additions & 0 deletions packages/foundry/contracts/BridgeMonitors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "packages/foundry/contracts/flare-periphery-contracts/coston/EVMTransaction.sol";
import "packages/foundry/contracts/flare-periphery-contracts/coston/IStateConnector.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract BridgeMonitor {
// Bridge event signatures we care about
bytes32 constant DEPOSIT_EVENT_SIG = keccak256("Deposit(address,uint256)");
bytes32 constant WITHDRAW_EVENT_SIG =
keccak256("Withdraw(address,uint256)");
bytes32 constant UPGRADE_EVENT_SIG = keccak256("Upgraded(address)");

// Bridge addresses
address public sepoliaBridge;
address public goerliBridge;

struct SecurityAlert {
bytes32 transactionId;
uint8 severity; // 1: Info, 2: Warning, 3: Critical
string description;
uint256 timestamp;
address bridgeAddress;
}

// Store alerts and processed transactions
mapping(bytes32 => bool) public processedTxs;
SecurityAlert[] public alerts;

event AlertRaised(
bytes32 indexed transactionId,
uint8 severity,
string description,
address indexed bridgeAddress
);

constructor(address _sepoliaBridge, address _goerliBridge) {
sepoliaBridge = _sepoliaBridge;
goerliBridge = _goerliBridge;
}

function verifyAndProcessTransaction(
EVMTransaction.Proof calldata proof
) external {
bytes32 txHash = proof.data.requestBody.transactionHash;
require(!processedTxs[txHash], "Transaction already processed");

// Verify this transaction is from one of our bridges
address txSource = proof.data.responseBody.receivingAddress;
require(
txSource == sepoliaBridge || txSource == goerliBridge,
"Not a monitored bridge"
);

// Process events
for (uint i = 0; i < proof.data.responseBody.events.length; i++) {
EVMTransaction.Event memory evt = proof.data.responseBody.events[i];

// Check if event is from our bridge contract
if (evt.emitterAddress != txSource) continue;

// Check event signatures
bytes32 eventSig = evt.topics[0];

if (eventSig == DEPOSIT_EVENT_SIG) {
processDepositEvent(txHash, evt, txSource);
} else if (eventSig == WITHDRAW_EVENT_SIG) {
processWithdrawEvent(txHash, evt, txSource);
} else if (eventSig == UPGRADE_EVENT_SIG) {
processUpgradeEvent(txHash, evt, txSource);
}
}

processedTxs[txHash] = true;
}

function processDepositEvent(
bytes32 txHash,
EVMTransaction.Event memory evt,
address bridge
) internal {
// Decode event data
(address user, uint256 amount) = abi.decode(
evt.data,
(address, uint256)
);

// Check for large deposits
if (amount > 100 ether) {
// Example threshold
raiseAlert(
txHash,
2, // Warning
"Large deposit detected",
bridge
);
}
}

function processWithdrawEvent(
bytes32 txHash,
EVMTransaction.Event memory evt,
address bridge
) internal {
(address user, uint256 amount) = abi.decode(
evt.data,
(address, uint256)
);

// Check for large withdrawals
if (amount > 100 ether) {
raiseAlert(
txHash,
3, // Critical
"Large withdrawal detected",
bridge
);
}
}

function processUpgradeEvent(
bytes32 txHash,
EVMTransaction.Event memory evt,
address bridge
) internal {
address newImplementation = address(uint160(uint256(evt.topics[1])));

raiseAlert(
txHash,
3, // Critical
"Bridge contract upgraded",
bridge
);
}

function raiseAlert(
bytes32 txHash,
uint8 severity,
string memory description,
address bridge
) internal {
SecurityAlert memory alert = SecurityAlert({
transactionId: txHash,
severity: severity,
description: description,
timestamp: block.timestamp,
bridgeAddress: bridge
});

alerts.push(alert);
emit AlertRaised(txHash, severity, description, bridge);
}

// View functions
function getAlerts() external view returns (SecurityAlert[] memory) {
return alerts;
}
}
26 changes: 26 additions & 0 deletions packages/foundry/contracts/VulnerableBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// VulnerableBridge.sol
pragma solidity ^0.8.24;

contract VulnerableBridge {
uint256 public mintLimit;
uint256 public withdrawalLimit;
uint256 public withdrawalDelay;
address public pendingOwner;
uint256 public ownershipTransferDelay;
uint256 public requiredSigners;

function configureSecurityFeatures(
bool hasMintLimit,
bool hasWithdrawLimit,
bool hasWithdrawDelay,
bool hasMultiSig,
bool hasPendingOwner
) external {
mintLimit = hasMintLimit ? type(uint256).max : 0;
withdrawalLimit = hasWithdrawLimit ? type(uint256).max : 0;
withdrawalDelay = hasWithdrawDelay ? 1 days : 0;
requiredSigners = hasMultiSig ? 3 : 1;
pendingOwner = hasPendingOwner ? address(1) : address(0);
ownershipTransferDelay = hasPendingOwner ? 1 days : 0;
}
}
1 change: 1 addition & 0 deletions packages/foundry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dotenv": "~16.3.1",
"envfile": "~6.18.0",
"ethers": "~5.7.1",
"forge-std": "^1.1.2",
"prettier": "~2.8.8",
"qrcode": "~1.5.3",
"toml": "~3.0.0"
Expand Down
24 changes: 24 additions & 0 deletions packages/foundry/script/DeployBridgeAnalyzer.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// script/DeployBridgeAnalyzer.s.sol
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import "../contracts/BridgeAnalyzer.sol";

contract DeployBridgeAnalyzer is Script {
function run() external {
// Retrieve private key from environment
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

// Start broadcasting transactions
vm.startBroadcast(deployerPrivateKey);

// Deploy BridgeAnalyzer
BridgeAnalyzer analyzer = new BridgeAnalyzer();

// Stop broadcasting transactions
vm.stopBroadcast();

// Log the address
console.log("BridgeAnalyzer deployed to:", address(analyzer));
}
}
Loading