Skip to content

Commit

Permalink
decryptchanupdate: add new command.
Browse files Browse the repository at this point in the history
  • Loading branch information
ziggie1984 committed Jan 14, 2024
1 parent 450cb04 commit c6fcebb
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 3 deletions.
7 changes: 4 additions & 3 deletions btc/explorer_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ type ExplorerAPI struct {
}

type TX struct {
TXID string `json:"txid"`
Vin []*Vin `json:"vin"`
Vout []*Vout `json:"vout"`
TXID string `json:"txid"`
Vin []*Vin `json:"vin"`
Vout []*Vout `json:"vout"`
Locktime uint32 `json:"locktime"`
}

type Vin struct {
Expand Down
190 changes: 190 additions & 0 deletions cmd/chantools/decryptchanupate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package main

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/chantools/dump"
"github.com/lightninglabs/chantools/lnd"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/spf13/cobra"
)

type decryptChanUpdateCommand struct {
APIURL string
commitmentTx *wire.MsgTx
TxID string
RawTx string

ChannelBackup string
ChannelPoint string

rootKey *rootKey
cmd *cobra.Command
}

const (
StateHintSize = 6
)

func newDecryptChanUpdateCommand() *cobra.Command {
cc := &decryptChanUpdateCommand{}
cc.cmd = &cobra.Command{
Use: "decryptchanupdate",
Short: "extracts the exact chanupdate number of a force close " +
"transaction",
Long: `TBD`,
RunE: cc.Execute,
}
cc.cmd.Flags().StringVar(
&cc.APIURL, "apiurl", defaultAPIURL, "API URL to use (must "+
"be esplora compatible)",
)
cc.cmd.Flags().StringVar(
&cc.TxID, "txid", "", "force close txid",
)
cc.cmd.Flags().StringVar(
&cc.RawTx, "txhex", "", "force close rawtx",
)
cc.cmd.Flags().StringVar(
&cc.ChannelBackup, "frombackup", "", "channel backup file to "+
"read the channel information from",
)
cc.cmd.Flags().StringVar(
&cc.ChannelPoint, "channelpoint", "", "channel point to use "+
"for locating the channel in the channel backup file "+
"specified in the --frombackup flag, "+
"format: txid:index",
)

cc.rootKey = newRootKey(cc.cmd, "deriving keys")

return cc.cmd
}

func createStateHintObfuscator(state *dump.BackupSingle) [StateHintSize]byte {
localPaymentBasePoint, err := pubKeyFromHex(state.LocalChanCfg.PaymentBasePoint.PubKey)
if err != nil {
fmt.Errorf("error converting pubkey: %v", err)
}

remotePaymentBasePoint, err := pubKeyFromHex(state.RemoteChanCfg.PaymentBasePoint.PubKey)
if err != nil {
fmt.Errorf("error converting pubkey: %v", err)
}

if state.IsInitiator {
return deriveStateHintObfuscator(
localPaymentBasePoint,
remotePaymentBasePoint,
)
}

return deriveStateHintObfuscator(
remotePaymentBasePoint,
localPaymentBasePoint,
)
}

func deriveStateHintObfuscator(key1, key2 *btcec.PublicKey) [StateHintSize]byte {
h := sha256.New()
h.Write(key1.SerializeCompressed())
h.Write(key2.SerializeCompressed())

sha := h.Sum(nil)

var obfuscator [StateHintSize]byte
copy(obfuscator[:], sha[26:])

return obfuscator
}

func (c *decryptChanUpdateCommand) Execute(_ *cobra.Command, _ []string) error {
extendedKey, err := c.rootKey.read()
if err != nil {
return fmt.Errorf("error reading root key: %w", err)
}

// Parse the commitment transaction via the API
if c.RawTx != "" && c.TxID != "" {
return fmt.Errorf("either provide the txid or the rawtx")
}

var locktime, sequence uint32
if c.TxID != "" {
api := newExplorerAPI(c.APIURL)
tx, err := api.Transaction(c.TxID)
if err != nil {
return fmt.Errorf("error fetching tx: %w", err)
}

locktime = tx.Locktime
sequence = tx.Vin[0].Sequence
}

if c.RawTx != "" {
tx, err := hex.DecodeString(c.RawTx)
if err != nil {
return err
}

// Deserialize the transaction to get the transaction hash.
msgTx := &wire.MsgTx{}
txReader := bytes.NewReader(tx)
if err := msgTx.Deserialize(txReader); err != nil {
return err
}
locktime = msgTx.LockTime
sequence = msgTx.TxIn[0].Sequence
}

// We have the Encrypted Locktime and Sequence, now we need to
// decrypt these.
// We create

if c.ChannelPoint == "" || c.ChannelBackup == "" {
return fmt.Errorf("channel point is required with " +
"--frombackup")
}

backupChan, err := lnd.ExtractChannel(
extendedKey, chainParams, c.ChannelBackup,
c.ChannelPoint,
)
if err != nil {
return fmt.Errorf("error extracting channel: %w", err)
}

path, err := lnd.ParsePath(backupChan.LocalChanCfg.PaymentBasePoint.Path)
if err != nil {
return err
}
localPrivPaymentKey, err := lnd.DeriveChildren(extendedKey, path)
if err != nil {
return err
}
localPaymentKey, err := localPrivPaymentKey.ECPubKey()
backupChan.LocalChanCfg.PaymentBasePoint.PubKey = hex.EncodeToString(localPaymentKey.SerializeCompressed())

fmt.Println("Local:", backupChan.LocalChanCfg.PaymentBasePoint)
fmt.Println("Remote:", backupChan.RemoteChanCfg.PaymentBasePoint)

txShell := &wire.MsgTx{
LockTime: locktime,
TxIn: []*wire.TxIn{{
Sequence: sequence,
}},
}

obfuscator := createStateHintObfuscator(backupChan)

stateHint := lnwallet.GetStateNumHint(txShell, obfuscator)

fmt.Println("StateHint:", stateHint)

return nil
}
1 change: 1 addition & 0 deletions cmd/chantools/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func main() {
newVanityGenCommand(),
newWalletInfoCommand(),
newZombieRecoveryCommand(),
newDecryptChanUpdateCommand(),
)

if err := rootCmd.Execute(); err != nil {
Expand Down

0 comments on commit c6fcebb

Please sign in to comment.