Started working on state machine

* 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)
This commit is contained in:
Joseph Poon 2016-01-14 17:02:23 -08:00 committed by Olaoluwa Osuntokun
parent 0c304cbb2f
commit 1981001a29
20 changed files with 727 additions and 80 deletions

5
lnstate/README.md Normal file

@ -0,0 +1,5 @@
I'm using this as a temporary repo for the state machine.
All hooks into real stuff isn't used right now.
Will merge into lnwallet/channel.go later?

418
lnstate/lnstate.go Normal file

@ -0,0 +1,418 @@
package lnstate
import (
"fmt"
//"github.com/btcsuite/btcd/btcec"
//"atomic"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"li.lan/labs/plasma/channeldb"
"li.lan/labs/plasma/lnwire"
"li.lan/labs/plasma/shachain"
"sync"
)
//This is a state machine which allows for simultaneous high-volume
//bidirectional payments. The design goal is to for all steps to be
//non-blocking over-the-wire on signing/revoking with the counterparty.
//
//A *single* Lightning Network node on one reasonably high-speced computer
//should be able to conduct transactions at Visa-scale (one Lightning Network
//node can process thousands of transactions per second total), enabling
//incredible volume for the entire network. This state machine design should
//enable transaction volume for the entire network greater than current Visa
//card transaction load by many orders of magnitude.
//
//More info on the design is in lnwire/README.md
//NOTE: WORK IN PROGRESS
//TODO: Will make channel-friendly & add in mutex stuff
//DUMMY FunCTIONS FOR UPDATING LATER
func disk() {
}
func net(msg lnwire.Message) {
fmt.Println(msg)
}
func (l *LNChannel) sendErrorPkt() {
}
//Will copy in code from channel.go...
const (
MAX_STAGED_HTLCS = 1000
MAX_UNREVOKED_COMMITMENTS = 16
MAX_UPDATED_HTLCS_PER_COMMITMENT = 1000
)
//Currently, the mutex locks for across the entire channel, it's possible to
//update it to be more granular and to have each PaymentDescriptor have its own
//locks (and lock both simultaneously). However, this is complicated and will
//be updated in the future. Right now, since there is a per-channel
//global-lock, this should work fine...
//
//Before calling another function which does a lock, make sure to l.Unlock()
//first, e.g. if a packet received triggers a packet to be sent.
//PaymentDescriptor states
//PRESTAGE: We're not sure if the other guy wants to follow through
//STAGED: Both parties informally agree to add to their commitments
//SIGNING_AND_REVOKING: In the process of activating the HTLC
//
//Only one state is allowed at a time. timeout/settle can only begin on an
//ADD_COMPLETE state. If it times out before it hits completion, channel closes
//out uncooperatively.
//
//NOTE: Current design assumes if value%1000 == 200 as partially
//signed/revoked, this means that the code is reliant upon this as knowing when
//to force close out channels, etc.
const (
//HTLC Add
ADD_PRESTAGE = 1000
ADD_STAGED = 1100
ADD_SIGNING_AND_REVOKING = 1200
ADD_COMPLETE = 1300 //Most HTLCs should be this
ADD_REJECTED = 1999 //Staging request rejected
//HTLC Timeout
TIMEOUT_PRESTAGE = 2000
TIMEOUT_STAGED = 2100
TIMEOUT_SIGNING_AND_REVOKING = 2200
TIMEOUT_COMPLETE = 2300
//HTLC Settle
SETTLE_PRESTAGE = 3000
SETTLE_STAGED = 3100
SETTLE_SIGNING_AND_REVOKING = 3200
SETTLE_COMPLETE = 3300
//TODO: Commitment states
)
//Example, Add Flow:
//1. Create the HTLC
//2. (you+they) Sign the commit
//3. (you+they) Send revocation when you get a new commit
//4. Update the state to account for revocation
//5. Both sides committed and revoked prior states, mark state as finished
type LNChannel struct {
fundingTxIn *wire.TxIn
channelDB *channeldb.DB
channelID uint64
//The person who set up the channel is even, the person who responded
//is odd. All HTLCKeys use even/odd numbering.
//NOTE: should we change this to be a int64 and use positive/negative?
isEven bool
sync.RWMutex
//Should update with new Commitments when fees are renegotiated
feePerKb btcutil.Amount
//HTLCs
//Even/odd numbering in effect
//Will just take the next number
HTLCs map[lnwire.HTLCKey]*PaymentDescriptor
//Last key in the map for incrementing
//If the other party wants to use some stupid high number, then
//they're basically wanting the channel to close out early...
ourLastKey lnwire.HTLCKey
theirLastKey lnwire.HTLCKey
//ourCommit/theirCommit is the most recent *signed* Commitment
ourCommit *wire.MsgTx
theirCommit *wire.MsgTx
ourCommitHeight lnwire.CommitHeight
ourUnrevokedCommitments []lnwire.CommitHeight
theirCommitHeight lnwire.CommitHeight
theirUnrevokedCommitments []lnwire.CommitHeight
//UnrevoedStates indexed by CommitHeight, slice of HTLCKeys to update
//as revoked/complete
//NOTE: I think it's EASIER to reconstruct the Commitment than
//to update because finding indicies and blah blah blah,
//validation/verification screw that, the state is in the HTLCs
//and the sig. We can optimize later.
//ShaChain Height
//Difference between ourCommitHeight and ourShaChain index is the
//Commitments not yet revoked. If the difference is 1, then all prior
//Commitments are revoked.
ourShaChain *shachain.HyperShaChain
theirShaChain *shachain.HyperShaChain
}
type PaymentDescriptor struct {
RHashes []*[20]byte
Timeout uint32
CreditsAmount lnwire.CreditsAmount
Revocation []*[20]byte
Blob []byte //next hop data
PayToUs bool
State uint32 //Current state
Index uint32 //Position in txout
p2sh [20]byte //cached p2sh script to use
//Used during the SIGNING_AND_REVOKING process
weSigned bool
theyRevoked bool
theySignedAndWeRevoked bool
//This is the height which is revoked, anything before that
//is assumed to still be valid.
//This data point is useful for ...UnrevokedCommitments
//if there's unrevoked commitments below these values,
//then the state is not locked in
//Used for add/settle/timeout
ourRevokedHeight lnwire.CommitHeight
theirRevokedHeight lnwire.CommitHeight
}
//addHTLC will take in a PaymentDescriptor, adds to HTLCs and writes to disk
//Assumes the data has already been validated!
//NOTE: **MUST** HAVE THE MUTEX LOCKED ALREADY WHEN CALLED
func (l *LNChannel) addHTLC(h *PaymentDescriptor) (lnwire.HTLCKey, error) {
//Sanity check
if h.State != ADD_PRESTAGE {
return 0, fmt.Errorf("addHTLC can only add PRESTAGE")
}
//Make sure we're not overfull
//We should *never* hit this...
//we subtract 3 in case we need to add one to correct for even/odd
if l.ourLastKey >= ^lnwire.HTLCKey(0)-3 {
return 0, fmt.Errorf("Channel full!!!")
}
//Assign a new value
//We add 2 due to even/odd assignments
l.ourLastKey += 2
//Check whether the even/odd is invalid..
//If it is, we iterate to the next one
if l.ourLastKey%1 == 1 {
if l.isEven {
l.ourLastKey += 1
}
} else {
if !l.isEven {
l.ourLastKey += 1
}
}
//Write the new HTLC
l.HTLCs[l.ourLastKey] = h
disk() //save l.ourLastKey and the htlcs
return l.ourLastKey, nil
}
func (l *LNChannel) CreateHTLC(h *PaymentDescriptor) error {
l.Lock()
var err error
//if h.State == ADD_PRESTAGE {
// //We already have it created, but let's re-send!
// //Send a payment request LNWire
//}
if h.State > ADD_PRESTAGE {
l.Unlock()
return fmt.Errorf("HTLC is already created!")
}
if !h.PayToUs { //We created the payment
//Validate the data
err = l.validateHTLC(h, false)
if err != nil {
return err
}
//Update state as pre-commit
h.State = ADD_PRESTAGE
l.addHTLC(h)
//Send a ADDHTLC LNWire
l.Unlock()
net() //TODO
} else {
//Future version may be able to do this..
l.Unlock()
fmt.Errorf("Cannot pull money")
}
return nil
}
//Receives an HTLCAddRequest message
//Adds to the HTLC staging list
func (l *LNChannel) recvHTLCAddRequest(p *lnwire.HTLCAddRequest) error {
l.Lock()
var err error
//Check if we already have a PaymentDescriptor
if l.HTLCs[p.HTLCKey] != nil {
//Don't do anything because we already have one
l.Unlock()
return fmt.Errorf("Counterparty attempted to re-send AddRequest")
}
//Make sure they aren't reusing
if p.HTLCKey <= l.theirLastKey {
//They're reusing.... uhoh
l.Unlock()
l.sendAddReject(p.HTLCKey) //This may be a dupe!
return fmt.Errorf("Counterparty cannot re-use HTLCKeys")
}
//Update newest id
l.theirLastKey = p.HTLCKey
//Create a new HTLCKey entry
htlc := new(PaymentDescriptor)
//Populate the entries
htlc.RHashes = p.RedemptionHashes
htlc.Timeout = p.Expiry
htlc.CreditsAmount = p.CreditsAmount
htlc.Blob = p.Blob
htlc.State = ADD_STAGED //mark as staged by both parties
htlc.PayToUs = true //assume this is paid to us, may change in the future
//Validate the HTLC
err = l.validateHTLC(htlc, true)
if err != nil {
//Update state just in case (not used but y'know..)
htlc.State = ADD_REJECTED
//currently not yet added to staging
//so we don't need to worry about the above htlc
//we also don't add to disk
//However, we do need to send a AddReject packet
l.Unlock()
sendAddReject(p.HTLCKey)
return err
}
//Validation passed, so we continue
addHTLC(h)
//Send add accept packet, and we're done
l.Unlock()
sendAddAccept(p.HTLCKey)
}
func (l *LNChannel) sendAddReject(htlckey lnwire.HTLCKey) error {
l.Lock()
defer l.Unlock()
msg = new(lnwire.HTLCAddReject)
msg.ChannelID = l.channelID
msg.HTLCKey = h.HTLCKey
net(msg)
return nil
}
func (l *LNChannel) recvAddReject(htlckey lnwire.HTLCKey) error {
l.Lock()
defer l.Unlock()
htlc = l.HTLCs[htlckey]
if htlc == nil {
return fmt.Errorf("Counterparty rejected non-existent HTLC")
}
if (*htlc).State != ADD_PRESTAGE {
return fmt.Errorf("Counterparty atttempted to reject invalid state")
}
(*htlc).State = ADD_REJECTED
disk()
return nil
}
//Notifies the other party that it is now staged on our end
func (l *LNChannel) sendAddAccept(htlckey lnwire.HTLCKey) error {
h = l.HTLCs[htlckey]
msg = new(lnwire.HTLCAddAccept)
msg.ChannelID = l.channelID
msg.HTLCKey = h.HTLCKey
disk()
net(msg)
return nil
}
//The other party has accepted the staging request, so we are staging now
func (l *LNChannel) recvAddAccept(p *HTLCAddAccept) error {
htlc = l.HTLCs[p.HTLCKey]
//Make sure it's in the list
if htlc == nil {
return fmt.Errorf("Counterparty accepted non-existent HTLC")
}
//Update pre-stage to staged
//Everything else it won't do anything
if (*htlc).State == ADD_PRESTAGE {
//Update to staged
(*htlc).State = ADD_STAGED
disk()
}
}
func (l *LNChannel) timeoutHTLC(htlcKey lnwire.HTLCKey) error {
}
func (l *LNChannel) settleHTLC(htlcKey lnwire.HTLCKey) error {
}
//receive AddAcceptHTLC: Find the HTLC and call createHTLC
func (l *LNChannel) addAccept(h *PaymentDescriptor) error {
if h.State == ADD_PRESTAGE {
//Mark stage as accepted
h.State = ADD_SIGNING_AND_REVOKING
//Write to disk
disk()
}
}
//Timeout, Settle
//Create a commitment
// Update the markings on the HTLCs
func (l *LNChannel) createCommitment() error {
//Take all staging marked as SIGNING_AND_REVOKING *and* we have not signed
// Mark each as weSigned
}
//They revoke prior
//After we send a Commitment, they should send a revocation
// Update shaChain and HTLCs
func (l *LNChannel) receiveRevocation() error {
//Revoke the prior Commitment
//Update HTLCs with theyRevoked
//Check each HTLC for being complete and mark as complete if so
}
//Receive a commitment & send out a revocation
// Update the markings on the HTLCs
func (l *LNChannel) receiveCommitment() error {
//Validate new Commitment (sigs, even/odd for commitment height, incremental commitment height, etc)
// Reconstruct Commitment
//Update with new Commitment
//Send revocation
//Mark as theySignedAndWeRevoked
//Check each HTLC for being complete and mark as complete if so
}
//Mark the HTLC as revoked if it is fully signed and revoked by both parties
func (l *LNChannel) addCompleteHTLC(h *PaymentDescriptor) error {
//Check/validate values
//Mark as ADD_COMPLETE
}
//Validate whether we want to add the HTLC
func (l *LNChannel) validateHTLC(h *PaymentDescriptor, toUs bool) error {
//Make sure we have available spots; not too many outputs
//Make sure there is available funds
//Make sure there is sufficient reserve for fees
//Make sure the money is going in the right direction
}

