public abstract class PaymentChannelServerState
extends java.lang.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.
There are two subclasses that implement this one, for versions 1 and 2 of the protocol -
PaymentChannelV1ServerState
and PaymentChannelV2ServerState
.
This class implements the core state machine for the server side of the protocol. The client side is implemented
by PaymentChannelV1ClientState
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.
|
Modifier and Type | Field and Description |
---|---|
protected byte[] |
bestValueSignature |
protected Coin |
bestValueToMe |
protected TransactionBroadcaster |
broadcaster |
protected Transaction |
contract |
protected long |
minExpireTime |
protected ECKey |
serverKey |
protected StateMachine<PaymentChannelServerState.State> |
stateMachine |
protected StoredServerChannel |
storedServerChannel |
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.
|
abstract com.google.common.util.concurrent.ListenableFuture<Transaction> |
close(org.bouncycastle.crypto.params.KeyParameter userKey)
Closes this channel and broadcasts the highest value payment transaction on the network.
|
protected abstract Script |
createOutputScript() |
Coin |
getBestValueToMe()
Gets the highest payment to ourselves (which we will receive on settle(), not including fees)
|
protected abstract ECKey |
getClientKey() |
abstract TransactionOutput |
getClientOutput() |
Transaction |
getContract()
Gets the multisig contract which was used to initialize this channel
|
Script |
getContractScript() |
long |
getExpiryTime() |
abstract Coin |
getFeePaid()
Gets the fee paid in the final payment transaction (only available if settle() did not throw an exception)
|
abstract int |
getMajorVersion() |
protected abstract Script |
getSignedScript()
Gets the script that signatures should sign against.
|
PaymentChannelServerState.State |
getState() |
protected abstract com.google.common.collect.Multimap<PaymentChannelServerState.State,PaymentChannelServerState.State> |
getStateTransitions() |
protected Coin |
getTotalValue() |
boolean |
incrementPayment(Coin refundSize,
byte[] signatureBytes)
Called when the client provides us with a new signature and wishes to increment total payment by size.
|
protected SendRequest |
makeUnsignedChannelContract(Coin valueToMe) |
com.google.common.util.concurrent.ListenableFuture<PaymentChannelServerState> |
provideContract(Transaction contract)
Called when the client provides the multi-sig contract.
|
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. |
protected void |
updateChannelInWallet() |
protected void |
verifyContract(Transaction contract)
Verifies that the given contract meets a set of extra requirements
|
protected StateMachine<PaymentChannelServerState.State> stateMachine
protected final TransactionBroadcaster broadcaster
protected byte[] bestValueSignature
protected Coin bestValueToMe
protected ECKey serverKey
protected long minExpireTime
protected StoredServerChannel storedServerChannel
protected Transaction contract
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 abstract int getMajorVersion()
public PaymentChannelServerState.State getState()
protected abstract com.google.common.collect.Multimap<PaymentChannelServerState.State,PaymentChannelServerState.State> getStateTransitions()
public com.google.common.util.concurrent.ListenableFuture<PaymentChannelServerState> provideContract(Transaction contract) throws VerificationException
contract
- 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 parametersprotected SendRequest makeUnsignedChannelContract(Coin valueToMe)
public boolean incrementPayment(Coin refundSize, byte[] signatureBytes) throws SignatureDecodeException, 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).SignatureDecodeException
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.
InsufficientMoneyException
- If the payment tx would have cost more in fees to spend than it is worth.public abstract com.google.common.util.concurrent.ListenableFuture<Transaction> close(@Nullable org.bouncycastle.crypto.params.KeyParameter userKey) throws InsufficientMoneyException
Closes this channel and broadcasts the highest value payment transaction on the network.
userKey
- The AES key to use for decryption of the private key. If null then no decryption is required.InsufficientMoneyException
- If the payment tx would have cost more in fees to spend than it is worth.public Coin getBestValueToMe()
public abstract Coin getFeePaid()
public Transaction getContract()
public long getExpiryTime()
protected void updateChannelInWallet()
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.public abstract TransactionOutput getClientOutput()
public Script getContractScript()
protected abstract Script getSignedScript()
protected void verifyContract(Transaction contract)
contract
- protected abstract Script createOutputScript()
protected Coin getTotalValue()
protected abstract ECKey getClientKey()