public class PaymentChannelServerState 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 received to be incremented, not decremented.
This class implements the core state machine for the server side of the protocol. The client side is implemented
by PaymentChannelClientState
and PaymentChannelServerListener
implements the server-side network
protocol listening for TCP/IP connections and moving 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.
To protect clients from malicious servers, a channel has an expiry parameter. When this expiration is reached, the client will broadcast the created refund transaction and take back all the money in this channel. Because this is specified in terms of block timestamps, it is fairly fuzzy and it is possible to spend the refund transaction up to a few hours before the actual timestamp. Thus, it is very important that the channel be closed with plenty of time left to get the highest value payment transaction confirmed before the expire time (minimum 3-4 hours is suggested if the payment transaction has enough fee to be confirmed in the next block or two).
To begin, we must provide the client with a pubkey which we wish to use for the multi-sig contract which locks in the channel. The client will then provide us with an incomplete refund transaction and the pubkey which they used in the multi-sig contract. We use this pubkey to recreate the multi-sig output and then sign that to the refund transaction. We provide that signature to the client and they then have the ability to spend the refund transaction at the specified expire time. The client then provides us with the full, signed multi-sig contract which we verify and broadcast, locking in their funds until we spend a payment transaction or the expire time is reached. The client can then begin paying by providing us with signatures for the multi-sig contract which pay some amount back to the client, and the rest is ours to do with as we wish.
Modifier and Type | Class and Description |
---|---|
static class |
PaymentChannelServerState.State
The different logical states the channel can be in.
|
Constructor and Description |
---|
PaymentChannelServerState(TransactionBroadcaster broadcaster,
Wallet wallet,
ECKey serverKey,
long minExpireTime)
Creates a new state object to track the server side of a payment channel.
|
Modifier and Type | Method and Description |
---|---|
com.google.common.util.concurrent.ListenableFuture<Transaction> |
close()
Closes this channel and broadcasts the highest value payment transaction on the network.
|
BigInteger |
getBestValueToMe()
Gets the highest payment to ourselves (which we will receive on settle(), not including fees)
|
BigInteger |
getFeePaid()
Gets the fee paid in the final payment transaction (only available if settle() did not throw an exception)
|
Transaction |
getMultisigContract()
Gets the multisig contract which was used to initialize this channel
|
long |
getRefundTransactionUnlockTime()
Gets the client's refund transaction which they can spend to get the entire channel value back if it reaches its
lock time.
|
PaymentChannelServerState.State |
getState()
This object implements a state machine, and this accessor returns which state it's currently in.
|
boolean |
incrementPayment(BigInteger refundSize,
byte[] signatureBytes)
Called when the client provides us with a new signature and wishes to increment total payment by size.
|
com.google.common.util.concurrent.ListenableFuture<PaymentChannelServerState> |
provideMultiSigContract(Transaction multisigContract)
Called when the client provides the multi-sig contract.
|
byte[] |
provideRefundTransaction(Transaction refundTx,
byte[] clientMultiSigPubKey)
Called when the client provides the refund transaction.
|
void |
storeChannelInWallet(PaymentChannelServer connectedHandler)
Stores this channel's state in the wallet as a part of a
StoredPaymentChannelServerStates wallet
extension and keeps it up-to-date each time payment is incremented. |
public PaymentChannelServerState(TransactionBroadcaster broadcaster, Wallet wallet, ECKey serverKey, long minExpireTime)
broadcaster
- The peer group which we will broadcast transactions to, this should have multiple peerswallet
- The wallet which will be used to complete transactionsserverKey
- The private key which we use for our part of the multi-sig contract
(this MUST be fresh and CANNOT be used elsewhere)minExpireTime
- The earliest time at which the client can claim the refund transaction (UNIX timestamp of block)public PaymentChannelServerState.State getState()
public byte[] provideRefundTransaction(Transaction refundTx, byte[] clientMultiSigPubKey) throws VerificationException
refundTx
- The refund transaction, this object will be mutated when payment is incremented.clientMultiSigPubKey
- The client's pubkey which is required for the multisig outputVerificationException
- If the transaction isnt valid or did not meet the requirements of a refund transaction.public com.google.common.util.concurrent.ListenableFuture<PaymentChannelServerState> provideMultiSigContract(Transaction multisigContract) throws VerificationException
multisigContract
- The provided multisig contract. Do not mutate this object after this call.VerificationException
- If the provided multisig contract is not well-formed or does not meet previously-specified parameterspublic boolean incrementPayment(BigInteger refundSize, byte[] signatureBytes) throws VerificationException, ValueOutOfRangeException, InsufficientMoneyException
refundSize
- How many satoshis of the original contract are refunded to the client (the rest are ours)signatureBytes
- The new signature spending the multi-sig contract to a new payment transactionVerificationException
- If the signature does not verify or size is out of range (incl being rejected by the network as dust).ValueOutOfRangeException
InsufficientMoneyException
public com.google.common.util.concurrent.ListenableFuture<Transaction> close() throws InsufficientMoneyException
Closes this channel and broadcasts the highest value payment transaction on the network.
This will set the state to PaymentChannelServerState.State.CLOSED
if the transaction is successfully broadcast on the network.
If we fail to broadcast for some reason, the state is set to PaymentChannelServerState.State.ERROR
.
If the current state is before PaymentChannelServerState.State.READY
(ie we have not finished initializing the channel), we
simply set the state to PaymentChannelServerState.State.CLOSED
and let the client handle getting its refund transaction confirmed.
InsufficientMoneyException
- If the payment tx would have cost more in fees to spend than it is worth.public BigInteger getBestValueToMe()
public BigInteger getFeePaid()
public Transaction getMultisigContract()
public long getRefundTransactionUnlockTime()
public void storeChannelInWallet(@Nullable PaymentChannelServer connectedHandler)
StoredPaymentChannelServerStates
wallet
extension and keeps it up-to-date each time payment is incremented. This will be automatically removed when
a call to close()
completes successfully. A channel may only be stored after it
has fully opened (ie state == State.READY).connectedHandler
- Optional PaymentChannelServer
object that manages this object. This will
set the appropriate pointer in the newly created StoredServerChannel
before it is
committed to wallet. If set, closing the state object will propagate the close to the
handler which can then do a TCP disconnect.Copyright © 2014. All rights reserved.