Refactor BTC utils into package

pull/3/head
Oliver Gugger 4 years ago
parent 9dc79bbaa0
commit 9508a0d8e0
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -0,0 +1,77 @@
package btc
import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
)
type LightningChannel struct {
LocalChanCfg channeldb.ChannelConfig
RemoteChanCfg channeldb.ChannelConfig
SignDesc *input.SignDescriptor
ChannelState *channeldb.OpenChannel
TXSigner *Signer
}
// CreateSignDesc derives the SignDescriptor for commitment transactions from
// other fields on the LightningChannel.
func (lc *LightningChannel) CreateSignDesc() error {
localKey := lc.LocalChanCfg.MultiSigKey.PubKey.SerializeCompressed()
remoteKey := lc.RemoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
multiSigScript, err := input.GenMultiSigScript(localKey, remoteKey)
if err != nil {
return err
}
fundingPkScript, err := input.WitnessScriptHash(multiSigScript)
if err != nil {
return err
}
lc.SignDesc = &input.SignDescriptor{
KeyDesc: lc.LocalChanCfg.MultiSigKey,
WitnessScript: multiSigScript,
Output: &wire.TxOut{
PkScript: fundingPkScript,
Value: int64(lc.ChannelState.Capacity),
},
HashType: txscript.SigHashAll,
InputIndex: 0,
}
return nil
}
// SignedCommitTx function take the latest commitment transaction and populate
// it with witness data.
func (lc *LightningChannel) SignedCommitTx() (*wire.MsgTx, error) {
// Fetch the current commitment transaction, along with their signature
// for the transaction.
localCommit := lc.ChannelState.LocalCommitment
commitTx := localCommit.CommitTx.Copy()
theirSig := append(localCommit.CommitSig, byte(txscript.SigHashAll))
// With this, we then generate the full witness so the caller can
// broadcast a fully signed transaction.
lc.SignDesc.SigHashes = txscript.NewTxSigHashes(commitTx)
ourSigRaw, err := lc.TXSigner.SignOutputRaw(commitTx, lc.SignDesc)
if err != nil {
return nil, err
}
ourSig := append(ourSigRaw, byte(txscript.SigHashAll))
// With the final signature generated, create the witness stack
// required to spend from the multi-sig output.
ourKey := lc.LocalChanCfg.MultiSigKey.PubKey.SerializeCompressed()
theirKey := lc.RemoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
commitTx.TxIn[0].Witness = input.SpendMultiSig(
lc.SignDesc.WitnessScript, ourKey,
ourSig, theirKey, theirSig,
)
return commitTx, nil
}

