diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 9641f922..2d595931 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1,12 +1,9 @@ package lnwallet import ( - "bytes" "sync" - "time" "li.lan/labs/plasma/chainntfs" - "li.lan/labs/plasma/revocation" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/txscript" @@ -21,80 +18,6 @@ const ( MaxPendingPayments = 10 ) -type nodeId [32]byte - -// OpenChannelState... -// TODO(roasbeef): script gen methods on this? -type OpenChannelState struct { - // Hash? or Their current pubKey? - // TODO(roasbeef): switch to Tadge's LNId - theirLNID nodeId - - minFeePerKb btcutil.Amount - // Our reserve. Assume symmetric reserve amounts. Only needed if the - // funding type is CLTV. - reserveAmount btcutil.Amount - - // Keys for both sides to be used for the commitment transactions. - ourCommitKey *btcec.PrivateKey // TODO(roasbeef): again unencrypted - theirCommitKey *btcec.PublicKey - - // Tracking total channel capacity, and the amount of funds allocated - // to each side. - capacity btcutil.Amount - ourBalance btcutil.Amount - theirBalance btcutil.Amount - - // Commitment transactions for both sides (they're asymmetric). Also - // their signature which lets us spend our version of the commitment - // transaction. - theirCommitTx *wire.MsgTx - ourCommitTx *wire.MsgTx - theirCommitSig []byte - - // The final funding transaction. Kept wallet-related records. - fundingTx *wire.MsgTx - - // TODO(roasbeef): instead store a btcutil.Address here? Otherwise key - // is stored unencrypted! Use manager.Encrypt() when storing. - multiSigKey *btcec.PrivateKey - // TODO(roasbeef): encrypt also, or store in waddrmanager? - fundingRedeemScript []byte - - // Current revocation for their commitment transaction. However, since - // this is the hash, and not the pre-image, we can't yet verify that - // it's actually in the chain. - theirCurrentRevocation [wire.HashSize]byte - theirShaChain *revocation.HyperShaChain - ourShaChain *revocation.HyperShaChain - - // Final delivery address - ourDeliveryAddress btcutil.Address - theirDeliveryAddress btcutil.Address - - // In blocks - htlcTimeout uint32 - csvDelay uint32 - - // TODO(roasbeef): track fees, other stats? - numUpdates uint64 - totalSatoshisSent uint64 - totalSatoshisReceived uint64 - creationTime time.Time -} - -func (o *OpenChannelState) Encode(b bytes.Buffer) error { - return nil -} - -func (o *OpenChannelState) Decode(b bytes.Buffer) error { - return nil -} - -func newOpenChannelState(ID [32]byte) *OpenChannelState { - return &OpenChannelState{theirLNID: ID} -} - // LightningChannel... // TODO(roasbeef): future peer struct should embed this struct type LightningChannel struct { diff --git a/lnwallet/channeldb.go b/lnwallet/channeldb.go new file mode 100644 index 00000000..0547a297 --- /dev/null +++ b/lnwallet/channeldb.go @@ -0,0 +1,76 @@ +package lnwallet + +import ( + "encoding/binary" + "io" + "time" + + "li.lan/labs/plasma/shachain" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/btcsuite/btcwallet/walletdb" +) +// OpenChannelState... +// TODO(roasbeef): trim... +type OpenChannelState struct { + // Hash? or Their current pubKey? + // TODO(roasbeef): switch to Tadge's LNId + TheirLNID [wire.HashSize]byte + + // The ID of a channel is the txid of the funding transaction. + ChanID [wire.HashSize]byte + + MinFeePerKb btcutil.Amount + // Our reserve. Assume symmetric reserve amounts. Only needed if the + // funding type is CLTV. + //ReserveAmount btcutil.Amount + + // Keys for both sides to be used for the commitment transactions. + OurCommitKey *btcec.PrivateKey + TheirCommitKey *btcec.PublicKey + + // Tracking total channel capacity, and the amount of funds allocated + // to each side. + Capacity btcutil.Amount + OurBalance btcutil.Amount + TheirBalance btcutil.Amount + + // Commitment transactions for both sides (they're asymmetric). Also + // their signature which lets us spend our version of the commitment + // transaction. + TheirCommitTx *wire.MsgTx + OurCommitTx *wire.MsgTx + TheirCommitSig []byte + + // The final funding transaction. Kept wallet-related records. + FundingTx *wire.MsgTx + + // TODO(roasbeef): instead store a btcutil.Address here? Otherwise key + // is stored unencrypted! Use manager.Encrypt() when storing. + MultiSigKey *btcec.PrivateKey + // TODO(roasbeef): encrypt also, or store in waddrmanager? + FundingRedeemScript []byte + + // Current revocation for their commitment transaction. However, since + // this is the hash, and not the pre-image, we can't yet verify that + // it's actually in the chain. + TheirCurrentRevocation [wire.HashSize]byte + TheirShaChain *shachain.HyperShaChain + OurShaChain *shachain.HyperShaChain + + // Final delivery address + OurDeliveryAddress btcutil.Address + TheirDeliveryAddress btcutil.Address + + // In blocks + CsvDelay uint32 + + // TODO(roasbeef): track fees, other stats? + NumUpdates uint64 + TotalSatoshisSent uint64 + TotalSatoshisReceived uint64 + CreationTime time.Time +} diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index b381c70d..668ce6db 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -92,8 +92,8 @@ func newChannelReservation(t FundingType, fundingAmt btcutil.Amount, }, partialState: &OpenChannelState{ // TODO(roasbeef): assumes balanced symmetric channels. - capacity: fundingAmt * 2, - minFeePerKb: minFeeRate, + Capacity: fundingAmt * 2, + MinFeePerKb: minFeeRate, }, reservationID: id, wallet: wallet, @@ -153,14 +153,14 @@ func (r *ChannelReservation) CompleteReservation(fundingSigs [][]byte, commitmen func (r *ChannelReservation) TheirSignatures() ([][]byte, []byte) { r.RLock() defer r.RUnlock() - return r.theirFundingSigs, r.partialState.theirCommitSig + return r.theirFundingSigs, r.partialState.TheirCommitSig } // FinalFundingTransaction... func (r *ChannelReservation) FinalFundingTx() *wire.MsgTx { r.RLock() defer r.RUnlock() - return r.partialState.fundingTx + return r.partialState.FundingTx } // RequestFundingReserveCancellation... diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index b954daaf..37c9a588 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -417,9 +417,9 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg req.resp <- nil return } - reservation.partialState.multiSigKey = multiSigKey + reservation.partialState.MultiSigKey = multiSigKey ourContribution.MultiSigKey = multiSigKey.PubKey() - reservation.partialState.ourCommitKey = commitKey + reservation.partialState.OurCommitKey = commitKey ourContribution.CommitKey = commitKey.PubKey() // Generate a fresh address to be used in the case of a cooperative @@ -433,7 +433,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg req.resp <- nil return } - reservation.partialState.ourDeliveryAddress = addrs[0].Address() + reservation.partialState.OurDeliveryAddress = addrs[0].Address() ourContribution.DeliveryAddress = addrs[0].Address() // Create a new shaChain for verifiable transaction revocations. @@ -443,7 +443,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg req.resp <- nil return } - reservation.partialState.ourShaChain = shaChain + reservation.partialState.OurShaChain = shaChain ourContribution.RevocationHash = shaChain.CurrentRevocationHash() // Funding reservation request succesfully handled. The funding inputs @@ -503,8 +503,8 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // Create a blank, fresh transaction. Soon to be a complete funding // transaction which will allow opening a lightning channel. - pendingReservation.partialState.fundingTx = wire.NewMsgTx() - fundingTx := pendingReservation.partialState.fundingTx + pendingReservation.partialState.FundingTx = wire.NewMsgTx() + fundingTx := pendingReservation.partialState.FundingTx pendingReservation.theirContribution = req.contribution theirContribution := req.contribution @@ -534,10 +534,10 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // Finally, add the 2-of-2 multi-sig output which will set up the lightning // channel. - ourKey := pendingReservation.partialState.multiSigKey + ourKey := pendingReservation.partialState.MultiSigKey theirKey := theirContribution.MultiSigKey - channelCapacity := int64(pendingReservation.partialState.capacity) + channelCapacity := int64(pendingReservation.partialState.Capacity) redeemScript, multiSigOut, err := fundMultiSigOut(ourKey.PubKey().SerializeCompressed(), theirKey.SerializeCompressed(), channelCapacity) if err != nil { @@ -550,7 +550,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // Sort the transaction. Since both side agree to a cannonical // ordering, by sorting we no longer need to send the entire // transaction. Only signatures will be exchanged. - txsort.InPlaceSort(pendingReservation.partialState.fundingTx) + txsort.InPlaceSort(pendingReservation.partialState.FundingTx) // Now that the transaction has been cannonically sorted, compute the // normalized transation ID before we attach our signatures. @@ -589,7 +589,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { return } - sigscript, err := txscript.SignatureScript(pendingReservation.partialState.fundingTx, i, + sigscript, err := txscript.SignatureScript(pendingReservation.partialState.FundingTx, i, prevOut.PkScript, txscript.SigHashAll, privkey, ai.Compressed()) if err != nil { @@ -597,7 +597,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { return } - pendingReservation.partialState.fundingTx.TxIn[i].SignatureScript = sigscript + fundingTx.TxIn[i].SignatureScript = sigscript pendingReservation.ourFundingSigs = append(pendingReservation.ourFundingSigs, sigscript) } @@ -610,15 +610,14 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // Grab the hash of the current pre-image in our chain, this is needed // for out commitment tx. // TODO(roasbeef): grab partial state above to avoid long attr chain - ourCurrentRevokeHash := pendingReservation.partialState.ourShaChain.CurrentRevocationHash() + ourCurrentRevokeHash := pendingReservation.partialState.OurShaChain.CurrentRevocationHash() ourContribution.RevocationHash = ourCurrentRevokeHash // Create the txIn to our commitment transaction. In the process, we // need to locate the index of the multi-sig output on the funding tx // since the outputs are cannonically sorted. - fundingNTxid := pendingReservation.partialState.fundingTx.TxSha() // NOTE: assumes testnet-L - _, multiSigIndex := findScriptOutputIndex(pendingReservation.partialState.fundingTx, - multiSigOut.PkScript) + fundingNTxid := fundingTx.TxSha() // NOTE: assumes testnet-L + _, multiSigIndex := findScriptOutputIndex(fundingTx, multiSigOut.PkScript) fundingTxIn := wire.NewTxIn(wire.NewOutPoint(&fundingNTxid, multiSigIndex), nil) // With the funding tx complete, create both commitment transactions. @@ -639,9 +638,9 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { return } - pendingReservation.partialState.theirCommitKey = theirCommitKey - pendingReservation.partialState.theirCommitTx = theirCommitTx - pendingReservation.partialState.ourCommitTx = ourCommitTx + pendingReservation.partialState.TheirCommitKey = theirCommitKey + pendingReservation.partialState.TheirCommitTx = theirCommitTx + pendingReservation.partialState.OurCommitTx = ourCommitTx // Generate a signature for their version of the initial commitment // transaction. @@ -651,7 +650,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { req.err <- err return } - pendingReservation.partialState.theirCommitSig = sigTheirCommit + pendingReservation.partialState.TheirCommitSig = sigTheirCommit pendingReservation.ourCommitmentSig = sigTheirCommit req.err <- nil @@ -674,7 +673,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // Now we can complete the funding transaction by adding their // signatures to their inputs. pendingReservation.theirFundingSigs = msg.theirFundingSigs - fundingTx := pendingReservation.partialState.fundingTx + fundingTx := pendingReservation.partialState.FundingTx for i, txin := range fundingTx.TxIn { if txin.SignatureScript == nil { txin.SignatureScript = pendingReservation.theirFundingSigs[i] @@ -712,7 +711,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // At this point, wen calso record and verify their isgnature for our // commitment transaction. - pendingReservation.partialState.theirCommitSig = msg.theirCommitmentSig + pendingReservation.partialState.TheirCommitSig = msg.theirCommitmentSig // TODO(roasbeef): verify //commitSig := msg.theirCommitmentSig @@ -740,7 +739,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // Create a new sub-bucket within the open channel bucket // specifically for this channel. // TODO(roasbeef): should def be indexed by LNID, cuz mal etc. - txID := pendingReservation.partialState.fundingTx.TxSha() + txID := fundingTx.TxSha() chanBucket, err := openChanBucket.CreateBucketIfNotExists(txID.Bytes()) if err != nil { return err diff --git a/lnwallet/wallet_test.go b/lnwallet/wallet_test.go index 71888bc0..257ac489 100644 --- a/lnwallet/wallet_test.go +++ b/lnwallet/wallet_test.go @@ -360,7 +360,7 @@ func testBasicWalletReservationWorkFlow(lnwallet *LightningWallet, t *testing.T) t.Fatalf("commitment sig not found") } // Additionally, the funding tx should have been populated. - if chanReservation.partialState.fundingTx == nil { + if chanReservation.partialState.FundingTx == nil { t.Fatalf("funding transaction never created!") } // Their funds should also be filled in. @@ -388,7 +388,7 @@ func testBasicWalletReservationWorkFlow(lnwallet *LightningWallet, t *testing.T) // Alice responds with her output, change addr, multi-sig key and signatures. // Bob then responds with his signatures. - bobsSigs, err := bobNode.signFundingTx(chanReservation.partialState.fundingTx) + bobsSigs, err := bobNode.signFundingTx(chanReservation.partialState.FundingTx) if err != nil { t.Fatalf("unable to sign inputs for bob: %v", err) } @@ -414,7 +414,7 @@ func testBasicWalletReservationWorkFlow(lnwallet *LightningWallet, t *testing.T) // The sub-bucket for this channel, keyed by the txid of the // funding transaction - txid := chanReservation.partialState.fundingTx.TxSha() + txid := chanReservation.partialState.FundingTx.TxSha() bobChanBucket := openChanBucket.Bucket(txid[:]) if bobChanBucket == nil { t.Fatalf("bucket for the alice-bob channe should exist, " + @@ -427,7 +427,7 @@ func testBasicWalletReservationWorkFlow(lnwallet *LightningWallet, t *testing.T) // The hash of the stored tx should be indentical to our funding tx. storedTxId := storedTx.TxSha() - actualTxId := chanReservation.partialState.fundingTx.TxSha() + actualTxId := chanReservation.partialState.FundingTx.TxSha() if !bytes.Equal(storedTxId.Bytes(), actualTxId.Bytes()) { t.Fatalf("stored funding tx doesn't match actual") }