decred.org/dcrdex@v1.0.5/tatanka/client/client.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package client 5 6 import ( 7 "context" 8 "crypto/rand" 9 "crypto/rsa" 10 "encoding/binary" 11 "encoding/json" 12 "errors" 13 "fmt" 14 "math/big" 15 "strings" 16 "sync" 17 "sync/atomic" 18 "time" 19 20 "decred.org/dcrdex/dex" 21 "decred.org/dcrdex/dex/fiatrates" 22 "decred.org/dcrdex/dex/msgjson" 23 "decred.org/dcrdex/tatanka/mj" 24 "decred.org/dcrdex/tatanka/tanka" 25 tcpclient "decred.org/dcrdex/tatanka/tcp/client" 26 "github.com/decred/dcrd/crypto/blake256" 27 "github.com/decred/dcrd/dcrec/secp256k1/v4" 28 ) 29 30 const rsaPrivateKeyLength = 2048 31 32 // NetworkBackend represents a peer's communication protocol. 33 type NetworkBackend interface { 34 Send(*msgjson.Message) error 35 Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error 36 } 37 38 // tatanka is a Tatanka mesh server node. 39 type tatanka struct { 40 NetworkBackend 41 cm *dex.ConnectionMaster 42 peerID tanka.PeerID 43 pub *secp256k1.PublicKey 44 config atomic.Value // *mj.TatankaConfig 45 } 46 47 type order struct { 48 *tanka.Order 49 oid tanka.ID32 50 proposed map[tanka.ID32]*tanka.Match 51 accepted map[tanka.ID32]*tanka.Match 52 } 53 54 type market struct { 55 log dex.Logger 56 57 ordsMtx sync.RWMutex 58 ords map[tanka.ID32]*order 59 } 60 61 func (m *market) addOrder(ord *tanka.Order) { 62 m.ordsMtx.Lock() 63 defer m.ordsMtx.Unlock() 64 oid := ord.ID() 65 if _, exists := m.ords[oid]; exists { 66 // ignore it then 67 return 68 } 69 m.ords[oid] = &order{ 70 Order: ord, 71 oid: oid, 72 proposed: make(map[tanka.ID32]*tanka.Match), 73 accepted: make(map[tanka.ID32]*tanka.Match), 74 } 75 } 76 77 func (m *market) addMatchProposal(match *tanka.Match) { 78 m.ordsMtx.Lock() 79 defer m.ordsMtx.Unlock() 80 ord, found := m.ords[match.OrderID] 81 if !found { 82 m.log.Debugf("ignoring match proposal for unknown order %s", match.OrderID) 83 } 84 // Make sure it's not already known or accepted 85 mid := match.ID() 86 if ord.proposed[mid] != nil { 87 // Already known 88 return 89 } 90 if ord.accepted[mid] != nil { 91 // Already accepted 92 return 93 } 94 ord.proposed[mid] = match 95 } 96 97 func (m *market) addMatchAcceptance(match *tanka.Match) { 98 m.ordsMtx.Lock() 99 defer m.ordsMtx.Unlock() 100 ord, found := m.ords[match.OrderID] 101 if !found { 102 m.log.Debugf("ignoring match proposal for unknown order %s", match.OrderID) 103 } 104 // Make sure it's not already known or accepted 105 mid := match.ID() 106 if ord.proposed[mid] != nil { 107 delete(ord.proposed, mid) 108 } 109 if ord.accepted[mid] != nil { 110 // Already accepted 111 return 112 } 113 ord.accepted[mid] = match 114 } 115 116 // peer is a network peer with which we have established encrypted 117 // communication. 118 type peer struct { 119 id tanka.PeerID 120 pub *secp256k1.PublicKey 121 decryptionKey *rsa.PrivateKey // ours 122 encryptionKey *rsa.PublicKey 123 } 124 125 // wireKey encrypts our RSA public key for transmission. 126 func (p *peer) wireKey() []byte { 127 modulusB := p.decryptionKey.PublicKey.N.Bytes() 128 encryptionKey := make([]byte, 8+len(modulusB)) 129 binary.BigEndian.PutUint64(encryptionKey[:8], uint64(p.decryptionKey.PublicKey.E)) 130 copy(encryptionKey[8:], modulusB) 131 return encryptionKey 132 } 133 134 // https://stackoverflow.com/a/67035019 135 func (p *peer) decryptRSA(enc []byte) ([]byte, error) { 136 msgLen := len(enc) 137 hasher := blake256.New() 138 step := p.decryptionKey.PublicKey.Size() 139 var b []byte 140 for start := 0; start < msgLen; start += step { 141 finish := start + step 142 if finish > msgLen { 143 finish = msgLen 144 } 145 block, err := rsa.DecryptOAEP(hasher, rand.Reader, p.decryptionKey, enc[start:finish], []byte{}) 146 if err != nil { 147 return nil, err 148 } 149 b = append(b, block...) 150 } 151 return b, nil 152 } 153 154 func (p *peer) encryptRSA(b []byte) ([]byte, error) { 155 msgLen := len(b) 156 hasher := blake256.New() 157 step := p.encryptionKey.Size() - 2*hasher.Size() - 2 158 var enc []byte 159 for start := 0; start < msgLen; start += step { 160 finish := start + step 161 if finish > msgLen { 162 finish = msgLen 163 } 164 block, err := rsa.EncryptOAEP(hasher, rand.Reader, p.encryptionKey, b[start:finish], []byte{}) 165 if err != nil { 166 return nil, err 167 } 168 enc = append(enc, block...) 169 } 170 return enc, nil 171 } 172 173 // IncomingPeerConnect will be emitted when a peer requests a connection for 174 // the transmission of tankagrams. 175 type IncomingPeerConnect struct { 176 PeerID tanka.PeerID 177 Reject func() 178 } 179 180 // IncomingTankagram will be emitted when we receive a tankagram from a 181 // connected peer. 182 type IncomingTankagram struct { 183 Msg *msgjson.Message 184 Respond func(thing interface{}) error 185 } 186 187 // NewMarketSubscriber will be emitted when a new client subscribes to a market. 188 type NewMarketSubscriber struct { 189 MarketName string 190 PeerID tanka.PeerID 191 } 192 193 // Config is the configuration for the TankaClient. 194 type Config struct { 195 Logger dex.Logger 196 PrivateKey *secp256k1.PrivateKey 197 } 198 199 type TankaClient struct { 200 wg *sync.WaitGroup 201 peerID tanka.PeerID 202 priv *secp256k1.PrivateKey 203 log dex.Logger 204 requestHandlers map[string]func(*tatanka, *msgjson.Message) *msgjson.Error 205 notificationHandlers map[string]func(*tatanka, *msgjson.Message) 206 payloads chan interface{} 207 208 tankaMtx sync.RWMutex 209 tankas map[tanka.PeerID]*tatanka 210 211 peersMtx sync.RWMutex 212 peers map[tanka.PeerID]*peer 213 214 marketsMtx sync.RWMutex 215 markets map[string]*market 216 217 fiatRatesMtx sync.RWMutex 218 fiatRates map[string]*fiatrates.FiatRateInfo 219 } 220 221 func New(cfg *Config) *TankaClient { 222 var peerID tanka.PeerID 223 copy(peerID[:], cfg.PrivateKey.PubKey().SerializeCompressed()) 224 225 c := &TankaClient{ 226 log: cfg.Logger, 227 peerID: peerID, 228 priv: cfg.PrivateKey, 229 tankas: make(map[tanka.PeerID]*tatanka), 230 peers: make(map[tanka.PeerID]*peer), 231 markets: make(map[string]*market), 232 payloads: make(chan interface{}, 128), 233 fiatRates: make(map[string]*fiatrates.FiatRateInfo), 234 } 235 c.prepareHandlers() 236 return c 237 } 238 239 func (c *TankaClient) ID() tanka.PeerID { 240 return c.peerID 241 } 242 243 func (c *TankaClient) prepareHandlers() { 244 c.requestHandlers = map[string]func(*tatanka, *msgjson.Message) *msgjson.Error{ 245 mj.RouteTankagram: c.handleTankagram, 246 } 247 248 c.notificationHandlers = map[string]func(*tatanka, *msgjson.Message){ 249 mj.RouteBroadcast: c.handleBroadcast, 250 mj.RouteRates: c.handleRates, 251 } 252 } 253 254 func (c *TankaClient) Next() <-chan interface{} { 255 return c.payloads 256 } 257 258 func (c *TankaClient) handleTankagram(tt *tatanka, tankagram *msgjson.Message) *msgjson.Error { 259 var gram mj.Tankagram 260 if err := tankagram.Unmarshal(&gram); err != nil { 261 return msgjson.NewError(mj.ErrBadRequest, "unmarshal error") 262 } 263 264 c.peersMtx.Lock() 265 defer c.peersMtx.Unlock() 266 p, peerExisted := c.peers[gram.From] 267 if !peerExisted { 268 // TODO: We should do a little message verification before accepting 269 // new peers. 270 if gram.Message == nil || gram.Message.Route != mj.RouteEncryptionKey { 271 return msgjson.NewError(mj.ErrBadRequest, "where's your key?") 272 } 273 pub, err := secp256k1.ParsePubKey(gram.From[:]) 274 if err != nil { 275 c.log.Errorf("could not parse pubkey for tankagram from %s: %w", gram.From, err) 276 return msgjson.NewError(mj.ErrBadRequest, "bad pubkey") 277 } 278 priv, err := rsa.GenerateKey(rand.Reader, rsaPrivateKeyLength) 279 if err != nil { 280 return msgjson.NewError(mj.ErrInternal, "error generating rsa key: %v", err) 281 } 282 p = &peer{ 283 id: gram.From, 284 pub: pub, 285 decryptionKey: priv, 286 } 287 288 msg := gram.Message 289 if err := mj.CheckSig(msg, p.pub); err != nil { 290 c.log.Errorf("%s sent a unencrypted message with a bad signature: %w", p.id, err) 291 return msgjson.NewError(mj.ErrBadRequest, "bad gram sig") 292 } 293 294 var b dex.Bytes 295 if err := msg.Unmarshal(&b); err != nil { 296 c.log.Errorf("%s tankagram unmarshal error: %w", err) 297 return msgjson.NewError(mj.ErrBadRequest, "unmarshal key error") 298 } 299 300 p.encryptionKey, err = decodePubkeyRSA(b) 301 if err != nil { 302 c.log.Errorf("error decoding RSA pub key from %s: %v", p.id, err) 303 return msgjson.NewError(mj.ErrBadRequest, "bad key encoding") 304 } 305 306 if err := c.sendResult(tt, tankagram.ID, dex.Bytes(p.wireKey())); err != nil { 307 c.log.Errorf("error responding to encryption key message from peer %s", p.id) 308 } else { 309 c.peers[p.id] = p 310 c.emit(&IncomingPeerConnect{ 311 PeerID: p.id, 312 Reject: func() { 313 c.peersMtx.Lock() 314 delete(c.peers, p.id) 315 c.peersMtx.Unlock() 316 }, 317 }) 318 } 319 return nil 320 } 321 322 // If this isn't the encryption key, this gram.Message is ignored and this 323 // is assumed to be encrypted. 324 if len(gram.EncryptedMsg) == 0 { 325 c.log.Errorf("%s sent a tankagram with no message or data", p.id) 326 return msgjson.NewError(mj.ErrBadRequest, "bad gram") 327 } 328 329 b, err := p.decryptRSA(gram.EncryptedMsg) 330 if err != nil { 331 c.log.Errorf("%s sent an enrypted message that didn't decrypt: %v", p.id, err) 332 return msgjson.NewError(mj.ErrBadRequest, "bad encryption") 333 } 334 msg, err := msgjson.DecodeMessage(b) 335 if err != nil { 336 c.log.Errorf("%s sent a tankagram that didn't encode a message: %v", p.id, err) 337 return msgjson.NewError(mj.ErrBadRequest, "where's the message?") 338 } 339 340 c.emit(&IncomingTankagram{ 341 Msg: msg, 342 Respond: func(thing interface{}) error { 343 b, err := json.Marshal(thing) 344 if err != nil { 345 return err 346 } 347 enc, err := p.encryptRSA(b) 348 if err != nil { 349 return err 350 } 351 return c.sendResult(tt, tankagram.ID, dex.Bytes(enc)) 352 }, 353 }) 354 355 return nil 356 } 357 358 func (c *TankaClient) handleBroadcast(tt *tatanka, msg *msgjson.Message) { 359 var bcast mj.Broadcast 360 if err := msg.Unmarshal(&bcast); err != nil { 361 c.log.Errorf("%s broadcast unmarshal error: %w", err) 362 return 363 } 364 switch bcast.Topic { 365 case mj.TopicMarket: 366 c.handleMarketBroadcast(tt, &bcast) 367 } 368 c.emit(bcast) 369 } 370 371 func (c *TankaClient) handleRates(tt *tatanka, msg *msgjson.Message) { 372 var rm mj.RateMessage 373 if err := msg.Unmarshal(&rm); err != nil { 374 c.log.Errorf("%s rate message unmarshal error: %w", err) 375 return 376 } 377 switch rm.Topic { 378 case mj.TopicFiatRate: 379 c.fiatRatesMtx.Lock() 380 for ticker, rateInfo := range rm.Rates { 381 c.fiatRates[strings.ToLower(ticker)] = &fiatrates.FiatRateInfo{ 382 Value: rateInfo.Value, 383 LastUpdate: time.Now(), 384 } 385 } 386 c.fiatRatesMtx.Unlock() 387 } 388 c.emit(rm) 389 } 390 391 func (c *TankaClient) SubscribeToFiatRates() error { 392 msg := mj.MustRequest(mj.RouteSubscribe, &mj.Subscription{ 393 Topic: mj.TopicFiatRate, 394 }) 395 396 var nSuccessful int 397 for _, tt := range c.tankaNodes() { 398 var ok bool // true is only possible non-error payload. 399 if err := c.request(tt, msg, &ok); err != nil { 400 c.log.Errorf("Error subscribing to fiat rates with %s: %v", tt.peerID, err) 401 continue 402 } 403 nSuccessful++ 404 } 405 406 if nSuccessful == 0 { 407 return errors.New("failed to subscribe to fiat rates on any servers") 408 } 409 410 return nil 411 } 412 413 func (c *TankaClient) FiatRate(assetID uint32) float64 { 414 c.fiatRatesMtx.RLock() 415 defer c.fiatRatesMtx.RUnlock() 416 sym := dex.BipIDSymbol(assetID) 417 rateInfo := c.fiatRates[sym] 418 if rateInfo != nil && time.Since(rateInfo.LastUpdate) < fiatrates.FiatRateDataExpiry && rateInfo.Value > 0 { 419 return rateInfo.Value 420 } 421 return 0 422 } 423 424 func (c *TankaClient) emit(thing interface{}) { 425 select { 426 case c.payloads <- thing: 427 default: 428 c.log.Errorf("payload channel is blocking") 429 } 430 } 431 432 func (c *TankaClient) handleMarketBroadcast(_ *tatanka, bcast *mj.Broadcast) { 433 mktName := string(bcast.Subject) 434 c.marketsMtx.RLock() 435 mkt, found := c.markets[mktName] 436 c.marketsMtx.RUnlock() 437 if !found { 438 c.log.Debugf("received order notification for unknown market %q", mktName) 439 return 440 } 441 switch bcast.MessageType { 442 case mj.MessageTypeTrollBox: 443 var troll mj.Troll 444 if err := json.Unmarshal(bcast.Payload, &troll); err != nil { 445 c.log.Errorf("error unmarshaling trollbox message: %v", err) 446 return 447 } 448 fmt.Printf("trollbox message for market %s: %s\n", mktName, troll.Msg) 449 case mj.MessageTypeNewOrder: 450 var ord tanka.Order 451 if err := json.Unmarshal(bcast.Payload, &ord); err != nil { 452 c.log.Errorf("error unmarshaling new order: %v", err) 453 return 454 } 455 mkt.addOrder(&ord) 456 case mj.MessageTypeProposeMatch: 457 var match tanka.Match 458 if err := json.Unmarshal(bcast.Payload, &match); err != nil { 459 c.log.Errorf("error unmarshaling match proposal: %v", err) 460 return 461 } 462 mkt.addMatchProposal(&match) 463 case mj.MessageTypeAcceptMatch: 464 var match tanka.Match 465 if err := json.Unmarshal(bcast.Payload, &match); err != nil { 466 c.log.Errorf("error unmarshaling match proposal: %v", err) 467 return 468 } 469 mkt.addMatchAcceptance(&match) 470 case mj.MessageTypeNewSubscriber: 471 var ns mj.NewSubscriber 472 if err := json.Unmarshal(bcast.Payload, &ns); err != nil { 473 c.log.Errorf("error decoding new_subscriber payload: %v", err) 474 } 475 // c.emit(&NewMarketSubscriber{ 476 // MarketName: mktName, 477 // PeerID: bcast.PeerID, 478 // }) 479 default: 480 c.log.Errorf("received broadcast on %s -> %s with unknown message type %s", bcast.Topic, bcast.Subject) 481 } 482 } 483 484 func (c *TankaClient) Broadcast(topic tanka.Topic, subject tanka.Subject, msgType mj.BroadcastMessageType, thing interface{}) error { 485 payload, err := json.Marshal(thing) 486 if err != nil { 487 return fmt.Errorf("error marshaling broadcast payload: %v", err) 488 } 489 note := mj.MustRequest(mj.RouteBroadcast, &mj.Broadcast{ 490 PeerID: c.peerID, 491 Topic: topic, 492 Subject: subject, 493 MessageType: msgType, 494 Payload: payload, 495 Stamp: time.Now(), 496 }) 497 var oks int 498 for _, tt := range c.tankas { 499 var ok bool 500 if err := c.request(tt, note, &ok); err != nil || !ok { 501 c.log.Errorf("error sending to %s: ok = %t, err = %v", tt.peerID, ok, err) 502 } else { 503 oks++ 504 } 505 } 506 if oks == 0 { 507 return errors.New("broadcast failed for all servers") 508 } 509 return nil 510 } 511 512 func (c *TankaClient) Connect(ctx context.Context) (*sync.WaitGroup, error) { 513 var wg sync.WaitGroup 514 c.wg = &wg 515 516 c.log.Infof("Starting TankaClient with peer ID %s", c.peerID) 517 518 wg.Add(1) 519 go func() { 520 defer wg.Done() 521 <-ctx.Done() 522 c.tankaMtx.Lock() 523 for _, tt := range c.tankas { 524 tt.cm.Disconnect() 525 } 526 c.tankas = make(map[tanka.PeerID]*tatanka) 527 c.tankaMtx.Unlock() 528 }() 529 530 return c.wg, nil 531 } 532 533 func (c *TankaClient) AddTatankaNode(ctx context.Context, peerID tanka.PeerID, uri string, cert []byte) error { 534 pub, err := secp256k1.ParsePubKey(peerID[:]) 535 if err != nil { 536 return fmt.Errorf("error parsing pubkey from peer ID: %w", err) 537 } 538 539 log := c.log.SubLogger("TCP") 540 cl, err := tcpclient.New(&tcpclient.Config{ 541 Logger: log, 542 URL: uri + "/ws", 543 Cert: cert, 544 HandleMessage: func(msg *msgjson.Message) *msgjson.Error { 545 return c.handleTatankaMessage(peerID, msg) 546 }, 547 }) 548 549 if err != nil { 550 return fmt.Errorf("error creating connection: %w", err) 551 } 552 553 cm := dex.NewConnectionMaster(cl) 554 if err := cm.ConnectOnce(ctx); err != nil { 555 return fmt.Errorf("error connecting: %w", err) 556 } 557 558 c.tankaMtx.Lock() 559 defer c.tankaMtx.Unlock() 560 561 if oldTanka, exists := c.tankas[peerID]; exists { 562 oldTanka.cm.Disconnect() 563 log.Infof("replacing existing connection") 564 } 565 566 c.tankas[peerID] = &tatanka{ 567 NetworkBackend: cl, 568 cm: cm, 569 peerID: peerID, 570 pub: pub, 571 } 572 573 return nil 574 } 575 576 func (c *TankaClient) handleTatankaMessage(peerID tanka.PeerID, msg *msgjson.Message) *msgjson.Error { 577 if c.log.Level() == dex.LevelTrace { 578 c.log.Tracef("Client handling message from tatanka node: route = %s, payload = %s", msg.Route, mj.Truncate(msg.Payload)) 579 } 580 581 c.tankaMtx.RLock() 582 tt, exists := c.tankas[peerID] 583 c.tankaMtx.RUnlock() 584 if !exists { 585 c.log.Errorf("%q message received from unknown peer %s", msg.Route, peerID) 586 return msgjson.NewError(mj.ErrAuth, "who the heck are you?") 587 } 588 589 if err := mj.CheckSig(msg, tt.pub); err != nil { 590 // DRAFT TODO: Record for reputation somehow, no? 591 c.log.Errorf("tatanka node %s sent a bad signature. disconnecting", tt.peerID) 592 return msgjson.NewError(mj.ErrAuth, "bad sig") 593 } 594 595 switch msg.Type { 596 case msgjson.Request: 597 handle, found := c.requestHandlers[msg.Route] 598 if !found { 599 // DRAFT NOTE: We should pontentially be more permissive of unknown 600 // routes in order to support minor network upgrades that add new 601 // routes. 602 c.log.Errorf("tatanka node %s sent a request to an unknown route %q", peerID, msg.Route) 603 return msgjson.NewError(mj.ErrBadRequest, "what is route %q?", msg.Route) 604 } 605 c.handleRequest(tt, msg, handle) 606 case msgjson.Notification: 607 handle, found := c.notificationHandlers[msg.Route] 608 if !found { 609 // DRAFT NOTE: We should pontentially be more permissive of unknown 610 // routes in order to support minor network upgrades that add new 611 // routes. 612 c.log.Errorf("tatanka node %s sent a notification to an unknown route %q", peerID, msg.Route) 613 return msgjson.NewError(mj.ErrBadRequest, "what is route %q?", msg.Route) 614 } 615 handle(tt, msg) 616 default: 617 c.log.Errorf("tatanka node %s send a message with an unhandleable type %d", msg.Type) 618 return msgjson.NewError(mj.ErrBadRequest, "message type %d doesn't work for me", msg.Type) 619 } 620 621 return nil 622 } 623 624 func (c *TankaClient) sendResult(tt *tatanka, msgID uint64, result interface{}) error { 625 resp, err := msgjson.NewResponse(msgID, result, nil) 626 if err != nil { 627 return err 628 } 629 if err := c.send(tt, resp); err != nil { 630 return err 631 } 632 return nil 633 } 634 635 func (c *TankaClient) handleRequest(tt *tatanka, msg *msgjson.Message, handle func(*tatanka, *msgjson.Message) *msgjson.Error) { 636 if msgErr := handle(tt, msg); msgErr != nil { 637 respMsg := mj.MustResponse(msg.ID, nil, msgErr) 638 if err := c.send(tt, respMsg); err != nil { 639 c.log.Errorf("Send error: %v", err) 640 } 641 } 642 } 643 644 func (c *TankaClient) Auth(peerID tanka.PeerID) error { 645 c.tankaMtx.RLock() 646 tt, found := c.tankas[peerID] 647 c.tankaMtx.RUnlock() 648 if !found { 649 return fmt.Errorf("cannot auth with unknown server %s", peerID) 650 } 651 return c.auth(tt) 652 } 653 654 func (c *TankaClient) auth(tt *tatanka) error { 655 connectMsg := mj.MustRequest(mj.RouteConnect, &mj.Connect{ID: c.peerID}) 656 var cfg *mj.TatankaConfig 657 if err := c.request(tt, connectMsg, &cfg); err != nil { 658 return err 659 } 660 tt.config.Store(cfg) 661 return nil 662 } 663 664 func (c *TankaClient) tankaNodes() []*tatanka { 665 c.tankaMtx.RLock() 666 defer c.tankaMtx.RUnlock() 667 tankas := make([]*tatanka, 0, len(c.tankas)) 668 for _, tanka := range c.tankas { 669 tankas = append(tankas, tanka) 670 } 671 return tankas 672 } 673 674 func (c *TankaClient) PostBond(bond *tanka.Bond) error { 675 msg := mj.MustRequest(mj.RoutePostBond, []*tanka.Bond{bond}) 676 var success bool 677 for _, tt := range c.tankaNodes() { 678 var res bool 679 if err := c.request(tt, msg, &res); err != nil { 680 c.log.Errorf("error sending bond to tatanka node %s", tt.peerID) 681 } else { 682 success = true 683 } 684 } 685 if success { 686 return nil 687 } 688 return errors.New("failed to report bond to any tatanka nodes") 689 } 690 691 func (c *TankaClient) SubscribeMarket(baseID, quoteID uint32) error { 692 mktName, err := dex.MarketName(baseID, quoteID) 693 if err != nil { 694 return fmt.Errorf("error constructing market name: %w", err) 695 } 696 697 msg := mj.MustRequest(mj.RouteSubscribe, &mj.Subscription{ 698 Topic: mj.TopicMarket, 699 Subject: tanka.Subject(mktName), 700 }) 701 702 c.marketsMtx.Lock() 703 defer c.marketsMtx.Unlock() 704 705 ttNodes := c.tankaNodes() 706 subscribed := make([]*tatanka, 0, len(ttNodes)) 707 for _, tt := range ttNodes { 708 var ok bool // true is only possible non-error payload. 709 if err := c.request(tt, msg, &ok); err != nil { 710 c.log.Errorf("Error subscribing to %s market with %s: %w", mktName, tt.peerID, err) 711 continue 712 } 713 subscribed = append(subscribed, tt) 714 } 715 716 if len(subscribed) == 0 { 717 return fmt.Errorf("failed to subscribe to market %s on any servers", mktName) 718 } else { 719 c.markets[mktName] = &market{ 720 log: c.log.SubLogger(mktName), 721 ords: make(map[tanka.ID32]*order), 722 } 723 } 724 725 return nil 726 } 727 728 func (c *TankaClient) send(tt *tatanka, msg *msgjson.Message) error { 729 mj.SignMessage(c.priv, msg) 730 return tt.Send(msg) 731 } 732 733 func (c *TankaClient) request(tt *tatanka, msg *msgjson.Message, resp interface{}) error { 734 mj.SignMessage(c.priv, msg) 735 errChan := make(chan error) 736 if err := tt.Request(msg, func(msg *msgjson.Message) { 737 errChan <- msg.UnmarshalResult(&resp) 738 }); err != nil { 739 errChan <- fmt.Errorf("request error: %w", err) 740 } 741 742 select { 743 case err := <-errChan: 744 if err != nil { 745 return fmt.Errorf("tankagram error: %w", err) 746 } 747 case <-time.After(time.Second * 30): 748 return errors.New("timed out waiting for tankagram result") 749 } 750 return nil 751 } 752 753 func (c *TankaClient) ConnectPeer(peerID tanka.PeerID, hosts ...tanka.PeerID) (*mj.TankagramResult, error) { 754 c.peersMtx.Lock() 755 defer c.peersMtx.Unlock() 756 p, exists := c.peers[peerID] 757 if !exists { 758 priv, err := rsa.GenerateKey(rand.Reader, rsaPrivateKeyLength) 759 if err != nil { 760 return nil, fmt.Errorf("error generating rsa key: %v", err) 761 } 762 remotePub, err := secp256k1.ParsePubKey(peerID[:]) 763 if err != nil { 764 return nil, fmt.Errorf("error parsing remote pubkey: %v", err) 765 } 766 p = &peer{ 767 id: peerID, 768 pub: remotePub, 769 decryptionKey: priv, 770 } 771 } 772 773 msg := mj.MustNotification(mj.RouteEncryptionKey, dex.Bytes(p.wireKey())) 774 mj.SignMessage(c.priv, msg) // We sign the embedded message separately. 775 776 req := mj.MustRequest(mj.RouteTankagram, &mj.Tankagram{ 777 To: peerID, 778 From: c.peerID, 779 Message: msg, 780 }) 781 782 var tts []*tatanka 783 if len(hosts) == 0 { 784 tts = c.tankaNodes() 785 } else { 786 tts = make([]*tatanka, 0, len(hosts)) 787 c.tankaMtx.RLock() 788 for _, host := range hosts { 789 tt, exists := c.tankas[host] 790 if !exists { 791 c.log.Warnf("Requested host %q is not known", host) 792 continue 793 } 794 tts = append(tts, tt) 795 } 796 c.tankaMtx.RUnlock() 797 } 798 if len(tts) == 0 { 799 return nil, errors.New("no hosts") 800 } 801 802 for _, tt := range tts { 803 var r mj.TankagramResult 804 if err := c.request(tt, req, &r); err != nil { 805 c.log.Errorf("error sending rsa key to %s: %v", peerID, err) 806 continue 807 } 808 if r.Result == mj.TRTTransmitted { 809 // We need to get this to the caller, as a Tankagram result can 810 // be used as part of an audit request for reporting penalties. 811 pub, err := decodePubkeyRSA(r.Response) 812 if err != nil { 813 return nil, fmt.Errorf("error decoding RSA pub key from %s: %v", p.id, err) 814 } 815 p.encryptionKey = pub 816 c.peers[peerID] = p 817 return &r, nil 818 } 819 return &r, nil 820 } 821 return nil, errors.New("no path") 822 } 823 824 func (c *TankaClient) SendTankagram(peerID tanka.PeerID, msg *msgjson.Message) (_ *mj.TankagramResult, _ json.RawMessage, err error) { 825 c.peersMtx.RLock() 826 p, known := c.peers[peerID] 827 c.peersMtx.RUnlock() 828 829 if !known { 830 return nil, nil, fmt.Errorf("not connected to peer %s", peerID) 831 } 832 833 mj.SignMessage(c.priv, msg) 834 tankaGram := &mj.Tankagram{ 835 From: c.peerID, 836 To: peerID, 837 } 838 tankaGram.EncryptedMsg, err = c.signAndEncryptTankagram(p, msg) 839 if err != nil { 840 return nil, nil, fmt.Errorf("error signing and encrypting tankagram for %s: %w", p.id, err) 841 } 842 wrappedMsg := mj.MustRequest(mj.RouteTankagram, tankaGram) 843 for _, tt := range c.tankaNodes() { 844 var r mj.TankagramResult 845 if err := c.request(tt, wrappedMsg, &r); err != nil { 846 c.log.Errorf("error sending tankagram to %s via %s: %v", p.id, tt.peerID, err) 847 continue 848 } 849 switch r.Result { 850 case mj.TRTTransmitted: 851 resB, err := p.decryptRSA(r.Response) 852 return &r, resB, err 853 case mj.TRTErrFromPeer, mj.TRTErrBadClient: 854 return &r, nil, nil 855 case mj.TRTNoPath, mj.TRTErrFromTanka: 856 continue 857 default: 858 return nil, nil, fmt.Errorf("unknown result %q", r.Result) 859 } 860 861 } 862 return nil, nil, fmt.Errorf("no path") 863 } 864 865 func (c *TankaClient) signAndEncryptTankagram(p *peer, msg *msgjson.Message) ([]byte, error) { 866 mj.SignMessage(c.priv, msg) 867 b, err := json.Marshal(msg) 868 if err != nil { 869 return nil, fmt.Errorf("error marshaling tankagram: %w", err) 870 } 871 return p.encryptRSA(b) 872 } 873 874 func decodePubkeyRSA(b []byte) (*rsa.PublicKey, error) { 875 if len(b) < 9 { 876 return nil, fmt.Errorf("invalid payload length of %d", len(b)) 877 } 878 exponentB, modulusB := b[:8], b[8:] 879 exponent := int(binary.BigEndian.Uint64(exponentB)) 880 modulus := new(big.Int).SetBytes(modulusB) 881 return &rsa.PublicKey{ 882 E: exponent, 883 N: modulus, 884 }, nil 885 }