@ -56,3 +56,184 @@ Close Complete
Returns the Txid and sig as a courtesy. The counterparty might not send this if
they're being non-cooperative.
Commitments and HTLCs
=====================
This is designed to be non-blocking where there can be multiple Commitments per
person and the Commitments do not need to match. A HTLC is only believed to be
added when it's in both parties' most recent Commitment (same with
timeout/settle).
As a result, there can easily be hundreds of state updates/payments per second
per channel.
Commitment States
-----------------
Commitments:
1. HTLCs, can be modified. Any add/settlement/timeout/etc. gets added to
staging.
2. Signed, only one signed state at a time can exist per party. Takes HTLCs
staging and locks it in, can now be broadcast on-chain by the counterparty.
3. Completed and Revoked, other party sends their revocation accepting this
Commitment. Sending a revocation means you *ACCEPT* the Commitment. There
should never be a case where a Commitment Signature happens and the client
refusees to revoke -- instead the client should immediately close out the
channel.
4. Deprecated, a commitment is old, marked as deprecated when there is a new
Commitment and this one is revoked. These commitments never be broadcasted.
5. Invalid, close out channel immediately.
There can be multiple commitments going at a time per party (currently limits a
total of 16 possible in-flight that can be broadcast for sanity, but there's no
real limit).
For validity, all you do is ensure that the changes from the old commitment are
legit (based on your HTLC/staging data)
COMMIT\_STAGING
COMMIT\_SIGNED
COMMIT\_COMPLETE
Messages:
CommitSignature: Signature to establish COMMIT\_SIGNED state
CommitRevocation: Revoke prior states
ADD HTLCs
---------
Requester Add HTLC states (Adding HTLCs):
1. Pre-staged, don't know if the other person wants it
2. Staged, both parties agree to add this HTLC. If a staging request packet is
received, then BOTH PARTIES will have it in their next Commitment. Nothing
is guaranteed here, but violations are treated as immediate channel closure.
3. Signed and sent the Commitment Tx to the counterparty, one should now assume
that there's a possibility that this HTLC will be boradcast on-chain.
4. Completed and Revoked, *counterparty* has included this in the Commitment
they're broadcasting and revoked their prior state. This means the
*Requeseter* can continue to take action, since the Commitment they have,
the HTLC doesn't exist (no payment), and the *Responder* will broadcast with
the payment to the *Responder*. However, the *Responder* cannot treat the
HTLC as cleared.
5. Cleared. Both parties have signed and revoked. Responder can continue
routing. Make sure it's included in *BOTH COMMITMENTS and ALL PREVIOUS
REVOKED*
6. Staging Reject, removal request, tx rejected, begin flow to reject HTLC from
other channels, can only be sent during the *pre-staging* state
In the event that an HTLC stays in "Completed and Revoked" and it is timed out,
and the counterparty refuses to add it into a new Commitment, the channel is
*closed out on-chain*. In other words, when checking which ones to send a
settle/timeout notification, do it for anything which is
ADD\_SIGNING\_AND\_REVOKING, *or* ADD\_COMPLETE (AND ALL OTHER PRE-COMPLETE
STAGES, e.g. in timeout or settlement).
As part of moving to any further stage, check if it's timed out.
If there is a request to stage and it's already staged, treat it as accepting.
When it has cleared and timed out, a timeout notification is sent.
HTLC ID numbers are uint64 and each counterparty is responsible to only make
sequential/incremental, and each party can only make evens/odds (odd channel
creation responder, evens channel creation initiator)
State is for *YOUR* signatures (what kind of action you need to do in the future)
ADD\_PRESTAGE
ADD\_STAGED
ADD\_SIGNING\_AND\_REVOKING
ADD\_COMPLETE
ADD\_REJECTED
Messages:
HTLCAddRequest: Request to add to staging
HTLCAddAccept: Add to staging (both parties have added when recv)
HTLCAddReject: Deny add to staging (both parties don't have in staging)
HTLC Settle (payment success)
-----------------------------
Requester Settle HTLC states (Fulfill HTLCs):
1. Pre-staged, don't know if the other person will agree to settle
2. Staged, both parties agree to settle this HTLC
3. Signed and sent Commitment Tx to the counterparty, there is now the
possibility that the HTLC does not exist on-chain (of course, the Commitment
includes the payment so there's no real loss of funds). In the event that it
does not complete past this step, then one *must* close out on-chain as if
it was never staged/signed in the first place and the counterparty went
offline.
4. Both parties have signed and revoked, the settlement is complete (there is
no intermediate step of Revoked because this is only reliable and actionable
if BOTH PARTIES have updated their settlement state).
This has one less state because when adding, you're encumbering yourself. With
removing, both parties are potentially encumbered, so they cannot take action
until it's fully settled.
State is for *your* signatures
SETTLE\_PRESTAGE
SETTLE\_STAGED
SETTLE\_SIGNING\_AND\_REVOKING
SETTLE\_COMPLETE
Message:
HTLCSettleRequest: Request to add to staging the removal from Commitment.
HTLCSettleAccept: Add to staging the removal from Commitment.
(There is no HTLCSettleReject as the counterparty should immediately close out
or at worst ignore if it's getting garbage requests)
Timeout (falure/refund)
-----------------------
Requester Timeout HTLC States:
1. Pre-staged
2. Staged, both parties agree to time out the HTLC and refund the money
3. Signe dnad sent commitment to the counterparty, there is now the possibility
that the transaction will no longer exist on-chain (of course, they can be
redeemed either way). In the even that it does not complete past this step,
then one *must* close out on-chain as if it was never staged/signed in the
first place adn the counterparty was offline.
4. Both parties have signed and revoked, the settlement is complete (there is no
intermediate step of Revoked because there is only reliable and actionable if
BOTH PARTIES have updated their settlement state).
Similar to HTLC Settlement, there is one less state.
State is for *your* signatures
TIMEOUT\_PRESTAGE
TIMEOUT\_STAGED
TIMEOUT\_SIGNING\_AND\_REVOKING
TIMEOUT\_COMPLETE
Example
-------
Adding a single HTLC process:
1. Requester flags as pre-staged, and sends an "add requeset"
2. Responder decides whether to add. If they don't, they invalidate it. If they
do, they send a message accepting the staging request. It is now marked as
staged on both sides and is ready to be accepted into a Commitment.
3. When a party wants to update with a new Commitment, they send a new signed
Commitment, this includes data that the HTLC is part of it. Let's say it's
the Requester that sends this new Commitment. As a result, the HTLC is
marked *BY THE RESPONDER* as Signed. It's only when the Responder includes a
transaction including the new HTLC in a new Commitment that the Requester
marks it as Signed.
4. Upon the Responder receiving the new Commitment, they send the revocation
for the old Commitment, and commit to broadcasting only the new one.
5. The *Requester* marks the HTLC as complete, but the *Responder* waits until
they receive a Commitment (and the old one is revoked) before marking it as
complete on the Responder's end.
6. When both parties have the new Commitments and the old ones are revoked,
then the HTLC is marked as complete
The two Commitment Transactions may not be completely in sync, but that's OK!
What's added in both (and removed in both) are regarded as valid and locked-in.
If it's only added to one, then it's regarded as in-transit and can go either
way.
The behavior is to sign after all additions/removals/cancellations, but there
may be multiple in the staging buffer.
Each party has their own revocation height (*for the other party to use*), and
they may be different.

@ -21,12 +21,8 @@ type CommitSignature struct {
//each part of the Commitment.
CommitmentHeight uint64
//The last staging included by both parties
//Basically, the state is updated to this point on both parties'
//staging
//Staging inclusion is in order.
CommitterLastStaging uint64
ReceiverLastStaging uint64
//List of HTLC Keys which are updated from all parties
UpdatedHTLCKeys []uint64
//Hash of the revocation to use
RevocationHash [20]byte
@ -41,16 +37,14 @@ type CommitSignature struct {
func (c *CommitSignature) Decode(r io.Reader, pver uint32) error {
//ChannelID(8)
//CommitmentHeight(8)
//CommiterLastStaging(8)
//ReceiverLastStaging(8)
//c.UpdatedHTLCKeys(8*1000max)
//RevocationHash(20)
//Fee(8)
//RequesterCommitSig(73max+2)
err := readElements(r,
&c.ChannelID,
&c.CommitmentHeight,
&c.CommitterLastStaging,
&c.ReceiverLastStaging,
&c.UpdatedHTLCKeys,
&c.RevocationHash,
&c.Fee,
&c.CommitSig,
@ -73,8 +67,7 @@ func (c *CommitSignature) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.CommitmentHeight,
c.CommitterLastStaging,
c.ReceiverLastStaging,
c.UpdatedHTLCKeys,
c.RevocationHash,
c.Fee,
c.CommitSig,
@ -91,7 +84,7 @@ func (c *CommitSignature) Command() uint32 {
}
func (c *CommitSignature) MaxPayloadLength(uint32) uint32 {
return 135
return 8192
}
//Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts)
@ -108,21 +101,23 @@ func (c *CommitSignature) Validate() error {
func (c *CommitSignature) String() string {
//c.ChannelID,
//c.CommitmentHeight,
//c.CommitterLastStaging,
//c.ReceiverLastStaging,
//c.RevocationHash,
//c.UpdatedHTLCKeys,
//c.Fee,
//c.CommitSig,
var serializedSig []byte
if &c.CommitSig != nil && c.CommitSig.R != nil {
serializedSig = c.CommitSig.Serialize()
}
var items string
for i := 0; i < len(c.UpdatedHTLCKeys); i++ {
items += fmt.Sprintf("%d ", c.UpdatedHTLCKeys[i])
}
return fmt.Sprintf("\n--- Begin CommitSignature ---\n") +
fmt.Sprintf("ChannelID:\t\t%d\n", c.ChannelID) +
fmt.Sprintf("CommitmentHeight:\t%d\n", c.CommitmentHeight) +
fmt.Sprintf("CommitterLastStaging:\t%d\n", c.CommitterLastStaging) +
fmt.Sprintf("ReceiverLastStaging:\t%d\n", c.ReceiverLastStaging) +
fmt.Sprintf("UpdatedHTLCKeys:\t%s\n", items) +
fmt.Sprintf("RevocationHash:\t\t%x\n", c.RevocationHash) +
fmt.Sprintf("Fee:\t\t\t%s\n", c.Fee.String()) +
fmt.Sprintf("CommitSig:\t\t%x\n", serializedSig) +

@ -10,16 +10,16 @@ var (
_ = copy(revocationHash[:], revocationHashBytes)
commitSignature = &CommitSignature{
ChannelID: uint64(12345678),
CommitmentHeight: uint64(12345),
CommitterLastStaging: uint64(12345678),
ReceiverLastStaging: uint64(87654321),
RevocationHash: revocationHash,
Fee: btcutil.Amount(10000),
CommitSig: commitSig,
ChannelID: uint64(12345678),
CommitmentHeight: uint64(12345),
//CommitterLastStaging: uint64(12345678),
UpdatedHTLCKeys: []uint64{1, 2, 3, 4, 5},
RevocationHash: revocationHash,
Fee: btcutil.Amount(10000),
CommitSig: commitSig,
}
commitSignatureSerializedString = "0000000000bc614e00000000000030390000000000bc614e0000000005397fb14132b6b48371f7b022a16eacb9b2b0ebee134d4100000000000027104630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df"
commitSignatureSerializedMessage = "0709110b000007d0000000830000000000bc614e00000000000030390000000000bc614e0000000005397fb14132b6b48371f7b022a16eacb9b2b0ebee134d4100000000000027104630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df"
commitSignatureSerializedString = "0000000000bc614e00000000000030390005000000000000000100000000000000020000000000000003000000000000000400000000000000054132b6b48371f7b022a16eacb9b2b0ebee134d4100000000000027104630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df"
commitSignatureSerializedMessage = "0709110b000007d00000009d0000000000bc614e00000000000030390005000000000000000100000000000000020000000000000003000000000000000400000000000000054132b6b48371f7b022a16eacb9b2b0ebee134d4100000000000027104630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df"
)
func TestCommitSignatureEncodeDecode(t *testing.T) {

@ -7,18 +7,15 @@ import (
type HTLCAddAccept struct {
ChannelID uint64
StagingID uint64
HTLCKey HTLCKey
}
func (c *HTLCAddAccept) Decode(r io.Reader, pver uint32) error {
//ChannelID(8)
//CommitmentHeight(8)
//NextResponderCommitmentRevocationHash(20)
//ResponderRevocationPreimage(20)
//ResponderCommitSig(2+73max)
//HTLCKey(8)
err := readElements(r,
&c.ChannelID,
&c.StagingID,
&c.HTLCKey,
)
if err != nil {
return err
@ -37,7 +34,7 @@ func NewHTLCAddAccept() *HTLCAddAccept {
func (c *HTLCAddAccept) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.StagingID,
c.HTLCKey,
)
if err != nil {
@ -65,6 +62,6 @@ func (c *HTLCAddAccept) Validate() error {
func (c *HTLCAddAccept) String() string {
return fmt.Sprintf("\n--- Begin HTLCAddAccept ---\n") +
fmt.Sprintf("ChannelID:\t\t%d\n", c.ChannelID) +
fmt.Sprintf("StagingID:\t\t%d\n", c.StagingID) +
fmt.Sprintf("HTLCKey:\t\t%d\n", c.HTLCKey) +
fmt.Sprintf("--- End HTLCAddAccept ---\n")
}

@ -7,7 +7,7 @@ import (
var (
htlcAddAccept = &HTLCAddAccept{
ChannelID: uint64(12345678),
StagingID: uint64(12345),
HTLCKey: HTLCKey(12345),
}
htlcAddAcceptSerializedString = "0000000000bc614e0000000000003039"
htlcAddAcceptSerializedMessage = "0709110b000003f2000000100000000000bc614e0000000000003039"

@ -7,7 +7,7 @@ import (
type HTLCAddReject struct {
ChannelID uint64
StagingID uint64
HTLCKey HTLCKey
}
func (c *HTLCAddReject) Decode(r io.Reader, pver uint32) error {
@ -18,7 +18,7 @@ func (c *HTLCAddReject) Decode(r io.Reader, pver uint32) error {
//ResponderCommitSig(2+73max)
err := readElements(r,
&c.ChannelID,
&c.StagingID,
&c.HTLCKey,
)
if err != nil {
return err
@ -37,7 +37,7 @@ func NewHTLCAddReject() *HTLCAddReject {
func (c *HTLCAddReject) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.StagingID,
c.HTLCKey,
)
if err != nil {
@ -65,6 +65,6 @@ func (c *HTLCAddReject) Validate() error {
func (c *HTLCAddReject) String() string {
return fmt.Sprintf("\n--- Begin HTLCAddReject ---\n") +
fmt.Sprintf("ChannelID:\t\t%d\n", c.ChannelID) +
fmt.Sprintf("StagingID:\t\t%d\n", c.StagingID) +
fmt.Sprintf("HTLCKey:\t\t%d\n", c.HTLCKey) +
fmt.Sprintf("--- End HTLCAddReject ---\n")
}

@ -7,7 +7,7 @@ import (
var (
htlcAddReject = &HTLCAddReject{
ChannelID: uint64(12345678),
StagingID: uint64(12345),
HTLCKey: HTLCKey(12345),
}
htlcAddRejectSerializedString = "0000000000bc614e0000000000003039"
htlcAddRejectSerializedMessage = "0709110b000003fc000000100000000000bc614e0000000000003039"

@ -12,7 +12,7 @@ type HTLCAddRequest struct {
ChannelID uint64
//ID of this request
StagingID uint64
HTLCKey HTLCKey
//When the HTLC expires
Expiry uint32
@ -21,8 +21,9 @@ type HTLCAddRequest struct {
//Difference between hop and first item in blob is the fee to complete
Amount CreditsAmount
//Hash160 address of the next hop.
NextHop [20]byte
//RefundContext is for payment cancellation
//TODO (j): not currently in use, add later
RefundContext HTLCKey
//Contract Type
//first 4 bits is n, second for is m, in n-of-m "multisig"
@ -38,19 +39,17 @@ type HTLCAddRequest struct {
func (c *HTLCAddRequest) Decode(r io.Reader, pver uint32) error {
//ChannelID(8)
//StagingID(8)
//HTLCKey(8)
//Expiry(4)
//Amount(4)
//NextHop(20)
//ContractType(1)
//RedemptionHashes (numOfHashes * 20 + numOfHashes)
//Blob(2+blobsize)
err := readElements(r,
&c.ChannelID,
&c.StagingID,
&c.HTLCKey,
&c.Expiry,
&c.Amount,
&c.NextHop,
&c.ContractType,
&c.RedemptionHashes,
&c.Blob,
@ -72,10 +71,9 @@ func NewHTLCAddRequest() *HTLCAddRequest {
func (c *HTLCAddRequest) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.StagingID,
c.HTLCKey,
c.Expiry,
c.Amount,
c.NextHop,
c.ContractType,
c.RedemptionHashes,
c.Blob,
@ -117,10 +115,9 @@ func (c *HTLCAddRequest) String() string {
return fmt.Sprintf("\n--- Begin HTLCAddRequest ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("StagingID:\t%d\n", c.StagingID) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("Expiry:\t\t%d\n", c.Expiry) +
fmt.Sprintf("Amount\t\t%d\n", c.Amount) +
fmt.Sprintf("NextHop\t\t%x\n", c.NextHop) +
fmt.Sprintf("ContractType:\t%d (%b)\n", c.ContractType, c.ContractType) +
fmt.Sprintf("RedemptionHashes:") +
redemptionHashes +

@ -8,23 +8,21 @@ var (
//Need to to do this here
_ = copy(revocationHash[:], revocationHashBytes)
_ = copy(redemptionHash[:], redemptionHashBytes)
_ = copy(nextHop[:], nextHopBytes)
emptyRedemptionHashes = []*[20]byte{}
redemptionHashes = append(emptyRedemptionHashes, &redemptionHash)
htlcAddRequest = &HTLCAddRequest{
ChannelID: uint64(12345678),
StagingID: uint64(12345),
HTLCKey: HTLCKey(12345),
Expiry: uint32(144),
Amount: CreditsAmount(123456000),
NextHop: nextHop,
ContractType: uint8(17),
RedemptionHashes: redemptionHashes,
Blob: []byte{255, 0, 255, 0, 255, 0, 255, 0},
}
htlcAddRequestSerializedString = "0000000000bc614e000000000000303900000090075bca0094a9ded5a30fc5944cb1e2cbcd980f30616a14401100015b315ebabb0d8c0d94281caa2dfee69a1a00436e0008ff00ff00ff00ff00"
htlcAddRequestSerializedMessage = "0709110b000003e80000004d0000000000bc614e000000000000303900000090075bca0094a9ded5a30fc5944cb1e2cbcd980f30616a14401100015b315ebabb0d8c0d94281caa2dfee69a1a00436e0008ff00ff00ff00ff00"
htlcAddRequestSerializedString = "0000000000bc614e000000000000303900000090075bca001100015b315ebabb0d8c0d94281caa2dfee69a1a00436e0008ff00ff00ff00ff00"
htlcAddRequestSerializedMessage = "0709110b000003e8000000390000000000bc614e000000000000303900000090075bca001100015b315ebabb0d8c0d94281caa2dfee69a1a00436e0008ff00ff00ff00ff00"
)
func TestHTLCAddRequestEncodeDecode(t *testing.T) {

@ -12,15 +12,15 @@ type HTLCSettleAccept struct {
ChannelID uint64
//ID of this request
StagingID uint64
HTLCKey HTLCKey
}
func (c *HTLCSettleAccept) Decode(r io.Reader, pver uint32) error {
//ChannelID(8)
//StagingID(8)
//HTLCKey(8)
err := readElements(r,
&c.ChannelID,
&c.StagingID,
&c.HTLCKey,
)
if err != nil {
return err
@ -39,7 +39,7 @@ func NewHTLCSettleAccept() *HTLCSettleAccept {
func (c *HTLCSettleAccept) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.StagingID,
c.HTLCKey,
)
if err != nil {
return err
@ -66,6 +66,6 @@ func (c *HTLCSettleAccept) Validate() error {
func (c *HTLCSettleAccept) String() string {
return fmt.Sprintf("\n--- Begin HTLCSettleAccept ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("StagingID:\t%d\n", c.StagingID) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("--- End HTLCSettleAccept ---\n")
}

@ -7,7 +7,7 @@ import (
var (
htlcSettleAccept = &HTLCSettleAccept{
ChannelID: uint64(12345678),
StagingID: uint64(12345),
HTLCKey: HTLCKey(12345),
}
htlcSettleAcceptSerializedString = "0000000000bc614e0000000000003039"
htlcSettleAcceptSerializedMessage = "0709110b00000456000000100000000000bc614e0000000000003039"

@ -12,7 +12,7 @@ type HTLCSettleRequest struct {
ChannelID uint64
//ID of this request
StagingID uint64
HTLCKey HTLCKey
//Redemption Proofs (R-Values)
RedemptionProofs []*[20]byte
@ -20,7 +20,7 @@ type HTLCSettleRequest struct {
func (c *HTLCSettleRequest) Decode(r io.Reader, pver uint32) error {
//ChannelID(8)
//StagingID(8)
//HTLCKey(8)
//Expiry(4)
//Amount(4)
//NextHop(20)
@ -29,7 +29,7 @@ func (c *HTLCSettleRequest) Decode(r io.Reader, pver uint32) error {
//Blob(2+blobsize)
err := readElements(r,
&c.ChannelID,
&c.StagingID,
&c.HTLCKey,
&c.RedemptionProofs,
)
if err != nil {
@ -49,7 +49,7 @@ func NewHTLCSettleRequest() *HTLCSettleRequest {
func (c *HTLCSettleRequest) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.StagingID,
c.HTLCKey,
c.RedemptionProofs,
)
if err != nil {
@ -83,7 +83,7 @@ func (c *HTLCSettleRequest) String() string {
return fmt.Sprintf("\n--- Begin HTLCSettleRequest ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("StagingID:\t%d\n", c.StagingID) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("RedemptionHashes:") +
redemptionProofs +
fmt.Sprintf("--- End HTLCSettleRequest ---\n")

@ -12,7 +12,7 @@ var (
htlcSettleRequest = &HTLCSettleRequest{
ChannelID: uint64(12345678),
StagingID: uint64(12345),
HTLCKey: HTLCKey(12345),
RedemptionProofs: redemptionProofs,
}
htlcSettleRequestSerializedString = "0000000000bc614e000000000000303900015b315ebabb0d8c0d94281caa2dfee69a1a00436e"

@ -12,15 +12,15 @@ type HTLCTimeoutAccept struct {
ChannelID uint64
//ID of this request
StagingID uint64
HTLCKey HTLCKey
}
func (c *HTLCTimeoutAccept) Decode(r io.Reader, pver uint32) error {
//ChannelID(8)
//StagingID(8)
//HTLCKey(8)
err := readElements(r,
&c.ChannelID,
&c.StagingID,
&c.HTLCKey,
)
if err != nil {
return err
@ -39,7 +39,7 @@ func NewHTLCTimeoutAccept() *HTLCTimeoutAccept {
func (c *HTLCTimeoutAccept) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.StagingID,
c.HTLCKey,
)
if err != nil {
return err
@ -66,6 +66,6 @@ func (c *HTLCTimeoutAccept) Validate() error {
func (c *HTLCTimeoutAccept) String() string {
return fmt.Sprintf("\n--- Begin HTLCTimeoutAccept ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("StagingID:\t%d\n", c.StagingID) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("--- End HTLCTimeoutAccept ---\n")
}

@ -7,7 +7,7 @@ import (
var (
htlcTimeoutAccept = &HTLCTimeoutAccept{
ChannelID: uint64(12345678),
StagingID: uint64(12345),
HTLCKey: HTLCKey(12345),
}
htlcTimeoutAcceptSerializedString = "0000000000bc614e0000000000003039"
htlcTimeoutAcceptSerializedMessage = "0709110b0000051e000000100000000000bc614e0000000000003039"

@ -12,15 +12,15 @@ type HTLCTimeoutRequest struct {
ChannelID uint64
//ID of this request
StagingID uint64
HTLCKey HTLCKey
}
func (c *HTLCTimeoutRequest) Decode(r io.Reader, pver uint32) error {
//ChannelID(8)
//StagingID(8)
//HTLCKey(8)
err := readElements(r,
&c.ChannelID,
&c.StagingID,
&c.HTLCKey,
)
if err != nil {
return err
@ -39,7 +39,7 @@ func NewHTLCTimeoutRequest() *HTLCTimeoutRequest {
func (c *HTLCTimeoutRequest) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.StagingID,
c.HTLCKey,
)
if err != nil {
return err
@ -66,6 +66,6 @@ func (c *HTLCTimeoutRequest) Validate() error {
func (c *HTLCTimeoutRequest) String() string {
return fmt.Sprintf("\n--- Begin HTLCTimeoutRequest ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("StagingID:\t%d\n", c.StagingID) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("--- End HTLCTimeoutRequest ---\n")
}

@ -7,7 +7,7 @@ import (
var (
htlcTimeoutRequest = &HTLCTimeoutRequest{
ChannelID: uint64(12345678),
StagingID: uint64(12345),
HTLCKey: HTLCKey(12345),
}
htlcTimeoutRequestSerializedString = "0000000000bc614e0000000000003039"
htlcTimeoutRequestSerializedMessage = "0709110b00000514000000100000000000bc614e0000000000003039"

@ -16,6 +16,9 @@ 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
//
@ -76,6 +79,11 @@ func writeElement(w io.Writer, element interface{}) error {
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 {
@ -94,6 +102,24 @@ func writeElement(w io.Writer, element interface{}) error {
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 {
@ -313,6 +339,14 @@ func readElement(r io.Reader, element interface{}) error {
}
*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[:])
@ -341,6 +375,28 @@ func readElement(r io.Reader, element interface{}) error {
}
*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)