1981001a29
* Added description in lnwire/README.md for state machine * Figured out mutex stuff... * Started the State Machine (using dummy functions for net/db) * Minor corrections in wire protocol (changed some names/types) - Renamed StagingID to HTLCKey of type HTLCKey (uint64)
636 lines
13 KiB
Go
636 lines
13 KiB
Go
package lnwire
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"github.com/btcsuite/btcd/btcec"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btcutil"
|
|
"io"
|
|
"io/ioutil"
|
|
)
|
|
|
|
var MAX_SLICE_LENGTH = 65535
|
|
|
|
//Actual pkScript, not redeemScript
|
|
type PkScript []byte
|
|
|
|
type HTLCKey uint64
|
|
type CommitHeight uint64
|
|
|
|
//Subsatoshi amount (Micro-Satoshi, 1/1000th)
|
|
//Should be a signed int to account for negative fees
|
|
//
|
|
//"In any science-fiction movie, anywhere in the galaxy, currency is referred
|
|
//to as 'credits.'"
|
|
// --Sam Humphries. Ebert, Roger (1999). Ebert's bigger little movie
|
|
// glossary. Andrews McMeel. p. 172.
|
|
//
|
|
//https://en.wikipedia.org/wiki/List_of_fictional_currencies
|
|
//https://en.wikipedia.org/wiki/Fictional_currency#Trends_in_the_use_of_fictional_currencies
|
|
//http://tvtropes.org/pmwiki/pmwiki.php/Main/WeWillSpendCreditsInTheFuture
|
|
type CreditsAmount int32 //Credits (XCB, accountants should use XCB :^)
|
|
//US Display format: 1 BTC = 100,000,000'000 XCB
|
|
//Or in BTC = 1.00000000'000
|
|
|
|
//Writes the big endian representation of element
|
|
//Unified function to call when writing different types
|
|
//Pre-allocate a byte-array of the correct size for cargo-cult security
|
|
//More copies but whatever...
|
|
func writeElement(w io.Writer, element interface{}) error {
|
|
var err error
|
|
switch e := element.(type) {
|
|
case uint8:
|
|
var b [1]byte
|
|
b[0] = byte(e)
|
|
_, err = w.Write(b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case uint16:
|
|
var b [2]byte
|
|
binary.BigEndian.PutUint16(b[:], uint16(e))
|
|
_, err = w.Write(b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case CreditsAmount:
|
|
err = binary.Write(w, binary.BigEndian, int32(e))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case uint32:
|
|
var b [4]byte
|
|
binary.BigEndian.PutUint32(b[:], uint32(e))
|
|
_, err = w.Write(b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case uint64:
|
|
var b [8]byte
|
|
binary.BigEndian.PutUint64(b[:], uint64(e))
|
|
_, err = w.Write(b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case HTLCKey:
|
|
err = writeElement(w, uint64(e))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case btcutil.Amount:
|
|
err = binary.Write(w, binary.BigEndian, int64(e))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case *btcec.PublicKey:
|
|
var b [33]byte
|
|
serializedPubkey := e.SerializeCompressed()
|
|
if len(serializedPubkey) != 33 {
|
|
return fmt.Errorf("Wrong size pubkey")
|
|
}
|
|
copy(b[:], serializedPubkey)
|
|
_, err = w.Write(b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case []uint64:
|
|
numItems := len(e)
|
|
if numItems > 65535 {
|
|
return fmt.Errorf("Too many []uint64s")
|
|
}
|
|
//Write the size
|
|
err = writeElement(w, uint16(numItems))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Write the data
|
|
for i := 0; i < numItems; i++ {
|
|
err = writeElement(w, e[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
case []*btcec.Signature:
|
|
numSigs := len(e)
|
|
if numSigs > 127 {
|
|
return fmt.Errorf("Too many signatures!")
|
|
}
|
|
//Write the size
|
|
err = writeElement(w, uint8(numSigs))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Write the data
|
|
for i := 0; i < numSigs; i++ {
|
|
err = writeElement(w, e[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
case *btcec.Signature:
|
|
sig := e.Serialize()
|
|
sigLength := len(sig)
|
|
if sigLength > 73 {
|
|
return fmt.Errorf("Signature too long!")
|
|
}
|
|
//Write the size
|
|
err = writeElement(w, uint8(sigLength))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Write the data
|
|
_, err = w.Write(sig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case *wire.ShaHash:
|
|
_, err = w.Write(e[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case []*[20]byte:
|
|
//Get size of slice and dump in slice
|
|
sliceSize := len(e)
|
|
err = writeElement(w, uint16(sliceSize))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Write in each sequentially
|
|
for _, element := range e {
|
|
err = writeElement(w, &element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
case **[20]byte:
|
|
_, err = w.Write((*e)[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case [20]byte:
|
|
_, err = w.Write(e[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case wire.BitcoinNet:
|
|
var b [4]byte
|
|
binary.BigEndian.PutUint32(b[:], uint32(e))
|
|
_, err := w.Write(b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case []byte:
|
|
sliceLength := len(e)
|
|
if sliceLength > MAX_SLICE_LENGTH {
|
|
return fmt.Errorf("Slice length too long!")
|
|
}
|
|
//Write the size
|
|
err = writeElement(w, uint16(sliceLength))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Write the data
|
|
_, err = w.Write(e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case PkScript:
|
|
scriptLength := len(e)
|
|
//Make sure it's P2PKH or P2SH size or less
|
|
if scriptLength > 25 {
|
|
return fmt.Errorf("PkScript too long!")
|
|
}
|
|
//Write the size (1-byte)
|
|
err = writeElement(w, uint8(scriptLength))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Write the data
|
|
_, err = w.Write(e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case string:
|
|
strlen := len(e)
|
|
if strlen > 65535 {
|
|
return fmt.Errorf("String too long!")
|
|
}
|
|
//Write the size (2-bytes)
|
|
err = writeElement(w, uint16(strlen))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Write the data
|
|
_, err = w.Write([]byte(e))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case []*wire.TxIn:
|
|
//Append the unsigned(!!!) txins
|
|
//Write the size (1-byte)
|
|
if len(e) > 127 {
|
|
return fmt.Errorf("Too many txins")
|
|
}
|
|
err = writeElement(w, uint8(len(e)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Append the actual TxIns (Size: NumOfTxins * 36)
|
|
//Do not include the sequence number to eliminate funny business
|
|
for _, in := range e {
|
|
err = writeElement(w, in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
case *wire.TxIn:
|
|
//Hash
|
|
var h [32]byte
|
|
copy(h[:], e.PreviousOutPoint.Hash.Bytes())
|
|
_, err = w.Write(h[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Index
|
|
var idx [4]byte
|
|
binary.BigEndian.PutUint32(idx[:], e.PreviousOutPoint.Index)
|
|
_, err = w.Write(idx[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
default:
|
|
return fmt.Errorf("Unknown type in writeElement: %T", e)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeElements(w io.Writer, elements ...interface{}) error {
|
|
for _, element := range elements {
|
|
err := writeElement(w, element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func readElement(r io.Reader, element interface{}) error {
|
|
var err error
|
|
switch e := element.(type) {
|
|
case *uint8:
|
|
var b [1]uint8
|
|
_, err = r.Read(b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = b[0]
|
|
return nil
|
|
case *uint16:
|
|
var b [2]byte
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = binary.BigEndian.Uint16(b[:])
|
|
return nil
|
|
case *CreditsAmount:
|
|
var b [4]byte
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = CreditsAmount(int32(binary.BigEndian.Uint32(b[:])))
|
|
return nil
|
|
case *uint32:
|
|
var b [4]byte
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = binary.BigEndian.Uint32(b[:])
|
|
return nil
|
|
case *uint64:
|
|
var b [8]byte
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = binary.BigEndian.Uint64(b[:])
|
|
return nil
|
|
case *HTLCKey:
|
|
var b [8]byte
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = HTLCKey(binary.BigEndian.Uint64(b[:]))
|
|
return nil
|
|
case *btcutil.Amount:
|
|
var b [8]byte
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
|
|
return nil
|
|
case **wire.ShaHash:
|
|
var b wire.ShaHash
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = &b
|
|
return nil
|
|
case **btcec.PublicKey:
|
|
var b [33]byte
|
|
_, err = io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
x, err := btcec.ParsePubKey(b[:], btcec.S256())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = &*x
|
|
return nil
|
|
case *[]uint64:
|
|
var numItems uint16
|
|
err = readElement(r, &numItems)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//if numItems > 65535 {
|
|
// return fmt.Errorf("Too many items in []uint64")
|
|
//}
|
|
|
|
//Read the number of items
|
|
var items []uint64
|
|
for i := uint16(0); i < numItems; i++ {
|
|
var item uint64
|
|
err = readElement(r, &item)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
*e = *&items
|
|
return nil
|
|
case *[]*btcec.Signature:
|
|
var numSigs uint8
|
|
err = readElement(r, &numSigs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if numSigs > 127 {
|
|
return fmt.Errorf("Too many signatures!")
|
|
}
|
|
|
|
//Read that number of signatures
|
|
var sigs []*btcec.Signature
|
|
for i := uint8(0); i < numSigs; i++ {
|
|
sig := new(btcec.Signature)
|
|
err = readElement(r, &sig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sigs = append(sigs, sig)
|
|
}
|
|
*e = *&sigs
|
|
return nil
|
|
case **btcec.Signature:
|
|
var sigLength uint8
|
|
err = readElement(r, &sigLength)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if sigLength > 73 {
|
|
return fmt.Errorf("Signature too long!")
|
|
}
|
|
|
|
//Read the sig length
|
|
l := io.LimitReader(r, int64(sigLength))
|
|
sig, err := ioutil.ReadAll(l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(sig) != int(sigLength) {
|
|
return fmt.Errorf("EOF: Signature length mismatch.")
|
|
}
|
|
btcecSig, err := btcec.ParseSignature(sig, btcec.S256())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = &*btcecSig
|
|
return nil
|
|
case *[]*[20]byte:
|
|
//How many to read
|
|
var sliceSize uint16
|
|
err = readElement(r, &sliceSize)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var data []*[20]byte
|
|
//Append the actual
|
|
for i := uint16(0); i < sliceSize; i++ {
|
|
var element [20]byte
|
|
err = readElement(r, &element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data = append(data, &element)
|
|
}
|
|
*e = data
|
|
return nil
|
|
case *[20]byte:
|
|
_, err = io.ReadFull(r, e[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case *wire.BitcoinNet:
|
|
var b [4]byte
|
|
_, err := io.ReadFull(r, b[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*e = wire.BitcoinNet(binary.BigEndian.Uint32(b[:]))
|
|
return nil
|
|
case *[]byte:
|
|
//Get the blob length first
|
|
var blobLength uint16
|
|
err = readElement(r, &blobLength)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
//Shouldn't need to do this, since it's uint16, but we
|
|
//might have a different value for MAX_SLICE_LENGTH...
|
|
if int(blobLength) > MAX_SLICE_LENGTH {
|
|
return fmt.Errorf("Slice length too long!")
|
|
}
|
|
|
|
//Read the slice length
|
|
l := io.LimitReader(r, int64(blobLength))
|
|
*e, err = ioutil.ReadAll(l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(*e) != int(blobLength) {
|
|
return fmt.Errorf("EOF: Slice length mismatch.")
|
|
}
|
|
return nil
|
|
case *PkScript:
|
|
//Get the script length first
|
|
var scriptLength uint8
|
|
err = readElement(r, &scriptLength)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if scriptLength > 25 {
|
|
return fmt.Errorf("PkScript too long!")
|
|
}
|
|
|
|
//Read the script length
|
|
l := io.LimitReader(r, int64(scriptLength))
|
|
*e, err = ioutil.ReadAll(l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(*e) != int(scriptLength) {
|
|
return fmt.Errorf("EOF: Signature length mismatch.")
|
|
}
|
|
return nil
|
|
case *string:
|
|
//Get the string length first
|
|
var strlen uint16
|
|
err = readElement(r, &strlen)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//Read the string for the length
|
|
l := io.LimitReader(r, int64(strlen))
|
|
b, err := ioutil.ReadAll(l)
|
|
if len(b) != int(strlen) {
|
|
return fmt.Errorf("EOF: String length mismatch.")
|
|
}
|
|
*e = string(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case *[]*wire.TxIn:
|
|
//Read the size (1-byte number of txins)
|
|
var numScripts uint8
|
|
err = readElement(r, &numScripts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if numScripts > 127 {
|
|
return fmt.Errorf("Too many txins")
|
|
}
|
|
|
|
//Append the actual TxIns
|
|
var txins []*wire.TxIn
|
|
for i := uint8(0); i < numScripts; i++ {
|
|
outpoint := new(wire.OutPoint)
|
|
txin := wire.NewTxIn(outpoint, nil)
|
|
err = readElement(r, &txin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
txins = append(txins, txin)
|
|
}
|
|
*e = *&txins
|
|
return nil
|
|
case **wire.TxIn:
|
|
//Hash
|
|
var h [32]byte
|
|
_, err = io.ReadFull(r, h[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hash, err := wire.NewShaHash(h[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
(*e).PreviousOutPoint.Hash = *hash
|
|
//Index
|
|
var idxBytes [4]byte
|
|
_, err = io.ReadFull(r, idxBytes[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
(*e).PreviousOutPoint.Index = binary.BigEndian.Uint32(idxBytes[:])
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("Unknown type in readElement: %T", e)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func readElements(r io.Reader, elements ...interface{}) error {
|
|
for _, element := range elements {
|
|
err := readElement(r, element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//Validates whether a PkScript byte array is P2SH or P2PKH
|
|
func ValidatePkScript(pkScript PkScript) error {
|
|
if &pkScript == nil {
|
|
return fmt.Errorf("PkScript should not be empty!")
|
|
}
|
|
if len(pkScript) == 25 {
|
|
//P2PKH
|
|
//Begins with OP_DUP OP_HASH160 PUSHDATA(20)
|
|
if !bytes.Equal(pkScript[0:3], []byte{118, 169, 20}) ||
|
|
//Ends with OP_EQUALVERIFY OP_CHECKSIG
|
|
!bytes.Equal(pkScript[23:25], []byte{136, 172}) {
|
|
//If it's not correct, return error
|
|
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
|
}
|
|
} else if len(pkScript) == 23 {
|
|
//P2SH
|
|
//Begins with OP_HASH160 PUSHDATA(20)
|
|
if !bytes.Equal(pkScript[0:2], []byte{169, 20}) ||
|
|
//Ends with OP_EQUAL
|
|
!bytes.Equal(pkScript[22:23], []byte{135}) {
|
|
//If it's not correct, return error
|
|
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
|
}
|
|
} else {
|
|
//Length not 23 or 25
|
|
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
|
}
|
|
|
|
return nil
|
|
}
|