@ -1,4 +1,4 @@
package chain
package btc
import (
"bytes"
@ -13,7 +13,7 @@ var (
ErrTxNotFound = errors.New("transaction not found")
)
type Api struct {
type ExplorerApi struct {
BaseUrl string
}
@ -51,9 +51,9 @@ type Status struct {
BlockHash string `json:"block_hash"`
}
func (a *Api) Transaction(txid string) (*TX, error) {
func (a *ExplorerApi) Transaction(txid string) (*TX, error) {
tx := &TX{}
err := Fetch(fmt.Sprintf("%s/tx/%s", a.BaseUrl, txid), tx)
err := fetchJSON(fmt.Sprintf("%s/tx/%s", a.BaseUrl, txid), tx)
if err != nil {
return nil, err
}
@ -62,7 +62,7 @@ func (a *Api) Transaction(txid string) (*TX, error) {
"%s/tx/%s/outspend/%d", a.BaseUrl, txid, idx,
)
outspend := Outspend{}
err := Fetch(url, &outspend)
err := fetchJSON(url, &outspend)
if err != nil {
return nil, err
}
@ -71,7 +71,7 @@ func (a *Api) Transaction(txid string) (*TX, error) {
return tx, nil
}
func (a *Api) PublishTx(rawTxHex string) (string, error) {
func (a *ExplorerApi) PublishTx(rawTxHex string) (string, error) {
url := fmt.Sprintf("%s/tx", a.BaseUrl)
resp, err := http.Post(url, "text/plain", strings.NewReader(rawTxHex))
if err != nil {
@ -85,7 +85,7 @@ func (a *Api) PublishTx(rawTxHex string) (string, error) {
return body.String(), nil
}
func Fetch(url string, target interface{}) error {
func fetchJSON(url string, target interface{}) error {
resp, err := http.Get(url)
if err != nil {
return err

@ -1,19 +1,20 @@
package chantools
package btc
import (
"fmt"
"strconv"
"strings"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/keychain"
"strconv"
"strings"
)
const (
hardenedKeyStart = uint32(hdkeychain.HardenedKeyStart)
HardenedKeyStart = uint32(hdkeychain.HardenedKeyStart)
)
func deriveChildren(key *hdkeychain.ExtendedKey, path []uint32) (
func DeriveChildren(key *hdkeychain.ExtendedKey, path []uint32) (
*hdkeychain.ExtendedKey, error) {
var (
@ -29,7 +30,7 @@ func deriveChildren(key *hdkeychain.ExtendedKey, path []uint32) (
return currentKey, nil
}
func parsePath(path string) ([]uint32, error) {
func ParsePath(path string) ([]uint32, error) {
path = strings.TrimSpace(path)
if len(path) == 0 {
return nil, fmt.Errorf("path cannot be empty")
@ -43,7 +44,7 @@ func parsePath(path string) ([]uint32, error) {
index := uint32(0)
part := parts[i]
if strings.Contains(parts[i], "'") {
index += hardenedKeyStart
index += HardenedKeyStart
part = strings.TrimRight(parts[i], "'")
}
parsed, err := strconv.Atoi(part)
@ -56,25 +57,25 @@ func parsePath(path string) ([]uint32, error) {
return indices, nil
}
type channelBackupEncryptionRing struct {
extendedKey *hdkeychain.ExtendedKey
chainParams *chaincfg.Params
type ChannelBackupEncryptionRing struct {
ExtendedKey *hdkeychain.ExtendedKey
ChainParams *chaincfg.Params
}
func (r *channelBackupEncryptionRing) DeriveNextKey(_ keychain.KeyFamily) (
func (r *ChannelBackupEncryptionRing) DeriveNextKey(_ keychain.KeyFamily) (
keychain.KeyDescriptor, error) {
return keychain.KeyDescriptor{}, nil
}
func (r *channelBackupEncryptionRing) DeriveKey(keyLoc keychain.KeyLocator) (
func (r *ChannelBackupEncryptionRing) DeriveKey(keyLoc keychain.KeyLocator) (
keychain.KeyDescriptor, error) {
var empty = keychain.KeyDescriptor{}
keyBackup, err := deriveChildren(r.extendedKey, []uint32{
hardenedKeyStart + uint32(keychain.BIP0043Purpose),
hardenedKeyStart + r.chainParams.HDCoinType,
hardenedKeyStart + uint32(keyLoc.Family),
keyBackup, err := DeriveChildren(r.ExtendedKey, []uint32{
HardenedKeyStart + uint32(keychain.BIP0043Purpose),
HardenedKeyStart + r.ChainParams.HDCoinType,
HardenedKeyStart + uint32(keyLoc.Family),
0,
keyLoc.Index,
})

@ -1,9 +1,10 @@
package chantools
package btc
import (
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil/hdkeychain"
@ -11,17 +12,18 @@ import (
"github.com/lightningnetwork/lnd/keychain"
)
type signer struct {
extendedKey *hdkeychain.ExtendedKey
type Signer struct {
ExtendedKey *hdkeychain.ExtendedKey
ChainParams *chaincfg.Params
}
func (s *signer) SignOutputRaw(tx *wire.MsgTx,
func (s *Signer) SignOutputRaw(tx *wire.MsgTx,
signDesc *input.SignDescriptor) ([]byte, error) {
witnessScript := signDesc.WitnessScript
// First attempt to fetch the private key which corresponds to the
// specified public key.
privKey, err := s.fetchPrivKey(&signDesc.KeyDesc)
privKey, err := s.FetchPrivKey(&signDesc.KeyDesc)
if err != nil {
return nil, err
}
@ -40,18 +42,19 @@ func (s *signer) SignOutputRaw(tx *wire.MsgTx,
return sig[:len(sig)-1], nil
}
func (s *signer) ComputeInputScript(tx *wire.MsgTx,
signDesc *input.SignDescriptor) (*input.Script, error) {
func (s *Signer) ComputeInputScript(_ *wire.MsgTx, _ *input.SignDescriptor) (
*input.Script, error) {
return nil, fmt.Errorf("unimplemented")
}
func (s *signer) fetchPrivKey(descriptor *keychain.KeyDescriptor) (
func (s *Signer) FetchPrivKey(descriptor *keychain.KeyDescriptor) (
*btcec.PrivateKey, error) {
key, err := deriveChildren(s.extendedKey, []uint32{
hardenedKeyStart + uint32(keychain.BIP0043Purpose),
hardenedKeyStart + chainParams.HDCoinType,
hardenedKeyStart + uint32(descriptor.Family),
key, err := DeriveChildren(s.ExtendedKey, []uint32{
HardenedKeyStart + uint32(keychain.BIP0043Purpose),
HardenedKeyStart + s.ChainParams.HDCoinType,
HardenedKeyStart + uint32(descriptor.Family),
0,
descriptor.Index,
})

@ -5,17 +5,18 @@ import (
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/guggero/chantools/btc"
)
func deriveKey(extendedKey *hdkeychain.ExtendedKey, path string,
neuter bool) error {
fmt.Printf("Deriving path %s for network %s.\n", path, chainParams.Name)
parsedPath, err := parsePath(path)
parsedPath, err := btc.ParsePath(path)
if err != nil {
return fmt.Errorf("could not parse derivation path: %v", err)
}
derivedKey, err := deriveChildren(extendedKey, parsedPath)
derivedKey, err := btc.DeriveChildren(extendedKey, parsedPath)
if err != nil {
return fmt.Errorf("could not derive children: %v", err)
}

@ -10,9 +10,8 @@ import (
"time"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/guggero/chantools/chain"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dataformat"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
@ -26,8 +25,8 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey,
if err != nil {
return err
}
chainApi := &chain.Api{BaseUrl: cfg.ApiUrl}
signer := &signer{extendedKey: extendedKey}
chainApi := &btc.ExplorerApi{BaseUrl: cfg.ApiUrl}
signer := &btc.Signer{ExtendedKey: extendedKey}
// Go through all channels in the DB, find the still open ones and
// publish their local commitment TX.
@ -54,19 +53,19 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey,
}
// Create signed transaction.
lc := &LightningChannel{
localChanCfg: channel.LocalChanCfg,
remoteChanCfg: channel.RemoteChanCfg,
channelState: channel,
txSigner: signer,
lc := &btc.LightningChannel{
LocalChanCfg: channel.LocalChanCfg,
RemoteChanCfg: channel.RemoteChanCfg,
ChannelState: channel,
TXSigner: signer,
}
err := lc.createSignDesc()
err := lc.CreateSignDesc()
if err != nil {
return err
}
// Serialize transaction.
signedTx, err := lc.getSignedCommitTx()
signedTx, err := lc.SignedCommitTx()
if err != nil {
return err
}
@ -149,72 +148,3 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey,
log.Infof("Writing result to %s", fileName)
return ioutil.WriteFile(fileName, summaryBytes, 0644)
}
type LightningChannel struct {
localChanCfg channeldb.ChannelConfig
remoteChanCfg channeldb.ChannelConfig
signDesc *input.SignDescriptor
channelState *channeldb.OpenChannel
txSigner *signer
}
// createSignDesc derives the SignDescriptor for commitment transactions from
// other fields on the LightningChannel.
func (lc *LightningChannel) createSignDesc() error {
localKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
remoteKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
multiSigScript, err := input.GenMultiSigScript(localKey, remoteKey)
if err != nil {
return err
}
fundingPkScript, err := input.WitnessScriptHash(multiSigScript)
if err != nil {
return err
}
lc.signDesc = &input.SignDescriptor{
KeyDesc: lc.localChanCfg.MultiSigKey,
WitnessScript: multiSigScript,
Output: &wire.TxOut{
PkScript: fundingPkScript,
Value: int64(lc.channelState.Capacity),
},
HashType: txscript.SigHashAll,
InputIndex: 0,
}
return nil
}
// getSignedCommitTx function take the latest commitment transaction and
// populate it with witness data.
func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// Fetch the current commitment transaction, along with their signature
// for the transaction.
localCommit := lc.channelState.LocalCommitment
commitTx := localCommit.CommitTx.Copy()
theirSig := append(localCommit.CommitSig, byte(txscript.SigHashAll))
// With this, we then generate the full witness so the caller can
// broadcast a fully signed transaction.
lc.signDesc.SigHashes = txscript.NewTxSigHashes(commitTx)
ourSigRaw, err := lc.txSigner.SignOutputRaw(commitTx, lc.signDesc)
if err != nil {
return nil, err
}
ourSig := append(ourSigRaw, byte(txscript.SigHashAll))
// With the final signature generated, create the witness stack
// required to spend from the multi-sig output.
ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
commitTx.TxIn[0].Witness = input.SpendMultiSig(
lc.signDesc.WitnessScript, ourKey,
ourSig, theirKey, theirSig,
)
return commitTx, nil
}

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/guggero/chantools/btc"
"io/ioutil"
"time"
@ -150,10 +151,11 @@ func fillCache(extendedKey *hdkeychain.ExtendedKey) error {
cache = make([]*cacheEntry, cacheSize)
for i := 0; i < cacheSize; i++ {
key, err := deriveChildren(extendedKey, []uint32{
hardenedKeyStart + uint32(keychain.BIP0043Purpose),
hardenedKeyStart + chainParams.HDCoinType,
hardenedKeyStart + uint32(keychain.KeyFamilyPaymentBase),
key, err := btc.DeriveChildren(extendedKey, []uint32{
btc.HardenedKeyStart + uint32(keychain.BIP0043Purpose),
btc.HardenedKeyStart + chainParams.HDCoinType,
btc.HardenedKeyStart +
uint32(keychain.KeyFamilyPaymentBase),
0,
uint32(i),
})

@ -6,7 +6,7 @@ import (
"io/ioutil"
"time"
"github.com/guggero/chantools/chain"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dataformat"
)
@ -16,11 +16,11 @@ func summarizeChannels(apiUrl string,
summaryFile := &dataformat.SummaryEntryFile{
Channels: channels,
}
chainApi := &chain.Api{BaseUrl: apiUrl}
chainApi := &btc.ExplorerApi{BaseUrl: apiUrl}
for idx, channel := range channels {
tx, err := chainApi.Transaction(channel.FundingTXID)
if err == chain.ErrTxNotFound {
if err == btc.ErrTxNotFound {
log.Errorf("Funding TX %s not found. Ignoring.",
channel.FundingTXID)
channel.ChanExists = false
@ -93,8 +93,9 @@ func summarizeChannels(apiUrl string,
return ioutil.WriteFile(fileName, summaryBytes, 0644)
}
func reportOutspend(api *chain.Api, summaryFile *dataformat.SummaryEntryFile,
entry *dataformat.SummaryEntry, os *chain.Outspend) error {
func reportOutspend(api *btc.ExplorerApi,
summaryFile *dataformat.SummaryEntryFile,
entry *dataformat.SummaryEntry, os *btc.Outspend) error {
spendTx, err := api.Transaction(os.Txid)
if err != nil {
@ -102,7 +103,7 @@ func reportOutspend(api *chain.Api, summaryFile *dataformat.SummaryEntryFile,
}
summaryFile.FundsClosedChannels += entry.LocalBalance
var utxo []*chain.Vout
var utxo []*btc.Vout
for _, vout := range spendTx.Vout {
if !vout.Outspend.Spent {
utxo = append(utxo, vout)
@ -174,7 +175,7 @@ func reportOutspend(api *chain.Api, summaryFile *dataformat.SummaryEntryFile,
return nil
}
func couldBeOurs(entry *dataformat.SummaryEntry, utxo []*chain.Vout) bool {
func couldBeOurs(entry *dataformat.SummaryEntry, utxo []*btc.Vout) bool {
if len(utxo) == 1 && utxo[0].Value == entry.RemoteBalance {
return false
}
@ -182,6 +183,6 @@ func couldBeOurs(entry *dataformat.SummaryEntry, utxo []*chain.Vout) bool {
return entry.LocalBalance != 0
}
func isCoopClose(tx *chain.TX) bool {
func isCoopClose(tx *btc.TX) bool {
return tx.Vin[0].Sequence == 0xffffffff
}

@ -10,7 +10,7 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/guggero/chantools/chain"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dataformat"
"github.com/lightningnetwork/lnd/input"
)
@ -24,8 +24,11 @@ func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiUrl string,
publish bool) error {
// Create signer and transaction template.
signer := &signer{extendedKey: extendedKey}
chainApi := &chain.Api{BaseUrl: apiUrl}
signer := &btc.Signer{
ExtendedKey: extendedKey,
ChainParams: chainParams,
}
chainApi := &btc.ExplorerApi{BaseUrl: apiUrl}
sweepTx := wire.NewMsgTx(2)
totalOutputValue := int64(0)
@ -78,7 +81,7 @@ func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiUrl string,
return fmt.Errorf("error parsing commit point: %v", err)
}
delayDesc := fc.DelayBasePoint.Desc()
delayPrivKey, err := signer.fetchPrivKey(delayDesc)
delayPrivKey, err := signer.FetchPrivKey(delayDesc)
if err != nil {
return fmt.Errorf("error getting private key: %v", err)
}

@ -4,9 +4,10 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/lightningnetwork/lnd/channeldb"
"strconv"
"strings"
"github.com/lightningnetwork/lnd/channeldb"
)
type NumberString uint64

@ -1,6 +1,8 @@
package dataformat
import "github.com/lightningnetwork/lnd/keychain"
import (
"github.com/lightningnetwork/lnd/keychain"
)
type ClosingTX struct {
TXID string `json:"txid"`

@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dataformat"
"github.com/jessevdk/go-flags"
"github.com/lightningnetwork/lnd/aezeed"
@ -291,9 +292,9 @@ func (c *dumpBackupCommand) Execute(_ []string) error {
return fmt.Errorf("backup file is required")
}
multiFile := chanbackup.NewMultiFile(c.MultiFile)
multi, err := multiFile.ExtractMulti(&channelBackupEncryptionRing{
extendedKey: extendedKey,
chainParams: chainParams,
multi, err := multiFile.ExtractMulti(&btc.ChannelBackupEncryptionRing{
ExtendedKey: extendedKey,
ChainParams: chainParams,
})
if err != nil {
return fmt.Errorf("could not extract multi file: %v", err)

Loading…
Cancel
Save