@btc-vision/btc-runtime 1.10.10 → 1.10.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +258 -137
- package/SECURITY.md +226 -0
- package/docs/README.md +614 -0
- package/docs/advanced/bitcoin-scripts.md +939 -0
- package/docs/advanced/cross-contract-calls.md +579 -0
- package/docs/advanced/plugins.md +1006 -0
- package/docs/advanced/quantum-resistance.md +660 -0
- package/docs/advanced/signature-verification.md +715 -0
- package/docs/api-reference/blockchain.md +729 -0
- package/docs/api-reference/events.md +642 -0
- package/docs/api-reference/op20.md +902 -0
- package/docs/api-reference/op721.md +819 -0
- package/docs/api-reference/safe-math.md +510 -0
- package/docs/api-reference/storage.md +731 -0
- package/docs/contracts/op-net-base.md +786 -0
- package/docs/contracts/op20-token.md +687 -0
- package/docs/contracts/op20s-signatures.md +614 -0
- package/docs/contracts/op721-nft.md +785 -0
- package/docs/contracts/reentrancy-guard.md +787 -0
- package/docs/core-concepts/blockchain-environment.md +724 -0
- package/docs/core-concepts/decorators.md +466 -0
- package/docs/core-concepts/events.md +652 -0
- package/docs/core-concepts/pointers.md +391 -0
- package/docs/core-concepts/security.md +370 -0
- package/docs/core-concepts/storage-system.md +938 -0
- package/docs/examples/basic-token.md +745 -0
- package/docs/examples/nft-with-reservations.md +1210 -0
- package/docs/examples/oracle-integration.md +1212 -0
- package/docs/examples/stablecoin.md +1180 -0
- package/docs/getting-started/first-contract.md +575 -0
- package/docs/getting-started/installation.md +384 -0
- package/docs/getting-started/project-structure.md +630 -0
- package/docs/storage/memory-maps.md +721 -0
- package/docs/storage/stored-arrays.md +714 -0
- package/docs/storage/stored-maps.md +686 -0
- package/docs/storage/stored-primitives.md +608 -0
- package/docs/types/address.md +773 -0
- package/docs/types/bytes-writer-reader.md +938 -0
- package/docs/types/calldata.md +744 -0
- package/docs/types/safe-math.md +403 -0
- package/package.json +51 -26
- package/runtime/memory/MapOfMap.ts +1 -0
- package/runtime/types/SafeMath.ts +121 -1
- package/LICENSE.md +0 -21
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Security is paramount in smart contract development. This guide covers OPNet's security mechanisms and best practices for writing secure contracts.
|
|
4
|
+
|
|
5
|
+
## Security Mechanisms
|
|
6
|
+
|
|
7
|
+
OPNet provides several built-in security features:
|
|
8
|
+
|
|
9
|
+
| Feature | Description |
|
|
10
|
+
|---------|-------------|
|
|
11
|
+
| **SafeMath** | Overflow/underflow protection |
|
|
12
|
+
| **ReentrancyGuard** | Reentrancy attack prevention |
|
|
13
|
+
| **Access Control** | Role-based authorization |
|
|
14
|
+
| **Input Validation** | Calldata parsing with bounds checks |
|
|
15
|
+
| **Storage Safety** | SHA256-hashed storage keys |
|
|
16
|
+
|
|
17
|
+
## SafeMath
|
|
18
|
+
|
|
19
|
+
### The Problem
|
|
20
|
+
|
|
21
|
+
Integer overflow/underflow can cause critical bugs:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// DANGEROUS: Native arithmetic can overflow silently
|
|
25
|
+
const a: u256 = u256.Max;
|
|
26
|
+
const b: u256 = u256.One;
|
|
27
|
+
const result = a + b; // Wraps to 0!
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### The Solution
|
|
31
|
+
|
|
32
|
+
Always use SafeMath for arithmetic:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { SafeMath } from '@btc-vision/btc-runtime/runtime';
|
|
36
|
+
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
37
|
+
|
|
38
|
+
const a = u256.fromU64(100);
|
|
39
|
+
const b = u256.fromU64(50);
|
|
40
|
+
|
|
41
|
+
// Safe operations
|
|
42
|
+
const sum = SafeMath.add(a, b); // Reverts on overflow
|
|
43
|
+
const diff = SafeMath.sub(a, b); // Reverts on underflow
|
|
44
|
+
const product = SafeMath.mul(a, b); // Reverts on overflow
|
|
45
|
+
const quotient = SafeMath.div(a, b); // Reverts on division by zero
|
|
46
|
+
const remainder = SafeMath.mod(a, b); // Reverts on division by zero
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Solidity Comparison
|
|
50
|
+
|
|
51
|
+
In Solidity 0.8+, arithmetic operations revert on overflow/underflow by default. OPNet achieves the same behavior through SafeMath:
|
|
52
|
+
|
|
53
|
+
```solidity
|
|
54
|
+
// Solidity 0.8+ - automatic overflow protection
|
|
55
|
+
uint256 result = a + b; // Reverts on overflow
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// OPNet - explicit SafeMath usage
|
|
60
|
+
const result = SafeMath.add(a, b); // Reverts on overflow
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### SafeMath Operations
|
|
64
|
+
|
|
65
|
+
| Operation | Method | Behavior |
|
|
66
|
+
|-----------|--------|----------|
|
|
67
|
+
| Addition | `SafeMath.add(a, b)` | Reverts on overflow |
|
|
68
|
+
| Subtraction | `SafeMath.sub(a, b)` | Reverts on underflow |
|
|
69
|
+
| Multiplication | `SafeMath.mul(a, b)` | Reverts on overflow |
|
|
70
|
+
| Division | `SafeMath.div(a, b)` | Reverts on zero divisor |
|
|
71
|
+
| Modulo | `SafeMath.mod(a, b)` | Reverts on zero divisor |
|
|
72
|
+
| Power | `SafeMath.pow(base, exp)` | Reverts on overflow |
|
|
73
|
+
| Square Root | `SafeMath.sqrt(a)` | Newton-Raphson method |
|
|
74
|
+
|
|
75
|
+
See [SafeMath API](../api-reference/safe-math.md) for complete reference.
|
|
76
|
+
|
|
77
|
+
## Reentrancy Protection
|
|
78
|
+
|
|
79
|
+
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.
|
|
80
|
+
|
|
81
|
+
**Solution:** Extend `ReentrancyGuard` to automatically protect all methods:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { ReentrancyGuard, ReentrancyLevel } from '@btc-vision/btc-runtime/runtime';
|
|
85
|
+
|
|
86
|
+
@final
|
|
87
|
+
export class MyContract extends ReentrancyGuard {
|
|
88
|
+
protected override readonly reentrancyLevel: ReentrancyLevel = ReentrancyLevel.STANDARD;
|
|
89
|
+
|
|
90
|
+
@method()
|
|
91
|
+
public withdraw(calldata: Calldata): BytesWriter {
|
|
92
|
+
// Protected automatically - re-entry attempts will revert
|
|
93
|
+
const amount = this.balances.get(Blockchain.tx.sender);
|
|
94
|
+
this.balances.set(Blockchain.tx.sender, u256.Zero);
|
|
95
|
+
// ... transfer funds ...
|
|
96
|
+
return new BytesWriter(0);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| Mode | Description |
|
|
102
|
+
|------|-------------|
|
|
103
|
+
| `ReentrancyLevel.STANDARD` | Boolean lock (default) |
|
|
104
|
+
| `ReentrancyLevel.CALLBACK` | Depth counter for tracking |
|
|
105
|
+
|
|
106
|
+
Both modes block reentrancy. See [ReentrancyGuard](../contracts/reentrancy-guard.md) for detailed explanation, attack diagrams, and advanced usage.
|
|
107
|
+
|
|
108
|
+
## Access Control
|
|
109
|
+
|
|
110
|
+
### onlyDeployer Pattern
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
@final
|
|
114
|
+
export class MyContract extends OP_NET {
|
|
115
|
+
public constructor() {
|
|
116
|
+
super();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@method()
|
|
120
|
+
public adminFunction(calldata: Calldata): BytesWriter {
|
|
121
|
+
// Only deployer can call
|
|
122
|
+
this.onlyDeployer(Blockchain.tx.sender);
|
|
123
|
+
|
|
124
|
+
// Admin logic...
|
|
125
|
+
return new BytesWriter(0);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Solidity Comparison
|
|
131
|
+
|
|
132
|
+
```solidity
|
|
133
|
+
// Solidity
|
|
134
|
+
modifier onlyOwner() {
|
|
135
|
+
require(msg.sender == owner, "Not owner");
|
|
136
|
+
_;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// OPNet
|
|
142
|
+
protected onlyOwner(): void {
|
|
143
|
+
if (!Blockchain.tx.sender.equals(this.owner.value)) {
|
|
144
|
+
throw new Revert('Not owner');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Custom Roles
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
@final
|
|
153
|
+
export class MyContract extends OP_NET {
|
|
154
|
+
private readonly adminPointer: u16 = Blockchain.nextPointer;
|
|
155
|
+
private readonly admin: StoredAddress = new StoredAddress(this.adminPointer, Address.zero());
|
|
156
|
+
|
|
157
|
+
private readonly mintersPointer: u16 = Blockchain.nextPointer;
|
|
158
|
+
private readonly minters: AddressMemoryMap;
|
|
159
|
+
|
|
160
|
+
public constructor() {
|
|
161
|
+
super();
|
|
162
|
+
this.minters = new AddressMemoryMap(this.mintersPointer);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
protected onlyAdmin(): void {
|
|
166
|
+
if (!Blockchain.tx.sender.equals(this.admin.value)) {
|
|
167
|
+
throw new Revert('Not admin');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
protected onlyMinter(): void {
|
|
172
|
+
if (this.minters.get(Blockchain.tx.sender).isZero()) {
|
|
173
|
+
throw new Revert('Not minter');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@method()
|
|
178
|
+
public mint(calldata: Calldata): BytesWriter {
|
|
179
|
+
this.onlyMinter();
|
|
180
|
+
// ...
|
|
181
|
+
return new BytesWriter(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@method()
|
|
185
|
+
public setMinter(calldata: Calldata): BytesWriter {
|
|
186
|
+
this.onlyAdmin();
|
|
187
|
+
const minter = calldata.readAddress();
|
|
188
|
+
this.minters.set(minter, u256.One);
|
|
189
|
+
return new BytesWriter(0);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Input Validation
|
|
195
|
+
|
|
196
|
+
### Validate All Inputs
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
@method()
|
|
200
|
+
public transfer(calldata: Calldata): BytesWriter {
|
|
201
|
+
const to = calldata.readAddress();
|
|
202
|
+
const amount = calldata.readU256();
|
|
203
|
+
|
|
204
|
+
// Validate recipient
|
|
205
|
+
if (to.equals(Address.zero())) {
|
|
206
|
+
throw new Revert('Cannot transfer to zero address');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Validate amount
|
|
210
|
+
if (amount.isZero()) {
|
|
211
|
+
throw new Revert('Amount must be greater than zero');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Validate balance
|
|
215
|
+
const balance = this.balances.get(Blockchain.tx.sender);
|
|
216
|
+
if (balance < amount) {
|
|
217
|
+
throw new Revert('Insufficient balance');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ... proceed with transfer
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Common Validations
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// Zero address check
|
|
228
|
+
if (address.equals(Address.zero())) {
|
|
229
|
+
throw new Revert('Invalid address');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Self-transfer check
|
|
233
|
+
if (from.equals(to)) {
|
|
234
|
+
throw new Revert('Cannot transfer to self');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Amount checks
|
|
238
|
+
if (amount.isZero()) {
|
|
239
|
+
throw new Revert('Amount is zero');
|
|
240
|
+
}
|
|
241
|
+
if (amount > maxAmount) {
|
|
242
|
+
throw new Revert('Amount exceeds maximum');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Array bounds
|
|
246
|
+
if (index >= array.length) {
|
|
247
|
+
throw new Revert('Index out of bounds');
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Common Vulnerabilities
|
|
252
|
+
|
|
253
|
+
### 1. Integer Overflow/Underflow
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// VULNERABLE
|
|
257
|
+
const newBalance = balance + amount; // Can overflow!
|
|
258
|
+
|
|
259
|
+
// SAFE
|
|
260
|
+
const newBalance = SafeMath.add(balance, amount);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 2. Reentrancy
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// VULNERABLE
|
|
267
|
+
@method()
|
|
268
|
+
public withdraw(): void {
|
|
269
|
+
const amount = balances.get(sender);
|
|
270
|
+
externalCall(sender, amount); // Can re-enter!
|
|
271
|
+
balances.set(sender, u256.Zero);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// SAFE (Checks-Effects-Interactions pattern)
|
|
275
|
+
@method()
|
|
276
|
+
public withdraw(): void {
|
|
277
|
+
const amount = balances.get(sender);
|
|
278
|
+
balances.set(sender, u256.Zero); // Update state first
|
|
279
|
+
externalCall(sender, amount); // Then make external call
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 3. Access Control Bypass
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// VULNERABLE
|
|
287
|
+
@method()
|
|
288
|
+
public mint(calldata: Calldata): void {
|
|
289
|
+
// No access control!
|
|
290
|
+
this._mint(calldata.readAddress(), calldata.readU256());
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// SAFE
|
|
294
|
+
@method()
|
|
295
|
+
public mint(calldata: Calldata): void {
|
|
296
|
+
this.onlyDeployer(Blockchain.tx.sender);
|
|
297
|
+
this._mint(calldata.readAddress(), calldata.readU256());
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### 4. tx.origin Authentication
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// VULNERABLE (phishing attack possible)
|
|
305
|
+
@method()
|
|
306
|
+
public withdraw(): void {
|
|
307
|
+
if (Blockchain.tx.origin.equals(owner)) { // WRONG!
|
|
308
|
+
// ...
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// SAFE
|
|
313
|
+
@method()
|
|
314
|
+
public withdraw(): void {
|
|
315
|
+
if (Blockchain.tx.sender.equals(owner)) { // Correct
|
|
316
|
+
// ...
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 5. Floating Point
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// VULNERABLE (non-deterministic)
|
|
325
|
+
const price: f64 = 1.5; // NEVER use floats!
|
|
326
|
+
|
|
327
|
+
// SAFE (fixed-point)
|
|
328
|
+
const PRECISION: u256 = u256.fromU64(1_000_000);
|
|
329
|
+
const price: u256 = SafeMath.mul(u256.fromU64(15), SafeMath.div(PRECISION, u256.fromU64(10)));
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Security Checklist
|
|
333
|
+
|
|
334
|
+
Before deploying, verify:
|
|
335
|
+
|
|
336
|
+
- [ ] All arithmetic uses SafeMath
|
|
337
|
+
- [ ] ReentrancyGuard on sensitive functions
|
|
338
|
+
- [ ] Access control on admin functions
|
|
339
|
+
- [ ] Input validation on all public methods
|
|
340
|
+
- [ ] No floating-point arithmetic
|
|
341
|
+
- [ ] No tx.origin for authentication
|
|
342
|
+
- [ ] Events emitted for state changes
|
|
343
|
+
- [ ] Tests cover edge cases
|
|
344
|
+
- [ ] No hardcoded secrets
|
|
345
|
+
|
|
346
|
+
## Audit Information
|
|
347
|
+
|
|
348
|
+
btc-runtime has been audited by [Verichains](https://verichains.io). The audit covered:
|
|
349
|
+
|
|
350
|
+
- Contract standards (OP20, OP721)
|
|
351
|
+
- Storage system security
|
|
352
|
+
- Cryptographic implementations
|
|
353
|
+
- SafeMath operations
|
|
354
|
+
- ReentrancyGuard mechanisms
|
|
355
|
+
|
|
356
|
+
See [SECURITY.md](../../SECURITY.md) for full audit details.
|
|
357
|
+
|
|
358
|
+
## Reporting Vulnerabilities
|
|
359
|
+
|
|
360
|
+
If you discover a security issue:
|
|
361
|
+
|
|
362
|
+
1. **DO NOT** open a public GitHub issue
|
|
363
|
+
2. Report via [GitHub Security Advisories](https://github.com/btc-vision/btc-runtime/security/advisories)
|
|
364
|
+
3. Allow time for fix before disclosure
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
**Navigation:**
|
|
369
|
+
- Previous: [Events](./events.md)
|
|
370
|
+
- Next: [OP_NET Base Contract](../contracts/op-net-base.md)
|