channeldb: expand TestChannelStateTransition to expertise new state transition related methods

In this commit we’ve extended the TestChannelStateTransition method to
exercise the new state transition related messages. This includes
ensuring that when we add a new dangling commitment, and then the
remote party revokes it, then the on-disk state is update accordingly.
This commit is contained in:
Olaoluwa Osuntokun 2017-11-09 21:00:35 -08:00
parent 06e6649da5
commit 29f2f88abc
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21

@ -366,7 +366,7 @@ func TestChannelStateTransition(t *testing.T) {
// Add some HTLCs which were added during this new state transition.
// Half of the HTLCs are incoming, while the other half are outgoing.
var (
htlcs []*HTLC
htlcs []HTLC
htlcAmt lnwire.MilliSatoshi
)
for i := uint32(0); i < 10; i++ {
@ -374,36 +374,48 @@ func TestChannelStateTransition(t *testing.T) {
if i > 5 {
incoming = true
}
htlc := &HTLC{
htlc := HTLC{
Signature: testSig.Serialize(),
Incoming: incoming,
Amt: 10,
RHash: key,
RefundTimeout: i,
OutputIndex: int32(i * 3),
LogIndex: uint64(i * 2),
HtlcIndex: uint64(i),
}
htlc.OnionBlob = make([]byte, 10)
copy(htlc.OnionBlob[:], bytes.Repeat([]byte{2}, 10))
htlcs = append(htlcs, htlc)
htlcAmt += htlc.Amt
}
// TODO(roasbeef): ensure that expiry matches
// Create a new channel delta which includes the above HTLCs, some
// balance updates, and an increment of the current commitment height.
// Additionally, modify the signature and commitment transaction.
newSequence := uint32(129498)
newSig := bytes.Repeat([]byte{3}, 71)
newTx := channel.CommitTx.Copy()
newTx := channel.LocalCommitment.CommitTx.Copy()
newTx.TxIn[0].Sequence = newSequence
delta := &ChannelDelta{
LocalBalance: lnwire.MilliSatoshi(1e8),
RemoteBalance: lnwire.MilliSatoshi(1e8),
Htlcs: htlcs,
UpdateNum: 1,
commitment := ChannelCommitment{
CommitHeight: 1,
LocalLogIndex: 2,
LocalHtlcIndex: 1,
RemoteLogIndex: 2,
RemoteHtlcIndex: 1,
LocalBalance: lnwire.MilliSatoshi(1e8),
RemoteBalance: lnwire.MilliSatoshi(1e8),
CommitFee: 55,
FeePerKw: 99,
CommitTx: newTx,
CommitSig: newSig,
Htlcs: htlcs,
}
// First update the local node's broadcastable state.
if err := channel.UpdateCommitment(newTx, newSig, delta); err != nil {
// First update the local node's broadcastable state and also add a
// CommitDiff remote node's as well in order to simulate a proper state
// transition.
if err := channel.UpdateCommitment(&commitment); err != nil {
t.Fatalf("unable to update commitment: %v", err)
}
@ -414,44 +426,81 @@ func TestChannelStateTransition(t *testing.T) {
if err != nil {
t.Fatalf("unable to fetch updated channel: %v", err)
}
if !bytes.Equal(updatedChannel[0].CommitSig, newSig) {
t.Fatalf("sigs don't match %x vs %x",
updatedChannel[0].CommitSig, newSig)
}
if updatedChannel[0].CommitTx.TxIn[0].Sequence != newSequence {
t.Fatalf("sequence numbers don't match: %v vs %v",
updatedChannel[0].CommitTx.TxIn[0].Sequence, newSequence)
}
if updatedChannel[0].LocalBalance != delta.LocalBalance {
t.Fatalf("local balances don't match: %v vs %v",
updatedChannel[0].LocalBalance, delta.LocalBalance)
}
if updatedChannel[0].RemoteBalance != delta.RemoteBalance {
t.Fatalf("remote balances don't match: %v vs %v",
updatedChannel[0].RemoteBalance, delta.RemoteBalance)
}
if updatedChannel[0].NumUpdates != uint64(delta.UpdateNum) {
t.Fatalf("update # doesn't match: %v vs %v",
updatedChannel[0].NumUpdates, delta.UpdateNum)
}
assertCommitmentEqual(t, &commitment, &updatedChannel[0].LocalCommitment)
numDiskUpdates, err := updatedChannel[0].CommitmentHeight()
if err != nil {
t.Fatalf("unable to read commitment height from disk: %v", err)
}
if numDiskUpdates != uint64(delta.UpdateNum) {
if numDiskUpdates != uint64(commitment.CommitHeight) {
t.Fatalf("num disk updates doesn't match: %v vs %v",
numDiskUpdates, delta.UpdateNum)
numDiskUpdates, commitment.CommitHeight)
}
for i := 0; i < len(updatedChannel[0].Htlcs); i++ {
originalHTLC := updatedChannel[0].Htlcs[i]
diskHTLC := channel.Htlcs[i]
if !reflect.DeepEqual(originalHTLC, diskHTLC) {
t.Fatalf("htlc's dont match: %v vs %v",
spew.Sdump(originalHTLC),
spew.Sdump(diskHTLC))
}
// Attempting to query for a commitment diff should return
// ErrNoPendingCommit as we haven't yet created a new state for them.
_, err = channel.RemoteCommitChainTip()
if err != ErrNoPendingCommit {
t.Fatalf("expected ErrNoPendingCommit, instead got %v", err)
}
// To simulate us extending a new state to the remote party, we'll also
// create a new commit diff for them.
remoteCommit := commitment
remoteCommit.LocalBalance = lnwire.MilliSatoshi(2e8)
remoteCommit.RemoteBalance = lnwire.MilliSatoshi(3e8)
remoteCommit.CommitHeight = 1
commitDiff := &CommitDiff{
Commitment: remoteCommit,
CommitSig: &lnwire.CommitSig{
ChanID: lnwire.ChannelID(key),
CommitSig: testSig,
HtlcSigs: []*btcec.Signature{
testSig,
testSig,
},
},
LogUpdates: []LogUpdate{
{
LogIndex: 1,
UpdateMsg: &lnwire.UpdateAddHTLC{
ID: 1,
Amount: lnwire.NewMSatFromSatoshis(100),
Expiry: 25,
},
},
{
LogIndex: 2,
UpdateMsg: &lnwire.UpdateAddHTLC{
ID: 2,
Amount: lnwire.NewMSatFromSatoshis(200),
Expiry: 50,
},
},
},
}
copy(commitDiff.LogUpdates[0].UpdateMsg.(*lnwire.UpdateAddHTLC).PaymentHash[:],
bytes.Repeat([]byte{1}, 32))
copy(commitDiff.LogUpdates[1].UpdateMsg.(*lnwire.UpdateAddHTLC).PaymentHash[:],
bytes.Repeat([]byte{2}, 32))
if err := channel.AppendRemoteCommitChain(commitDiff); err != nil {
t.Fatalf("unable to add to commit chain: %v", err)
}
// The commitment tip should now match the the commitment that we just
// inserted.
diskCommitDiff, err := channel.RemoteCommitChainTip()
if err != nil {
t.Fatalf("unable to fetch commit diff: %v", err)
}
if !reflect.DeepEqual(commitDiff, diskCommitDiff) {
t.Fatalf("commit diffs don't match: %v vs %v", spew.Sdump(remoteCommit),
spew.Sdump(diskCommitDiff))
}
// We'll save the old remote commitment as this will be added to the
// revocation log shortly.
oldRemoteCommit := channel.RemoteCommitment
// Next, write to the log which tracks the necessary revocation state
// needed to rectify any fishy behavior by the remote party. Modify the
// current uncollapsed revocation state to simulate a state transition
@ -462,37 +511,28 @@ func TestChannelStateTransition(t *testing.T) {
t.Fatalf("unable to generate key: %v", err)
}
channel.RemoteNextRevocation = newPriv.PubKey()
if err := channel.AppendToRevocationLog(delta); err != nil {
if err := channel.AdvanceCommitChainTail(); err != nil {
t.Fatalf("unable to append to revocation log: %v", err)
}
// At this point, the remote commit chain shuold be nil, and the posted
// remote commitment should match the one we added as a diff above.
if _, err := channel.RemoteCommitChainTip(); err != ErrNoPendingCommit {
t.Fatalf("expected ErrNoPendingCommit, instead got %v", err)
}
// We should be able to fetch the channel delta created above by it's
// update number with all the state properly reconstructed.
diskDelta, err := channel.FindPreviousState(uint64(delta.UpdateNum))
diskPrevCommit, err := channel.FindPreviousState(
oldRemoteCommit.CommitHeight,
)
if err != nil {
t.Fatalf("unable to fetch past delta: %v", err)
}
// The two deltas (the original vs the on-disk version) should
// identical, and all HTLC data should properly be retained.
if delta.LocalBalance != diskDelta.LocalBalance {
t.Fatal("local balances don't match")
}
if delta.RemoteBalance != diskDelta.RemoteBalance {
t.Fatal("remote balances don't match")
}
if delta.UpdateNum != diskDelta.UpdateNum {
t.Fatal("update number doesn't match")
}
for i := 0; i < len(delta.Htlcs); i++ {
originalHTLC := delta.Htlcs[i]
diskHTLC := diskDelta.Htlcs[i]
if !reflect.DeepEqual(originalHTLC, diskHTLC) {
t.Fatalf("htlc's dont match: %v vs %v",
spew.Sdump(originalHTLC),
spew.Sdump(diskHTLC))
}
}
assertCommitmentEqual(t, &oldRemoteCommit, diskPrevCommit)
// The state number recovered from the tail of the revocation log
// should be identical to this current state.
@ -500,36 +540,31 @@ func TestChannelStateTransition(t *testing.T) {
if err != nil {
t.Fatalf("unable to retrieve log: %v", err)
}
if logTail.UpdateNum != delta.UpdateNum {
if logTail.CommitHeight != oldRemoteCommit.CommitHeight {
t.Fatal("update number doesn't match")
}
// Next modify the delta slightly, then create a new entry within the
// revocation log.
delta.UpdateNum = 2
delta.LocalBalance -= htlcAmt
delta.RemoteBalance += htlcAmt
delta.Htlcs = nil
if err := channel.AppendToRevocationLog(delta); err != nil {
oldRemoteCommit = channel.RemoteCommitment
// Next modify the posted diff commitment slightly, then create a new
// commitment diff and advance the tail.
commitDiff.Commitment.CommitHeight = 2
commitDiff.Commitment.LocalBalance -= htlcAmt
commitDiff.Commitment.RemoteBalance += htlcAmt
commitDiff.LogUpdates = []LogUpdate{}
if err := channel.AppendRemoteCommitChain(commitDiff); err != nil {
t.Fatal("unable to add to commit chain: %v", err)
}
if err := channel.AdvanceCommitChainTail(); err != nil {
t.Fatalf("unable to append to revocation log: %v", err)
}
// Once again, fetch the state and ensure it has been properly updated.
diskDelta, err = channel.FindPreviousState(uint64(delta.UpdateNum))
prevCommit, err := channel.FindPreviousState(oldRemoteCommit.CommitHeight)
if err != nil {
t.Fatalf("unable to fetch past delta: %v", err)
}
if len(diskDelta.Htlcs) != 0 {
t.Fatalf("expected %v htlcs, got %v", 0, len(diskDelta.Htlcs))
}
if delta.LocalBalance != 1e8-htlcAmt {
t.Fatalf("mismatched balances, expected %v got %v", 1e8-htlcAmt,
delta.LocalBalance)
}
if delta.RemoteBalance != 1e8+htlcAmt {
t.Fatalf("mismatched balances, expected %v got %v", 1e8+htlcAmt,
delta.RemoteBalance)
}
assertCommitmentEqual(t, &oldRemoteCommit, prevCommit)
// Once again, state number recovered from the tail of the revocation
// log should be identical to this current state.
@ -537,7 +572,7 @@ func TestChannelStateTransition(t *testing.T) {
if err != nil {
t.Fatalf("unable to retrieve log: %v", err)
}
if logTail.UpdateNum != delta.UpdateNum {
if logTail.CommitHeight != oldRemoteCommit.CommitHeight {
t.Fatal("update number doesn't match")
}
@ -579,7 +614,7 @@ func TestChannelStateTransition(t *testing.T) {
// Attempting to find previous states on the channel should fail as the
// revocation log has been deleted.
_, err = updatedChannel[0].FindPreviousState(uint64(delta.UpdateNum))
_, err = updatedChannel[0].FindPreviousState(oldRemoteCommit.CommitHeight)
if err == nil {
t.Fatal("revocation log search should've failed")
}