Skip to content

Commit 7ebb640

Browse files
authored
Merge pull request #1140 from lightninglabs/add-tls-support-to-example-price-oracle
examples: add TLS support to basic price oracle service
2 parents 65539b6 + 2d0b4a4 commit 7ebb640

File tree

3 files changed

+168
-11
lines changed

3 files changed

+168
-11
lines changed

docs/examples/basic-price-oracle/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ replace github.com/lightninglabs/taproot-assets => ../../../
88

99
require (
1010
github.com/lightninglabs/taproot-assets v0.0.0
11+
github.com/sirupsen/logrus v1.9.2
1112
google.golang.org/grpc v1.59.0
1213
)
1314

docs/examples/basic-price-oracle/go.sum

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
25
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
36
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -6,8 +9,17 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
69
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
710
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
811
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
12+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
13+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14+
github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
15+
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
16+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
17+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
18+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
19+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
920
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
1021
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
22+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1123
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
1224
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
1325
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
@@ -22,3 +34,7 @@ google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
2234
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
2335
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
2436
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
37+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
38+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
39+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
40+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

docs/examples/basic-price-oracle/main.go

Lines changed: 151 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,60 @@
66
package main
77

88
import (
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

1934
const (
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.
2564
type RpcPriceOracleServer struct {
2665
oraclerpc.UnimplementedPriceOracleServer
@@ -31,15 +70,35 @@ type RpcPriceOracleServer struct {
3170
func 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.
163236
func 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+
177301
func 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

Comments
 (0)