3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-12 18:25:51 +00:00

Store states in protobufs instead of using Java Serialization.

Saves having to use reflection.
This commit is contained in:
Matt Corallo 2013-07-10 18:36:26 +02:00 committed by Mike Hearn
parent 4b4405b7bc
commit c98badcf1c
7 changed files with 2775 additions and 25 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@ import com.google.bitcoin.core.*;
import com.google.bitcoin.utils.Threading;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
import com.google.protobuf.ByteString;
import net.jcip.annotations.GuardedBy;
import static com.google.common.base.Preconditions.checkNotNull;
@ -157,14 +158,22 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
public byte[] serializeWalletExtension() {
lock.lock();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
ClientState.StoredClientPaymentChannels.Builder builder = ClientState.StoredClientPaymentChannels.newBuilder();
for (StoredClientChannel channel : mapChannels.values()) {
oos.writeObject(channel);
// First a few asserts to make sure things won't break
checkState(channel.valueToMe.compareTo(BigInteger.ZERO) >= 0 && channel.valueToMe.compareTo(NetworkParameters.MAX_MONEY) < 0);
checkState(channel.refundFees.compareTo(BigInteger.ZERO) >= 0 && channel.refundFees.compareTo(NetworkParameters.MAX_MONEY) < 0);
checkNotNull(channel.myKey.getPrivKeyBytes());
checkState(channel.refund.getConfidence().getSource() == TransactionConfidence.Source.SELF);
builder.addChannels(ClientState.StoredClientPaymentChannel.newBuilder()
.setId(ByteString.copyFrom(channel.id.getBytes()))
.setContractTransaction(ByteString.copyFrom(channel.contract.bitcoinSerialize()))
.setRefundTransaction(ByteString.copyFrom(channel.refund.bitcoinSerialize()))
.setMyKey(ByteString.copyFrom(channel.myKey.getPrivKeyBytes()))
.setValueToMe(channel.valueToMe.longValue())
.setRefundFees(channel.refundFees.longValue()));
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
return builder.build().toByteArray();
} finally {
lock.unlock();
}
@ -176,10 +185,17 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
try {
checkState(this.containingWallet == null || this.containingWallet == containingWallet);
this.containingWallet = containingWallet;
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(inStream);
while (inStream.available() > 0) {
StoredClientChannel channel = (StoredClientChannel)ois.readObject();
NetworkParameters params = containingWallet.getParams();
ClientState.StoredClientPaymentChannels states = ClientState.StoredClientPaymentChannels.parseFrom(data);
for (ClientState.StoredClientPaymentChannel storedState : states.getChannelsList()) {
Transaction refundTransaction = new Transaction(params, storedState.getRefundTransaction().toByteArray());
refundTransaction.getConfidence().setSource(TransactionConfidence.Source.SELF);
StoredClientChannel channel = new StoredClientChannel(new Sha256Hash(storedState.getId().toByteArray()),
new Transaction(params, storedState.getContractTransaction().toByteArray()),
refundTransaction,
new ECKey(storedState.getMyKey().toByteArray(), null),
BigInteger.valueOf(storedState.getValueToMe()),
BigInteger.valueOf(storedState.getRefundFees()));
putChannel(channel, false);
}
} finally {
@ -193,14 +209,14 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
* channel which was interrupted (eg on connection failure) or keep track of refund transactions which need broadcast
* when they expire.
*/
class StoredClientChannel implements Serializable {
class StoredClientChannel {
Sha256Hash id;
Transaction contract, refund;
ECKey myKey;
BigInteger valueToMe, refundFees;
// In-memory flag to indicate intent to resume this channel (or that the channel is already in use)
transient boolean active = false;
boolean active = false;
StoredClientChannel(Sha256Hash id, Transaction contract, Transaction refund, ECKey myKey, BigInteger valueToMe, BigInteger refundFees) {
this.id = id;

View File

@ -17,16 +17,19 @@
package com.google.bitcoin.protocols.channels;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import com.google.bitcoin.core.*;
import com.google.bitcoin.utils.Threading;
import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.ByteString;
import net.jcip.annotations.GuardedBy;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* Keeps track of a set of {@link StoredServerChannel}s and expires them 2 hours before their refund transactions
@ -142,14 +145,23 @@ public class StoredPaymentChannelServerStates implements WalletExtension {
public byte[] serializeWalletExtension() {
lock.lock();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
ServerState.StoredServerPaymentChannels.Builder builder = ServerState.StoredServerPaymentChannels.newBuilder();
for (StoredServerChannel channel : mapChannels.values()) {
oos.writeObject(channel);
// First a few asserts to make sure things won't break
checkState(channel.bestValueToMe.compareTo(BigInteger.ZERO) >= 0 && channel.bestValueToMe.compareTo(NetworkParameters.MAX_MONEY) < 0);
checkState(channel.refundTransactionUnlockTimeSecs > 0);
checkNotNull(channel.myKey.getPrivKeyBytes());
ServerState.StoredServerPaymentChannel.Builder channelBuilder = ServerState.StoredServerPaymentChannel.newBuilder()
.setBestValueToMe(channel.bestValueToMe.longValue())
.setRefundTransactionUnlockTimeSecs(channel.refundTransactionUnlockTimeSecs)
.setContractTransaction(ByteString.copyFrom(channel.contract.bitcoinSerialize()))
.setClientOutput(ByteString.copyFrom(channel.clientOutput.bitcoinSerialize()))
.setMyKey(ByteString.copyFrom(channel.myKey.getPrivKeyBytes()));
if (channel.bestValueSignature != null)
channelBuilder.setBestValueSignature(ByteString.copyFrom(channel.bestValueSignature));
builder.addChannels(channelBuilder);
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
return builder.build().toByteArray();
} finally {
lock.unlock();
}
@ -160,10 +172,16 @@ public class StoredPaymentChannelServerStates implements WalletExtension {
lock.lock();
try {
checkArgument(containingWallet == wallet);
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(inStream);
while (inStream.available() > 0) {
StoredServerChannel channel = (StoredServerChannel)ois.readObject();
ServerState.StoredServerPaymentChannels states = ServerState.StoredServerPaymentChannels.parseFrom(data);
NetworkParameters params = containingWallet.getParams();
for (ServerState.StoredServerPaymentChannel storedState : states.getChannelsList()) {
StoredServerChannel channel = new StoredServerChannel(null,
new Transaction(params, storedState.getContractTransaction().toByteArray()),
new TransactionOutput(params, null, storedState.getClientOutput().toByteArray(), 0),
storedState.getRefundTransactionUnlockTimeSecs(),
new ECKey(storedState.getMyKey().toByteArray(), null),
BigInteger.valueOf(storedState.getBestValueToMe()),
storedState.hasBestValueSignature() ? storedState.getBestValueSignature().toByteArray() : null);
putChannel(channel);
}
} finally {

View File

@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument;
* channel which was interrupted (eg on connection failure) or close the channel automatically as the channel expire
* time approaches.
*/
public class StoredServerChannel implements Serializable {
public class StoredServerChannel {
BigInteger bestValueToMe;
byte[] bestValueSignature;
long refundTransactionUnlockTimeSecs;
@ -38,8 +38,8 @@ public class StoredServerChannel implements Serializable {
// In-memory pointer to the event handler which handles this channel if the client is connected.
// Used as a flag to prevent duplicate connections and to disconnect the channel if its expire time approaches.
transient PaymentChannelServer connectedHandler = null;
transient PaymentChannelServerState state = null;
PaymentChannelServer connectedHandler = null;
PaymentChannelServerState state = null;
StoredServerChannel(PaymentChannelServerState state, Transaction contract, TransactionOutput clientOutput,
long refundTransactionUnlockTimeSecs, ECKey myKey, BigInteger bestValueToMe, byte[] bestValueSignature) {

View File

@ -0,0 +1,45 @@
/** 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.
*/
/*
* Authors: Mike Hearn, Matt Corallo
*/
/* Notes:
* - Endianness: All byte arrays that represent numbers (such as hashes and private keys) are Big Endian
* - To regenerate after editing, run mvn clean package -DupdateProtobuf
*/
package paymentchannels;
option java_package = "com.google.bitcoin.protocols.channels";
option java_outer_classname = "ClientState";
// A set of StoredPaymentChannel's
message StoredClientPaymentChannels {
repeated StoredClientPaymentChannel channels = 1;
}
// A client-side payment channel in serialized form, which can be reloaded later if the client restarts and wants to
// reopen an existing channel
message StoredClientPaymentChannel {
required bytes id = 1;
required bytes contractTransaction = 2;
required bytes refundTransaction = 3;
required bytes myKey = 4;
required uint64 valueToMe = 5;
required uint64 refundFees = 6;
}

View File

@ -0,0 +1,44 @@
/** 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.
*/
/*
* Authors: Mike Hearn, Matt Corallo
*/
/* Notes:
* - Endianness: All byte arrays that represent numbers (such as hashes and private keys) are Big Endian
* - To regenerate after editing, run mvn clean package -DupdateProtobuf
*/
package paymentchannels;
option java_package = "com.google.bitcoin.protocols.channels";
option java_outer_classname = "ServerState";
// A set of StoredPaymentChannel's
message StoredServerPaymentChannels {
repeated StoredServerPaymentChannel channels = 1;
}
// A server-side payment channel in serialized form, which can be reloaded later if the server restarts
message StoredServerPaymentChannel {
required uint64 bestValueToMe = 1;
optional bytes bestValueSignature = 2;
required uint64 refundTransactionUnlockTimeSecs = 3;
required bytes contractTransaction = 4;
required bytes clientOutput = 5;
required bytes myKey = 6;
}