b86409cdb3
This commit gives the start for making the htlc manager and htlc switch testable. The testability of htlc switch have been achieved by mocking all external subsystems. The concrete list of updates: 1. create standalone package for htlc switch. 2. add "ChannelLink" interface, which represent the previous htlc link. 3. add "Peer" interface, which represent the remote node inside our subsystem. 4. add htlc switch config to htlc switch susbystem, which stores the handlers which are not elongs to any of the above interfaces. With this commit we are able test htlc switch even without having the concrete implementation of Peer, ChannelLink structures, they will be added later.
154 lines
4.6 KiB
Go
154 lines
4.6 KiB
Go
package htlcswitch
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"sync"
|
|
|
|
"github.com/go-errors/errors"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
)
|
|
|
|
// circuitKey uniquely identifies an active circuit between two open channels.
|
|
// Currently, the payment hash is used to uniquely identify each circuit.
|
|
type circuitKey [sha256.Size]byte
|
|
|
|
// String represent the circuit key in string format.
|
|
func (k *circuitKey) String() string {
|
|
return hex.EncodeToString(k[:])
|
|
}
|
|
|
|
// paymentCircuit is used by htlc switch subsystem in order to determine
|
|
// backward path for settle/fail htlc messages. A payment circuit will be
|
|
// created once a channel link forwards the htlc add request and removed when we
|
|
// receive settle/fail htlc message.
|
|
//
|
|
// NOTE: In current implementation of htlc switch, the payment circuit might be
|
|
// uniquely identified by payment hash but in future we implement the payment
|
|
// fragmentation which makes possible for number of payments to have
|
|
// identical payments hashes, but different source or destination.
|
|
//
|
|
// For example if Alice(A) want to send 2BTC to Bob(B), then payment will be
|
|
// split on two parts and node N3 will have circuit with the same payment hash,
|
|
// and destination, but different channel source (N1,N2).
|
|
//
|
|
// 1BTC N1 1BTC
|
|
// + --------- o --------- +
|
|
// 2BTC | | 2BTC
|
|
// A o ------ o N0 N3 o ------ o B
|
|
// | |
|
|
// + --------- o --------- +
|
|
// 1BTC N2 1BTC
|
|
//
|
|
type paymentCircuit struct {
|
|
// PaymentHash used as unique identifier of payment.
|
|
PaymentHash circuitKey
|
|
|
|
// Src identifies the channel from which add htlc request is came from
|
|
// and to which settle/fail htlc request will be returned back. Once the
|
|
// switch forwards the settle/fail message to the src the circuit is
|
|
// considered to be completed.
|
|
// TODO(andrew.shvv) use short channel id instead.
|
|
Src lnwire.ChannelID
|
|
|
|
// Dest identifies the channel to which we propagate the htlc add
|
|
// update and from which we are expecting to receive htlc settle/fail
|
|
// request back.
|
|
// TODO(andrew.shvv) use short channel id instead.
|
|
Dest lnwire.ChannelID
|
|
|
|
// RefCount is used to count the circuits with the same circuit key.
|
|
RefCount int
|
|
}
|
|
|
|
// newPaymentCircuit creates new payment circuit instance.
|
|
func newPaymentCircuit(src, dest lnwire.ChannelID, key circuitKey) *paymentCircuit {
|
|
return &paymentCircuit{
|
|
Src: src,
|
|
Dest: dest,
|
|
PaymentHash: key,
|
|
RefCount: 1,
|
|
}
|
|
}
|
|
|
|
// isEqual checks the equality of two payment circuits.
|
|
func (a *paymentCircuit) isEqual(b *paymentCircuit) bool {
|
|
return bytes.Equal(a.PaymentHash[:], b.PaymentHash[:]) &&
|
|
a.Src == b.Src &&
|
|
a.Dest == b.Dest
|
|
}
|
|
|
|
// circuitMap is a thread safe storage of circuits. Each circuit key (payment
|
|
// hash) might have numbers of circuits corresponding to it
|
|
// because of future payment fragmentation, now every circuit might be uniquely
|
|
// identified by payment hash (1-1 mapping).
|
|
//
|
|
// NOTE: Also we have the htlc debug mode and in this mode we have the same
|
|
// payment hash for all htlcs.
|
|
// TODO(andrew.shvv) make it persistent
|
|
type circuitMap struct {
|
|
mutex sync.RWMutex
|
|
circuits map[circuitKey]*paymentCircuit
|
|
}
|
|
|
|
// newCircuitMap initialized circuit map with previously stored circuits and
|
|
// return circuit map instance.
|
|
func newCircuitMap() *circuitMap {
|
|
return &circuitMap{
|
|
circuits: make(map[circuitKey]*paymentCircuit),
|
|
}
|
|
}
|
|
|
|
// add function adds circuit in circuit map.
|
|
func (m *circuitMap) add(circuit *paymentCircuit) error {
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
|
|
// Examine the circuit map to see if this
|
|
// circuit is already in use or not. If so,
|
|
// then we'll simply increment the reference
|
|
// count. Otherwise, we'll create a new circuit
|
|
// from scratch.
|
|
// TODO(roasbeef): include dest+src+amt in key
|
|
if c, ok := m.circuits[circuit.PaymentHash]; ok {
|
|
c.RefCount++
|
|
} else {
|
|
m.circuits[circuit.PaymentHash] = circuit
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// remove function removes circuit from map.
|
|
func (m *circuitMap) remove(key circuitKey) (
|
|
*paymentCircuit, error) {
|
|
|
|
m.mutex.Lock()
|
|
defer m.mutex.Unlock()
|
|
|
|
if circuit, ok := m.circuits[key]; ok {
|
|
if circuit.RefCount--; circuit.RefCount == 0 {
|
|
delete(m.circuits, key)
|
|
}
|
|
return circuit, nil
|
|
}
|
|
|
|
return nil, errors.Errorf("can't find circuit"+
|
|
" for key %v", key)
|
|
}
|
|
|
|
// pending returns number of circuits which are waiting for to be completed
|
|
// (settle/fail responses to be received)
|
|
func (m *circuitMap) pending() int {
|
|
m.mutex.RLock()
|
|
defer m.mutex.RUnlock()
|
|
|
|
var length int
|
|
for _, circuits := range m.circuits {
|
|
length += circuits.RefCount
|
|
}
|
|
|
|
return length
|
|
}
|