66package main
77
88import (
9+ "bytes"
910 "context"
11+ "crypto/ecdsa"
12+ "crypto/elliptic"
13+ "crypto/rand"
14+ "crypto/tls"
15+ "crypto/x509"
16+ "crypto/x509/pkix"
17+ "encoding/hex"
18+ "encoding/pem"
1019 "fmt"
20+ "io"
21+ "log"
22+ "math/big"
1123 "net"
24+ "os"
1225 "time"
1326
27+ "github.com/sirupsen/logrus"
28+
1429 oraclerpc "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
1530 "google.golang.org/grpc"
16- "google.golang.org/grpc/credentials/insecure "
31+ "google.golang.org/grpc/credentials"
1732)
1833
1934const (
2035 // serviceListenAddress is the listening address of the service.
2136 serviceListenAddress = "localhost:8095"
2237)
2338
39+ // setupLogger sets up the logger to write logs to a file.
40+ func setupLogger () {
41+ // Create a log file.
42+ flags := os .O_CREATE | os .O_WRONLY | os .O_APPEND
43+ file , err := os .OpenFile ("basic-price-oracle-example.log" , flags , 0666 )
44+ if err != nil {
45+ logrus .Fatalf ("Failed to open log file: %v" , err )
46+ }
47+
48+ // Create a multi-writer to write to both stdout and the file.
49+ multiWriter := io .MultiWriter (os .Stdout , file )
50+
51+ // Set the output of logrus to the multi-writer.
52+ logrus .SetOutput (multiWriter )
53+
54+ // Set the log level (optional).
55+ logrus .SetLevel (logrus .DebugLevel )
56+
57+ // Set the log format (optional).
58+ logrus .SetFormatter (& logrus.TextFormatter {
59+ FullTimestamp : true ,
60+ })
61+ }
62+
2463// RpcPriceOracleServer is a basic example RPC price oracle server.
2564type RpcPriceOracleServer struct {
2665 oraclerpc.UnimplementedPriceOracleServer
@@ -31,15 +70,35 @@ type RpcPriceOracleServer struct {
3170func isSupportedSubjectAsset (subjectAsset * oraclerpc.AssetSpecifier ) bool {
3271 // Ensure that the subject asset is set.
3372 if subjectAsset == nil {
73+ logrus .Info ("Subject asset is not set (nil)" )
3474 return false
3575 }
3676
37- // In this example we'll only support a single asset.
38- assetIdStr := subjectAsset .GetAssetIdStr ()
39- supportedAssetId := "7b4336d33b019df9438e586f83c587ca00fa65602497b9" +
77+ supportedAssetIdStr := "7b4336d33b019df9438e586f83c587ca00fa65602497b9" +
4078 "3ace193e9ce53b1a67"
79+ supportedAssetIdBytes , err := hex .DecodeString (supportedAssetIdStr )
80+ if err != nil {
81+ fmt .Println ("Error decoding supported asset hex string:" , err )
82+ return false
83+ }
4184
42- return assetIdStr == supportedAssetId
85+ // Check the subject asset bytes if set.
86+ subjectAssetIdBytes := subjectAsset .GetAssetId ()
87+ if len (subjectAssetIdBytes ) > 0 {
88+ logrus .Infof ("Subject asset ID bytes populated: %x" ,
89+ supportedAssetIdBytes )
90+ return bytes .Equal (supportedAssetIdBytes , subjectAssetIdBytes )
91+ }
92+
93+ subjectAssetIdStr := subjectAsset .GetAssetIdStr ()
94+ if len (subjectAssetIdStr ) > 0 {
95+ logrus .Infof ("Subject asset ID str populated: %s" ,
96+ supportedAssetIdStr )
97+ return subjectAssetIdStr == supportedAssetIdStr
98+ }
99+
100+ logrus .Infof ("Subject asset ID not set" )
101+ return false
43102}
44103
45104// getRateTick returns a rate tick for a given transaction type and subject
@@ -106,6 +165,8 @@ func (p *RpcPriceOracleServer) QueryRateTick(_ context.Context,
106165 // Ensure that the payment asset is BTC. We only support BTC as the
107166 // payment asset in this example.
108167 if ! oraclerpc .IsAssetBtc (req .PaymentAsset ) {
168+ logrus .Infof ("Payment asset is not BTC: %v" , req .PaymentAsset )
169+
109170 return & oraclerpc.QueryRateTickResponse {
110171 Result : & oraclerpc.QueryRateTickResponse_Error {
111172 Error : & oraclerpc.QueryRateTickErrResponse {
@@ -118,11 +179,15 @@ func (p *RpcPriceOracleServer) QueryRateTick(_ context.Context,
118179
119180 // Ensure that the subject asset is set.
120181 if req .SubjectAsset == nil {
182+ logrus .Info ("Subject asset is not set" )
121183 return nil , fmt .Errorf ("subject asset is not set" )
122184 }
123185
124186 // Ensure that the subject asset is supported.
125187 if ! isSupportedSubjectAsset (req .SubjectAsset ) {
188+ logrus .Infof ("Unsupported subject asset ID str: %v\n " ,
189+ req .SubjectAsset )
190+
126191 return & oraclerpc.QueryRateTickResponse {
127192 Result : & oraclerpc.QueryRateTickResponse_Error {
128193 Error : & oraclerpc.QueryRateTickErrResponse {
@@ -139,16 +204,24 @@ func (p *RpcPriceOracleServer) QueryRateTick(_ context.Context,
139204 // If a rate tick hint is provided, return it as the rate tick.
140205 // In doing so, we effectively accept the rate tick proposed by
141206 // our peer.
207+ logrus .Info ("Suggested asset to BTC rate provided, " +
208+ "returning rate as accepted rate" )
209+
142210 rateTick .Rate = req .RateTickHint .Rate
143211 rateTick .ExpiryTimestamp = req .RateTickHint .ExpiryTimestamp
144212 } else {
145213 // If a rate tick hint is not provided, fetch a rate tick from
146214 // our internal system.
215+ logrus .Info ("Suggested asset to BTC rate not provided, " +
216+ "querying internal system for rate" )
217+
147218 rateTick = getRateTick (
148219 req .TransactionType , req .SubjectAssetMaxAmount ,
149220 )
150221 }
151222
223+ logrus .Infof ("QueryRateTick returning rate: %v" , rateTick .Rate )
224+
152225 return & oraclerpc.QueryRateTickResponse {
153226 Result : & oraclerpc.QueryRateTickResponse_Success {
154227 Success : & oraclerpc.QueryRateTickSuccessResponse {
@@ -162,7 +235,8 @@ func (p *RpcPriceOracleServer) QueryRateTick(_ context.Context,
162235// shut down.
163236func startService (grpcServer * grpc.Server ) error {
164237 serviceAddr := fmt .Sprintf ("rfqrpc://%s" , serviceListenAddress )
165- println ("Starting RPC price oracle service at address: " , serviceAddr )
238+ logrus .Infof ("Starting RPC price oracle service at address: %s\n " ,
239+ serviceAddr )
166240
167241 server := RpcPriceOracleServer {}
168242 oraclerpc .RegisterPriceOracleServer (grpcServer , & server )
@@ -174,12 +248,78 @@ func startService(grpcServer *grpc.Server) error {
174248 return grpcServer .Serve (grpcListener )
175249}
176250
251+ // Generate a self-signed TLS certificate and private key.
252+ func generateSelfSignedCert () (tls.Certificate , error ) {
253+ privateKey , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
254+ if err != nil {
255+ return tls.Certificate {}, err
256+ }
257+
258+ keyUsage := x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature
259+ extKeyUsage := []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth }
260+ template := x509.Certificate {
261+ SerialNumber : big .NewInt (1 ),
262+ Subject : pkix.Name {
263+ Organization : []string {"basic-price-oracle" },
264+ },
265+ NotBefore : time .Now (),
266+ NotAfter : time .Now ().Add (24 * time .Hour ), // Valid for 1 day
267+
268+ KeyUsage : keyUsage ,
269+ ExtKeyUsage : extKeyUsage ,
270+ BasicConstraintsValid : true ,
271+ }
272+
273+ certDER , err := x509 .CreateCertificate (
274+ rand .Reader , & template , & template , & privateKey .PublicKey ,
275+ privateKey ,
276+ )
277+ if err != nil {
278+ return tls.Certificate {}, err
279+ }
280+
281+ privateKeyBits , err := x509 .MarshalECPrivateKey (privateKey )
282+ if err != nil {
283+ return tls.Certificate {}, err
284+ }
285+
286+ certPEM := pem .EncodeToMemory (
287+ & pem.Block {Type : "CERTIFICATE" , Bytes : certDER },
288+ )
289+ keyPEM := pem .EncodeToMemory (
290+ & pem.Block {Type : "EC PRIVATE KEY" , Bytes : privateKeyBits },
291+ )
292+
293+ tlsCert , err := tls .X509KeyPair (certPEM , keyPEM )
294+ if err != nil {
295+ return tls.Certificate {}, err
296+ }
297+
298+ return tlsCert , nil
299+ }
300+
177301func main () {
302+ setupLogger ()
303+
178304 // Start the mock RPC price oracle service.
179- serverOpts := []grpc.ServerOption {
180- grpc .Creds (insecure .NewCredentials ()),
305+ //
306+ // Generate self-signed certificate. This allows us to use TLS for the
307+ // gRPC server.
308+ tlsCert , err := generateSelfSignedCert ()
309+ if err != nil {
310+ log .Fatalf ("Failed to generate TLS certificate: %v" , err )
181311 }
182- backendService := grpc .NewServer (serverOpts ... )
183- _ = startService (backendService )
184- backendService .Stop ()
312+
313+ // Create the gRPC server with TLS
314+ transportCredentials := credentials .NewTLS (& tls.Config {
315+ Certificates : []tls.Certificate {tlsCert },
316+ })
317+ backendService := grpc .NewServer (grpc .Creds (transportCredentials ))
318+
319+ err = startService (backendService )
320+ if err != nil {
321+ log .Fatalf ("Start service error: %v" , err )
322+ }
323+
324+ backendService .GracefulStop ()
185325}
0 commit comments