UNPKG

@btc-vision/btc-runtime

Version:

Bitcoin L1 Smart Contract Runtime for OP_NET. Build decentralized applications on Bitcoin using AssemblyScript and WebAssembly. Fully audited.

371 lines (286 loc) 9.33 kB
# Security Security is paramount in smart contract development. This guide covers OP_NET's security mechanisms and best practices for writing secure contracts. ## Security Mechanisms OP_NET provides several built-in security features: | Feature | Description | |---------|-------------| | **SafeMath** | Overflow/underflow protection | | **ReentrancyGuard** | Reentrancy attack prevention | | **Access Control** | Role-based authorization | | **Input Validation** | Calldata parsing with bounds checks | | **Storage Safety** | SHA256-hashed storage keys | ## SafeMath ### The Problem Integer overflow/underflow can cause critical bugs: ```typescript // DANGEROUS: Native arithmetic can overflow silently const a: u256 = u256.Max; const b: u256 = u256.One; const result = a + b; // Wraps to 0! ``` ### The Solution Always use SafeMath for arithmetic: ```typescript import { SafeMath } from '@btc-vision/btc-runtime/runtime'; import { u256 } from '@btc-vision/as-bignum/assembly'; const a = u256.fromU64(100); const b = u256.fromU64(50); // Safe operations const sum = SafeMath.add(a, b); // Reverts on overflow const diff = SafeMath.sub(a, b); // Reverts on underflow const product = SafeMath.mul(a, b); // Reverts on overflow const quotient = SafeMath.div(a, b); // Reverts on division by zero const remainder = SafeMath.mod(a, b); // Reverts on division by zero ``` ### Solidity Comparison In Solidity 0.8+, arithmetic operations revert on overflow/underflow by default. OP_NET achieves the same behavior through SafeMath: ```solidity // Solidity 0.8+ - automatic overflow protection uint256 result = a + b; // Reverts on overflow ``` ```typescript // OP_NET - explicit SafeMath usage const result = SafeMath.add(a, b); // Reverts on overflow ``` ### SafeMath Operations | Operation | Method | Behavior | |-----------|--------|----------| | Addition | `SafeMath.add(a, b)` | Reverts on overflow | | Subtraction | `SafeMath.sub(a, b)` | Reverts on underflow | | Multiplication | `SafeMath.mul(a, b)` | Reverts on overflow | | Division | `SafeMath.div(a, b)` | Reverts on zero divisor | | Modulo | `SafeMath.mod(a, b)` | Reverts on zero divisor | | Power | `SafeMath.pow(base, exp)` | Reverts on overflow | | Square Root | `SafeMath.sqrt(a)` | Newton-Raphson method | See [SafeMath API](../api-reference/safe-math.md) for complete reference. ## Reentrancy Protection Reentrancy attacks occur when a contract calls back into itself before completing, allowing attackers to drain funds by repeatedly calling withdraw before the balance is updated. **Solution:** Extend `ReentrancyGuard` to automatically protect all methods: ```typescript import { ReentrancyGuard, ReentrancyLevel } from '@btc-vision/btc-runtime/runtime'; @final export class MyContract extends ReentrancyGuard { protected override readonly reentrancyLevel: ReentrancyLevel = ReentrancyLevel.STANDARD; @method() public withdraw(calldata: Calldata): BytesWriter { // Protected automatically - re-entry attempts will revert const amount = this.balances.get(Blockchain.tx.sender); this.balances.set(Blockchain.tx.sender, u256.Zero); // ... transfer funds ... return new BytesWriter(0); } } ``` | Mode | Description | |------|-------------| | `ReentrancyLevel.STANDARD` | Boolean lock (default) | | `ReentrancyLevel.CALLBACK` | Depth counter for tracking | Both modes block reentrancy. See [ReentrancyGuard](../contracts/reentrancy-guard.md) for detailed explanation, attack diagrams, and advanced usage. ## Access Control ### onlyDeployer Pattern ```typescript @final export class MyContract extends OP_NET { public constructor() { super(); } @method() public adminFunction(calldata: Calldata): BytesWriter { // Only deployer can call this.onlyDeployer(Blockchain.tx.sender); // Admin logic... return new BytesWriter(0); } } ``` ### Solidity Comparison ```solidity // Solidity modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } ``` ```typescript // OP_NET protected onlyOwner(): void { if (!Blockchain.tx.sender.equals(this.owner.value)) { throw new Revert('Not owner'); } } ``` ### Custom Roles ```typescript @final export class MyContract extends OP_NET { private readonly adminPointer: u16 = Blockchain.nextPointer; private readonly admin: StoredAddress = new StoredAddress(this.adminPointer, Address.zero()); private readonly mintersPointer: u16 = Blockchain.nextPointer; private readonly minters: AddressMemoryMap; public constructor() { super(); this.minters = new AddressMemoryMap(this.mintersPointer); } protected onlyAdmin(): void { if (!Blockchain.tx.sender.equals(this.admin.value)) { throw new Revert('Not admin'); } } protected onlyMinter(): void { if (this.minters.get(Blockchain.tx.sender).isZero()) { throw new Revert('Not minter'); } } @method() public mint(calldata: Calldata): BytesWriter { this.onlyMinter(); // ... return new BytesWriter(0); } @method() public setMinter(calldata: Calldata): BytesWriter { this.onlyAdmin(); const minter = calldata.readAddress(); this.minters.set(minter, u256.One); return new BytesWriter(0); } } ``` ## Input Validation ### Validate All Inputs ```typescript @method() public transfer(calldata: Calldata): BytesWriter { const to = calldata.readAddress(); const amount = calldata.readU256(); // Validate recipient if (to.equals(Address.zero())) { throw new Revert('Cannot transfer to zero address'); } // Validate amount if (amount.isZero()) { throw new Revert('Amount must be greater than zero'); } // Validate balance const balance = this.balances.get(Blockchain.tx.sender); if (balance < amount) { throw new Revert('Insufficient balance'); } // ... proceed with transfer } ``` ### Common Validations ```typescript // Zero address check if (address.equals(Address.zero())) { throw new Revert('Invalid address'); } // Self-transfer check if (from.equals(to)) { throw new Revert('Cannot transfer to self'); } // Amount checks if (amount.isZero()) { throw new Revert('Amount is zero'); } if (amount > maxAmount) { throw new Revert('Amount exceeds maximum'); } // Array bounds if (index >= array.length) { throw new Revert('Index out of bounds'); } ``` ## Common Vulnerabilities ### 1. Integer Overflow/Underflow ```typescript // VULNERABLE const newBalance = balance + amount; // Can overflow! // SAFE const newBalance = SafeMath.add(balance, amount); ``` ### 2. Reentrancy ```typescript // VULNERABLE @method() public withdraw(): void { const amount = balances.get(sender); externalCall(sender, amount); // Can re-enter! balances.set(sender, u256.Zero); } // SAFE (Checks-Effects-Interactions pattern) @method() public withdraw(): void { const amount = balances.get(sender); balances.set(sender, u256.Zero); // Update state first externalCall(sender, amount); // Then make external call } ``` ### 3. Access Control Bypass ```typescript // VULNERABLE @method() public mint(calldata: Calldata): void { // No access control! this._mint(calldata.readAddress(), calldata.readU256()); } // SAFE @method() public mint(calldata: Calldata): void { this.onlyDeployer(Blockchain.tx.sender); this._mint(calldata.readAddress(), calldata.readU256()); } ``` ### 4. tx.origin Authentication ```typescript // VULNERABLE (phishing attack possible) @method() public withdraw(): void { if (Blockchain.tx.origin.equals(owner)) { // WRONG! // ... } } // SAFE @method() public withdraw(): void { if (Blockchain.tx.sender.equals(owner)) { // Correct // ... } } ``` ### 5. Floating Point ```typescript // VULNERABLE (non-deterministic) const price: f64 = 1.5; // NEVER use floats! // SAFE (fixed-point) const PRECISION: u256 = u256.fromU64(1_000_000); const price: u256 = SafeMath.mul(u256.fromU64(15), SafeMath.div(PRECISION, u256.fromU64(10))); ``` ## Security Checklist Before deploying, verify: - [ ] All arithmetic uses SafeMath - [ ] ReentrancyGuard on sensitive functions - [ ] Access control on admin functions - [ ] Input validation on all public methods - [ ] No floating-point arithmetic - [ ] No tx.origin for authentication - [ ] Events emitted for state changes - [ ] Tests cover edge cases - [ ] No hardcoded secrets ## Audit Information btc-runtime has been audited by [Verichains](https://verichains.io). The audit covered: - Contract standards (OP20, OP721) - Storage system security - Cryptographic implementations - SafeMath operations - ReentrancyGuard mechanisms See [SECURITY.md](../../SECURITY.md) for full audit details. ## Reporting Vulnerabilities If you discover a security issue: 1. **DO NOT** open a public GitHub issue 2. Report via [GitHub Security Advisories](https://github.com/btc-vision/btc-runtime/security/advisories) 3. Allow time for fix before disclosure --- **Navigation:** - Previous: [Events](./events.md) - Next: [OP_NET Base Contract](../contracts/op-net-base.md)