@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,724 @@
|
|
|
1
|
+
# Blockchain Environment
|
|
2
|
+
|
|
3
|
+
The `Blockchain` object is the primary interface for interacting with the OPNet runtime. It provides access to block information, transaction context, storage operations, cryptographic functions, and cross-contract calls.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { Blockchain } from '@btc-vision/btc-runtime/runtime';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The `Blockchain` object is globally available in all contracts and provides:
|
|
12
|
+
|
|
13
|
+
| Category | Description |
|
|
14
|
+
|----------|-------------|
|
|
15
|
+
| **Block Context** | Current block information (height, hash, timestamp) |
|
|
16
|
+
| **Transaction Context** | Sender, origin, inputs, outputs |
|
|
17
|
+
| **Contract Context** | Contract address, deployer, identity |
|
|
18
|
+
| **Storage** | Read/write persistent and transient storage |
|
|
19
|
+
| **Pointers** | Allocate storage slots |
|
|
20
|
+
| **Cross-Contract Calls** | Call other contracts |
|
|
21
|
+
| **Cryptography** | Hashing and signature verification |
|
|
22
|
+
| **Events** | Emit events |
|
|
23
|
+
|
|
24
|
+
## Block Context
|
|
25
|
+
|
|
26
|
+
Access information about the current block:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// Current block number (height)
|
|
30
|
+
const blockNumber: u64 = Blockchain.block.number;
|
|
31
|
+
|
|
32
|
+
// Block number as u256
|
|
33
|
+
const blockNumberU256: u256 = Blockchain.block.numberU256;
|
|
34
|
+
|
|
35
|
+
// Block hash
|
|
36
|
+
const blockHash: Uint8Array = Blockchain.block.hash;
|
|
37
|
+
|
|
38
|
+
// Median time past (consensus timestamp)
|
|
39
|
+
const timestamp: u64 = Blockchain.block.medianTimestamp;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```mermaid
|
|
43
|
+
---
|
|
44
|
+
config:
|
|
45
|
+
theme: dark
|
|
46
|
+
---
|
|
47
|
+
flowchart LR
|
|
48
|
+
subgraph Block["Block Context"]
|
|
49
|
+
B1["Blockchain.block.number"] --> V1["u64"]
|
|
50
|
+
B2["Blockchain.block.numberU256"] --> V2["u256"]
|
|
51
|
+
B3["Blockchain.block.hash"] --> V3["Uint8Array (32 bytes)"]
|
|
52
|
+
B4["Blockchain.block.medianTimestamp"] --> V4["u64"]
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Solidity Comparison
|
|
57
|
+
|
|
58
|
+
| Solidity | OPNet | Notes |
|
|
59
|
+
|----------|-------|-------|
|
|
60
|
+
| `block.number` | `Blockchain.block.number` | Current block height |
|
|
61
|
+
| `block.timestamp` | `Blockchain.block.medianTimestamp` | OPNet uses Median Time Past |
|
|
62
|
+
| `blockhash(n)` | `Blockchain.getBlockHash(n)` | Historical block hash |
|
|
63
|
+
|
|
64
|
+
### Median Time Past
|
|
65
|
+
|
|
66
|
+
OPNet uses **Median Time Past (MTP)** instead of raw block timestamps. MTP is the median of the last 11 block timestamps, providing more reliable time measurements that are resistant to miner manipulation.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Get current timestamp (median time past)
|
|
70
|
+
const currentTime: u64 = Blockchain.block.medianTimestamp;
|
|
71
|
+
|
|
72
|
+
// Time-based logic
|
|
73
|
+
const ONE_HOUR: u64 = 3600;
|
|
74
|
+
if (currentTime > this.deadline.value) {
|
|
75
|
+
throw new Revert('Deadline passed');
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Transaction Context
|
|
80
|
+
|
|
81
|
+
Access information about the current transaction:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Immediate caller (the address that called this contract)
|
|
85
|
+
const sender: Address = Blockchain.tx.sender;
|
|
86
|
+
|
|
87
|
+
// Original transaction signer (EOA that initiated the transaction)
|
|
88
|
+
// Note: origin is ExtendedAddress for quantum-resistant key support
|
|
89
|
+
const origin: ExtendedAddress = Blockchain.tx.origin;
|
|
90
|
+
|
|
91
|
+
// Transaction ID and hash
|
|
92
|
+
const txId: Uint8Array = Blockchain.tx.txId;
|
|
93
|
+
const txHash: Uint8Array = Blockchain.tx.hash;
|
|
94
|
+
|
|
95
|
+
// Transaction inputs and outputs (UTXOs)
|
|
96
|
+
const inputs: TransactionInput[] = Blockchain.tx.inputs;
|
|
97
|
+
const outputs: TransactionOutput[] = Blockchain.tx.outputs;
|
|
98
|
+
|
|
99
|
+
// Consensus rules for current transaction
|
|
100
|
+
const consensus: ConsensusRules = Blockchain.tx.consensus;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```mermaid
|
|
104
|
+
---
|
|
105
|
+
config:
|
|
106
|
+
theme: dark
|
|
107
|
+
---
|
|
108
|
+
flowchart LR
|
|
109
|
+
subgraph TX["Transaction Context"]
|
|
110
|
+
T1["Blockchain.tx.sender"] --> A1["Address"]
|
|
111
|
+
T2["Blockchain.tx.origin"] --> A2["ExtendedAddress"]
|
|
112
|
+
T3["Blockchain.tx.txId"] --> A3["Uint8Array"]
|
|
113
|
+
T4["Blockchain.tx.hash"] --> A4["Uint8Array"]
|
|
114
|
+
T5["Blockchain.tx.inputs"] --> A5["TransactionInput[]"]
|
|
115
|
+
T6["Blockchain.tx.outputs"] --> A6["TransactionOutput[]"]
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### sender vs origin
|
|
120
|
+
|
|
121
|
+
This distinction is critical for security:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
👤 User (EOA) --> Contract A --> Contract B
|
|
125
|
+
origin=User origin=User
|
|
126
|
+
sender=User sender=ContractA
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// sender: The immediate caller (Address type)
|
|
131
|
+
// - Use for most authorization checks
|
|
132
|
+
// - Changes with each contract call
|
|
133
|
+
|
|
134
|
+
// origin: The original transaction signer (ExtendedAddress type)
|
|
135
|
+
// - Always the EOA that signed the transaction
|
|
136
|
+
// - Stays constant through the call chain
|
|
137
|
+
// - Supports quantum-resistant keys (ML-DSA)
|
|
138
|
+
// - Be careful: using origin can enable phishing attacks!
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Solidity Comparison
|
|
142
|
+
|
|
143
|
+
| Solidity | OPNet | Notes |
|
|
144
|
+
|----------|-------|-------|
|
|
145
|
+
| `msg.sender` | `Blockchain.tx.sender` | Immediate caller |
|
|
146
|
+
| `tx.origin` | `Blockchain.tx.origin` | Original signer (ExtendedAddress) |
|
|
147
|
+
| `address(this)` | `Blockchain.contractAddress` | This contract's address |
|
|
148
|
+
| N/A | `Blockchain.contractDeployer` | Who deployed the contract |
|
|
149
|
+
|
|
150
|
+
### Contract Execution Context Flow
|
|
151
|
+
|
|
152
|
+
```mermaid
|
|
153
|
+
---
|
|
154
|
+
config:
|
|
155
|
+
theme: dark
|
|
156
|
+
---
|
|
157
|
+
sequenceDiagram
|
|
158
|
+
participant User as User/EOA
|
|
159
|
+
participant TX as Transaction Broadcast
|
|
160
|
+
participant VM as OPNet VM
|
|
161
|
+
participant ContractA as Target Contract
|
|
162
|
+
participant ContractB as External Contract
|
|
163
|
+
participant Storage as Storage
|
|
164
|
+
|
|
165
|
+
Note over User,Storage: Transaction Initiation
|
|
166
|
+
User->>TX: Sign transaction
|
|
167
|
+
TX->>VM: Broadcast transaction
|
|
168
|
+
|
|
169
|
+
Note over User,Storage: OPNet Runtime Initialization
|
|
170
|
+
VM->>VM: Initialize Blockchain Context
|
|
171
|
+
VM->>VM: Set tx.origin = User Address
|
|
172
|
+
VM->>VM: Set tx.sender = User Address
|
|
173
|
+
|
|
174
|
+
Note over User,Storage: Contract Loading
|
|
175
|
+
VM->>ContractA: Load Target Contract
|
|
176
|
+
VM->>VM: Set contractAddress
|
|
177
|
+
VM->>VM: Set contractDeployer
|
|
178
|
+
VM->>ContractA: Execute Contract Method
|
|
179
|
+
|
|
180
|
+
Note over User,Storage: Cross-Contract Calls (if any)
|
|
181
|
+
alt Makes Cross-Contract Call
|
|
182
|
+
ContractA->>VM: Request external call
|
|
183
|
+
VM->>VM: Update tx.sender to ContractA
|
|
184
|
+
VM->>ContractB: Call External Contract
|
|
185
|
+
ContractB->>VM: Return result
|
|
186
|
+
VM->>VM: Restore tx.sender
|
|
187
|
+
VM->>ContractA: Return result
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
Note over User,Storage: Transaction Finalization
|
|
191
|
+
ContractA->>VM: Complete Execution
|
|
192
|
+
VM->>Storage: Commit Storage Changes
|
|
193
|
+
VM->>VM: Emit Events
|
|
194
|
+
VM->>User: Transaction Complete
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Security Warning
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// DANGEROUS: Using origin for authorization
|
|
201
|
+
@method()
|
|
202
|
+
public withdraw(calldata: Calldata): BytesWriter {
|
|
203
|
+
// BAD: Allows phishing attacks through malicious contracts
|
|
204
|
+
if (Blockchain.tx.origin.equals(this.owner)) {
|
|
205
|
+
// ...
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// SAFE: Using sender for authorization
|
|
210
|
+
@method()
|
|
211
|
+
public withdraw(calldata: Calldata): BytesWriter {
|
|
212
|
+
// GOOD: Only direct caller can access
|
|
213
|
+
if (Blockchain.tx.sender.equals(this.owner)) {
|
|
214
|
+
// ...
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Contract Context
|
|
220
|
+
|
|
221
|
+
Access contract metadata:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// This contract's address
|
|
225
|
+
const address: Address = Blockchain.contractAddress;
|
|
226
|
+
|
|
227
|
+
// Contract deployer address
|
|
228
|
+
const deployer: Address = Blockchain.contractDeployer;
|
|
229
|
+
|
|
230
|
+
// Current contract instance
|
|
231
|
+
const contract: OP_NET = Blockchain.contract;
|
|
232
|
+
|
|
233
|
+
// Chain ID (for replay protection)
|
|
234
|
+
const chainId: Uint8Array = Blockchain.chainId;
|
|
235
|
+
|
|
236
|
+
// Protocol ID
|
|
237
|
+
const protocolId: Uint8Array = Blockchain.protocolId;
|
|
238
|
+
|
|
239
|
+
// Current network
|
|
240
|
+
const network: Networks = Blockchain.network;
|
|
241
|
+
|
|
242
|
+
// Dead address for burns
|
|
243
|
+
const deadAddress: ExtendedAddress = Blockchain.DEAD_ADDRESS;
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
```mermaid
|
|
247
|
+
---
|
|
248
|
+
config:
|
|
249
|
+
theme: dark
|
|
250
|
+
---
|
|
251
|
+
flowchart LR
|
|
252
|
+
subgraph Contract["Contract Context"]
|
|
253
|
+
C1["Blockchain.contractAddress"] --> V1["Address"]
|
|
254
|
+
C2["Blockchain.contractDeployer"] --> V2["Address"]
|
|
255
|
+
C3["Blockchain.contract"] --> V3["OP_NET"]
|
|
256
|
+
C4["Blockchain.chainId"] --> V4["Uint8Array"]
|
|
257
|
+
C5["Blockchain.network"] --> V5["Networks"]
|
|
258
|
+
C6["Blockchain.DEAD_ADDRESS"] --> V6["ExtendedAddress"]
|
|
259
|
+
end
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Storage Operations
|
|
263
|
+
|
|
264
|
+
### Persistent Storage
|
|
265
|
+
|
|
266
|
+
Direct storage access (low-level):
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { encodePointer } from '@btc-vision/btc-runtime/runtime';
|
|
270
|
+
|
|
271
|
+
// Create storage key from pointer and subPointer
|
|
272
|
+
const pointerHash = encodePointer(pointer, subPointer);
|
|
273
|
+
|
|
274
|
+
// Write to storage
|
|
275
|
+
Blockchain.setStorageAt(pointerHash, value.toUint8Array(true));
|
|
276
|
+
|
|
277
|
+
// Read from storage
|
|
278
|
+
const stored = Blockchain.getStorageAt(pointerHash);
|
|
279
|
+
const value = u256.fromUint8ArrayBE(stored);
|
|
280
|
+
|
|
281
|
+
// Check if storage slot has value
|
|
282
|
+
const hasValue: bool = Blockchain.hasStorageAt(pointerHash);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Transient Storage
|
|
286
|
+
|
|
287
|
+
Transient storage is cleared after each transaction (useful for reentrancy guards):
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// Write to transient storage
|
|
291
|
+
Blockchain.setTransientStorageAt(pointerHash, value);
|
|
292
|
+
|
|
293
|
+
// Read from transient storage
|
|
294
|
+
const transientValue = Blockchain.getTransientStorageAt(pointerHash);
|
|
295
|
+
|
|
296
|
+
// Check if transient storage slot has value
|
|
297
|
+
const hasTransient: bool = Blockchain.hasTransientStorageAt(pointerHash);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Warning:** Transient storage is NOT CURRENTLY ENABLED IN PRODUCTION. It is experimental and only available in the testing framework.
|
|
301
|
+
|
|
302
|
+
**Note:** For most use cases, use the typed storage classes like `StoredU256`, `StoredString`, etc. Direct storage access is for advanced scenarios.
|
|
303
|
+
|
|
304
|
+
See [Storage System](./storage-system.md) for detailed information.
|
|
305
|
+
|
|
306
|
+
## Pointer Allocation
|
|
307
|
+
|
|
308
|
+
Allocate storage pointers for your contract:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
// Get the next available pointer
|
|
312
|
+
const myPointer: u16 = Blockchain.nextPointer;
|
|
313
|
+
|
|
314
|
+
// Use it for storage
|
|
315
|
+
private balancePointer: u16 = Blockchain.nextPointer;
|
|
316
|
+
private allowancePointer: u16 = Blockchain.nextPointer;
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Each `nextPointer` call returns a unique `u16` value. Pointers are allocated sequentially starting from 1. Limited to 65,535 storage slots per contract.
|
|
320
|
+
|
|
321
|
+
See [Pointers](./pointers.md) for more details.
|
|
322
|
+
|
|
323
|
+
## Cross-Contract Calls
|
|
324
|
+
|
|
325
|
+
Call other contracts:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { Blockchain, Address, BytesWriter, CallResult } from '@btc-vision/btc-runtime/runtime';
|
|
329
|
+
|
|
330
|
+
// Prepare the call
|
|
331
|
+
const targetContract: Address = /* ... */;
|
|
332
|
+
const calldata: BytesWriter = /* encoded call data */;
|
|
333
|
+
const stopOnFailure: bool = true;
|
|
334
|
+
|
|
335
|
+
// Make the call
|
|
336
|
+
const result: CallResult = Blockchain.call(targetContract, calldata, stopOnFailure);
|
|
337
|
+
|
|
338
|
+
// Handle the result
|
|
339
|
+
if (result.success) {
|
|
340
|
+
const returnData = result.data; // BytesReader
|
|
341
|
+
// Process return data...
|
|
342
|
+
} else {
|
|
343
|
+
throw new Revert('External call failed');
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Call Parameters
|
|
348
|
+
|
|
349
|
+
| Parameter | Type | Description |
|
|
350
|
+
|-----------|------|-------------|
|
|
351
|
+
| `destinationContract` | `Address` | Contract to call |
|
|
352
|
+
| `calldata` | `BytesWriter` | Encoded function call |
|
|
353
|
+
| `stopExecutionOnFailure` | `boolean` | If true (default), revert entire transaction on failure |
|
|
354
|
+
|
|
355
|
+
### Cross-Contract Call Sequence
|
|
356
|
+
|
|
357
|
+
```mermaid
|
|
358
|
+
---
|
|
359
|
+
config:
|
|
360
|
+
theme: dark
|
|
361
|
+
---
|
|
362
|
+
sequenceDiagram
|
|
363
|
+
participant UserEOA as 👤 User (EOA)
|
|
364
|
+
participant ContractA as Contract A
|
|
365
|
+
participant Blockchain as Blockchain Singleton
|
|
366
|
+
participant ContractB as Contract B
|
|
367
|
+
participant Storage as Bitcoin L1 Storage
|
|
368
|
+
|
|
369
|
+
UserEOA->>ContractA: Call methodA()
|
|
370
|
+
Note over ContractA: tx.sender = UserEOA<br/>tx.origin = UserEOA
|
|
371
|
+
|
|
372
|
+
ContractA->>ContractA: Prepare calldata for Contract B
|
|
373
|
+
ContractA->>Blockchain: call(ContractB, calldata, stopOnFailure)
|
|
374
|
+
|
|
375
|
+
Blockchain->>Blockchain: Save current context
|
|
376
|
+
Blockchain->>Blockchain: Update tx.sender = ContractA
|
|
377
|
+
Note over Blockchain: tx.origin remains UserEOA
|
|
378
|
+
|
|
379
|
+
Blockchain->>ContractB: Execute methodB(calldata)
|
|
380
|
+
Note over ContractB: tx.sender = ContractA<br/>tx.origin = UserEOA
|
|
381
|
+
|
|
382
|
+
ContractB->>Storage: Read/Write State
|
|
383
|
+
Storage-->>ContractB: State Data
|
|
384
|
+
|
|
385
|
+
ContractB-->>Blockchain: Return CallResult{success, data}
|
|
386
|
+
Blockchain->>Blockchain: Restore previous context
|
|
387
|
+
Blockchain-->>ContractA: Return CallResult
|
|
388
|
+
|
|
389
|
+
ContractA->>ContractA: Process return data
|
|
390
|
+
alt stopOnFailure = true AND success = false
|
|
391
|
+
ContractA->>UserEOA: Revert entire transaction
|
|
392
|
+
else success = true
|
|
393
|
+
ContractA->>Storage: Commit changes
|
|
394
|
+
ContractA-->>UserEOA: Return success
|
|
395
|
+
end
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Solidity Comparison
|
|
399
|
+
|
|
400
|
+
```solidity
|
|
401
|
+
// Solidity
|
|
402
|
+
(bool success, bytes memory data) = target.call(calldata);
|
|
403
|
+
|
|
404
|
+
// OPNet
|
|
405
|
+
const result = Blockchain.call(target, calldata, true);
|
|
406
|
+
// result.success: boolean
|
|
407
|
+
// result.data: BytesReader
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
See [Cross-Contract Calls](../advanced/cross-contract-calls.md) for advanced usage.
|
|
411
|
+
|
|
412
|
+
## Contract Deployment
|
|
413
|
+
|
|
414
|
+
Deploy new contracts from existing templates:
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
// Deploy a new contract using an existing contract as template
|
|
418
|
+
const newContractAddress: Address = Blockchain.deployContractFromExisting(
|
|
419
|
+
templateAddress, // Address of existing contract to clone
|
|
420
|
+
salt, // u256 salt for deterministic address
|
|
421
|
+
constructorData // BytesWriter with constructor parameters
|
|
422
|
+
);
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
This uses CREATE2-style deterministic addressing: same salt + template = same address.
|
|
426
|
+
|
|
427
|
+
## Cryptographic Operations
|
|
428
|
+
|
|
429
|
+
### SHA256 Hashing
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// Single SHA256
|
|
433
|
+
const hash: Uint8Array = Blockchain.sha256(data);
|
|
434
|
+
|
|
435
|
+
// Double SHA256 (hash256 - common in Bitcoin)
|
|
436
|
+
const doubleHash: Uint8Array = Blockchain.hash256(data);
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Signature Verification
|
|
440
|
+
|
|
441
|
+
OPNet supports both classical Schnorr signatures and quantum-resistant ML-DSA signatures:
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
// Recommended: Use consensus-aware verification
|
|
445
|
+
// Automatically selects Schnorr or ML-DSA based on consensus rules
|
|
446
|
+
const isValid: bool = Blockchain.verifySignature(
|
|
447
|
+
address, // ExtendedAddress
|
|
448
|
+
signature, // Uint8Array
|
|
449
|
+
hash // Uint8Array (32 bytes)
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
// Force ML-DSA verification even if Schnorr is allowed
|
|
453
|
+
const isValidMLDSA: bool = Blockchain.verifySignature(
|
|
454
|
+
address,
|
|
455
|
+
signature,
|
|
456
|
+
hash,
|
|
457
|
+
true // forceMLDSA
|
|
458
|
+
);
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
```mermaid
|
|
462
|
+
---
|
|
463
|
+
config:
|
|
464
|
+
theme: dark
|
|
465
|
+
---
|
|
466
|
+
flowchart LR
|
|
467
|
+
subgraph Verify["Blockchain.verifySignature()"]
|
|
468
|
+
Check{"unsafeSignaturesAllowed()<br/>AND !forceMLDSA?"}
|
|
469
|
+
Check -->|Yes| Schnorr["Schnorr Verification<br/>(64-byte signature)"]
|
|
470
|
+
Check -->|No| MLDSA["ML-DSA Verification<br/>(ML-DSA-44, Level2)"]
|
|
471
|
+
end
|
|
472
|
+
Input["address, signature, hash, forceMLDSA?"] --> Check
|
|
473
|
+
Schnorr --> Result["boolean"]
|
|
474
|
+
MLDSA --> Result
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
#### Legacy Methods (Deprecated)
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
// Verify Schnorr signature directly (deprecated)
|
|
481
|
+
const isValid: bool = Blockchain.verifySchnorrSignature(
|
|
482
|
+
publicKey, // ExtendedAddress
|
|
483
|
+
signature, // Uint8Array (64 bytes)
|
|
484
|
+
hash // Uint8Array (32 bytes)
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
// Verify ML-DSA signature with specific security level
|
|
488
|
+
const isValidQuantum: bool = Blockchain.verifyMLDSASignature(
|
|
489
|
+
MLDSASecurityLevel.Level2, // Level2, Level3, or Level5
|
|
490
|
+
publicKey, // Uint8Array (size depends on level)
|
|
491
|
+
signature, // Uint8Array (size depends on level)
|
|
492
|
+
hash // Uint8Array (32 bytes)
|
|
493
|
+
);
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
ML-DSA Security Levels:
|
|
497
|
+
- **Level2 (ML-DSA-44)**: 1312-byte public key, 2420-byte signature
|
|
498
|
+
- **Level3 (ML-DSA-65)**: 1952-byte public key, 3309-byte signature
|
|
499
|
+
- **Level5 (ML-DSA-87)**: 2592-byte public key, 4627-byte signature
|
|
500
|
+
|
|
501
|
+
See [Signature Verification](../advanced/signature-verification.md) for details.
|
|
502
|
+
|
|
503
|
+
## Utility Methods
|
|
504
|
+
|
|
505
|
+
### Account Type Check
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
// Check if an address is a contract
|
|
509
|
+
const isContract: bool = Blockchain.isContract(address);
|
|
510
|
+
|
|
511
|
+
// Get account type code (0 = EOA, >0 = contract type)
|
|
512
|
+
const accountType: u32 = Blockchain.getAccountType(address);
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Bitcoin Address Validation
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
// Validate a Bitcoin address for the current network
|
|
519
|
+
const isValid: bool = Blockchain.validateBitcoinAddress(addressString);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Block Hash Lookup
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// Get historical block hash (limited to ~256 recent blocks)
|
|
526
|
+
const oldBlockHash: Uint8Array = Blockchain.getBlockHash(blockNumber);
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Logging (Testing Only)
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
// Log debug messages (only works in unit testing framework)
|
|
533
|
+
Blockchain.log("Debug message");
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Warning:** `log()` is ONLY available in the unit testing framework. It will fail in production or testnet environments.
|
|
537
|
+
|
|
538
|
+
## Event Emission
|
|
539
|
+
|
|
540
|
+
Emit events for off-chain indexing:
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
import { NetEvent } from '@btc-vision/btc-runtime/runtime';
|
|
544
|
+
|
|
545
|
+
// Emit an event
|
|
546
|
+
Blockchain.emit(new TransferEvent(from, to, amount));
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
See [Events](./events.md) for complete event documentation.
|
|
550
|
+
|
|
551
|
+
## Blockchain Singleton Architecture
|
|
552
|
+
|
|
553
|
+
```mermaid
|
|
554
|
+
---
|
|
555
|
+
config:
|
|
556
|
+
theme: dark
|
|
557
|
+
---
|
|
558
|
+
classDiagram
|
|
559
|
+
class BlockchainEnvironment {
|
|
560
|
+
<<singleton>>
|
|
561
|
+
+DEAD_ADDRESS: ExtendedAddress
|
|
562
|
+
+network: Networks
|
|
563
|
+
+block: Block
|
|
564
|
+
+tx: Transaction
|
|
565
|
+
+contract: OP_NET
|
|
566
|
+
+nextPointer: u16
|
|
567
|
+
+contractDeployer: Address
|
|
568
|
+
+contractAddress: Address
|
|
569
|
+
+chainId: Uint8Array
|
|
570
|
+
+protocolId: Uint8Array
|
|
571
|
+
+call(destination, calldata, stopOnFailure) CallResult
|
|
572
|
+
+deployContractFromExisting(address, salt, calldata) Address
|
|
573
|
+
+getStorageAt(pointerHash) Uint8Array
|
|
574
|
+
+setStorageAt(pointerHash, value) void
|
|
575
|
+
+hasStorageAt(pointerHash) bool
|
|
576
|
+
+getTransientStorageAt(pointerHash) Uint8Array
|
|
577
|
+
+setTransientStorageAt(pointerHash, value) void
|
|
578
|
+
+hasTransientStorageAt(pointerHash) bool
|
|
579
|
+
+sha256(buffer) Uint8Array
|
|
580
|
+
+hash256(buffer) Uint8Array
|
|
581
|
+
+verifySignature(address, sig, hash, forceMLDSA?) bool
|
|
582
|
+
+verifySchnorrSignature(pubKey, sig, hash) bool
|
|
583
|
+
+verifyMLDSASignature(level, pubKey, sig, hash) bool
|
|
584
|
+
+isContract(address) bool
|
|
585
|
+
+getAccountType(address) u32
|
|
586
|
+
+validateBitcoinAddress(address) bool
|
|
587
|
+
+getBlockHash(blockNumber) Uint8Array
|
|
588
|
+
+emit(event) void
|
|
589
|
+
+log(data) void
|
|
590
|
+
+registerPlugin(plugin) void
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
class Block {
|
|
594
|
+
+hash: Uint8Array
|
|
595
|
+
+number: u64
|
|
596
|
+
+numberU256: u256
|
|
597
|
+
+medianTimestamp: u64
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
class Transaction {
|
|
601
|
+
+sender: Address
|
|
602
|
+
+origin: ExtendedAddress
|
|
603
|
+
+txId: Uint8Array
|
|
604
|
+
+hash: Uint8Array
|
|
605
|
+
+inputs: TransactionInput[]
|
|
606
|
+
+outputs: TransactionOutput[]
|
|
607
|
+
+consensus: ConsensusRules
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
class CallResult {
|
|
611
|
+
+success: boolean
|
|
612
|
+
+data: BytesReader
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
BlockchainEnvironment --> Block: block
|
|
616
|
+
BlockchainEnvironment --> Transaction: tx
|
|
617
|
+
BlockchainEnvironment ..> CallResult: returns
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
## Example: Using Blockchain in a Contract
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
624
|
+
import {
|
|
625
|
+
OP_NET,
|
|
626
|
+
Blockchain,
|
|
627
|
+
Calldata,
|
|
628
|
+
BytesWriter,
|
|
629
|
+
StoredU256,
|
|
630
|
+
Revert,
|
|
631
|
+
EMPTY_POINTER,
|
|
632
|
+
ABIDataTypes,
|
|
633
|
+
} from '@btc-vision/btc-runtime/runtime';
|
|
634
|
+
|
|
635
|
+
@final
|
|
636
|
+
export class MyContract extends OP_NET {
|
|
637
|
+
// Storage pointer declaration
|
|
638
|
+
private readonly lastUpdatePointer: u16 = Blockchain.nextPointer;
|
|
639
|
+
private readonly lastUpdate: StoredU256 = new StoredU256(
|
|
640
|
+
this.lastUpdatePointer,
|
|
641
|
+
EMPTY_POINTER
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
public constructor() {
|
|
645
|
+
super();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
public override onDeployment(_calldata: Calldata): void {
|
|
649
|
+
// Store deployment block
|
|
650
|
+
this.lastUpdate.value = u256.fromU64(Blockchain.block.number);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
@method()
|
|
654
|
+
public doSomething(calldata: Calldata): BytesWriter {
|
|
655
|
+
// Access control
|
|
656
|
+
this.onlyDeployer(Blockchain.tx.sender);
|
|
657
|
+
|
|
658
|
+
// Time check
|
|
659
|
+
const now = Blockchain.block.medianTimestamp;
|
|
660
|
+
const lastBlock = this.lastUpdate.value.toU64();
|
|
661
|
+
const currentBlock = Blockchain.block.number;
|
|
662
|
+
|
|
663
|
+
// Must wait at least 10 blocks
|
|
664
|
+
if (currentBlock - lastBlock < 10) {
|
|
665
|
+
throw new Revert('Must wait 10 blocks');
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Update timestamp
|
|
669
|
+
this.lastUpdate.value = u256.fromU64(currentBlock);
|
|
670
|
+
|
|
671
|
+
return new BytesWriter(0);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
## Summary
|
|
677
|
+
|
|
678
|
+
| Property/Method | Returns | Description |
|
|
679
|
+
|-----------------|---------|-------------|
|
|
680
|
+
| `Blockchain.block.number` | `u64` | Current block height |
|
|
681
|
+
| `Blockchain.block.numberU256` | `u256` | Current block height as u256 |
|
|
682
|
+
| `Blockchain.block.hash` | `Uint8Array` | Current block hash |
|
|
683
|
+
| `Blockchain.block.medianTimestamp` | `u64` | Median time past |
|
|
684
|
+
| `Blockchain.tx.sender` | `Address` | Immediate caller |
|
|
685
|
+
| `Blockchain.tx.origin` | `ExtendedAddress` | Original signer |
|
|
686
|
+
| `Blockchain.tx.txId` | `Uint8Array` | Transaction ID |
|
|
687
|
+
| `Blockchain.tx.hash` | `Uint8Array` | Transaction hash |
|
|
688
|
+
| `Blockchain.tx.inputs` | `TransactionInput[]` | Transaction inputs |
|
|
689
|
+
| `Blockchain.tx.outputs` | `TransactionOutput[]` | Transaction outputs |
|
|
690
|
+
| `Blockchain.tx.consensus` | `ConsensusRules` | Consensus rules |
|
|
691
|
+
| `Blockchain.contract` | `OP_NET` | Current contract instance |
|
|
692
|
+
| `Blockchain.contractAddress` | `Address` | This contract's address |
|
|
693
|
+
| `Blockchain.contractDeployer` | `Address` | Contract deployer |
|
|
694
|
+
| `Blockchain.chainId` | `Uint8Array` | Chain identifier |
|
|
695
|
+
| `Blockchain.protocolId` | `Uint8Array` | Protocol identifier |
|
|
696
|
+
| `Blockchain.network` | `Networks` | Current network |
|
|
697
|
+
| `Blockchain.DEAD_ADDRESS` | `ExtendedAddress` | Burn address |
|
|
698
|
+
| `Blockchain.nextPointer` | `u16` | Next storage pointer |
|
|
699
|
+
| `Blockchain.call()` | `CallResult` | Cross-contract call |
|
|
700
|
+
| `Blockchain.deployContractFromExisting()` | `Address` | Deploy new contract |
|
|
701
|
+
| `Blockchain.getStorageAt()` | `Uint8Array` | Read persistent storage |
|
|
702
|
+
| `Blockchain.setStorageAt()` | `void` | Write persistent storage |
|
|
703
|
+
| `Blockchain.hasStorageAt()` | `bool` | Check storage existence |
|
|
704
|
+
| `Blockchain.getTransientStorageAt()` | `Uint8Array` | Read transient storage |
|
|
705
|
+
| `Blockchain.setTransientStorageAt()` | `void` | Write transient storage |
|
|
706
|
+
| `Blockchain.hasTransientStorageAt()` | `bool` | Check transient storage existence |
|
|
707
|
+
| `Blockchain.sha256()` | `Uint8Array` | SHA256 hash |
|
|
708
|
+
| `Blockchain.hash256()` | `Uint8Array` | Double SHA256 |
|
|
709
|
+
| `Blockchain.verifySignature()` | `bool` | Consensus-aware signature verification |
|
|
710
|
+
| `Blockchain.verifySchnorrSignature()` | `bool` | Schnorr verification (deprecated) |
|
|
711
|
+
| `Blockchain.verifyMLDSASignature()` | `bool` | ML-DSA verification |
|
|
712
|
+
| `Blockchain.isContract()` | `bool` | Check if address is contract |
|
|
713
|
+
| `Blockchain.getAccountType()` | `u32` | Get account type code |
|
|
714
|
+
| `Blockchain.validateBitcoinAddress()` | `bool` | Validate Bitcoin address |
|
|
715
|
+
| `Blockchain.getBlockHash()` | `Uint8Array` | Historical block hash |
|
|
716
|
+
| `Blockchain.emit()` | `void` | Emit event |
|
|
717
|
+
| `Blockchain.log()` | `void` | Debug logging (testing only) |
|
|
718
|
+
| `Blockchain.registerPlugin()` | `void` | Register a plugin |
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
**Navigation:**
|
|
723
|
+
- Previous: [Project Structure](../getting-started/project-structure.md)
|
|
724
|
+
- Next: [Storage System](./storage-system.md)
|