-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.go
344 lines (286 loc) · 10.3 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
package main
import (
"context"
"crypto/tls"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
_ "net/http/pprof" // This is important as it registers pprof handlers with the default mux.
"os"
"path/filepath"
"strings"
firebase "firebase.google.com/go"
"github.com/thrylos-labs/thrylos"
"github.com/thrylos-labs/thrylos/core"
"github.com/thrylos-labs/thrylos/database"
pb "github.com/thrylos-labs/thrylos"
"github.com/joho/godotenv"
"google.golang.org/api/option"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
// Import your blockchain package
)
func loadEnv() {
env := os.Getenv("ENV")
var envPath string
if env == "production" {
envPath = "../../.env.prod" // The Cert is managed through the droplet
} else {
envPath = "../../.env.dev" // Managed through local host
}
if err := godotenv.Load(envPath); err != nil {
log.Fatalf("Error loading .env file from %s: %v", envPath, err)
}
}
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000") // Specify the exact origin
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Allow-Credentials", "true")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
func initializeFirebaseApp() *firebase.App {
ctx := context.Background()
sa := option.WithCredentialsFile("../.././serviceAccountKey.json")
projectID := os.Getenv("FIREBASE_PROJECT_ID")
if projectID == "" {
log.Fatalf("FIREBASE_PROJECT_ID environment variable is not set")
}
// Initialize the Firebase app with project ID
conf := &firebase.Config{
ProjectID: projectID, // Use the project ID from environment variable
}
app, err := firebase.NewApp(ctx, conf, sa)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
return app
}
func main() {
log.SetOutput(os.Stdout) // Change to os.Stdout for visibility in standard output
log.SetFlags(log.LstdFlags | log.Lshortfile) // Adding file name and line number for clarity
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("Error getting current working directory: %v", err)
}
log.Printf("Running from directory: %s", cwd)
loadEnv()
fmt.Println("Loaded FIREBASE_PROJECT_ID:", os.Getenv("FIREBASE_PROJECT_ID"))
firebaseApp := initializeFirebaseApp()
// Environment variables
grpcAddress := os.Getenv("GRPC_NODE_ADDRESS")
httpAddress := os.Getenv("HTTP_NODE_ADDRESS")
httpsAddress := os.Getenv("HTTPS_NODE_ADDRESS")
wsAddress := os.Getenv("WS_NODE_ADDRESS")
knownPeers := os.Getenv("PEERS")
nodeDataDir := os.Getenv("DATA")
testnet := os.Getenv("TESTNET") == "true" // Convert to boolean
wasmPath := os.Getenv("WASM_PATH")
dataDir := os.Getenv("DATA_DIR")
chainID := "0x539" // Default local chain ID (1337 in decimal)
// domainName := os.Getenv("DOMAIN_NAME")
if dataDir == "" {
log.Fatal("DATA_DIR environment variable is not set")
}
if testnet {
fmt.Println("Running in Testnet Mode")
chainID = "0x5" // Goerli Testnet chain ID
}
if wasmPath == "" {
log.Fatal("WASM_PATH environment variable not set")
}
// Fetch and load WebAssembly binary
response, err := http.Get(wasmPath)
if err != nil {
log.Fatalf("Failed to fetch wasm file from %s: %v", wasmPath, err)
}
defer response.Body.Close()
// Load WebAssembly binary
wasmBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatalf("Failed to read wasm file: %v", err)
}
// Execute the WebAssembly module
result := thrylos.ExecuteWasm(wasmBytes)
fmt.Printf("Result from wasm: %d\n", result)
// Fetch the Base64-encoded AES key from the environment variable
base64Key := os.Getenv("AES_KEY_ENV_VAR")
if base64Key == "" {
log.Fatal("AES key is not set in environment variables")
}
aesKey, err := base64.StdEncoding.DecodeString(base64Key)
if err != nil {
log.Fatalf("Error decoding AES key: %v", err)
}
// Genesis account
genesisAccount := os.Getenv("GENESIS_ACCOUNT")
if genesisAccount == "" {
log.Fatal("Genesis account is not set in environment variables. Please configure a genesis account before starting.")
}
// Get the absolute path of the node data directory
absPath, err := filepath.Abs(nodeDataDir)
if err != nil {
log.Fatalf("Error resolving the absolute path of the blockchain data directory: %v", err)
}
log.Printf("Using blockchain data directory: %s", absPath)
// Initialize the blockchain and database with the AES key
blockchain, _, err := core.NewBlockchain(absPath, aesKey, genesisAccount, firebaseApp)
if err != nil {
log.Fatalf("Failed to initialize the blockchain at %s: %v", absPath, err)
}
// Perform an integrity check on the blockchain
if !blockchain.CheckChainIntegrity() {
log.Fatal("Blockchain integrity check failed.")
} else {
fmt.Println("Blockchain integrity check passed.")
}
// Initialize the database
blockchainDB, err := database.InitializeDatabase(dataDir)
if err != nil {
log.Fatalf("Failed to create blockchain database: %v", err)
}
// Initialize a new node with the specified address and known peers
peersList := []string{}
if knownPeers != "" {
peersList = strings.Split(knownPeers, ",")
}
node := core.NewNode(grpcAddress, peersList, nodeDataDir, nil, false)
node.SetChainID(chainID)
// Set up routes
r := node.SetupRoutes()
r.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Blockchain status: %s", blockchain.Status())
})
// Start background tasks
node.StartBackgroundTasks()
// Define WebSocket server
wsServer := &http.Server{
Addr: wsAddress,
Handler: r,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{loadCertificate()},
},
}
// Start the HTTP server for development
// Start the HTTP server for development
if os.Getenv("ENV") == "development" {
go func() {
log.Printf("Starting HTTP server on %s\n", httpAddress)
if err := http.ListenAndServe(httpAddress, r); err != nil {
log.Fatalf("Failed to start HTTP server: %v", err)
}
}()
// Start WebSocket server in development
go func() {
log.Printf("Starting WebSocket server on %s\n", wsServer.Addr)
if err := wsServer.ListenAndServeTLS("", ""); err != nil {
log.Fatalf("Failed to start WebSocket server: %v", err)
}
}()
} else {
// Start WebSocket server in production
log.Printf("Starting WebSocket server on %s\n", wsServer.Addr)
go func() {
if err := wsServer.ListenAndServeTLS("", ""); err != nil {
log.Fatalf("Failed to start WebSocket server: %v", err)
}
}()
}
// Use static certificate files for local development
httpsServer := &http.Server{
Addr: httpsAddress, // Use the address from the environment variable
Handler: r, // Reference the CORS-wrapped handler
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{loadCertificate()}, // Load static certificate
},
}
// Serve using HTTPS with the static certificate
log.Printf("Starting HTTPS server on %s\n", httpsServer.Addr)
go func() {
err := httpsServer.ListenAndServeTLS("", "")
if err != nil {
log.Fatalf("Failed to start HTTPS server: %v", err)
}
}()
// Create BlockchainDB instance
encryptionKey := []byte(aesKey) // This should ideally come from a secure source
blockchainDatabase := database.NewBlockchainDB(blockchainDB, encryptionKey)
// Set up TLS credentials for the gRPC server
creds := loadTLSCredentials()
if err != nil {
log.Fatalf("Failed to load TLS credentials: %v", err)
}
// Setup and start gRPC server
lis, err := net.Listen("tcp", grpcAddress)
if err != nil {
log.Fatalf("Failed to listen on %s: %v", grpcAddress, err)
}
s := grpc.NewServer(grpc.Creds(creds))
pb.RegisterBlockchainServiceServer(s, &server{db: blockchainDatabase})
log.Printf("Starting gRPC server on %s\n", grpcAddress)
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve gRPC on %s: %v", grpcAddress, err)
}
}
func loadTLSCredentials() credentials.TransportCredentials {
var certPath, keyPath string
// Determine paths based on the environment
if os.Getenv("ENV") == "production" {
certPath = os.Getenv("TLS_CERT_PATH")
keyPath = os.Getenv("TLS_KEY_PATH")
} else { // Default to development paths
certPath = "../../localhost.crt"
keyPath = "../../localhost.key"
}
// Load the server's certificate and its private key
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
log.Fatalf("could not load TLS keys: %v", err)
}
// Create the credentials and return them
config := &tls.Config{
Certificates: []tls.Certificate{cert},
// Optionally set ClientCAs and ClientAuth if you need client certificates for mutual TLS
}
return credentials.NewTLS(config)
}
func loadCertificate() tls.Certificate {
var certPath, keyPath string
// Determine paths based on the environment
if os.Getenv("ENV") == "production" {
certPath = os.Getenv("TLS_CERT_PATH")
keyPath = os.Getenv("TLS_KEY_PATH")
} else { // Default to development paths
certPath = "../../localhost.crt"
keyPath = "../../localhost.key"
}
// Load the server's certificate and its private key
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
log.Fatalf("could not load TLS keys: %v", err)
}
return cert
}
// Get the blockchain stats: curl http://localhost:50051/get-stats
// Retrieve the genesis block: curl "http://localhost:50051/get-block?id=0"
// Retrieve pending transactions: curl http://localhost:50051/pending-transactions
// Retrive a balance from a specific address: curl "http://localhost:50051/get-balance?address=your_address_here"
// Server-Side Steps
// Blockchain Initialization:
// Initialize the blockchain database and genesis block upon starting the server.
// Load or create stakeholders, UTXOs, and transactions for the genesis block.
// Transaction Handling and Block Management:
// Receive transactions from clients, add to the pending transaction pool, and process them periodically.
// Create new blocks from pending transactions, ensuring transactions are valid, updating the UTXO set, and managing block links.
// Fork Resolution and Integrity Checks:
// Check for forks in the blockchain and resolve by selecting the longest chain.
// Perform regular integrity checks on the blockchain to ensure no tampering or inconsistencies.