com.google.bitcoin.protocols.channels
Class PaymentChannelClientState

java.lang.Object
  extended by com.google.bitcoin.protocols.channels.PaymentChannelClientState

public class PaymentChannelClientState
extends Object

A payment channel is a method of sending money to someone such that the amount of money you send can be adjusted after the fact, in an efficient manner that does not require broadcasting to the network. This can be used to implement micropayments or other payment schemes in which immediate settlement is not required, but zero trust negotiation is. Note that this class only allows the amount of money sent to be incremented, not decremented.

This class implements the core state machine for the client side of the protocol. The server side is implemented by PaymentChannelServerState and PaymentChannelClientConnection implements a network protocol suitable for TCP/IP connections which moves this class through each state. We say that the party who is sending funds is the client or initiating party. The party that is receiving the funds is the server or receiving party. Although the underlying Bitcoin protocol is capable of more complex relationships than that, this class implements only the simplest case.

A channel has an expiry parameter. If the server halts after the multi-signature contract which locks up the given value is broadcast you could get stuck in a state where you've lost all the money put into the contract. To avoid this, a refund transaction is agreed ahead of time but it may only be used/broadcast after the expiry time. This is specified in terms of block timestamps and once the timestamp of the chain chain approaches the given time (within a few hours), the channel must be closed or else the client will broadcast the refund transaction and take back all the money once the expiry time is reached.

To begin, the client calls initiate(), which moves the channel into state INITIATED and creates the initial multi-sig contract and refund transaction. If the wallet has insufficient funds an exception will be thrown at this point. Once this is done, call getIncompleteRefundTransaction() and pass the resultant transaction through to the server. Once you have retrieved the signature, use provideRefundSignature(byte[]). You must then call storeChannelInWallet(Sha256Hash) to store the refund transaction in the wallet, protecting you against a malicious server attempting to destroy all your coins. At this point, you can provide the server with the multi-sig contract (via getMultisigContract()) safely.


Nested Class Summary
static class PaymentChannelClientState.IncrementedPayment
          Container for a signature and an amount that was sent.
static class PaymentChannelClientState.State
          The different logical states the channel can be in.
 
Constructor Summary
PaymentChannelClientState(Wallet wallet, ECKey myKey, ECKey serverMultisigKey, BigInteger value, long expiryTimeInSeconds)
          Creates a state object for a payment channel client.
 
Method Summary
 void checkNotExpired()
          Checks if the channel is expired, setting state to PaymentChannelClientState.State.EXPIRED, removing this channel from wallet storage and throwing an IllegalStateException if it is.
 void disconnectFromChannel()
          Sets this channel's state in StoredPaymentChannelClientStates to unopened so this channel can be reopened later.
protected  void editContractSendRequest(Wallet.SendRequest req)
          You can override this method in order to control the construction of the initial contract that creates the channel.
 Transaction getCompletedRefundTransaction()
          Once the servers signature over the refund transaction has been received and provided using provideRefundSignature(byte[]) then this method can be called to receive the now valid and broadcastable refund transaction.
 Transaction getIncompleteRefundTransaction()
          Returns a partially signed (invalid) refund transaction that should be passed to the server.
 Transaction getMultisigContract()
          Returns the transaction that locks the money to the agreement of both parties.
 BigInteger getRefundTxFees()
          Returns the fees that will be paid if the refund transaction has to be claimed because the server failed to settle the channel properly.
 PaymentChannelClientState.State getState()
          This object implements a state machine, and this accessor returns which state it's currently in.
 BigInteger getTotalValue()
          Gets the total value of this channel (ie the maximum payment possible)
 BigInteger getValueRefunded()
          Gets the current amount refunded to us from the multisig contract (ie totalValue-valueSentToServer)
 BigInteger getValueSpent()
          Returns the amount of money sent on this channel so far.
 PaymentChannelClientState.IncrementedPayment incrementPaymentBy(BigInteger size)
          Updates the outputs on the payment contract transaction and re-signs it.
 void initiate()
          Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate time using getIncompleteRefundTransaction() and getMultisigContract().
 boolean isSettlementTransaction(Transaction tx)
          Returns true if the tx is a valid settlement transaction.
 void provideRefundSignature(byte[] theirSignature)
          When the servers signature for the refund transaction is received, call this to verify it and sign the complete refund ourselves.
 void storeChannelInWallet(Sha256Hash id)
          Stores this channel's state in the wallet as a part of a StoredPaymentChannelClientStates wallet extension and keeps it up-to-date each time payment is incremented.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

PaymentChannelClientState

public PaymentChannelClientState(Wallet wallet,
                                 ECKey myKey,
                                 ECKey serverMultisigKey,
                                 BigInteger value,
                                 long expiryTimeInSeconds)
                          throws VerificationException
Creates a state object for a payment channel client. It is expected that you be ready to initiate() after construction (to avoid creating objects for channels which are not going to finish opening) and thus some parameters provided here are only used in initiate() to create the Multisig contract and refund transaction.

