/* * Copyright 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.devcoin.examples; import com.google.devcoin.core.*; import com.google.devcoin.kits.WalletAppKit; import com.google.devcoin.params.TestNet3Params; import com.google.devcoin.protocols.channels.*; import com.google.devcoin.utils.BriefLogFormatter; import org.slf4j.LoggerFactory; import java.io.File; import java.math.BigInteger; import java.net.SocketAddress; /** * Simple server that listens on port 4242 for incoming payment channels. */ public class ExamplePaymentChannelServer implements PaymentChannelServerListener.HandlerFactory { private static final org.slf4j.Logger log = LoggerFactory.getLogger(ExamplePaymentChannelServer.class); private StoredPaymentChannelServerStates storedStates; private WalletAppKit appKit; public static void main(String[] args) throws Exception { BriefLogFormatter.init(); new ExamplePaymentChannelServer().run(); } public void run() throws Exception { NetworkParameters params = TestNet3Params.get(); // Bring up all the objects we need, create/load a wallet, sync the chain, etc. We override WalletAppKit so we // can customize it by adding the extension objects - we have to do this before the wallet file is loaded so // the plugin that knows how to parse all the additional data is present during the load. appKit = new WalletAppKit(params, new File("."), "payment_channel_example_server") { @Override protected void addWalletExtensions() { // The StoredPaymentChannelClientStates object is responsible for, amongst other things, broadcasting // the refund transaction if its lock time has expired. It also persists channels so we can resume them // after a restart. storedStates = new StoredPaymentChannelServerStates(wallet(), peerGroup()); wallet().addExtension(storedStates); } }; appKit.startAndWait(); System.out.println(appKit.wallet()); // We provide a peer group, a wallet, a timeout in seconds, the amount we require to start a channel and // an implementation of HandlerFactory, which we just implement ourselves. new PaymentChannelServerListener(appKit.peerGroup(), appKit.wallet(), 15, Utils.CENT, this).bindAndStart(4242); } @Override public ServerConnectionEventHandler onNewConnection(final SocketAddress clientAddress) { // Each connection needs a handler which is informed when that payment channel gets adjusted. Here we just log // things. In a real app this object would be connected to some business logic. return new ServerConnectionEventHandler() { @Override public void channelOpen(Sha256Hash channelId) { log.info("Channel open for {}: {}.", clientAddress, channelId); // Try to get the state object from the stored state set in our wallet PaymentChannelServerState state = null; try { state = storedStates.getChannel(channelId).getOrCreateState(appKit.wallet(), appKit.peerGroup()); } catch (VerificationException e) { // This indicates corrupted data, and since the channel was just opened, cannot happen throw new RuntimeException(e); } log.info(" with a maximum value of {}, expiring at UNIX timestamp {}.", // The channel's maximum value is the value of the multisig contract which locks in some // amount of money to the channel state.getMultisigContract().getOutput(0).getValue(), // The channel expires at some offset from when the client's refund transaction becomes // spendable. state.getRefundTransactionUnlockTime() + StoredPaymentChannelServerStates.CHANNEL_EXPIRE_OFFSET); } @Override public void paymentIncrease(BigInteger by, BigInteger to) { log.info("Client {} paid increased payment by {} for a total of " + to.toString(), clientAddress, by); } @Override public void channelClosed(PaymentChannelCloseException.CloseReason reason) { log.info("Client {} closed channel for reason {}", clientAddress, reason); } }; } }