Skip to content

Commit a54880f

Browse files
committed
Ethereum Oracle
1 parent 62f20b0 commit a54880f

43 files changed

Lines changed: 7149 additions & 69 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/cli/main.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"encoding/json"
56
"errors"
67
"flag"
@@ -15,6 +16,8 @@ import (
1516
"time"
1617

1718
"github.com/canopy-network/canopy/cmd/rpc"
19+
"github.com/canopy-network/canopy/cmd/rpc/oracle"
20+
"github.com/canopy-network/canopy/cmd/rpc/oracle/eth"
1821
"github.com/canopy-network/canopy/controller"
1922
"github.com/canopy-network/canopy/fsm"
2023
"github.com/canopy-network/canopy/lib"
@@ -75,6 +78,10 @@ func Start() {
7578
l.Infof("Sleeping until %s", untilTime.String())
7679
time.Sleep(untilTime)
7780
}
81+
// create a shared context for oracle and ethereum block provider
82+
ctx, cancel := context.WithCancel(context.Background())
83+
defer cancel()
84+
7885
// initialize and start the metrics server
7986
metrics := lib.NewMetricsServer(validatorKey.PublicKey().Address(), config.MetricsConfig)
8087
// create a new database object from the config
@@ -87,8 +94,24 @@ func Start() {
8794
if err != nil {
8895
l.Fatal(err.Error())
8996
}
97+
// create a seperate logger for the oracle and all oracle components
98+
oracleLogger := lib.NewOracleLogger(lib.LoggerConfig{Level: config.GetLogLevel()}, config.OracleConfig.LogPath)
99+
// create a new ethereum disk storage instance for the oracle order store
100+
oracleStorage, e := oracle.NewOracleDiskStorage(config.OracleConfig.OrderStorePath, oracleLogger)
101+
if e != nil {
102+
l.Fatal(e.Error())
103+
}
104+
// create the ethereum block provider
105+
ethBlockProvider := eth.NewEthBlockProvider(config.EthBlockProviderConfig, oracleLogger)
106+
// create a new oracle instance and pass the ethereum block provider with shared context
107+
oracle, e := oracle.NewOracle(ctx, config.OracleConfig, ethBlockProvider, oracleStorage, oracleLogger)
108+
if e != nil {
109+
l.Fatal(e.Error())
110+
}
111+
// start the oracle with shared context
112+
oracle.Start(ctx)
90113
// create a new instance of the application
91-
app, err := controller.New(sm, config, validatorKey, metrics, l)
114+
app, err := controller.New(sm, oracle, config, validatorKey, metrics, l)
92115
if err != nil {
93116
l.Fatal(err.Error())
94117
}
@@ -102,6 +125,10 @@ func Start() {
102125
rpcServer.Start()
103126
// block until a kill signal is received
104127
waitForKill()
128+
// cancel the shared context to stop oracle components
129+
cancel()
130+
// gracefuly stop oracle
131+
oracle.Stop()
105132
// gracefully stop the app
106133
app.Stop()
107134
// gracefully stop the metrics server

cmd/rpc/oracle/README.md

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Oracle Package
2+
3+
The Oracle package provides cross-chain transaction witnessing and validation capabilities for the Canopy blockchain system. It implements a chain-agnostic oracle that coordinates between external blockchains (like Ethereum) and the Canopy nested chain to facilitate cross-chain order execution and validation.
4+
5+
## Overview
6+
7+
The Oracle package is designed to handle:
8+
- Witnessing transactions on external blockchains containing Canopy lock & close orders
9+
- Validating and storing witnessed orders in a local order store
10+
- Participating in the BFT consensus process by providing witnessed orders for block proposals
11+
- Synchronizing with the root chain order book to maintain consistency
12+
- Managing persistent state for reliable order processing
13+
14+
## Core Components
15+
16+
### Oracle
17+
18+
The main entry point for the Transaction Oracle system. It manages the overall cross-chain witnessing process, including:
19+
- Receiving blocks from external blockchain providers
20+
- Validating witnessed orders against the root chain order book
21+
- Persisting witnessed orders to local storage
22+
- Coordinating with the BFT consensus mechanism
23+
- Maintaining synchronization with root chain state
24+
25+
### BlockProvider Integration
26+
27+
The Oracle integrates with external blockchain providers through the `BlockProvider` interface. It provides:
28+
- Real-time block monitoring from external chains
29+
- Transaction parsing and order extraction
30+
- Height management and state persistence
31+
- Graceful startup and shutdown capabilities
32+
33+
### Order Store Management
34+
35+
The Oracle manages witnessed orders through a persistent store that:
36+
- Stores validated lock and close orders separately
37+
- Provides atomic read/write operations for order data
38+
- Maintains submission history for resubmission logic
39+
- Supports cleanup operations based on root chain updates
40+
41+
## Sequence Diagram
42+
43+
The following sequence diagram illustrates the core interactions in the Oracle package:
44+
45+
```mermaid
46+
sequenceDiagram
47+
participant EXT as External Chain
48+
participant BP as BlockProvider
49+
participant O as Oracle
50+
participant OS as OrderStore
51+
participant BFT as BFT Consensus
52+
participant RC as Root Chain
53+
54+
%% Block Processing Flow
55+
Note over EXT,BP: Block Witnessing
56+
EXT->>BP: New Block with Transactions
57+
BP->>O: Block via BlockCh
58+
O->>O: Process Block
59+
O->>OS: Validate & Store Orders
60+
O->>O: Save Block Height
61+
62+
%% BFT Integration Flow
63+
Note over BFT,O: Consensus Participation
64+
BFT->>O: WitnessedOrders(orderBook, height)
65+
O->>OS: Read Witnessed Orders
66+
O->>BFT: Return Lock/Close Orders
67+
BFT->>O: ValidateProposedOrders(orders)
68+
O->>OS: Verify Orders Exist
69+
O->>BFT: Validation Result
70+
71+
%% Root Chain Sync Flow
72+
Note over RC,O: State Synchronization
73+
RC->>O: UpdateRootChainInfo(info)
74+
O->>O: Update Local OrderBook
75+
O->>OS: Cleanup Stale Orders
76+
```
77+
78+
## Technical Details
79+
80+
### Cross-Chain Transaction Witnessing
81+
82+
The Oracle system uses a block-based monitoring approach to witness transactions on external chains. This is achieved by:
83+
84+
- **Block Provider Integration**: Connects to external blockchain nodes through configurable providers
85+
- **Transaction Parsing**: Extracts Canopy-specific order data from external chain transactions
86+
- **Order Validation**: Performs comprehensive validation against root chain order book data
87+
- **State Persistence**: Maintains reliable state storage for witnessed orders and processing height
88+
89+
The system works like a specialized blockchain monitor that specifically looks for transactions containing Canopy order data, validates them against known orders, and stores them for later use in the consensus process.
90+
91+
State persistence ensures that the Oracle can recover from interruptions without losing witnessed orders or reprocessing previously seen blocks.
92+
93+
### BFT Consensus Integration
94+
95+
The Oracle system uses a dual-phase approach to participate in Byzantine Fault Tolerant consensus:
96+
97+
1. **Proposal Phase**: When acting as a proposer, the Oracle queries its witnessed order store to find orders that should be included in the next block proposal
98+
2. **Validation Phase**: When validating block proposals from other nodes, the Oracle verifies that all proposed orders exist in its local witnessed order store
99+
3. **Resubmission Logic**: Implements intelligent resubmission timing to handle cases where orders aren't immediately included in blocks
100+
101+
This ensures that only orders witnessed by a majority of validator nodes are included in the blockchain, providing strong guarantees about cross-chain transaction validity.
102+
103+
### Order Book Synchronization
104+
105+
The Oracle system implements several synchronization mechanisms to maintain consistency with the root chain:
106+
107+
- **Order Book Updates**: Receives periodic updates of the complete root chain order book state
108+
- **Stale Order Cleanup**: Automatically removes witnessed orders that are no longer relevant (locked orders, completed orders)
109+
- **State Validation**: Ensures witnessed orders match current order book entries before including them in block proposals
110+
- **Atomic Operations**: Uses mutex protection to ensure thread-safe access to shared order book state
111+
112+
This synchronization acts like a cache invalidation system, where the Oracle maintains local copies of relevant data but periodically synchronizes with the authoritative root chain state.
113+
114+
## Component Interactions
115+
116+
### 1. Block Processing: External Chain Monitoring
117+
118+
When a new block arrives from an external blockchain, the Oracle system performs comprehensive processing:
119+
120+
- **Block Reception**: Receives blocks through a channel-based interface from the configured BlockProvider
121+
- **Height Persistence**: Saves the current block height to disk before processing to enable recovery
122+
- **Transaction Analysis**: Examines each transaction in the block for Canopy-specific order data
123+
- **Order Validation**: Validates witnessed orders against the current root chain order book
124+
- **Storage Operations**: Persists valid orders to the local order store with appropriate metadata
125+
126+
This process is similar to how a blockchain indexer works, but with specific focus on cross-chain order validation and storage.
127+
128+
### 2. Consensus Participation: BFT Integration
129+
130+
The Oracle participates in the BFT consensus process through two key interfaces:
131+
132+
- **WitnessedOrders**: Called during block proposal to provide witnessed orders that should be included in the next block
133+
- **ValidateProposedOrders**: Called during block validation to verify that proposed orders were actually witnessed by this node
134+
- **Resubmission Logic**: Implements timing controls to handle cases where orders aren't immediately included in blocks
135+
- **State Updates**: Maintains submission history to prevent duplicate submissions and enable intelligent resubmission
136+
137+
This is analogous to how a validator node participates in consensus, but with specialized logic for cross-chain order validation.
138+
139+
### 3. Root Chain Synchronization: State Management
140+
141+
The Oracle system implements comprehensive state management for maintaining consistency with the root chain:
142+
143+
- **Order Book Updates**: Receives and processes complete order book state from the root chain
144+
- **Cleanup Operations**: Removes witnessed orders that are no longer needed based on root chain state
145+
- **Consistency Validation**: Ensures witnessed orders still match their corresponding root chain entries
146+
- **Thread Safety**: Uses mutex protection to ensure atomic updates to shared state
147+
148+
This synchronization system works like a distributed cache, where the Oracle maintains local copies of relevant data but periodically synchronizes with the authoritative source.
149+
150+
## Cross-Chain Security Features
151+
152+
The Oracle system implements several security mechanisms to ensure reliable cross-chain operation:
153+
154+
- **Comprehensive Validation**: Performs strict validation of all witnessed order fields against root chain data
155+
- **Atomic State Management**: Uses file-based persistence with atomic operations to prevent data corruption
156+
- **Graceful Recovery**: Implements startup logic that can recover from interruptions without data loss
157+
- **Error Isolation**: Continues processing even when individual orders fail validation
158+
159+
These features combine to create a robust system that can handle the challenges of cross-chain communication while maintaining strong security guarantees.
160+
161+
## Usage
162+
163+
To use the Oracle package:
164+
165+
1. Configure the Oracle with appropriate blockchain providers and storage systems
166+
2. Initialize the Oracle with context, configuration, and dependencies
167+
3. Start the Oracle to begin monitoring external chains
168+
169+
```go
170+
// Example usage
171+
ctx := context.Background()
172+
config := lib.OracleConfig{
173+
StateSaveFile: "/path/to/state.txt",
174+
OrderResubmitDelay: 10,
175+
}
176+
177+
oracle, err := NewOracle(ctx, config, blockProvider, orderStore, logger)
178+
if err != nil {
179+
log.Fatal(err)
180+
}
181+
182+
oracle.Start(ctx)
183+
defer oracle.Stop()
184+
```
185+
186+
## Configuration
187+
188+
The Oracle accepts configuration through `lib.OracleConfig` with the following parameters:
189+
190+
- `StateSaveFile`: File path for persisting the last processed block height
191+
- `OrderResubmitDelay`: Number of blocks to wait before resubmitting an order to consensus
192+
193+
## Error Handling
194+
195+
The Oracle implements comprehensive error handling patterns:
196+
197+
- **Graceful Degradation**: Continues processing even when individual operations fail
198+
- **Detailed Logging**: Provides extensive logging for debugging and monitoring
199+
- **State Recovery**: Can recover from interruptions by reading persisted state
200+
- **Validation Errors**: Distinguishes between validation failures and system errors
201+
202+
## Performance Considerations
203+
204+
The Oracle is designed for high-throughput operation with several optimizations:
205+
206+
- **Concurrent Processing**: Uses goroutines for non-blocking block processing
207+
- **Efficient State Management**: Minimizes disk I/O through strategic state persistence
208+
- **Memory Management**: Uses read-write mutexes to allow concurrent reads while protecting writes
209+
- **Batched Operations**: Processes multiple orders per block to improve throughput

cmd/rpc/oracle/error.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package oracle
2+
3+
import (
4+
"fmt"
5+
"math"
6+
7+
"github.com/canopy-network/canopy/lib"
8+
)
9+
10+
const (
11+
NoCode lib.ErrorCode = math.MaxUint32
12+
13+
// Oracle Module
14+
OracleModule lib.ErrorModule = "oracle"
15+
16+
// Oracle Module Error Codes
17+
CodeReadHeightFile lib.ErrorCode = 1
18+
CodeParseHeight lib.ErrorCode = 2
19+
CodeWriteHeightFile lib.ErrorCode = 3
20+
CodeCreateDirectory lib.ErrorCode = 4
21+
CodeGetHomeDirectory lib.ErrorCode = 5
22+
CodeUnmarshalOrder lib.ErrorCode = 6
23+
CodeMarshalOrder lib.ErrorCode = 7
24+
CodeOrderNotFound lib.ErrorCode = 8
25+
CodeGetOrderBook lib.ErrorCode = 9
26+
CodeAmountMismatch lib.ErrorCode = 10
27+
CodeOrderNotVerified lib.ErrorCode = 11
28+
CodeOrderValidation lib.ErrorCode = 12
29+
30+
// OrderStore Module
31+
OrderStoreModule lib.ErrorModule = "order_store"
32+
33+
// OracleStore Module Error Codes
34+
CodeValidateOrder lib.ErrorCode = 1
35+
CodeVerifyOrder lib.ErrorCode = 2
36+
CodeReadOrder lib.ErrorCode = 3
37+
CodeRemoveOrder lib.ErrorCode = 4
38+
CodeWriteOrder lib.ErrorCode = 5
39+
)
40+
41+
// Error functions for Order Store module
42+
func ErrValidateOrder(err error) lib.ErrorI {
43+
return lib.NewError(CodeValidateOrder, OrderStoreModule, "failed to validate order: "+err.Error())
44+
}
45+
46+
func ErrVerifyOrder(err error) lib.ErrorI {
47+
return lib.NewError(CodeVerifyOrder, OrderStoreModule, "failed to verify order: "+err.Error())
48+
}
49+
50+
func ErrReadOrder(err error) lib.ErrorI {
51+
return lib.NewError(CodeReadOrder, OrderStoreModule, "failed to read order: "+err.Error())
52+
}
53+
54+
func ErrRemoveOrder(err error) lib.ErrorI {
55+
return lib.NewError(CodeRemoveOrder, OrderStoreModule, "failed to remove order: "+err.Error())
56+
}
57+
58+
func ErrWriteOrder(err error) lib.ErrorI {
59+
return lib.NewError(CodeWriteOrder, OrderStoreModule, "failed to write order: "+err.Error())
60+
}
61+
62+
// Error functions for Oracle module
63+
func ErrReadHeightFile(err error) lib.ErrorI {
64+
return lib.NewError(CodeReadHeightFile, OracleModule, "failed to read height file: "+err.Error())
65+
}
66+
67+
func ErrParseHeight(err error) lib.ErrorI {
68+
return lib.NewError(CodeParseHeight, OracleModule, "failed to parse height: "+err.Error())
69+
}
70+
71+
func ErrWriteHeightFile(err error) lib.ErrorI {
72+
return lib.NewError(CodeWriteHeightFile, OracleModule, "failed to write height file: "+err.Error())
73+
}
74+
75+
func ErrCreateDirectory(err error) lib.ErrorI {
76+
return lib.NewError(CodeCreateDirectory, OracleModule, "failed to create directory: "+err.Error())
77+
}
78+
79+
func ErrGetHomeDirectory(err error) lib.ErrorI {
80+
return lib.NewError(CodeGetHomeDirectory, OracleModule, "failed to get home directory: "+err.Error())
81+
}
82+
83+
func ErrUnmarshalOrder(err error) lib.ErrorI {
84+
return lib.NewError(CodeUnmarshalOrder, OracleModule, "failed to unmarshal order: "+err.Error())
85+
}
86+
87+
func ErrMarshalOrder(err error) lib.ErrorI {
88+
return lib.NewError(CodeMarshalOrder, OracleModule, "failed to marshal order: "+err.Error())
89+
}
90+
91+
func ErrOrderNotFoundInOrderBook(orderId string) lib.ErrorI {
92+
return lib.NewError(CodeOrderNotFound, OracleModule, "order not found in order book: "+orderId)
93+
}
94+
95+
func ErrGetOrderBook(err error) lib.ErrorI {
96+
return lib.NewError(CodeGetOrderBook, OracleModule, "failed to get order book: "+err.Error())
97+
}
98+
99+
func ErrAmountMismatch(transferAmount, orderAmount uint64) lib.ErrorI {
100+
return lib.NewError(CodeAmountMismatch, OracleModule, fmt.Sprintf("transfer amount %d does not match order amount %d", transferAmount, orderAmount))
101+
}
102+
103+
func ErrOrderNotVerified(s string, err error) lib.ErrorI {
104+
return lib.NewError(CodeOrderNotVerified, OracleModule, "order not verified: "+err.Error())
105+
}
106+
107+
func ErrOrderValidation(s string) lib.ErrorI {
108+
return lib.NewError(CodeOrderValidation, OracleModule, "order validation failure: "+s)
109+
}

0 commit comments

Comments
 (0)