Parameters:
wallet - a wallet that contains at least the specified amount of value.
myKey - a freshly generated private key for this channel.
serverMultisigKey - a public key retrieved from the server used for the initial multisig contract
value - how many satoshis to put into this contract. If the channel reaches this limit, it must be closed. It is suggested you use at least Utils.CENT to avoid paying fees if you need to spend the refund transaction
expiryTimeInSeconds - At what point (UNIX timestamp +/- a few hours) the channel will expire
Throws:
VerificationException - If either myKey's pubkey or serverMultisigKey's pubkey are non-canonical (ie invalid)
Method Detail

isSettlementTransaction

public boolean isSettlementTransaction(Transaction tx)
Returns true if the tx is a valid settlement transaction.


getState

public PaymentChannelClientState.State getState()
This object implements a state machine, and this accessor returns which state it's currently in.


initiate

public void initiate()
              throws ValueOutOfRangeException,
                     InsufficientMoneyException
Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate time using getIncompleteRefundTransaction() and getMultisigContract(). The way the contract is crafted can be adjusted by overriding editContractSendRequest(com.google.bitcoin.core.Wallet.SendRequest). By default unconfirmed coins are allowed to be used, as for micropayments the risk should be relatively low.

Throws:
ValueOutOfRangeException - if the value being used is too small to be accepted by the network
InsufficientMoneyException - if the wallet doesn't contain enough balance to initiate

editContractSendRequest

protected void editContractSendRequest(Wallet.SendRequest req)
You can override this method in order to control the construction of the initial contract that creates the channel. For example if you want it to only use specific coins, you can adjust the coin selector here. The default implementation does nothing.


getMultisigContract

public Transaction getMultisigContract()
Returns the transaction that locks the money to the agreement of both parties. Do not mutate the result. Once this step is done, you can use incrementPaymentBy(java.math.BigInteger) to start paying the server.


getIncompleteRefundTransaction

public Transaction getIncompleteRefundTransaction()
Returns a partially signed (invalid) refund transaction that should be passed to the server. Once the server has checked it out and provided its own signature, call provideRefundSignature(byte[]) with the result.


provideRefundSignature

public void provideRefundSignature(byte[] theirSignature)
                            throws VerificationException

When the servers signature for the refund transaction is received, call this to verify it and sign the complete refund ourselves.

If this does not throw an exception, we are secure against the loss of funds and can safely provide the server with the multi-sig contract to lock in the agreement. In this case, both the multisig contract and the refund transaction are automatically committed to wallet so that it can handle broadcasting the refund transaction at the appropriate time if necessary.

Throws:
VerificationException

checkNotExpired

public void checkNotExpired()
Checks if the channel is expired, setting state to PaymentChannelClientState.State.EXPIRED, removing this channel from wallet storage and throwing an IllegalStateException if it is.


incrementPaymentBy

public PaymentChannelClientState.IncrementedPayment incrementPaymentBy(BigInteger size)
                                                                throws ValueOutOfRangeException

Updates the outputs on the payment contract transaction and re-signs it. The state must be READY in order to call this method. The signature that is returned should be sent to the server so it has the ability to broadcast the best seen payment when the channel closes or times out.

The returned signature is over the payment transaction, which we never have a valid copy of and thus there is no accessor for it on this object.

To spend the whole channel increment by getTotalValue() - getValueRefunded()

Parameters:
size - How many satoshis to increment the payment by (note: not the new total).
Throws:
ValueOutOfRangeException - If size is negative or the channel does not have sufficient money in it to complete this payment.

disconnectFromChannel

public void disconnectFromChannel()
Sets this channel's state in StoredPaymentChannelClientStates to unopened so this channel can be reopened later.

See Also:
storeChannelInWallet(Sha256Hash)

storeChannelInWallet

public void storeChannelInWallet(Sha256Hash id)

Stores this channel's state in the wallet as a part of a StoredPaymentChannelClientStates wallet extension and keeps it up-to-date each time payment is incremented. This allows the StoredPaymentChannelClientStates object to keep track of timeouts and broadcast the refund transaction when the channel expires.

A channel may only be stored after it has fully opened (ie state == State.READY). The wallet provided in the constructor must already have a StoredPaymentChannelClientStates object in its extensions set.

Parameters:
id - A hash providing this channel with an id which uniquely identifies this server. It does not have to be unique.

getRefundTxFees

public BigInteger getRefundTxFees()
Returns the fees that will be paid if the refund transaction has to be claimed because the server failed to settle the channel properly. May only be called after initiate()


getCompletedRefundTransaction

public Transaction getCompletedRefundTransaction()
Once the servers signature over the refund transaction has been received and provided using provideRefundSignature(byte[]) then this method can be called to receive the now valid and broadcastable refund transaction.


getTotalValue

public BigInteger getTotalValue()
Gets the total value of this channel (ie the maximum payment possible)


getValueRefunded

public BigInteger getValueRefunded()
Gets the current amount refunded to us from the multisig contract (ie totalValue-valueSentToServer)


getValueSpent

public BigInteger getValueSpent()
Returns the amount of money sent on this channel so far.



Copyright © 2014. All rights reserved.