mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 02:05:53 +00:00
Read Wallet from protobuf stream
This commit is contained in:
parent
6af16c863c
commit
319c52b2a6
@ -48,9 +48,10 @@ message Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See com.google.bitcoin.core.Wallet.java for detailed description of pool semantics
|
// See com.google.bitcoin.core.Wallet.java for detailed description of pool semantics
|
||||||
required Pool pool = 1;
|
required bytes hash = 1;
|
||||||
|
required Pool pool = 2;
|
||||||
|
|
||||||
optional int64 updated_at = 2; // millis since epoch the transaction was last updated
|
optional int64 updated_at = 3; // millis since epoch the transaction was last updated
|
||||||
|
|
||||||
message TransactionInput {
|
message TransactionInput {
|
||||||
required bytes transaction_out_point_hash = 1;
|
required bytes transaction_out_point_hash = 1;
|
||||||
@ -61,7 +62,7 @@ message Wallet {
|
|||||||
required bytes script_bytes = 3; // script of transaction input
|
required bytes script_bytes = 3; // script of transaction input
|
||||||
}
|
}
|
||||||
|
|
||||||
repeated TransactionInput transaction_input = 3;
|
repeated TransactionInput transaction_input = 4;
|
||||||
|
|
||||||
message TransactionOutput {
|
message TransactionOutput {
|
||||||
required int64 value = 1;
|
required int64 value = 1;
|
||||||
@ -70,10 +71,10 @@ message Wallet {
|
|||||||
optional int32 spent_by_transaction_index = 4;
|
optional int32 spent_by_transaction_index = 4;
|
||||||
// if spent, the index of the transaction output of the transaction doing the spend
|
// if spent, the index of the transaction output of the transaction doing the spend
|
||||||
}
|
}
|
||||||
repeated TransactionOutput transaction_output = 4;
|
repeated TransactionOutput transaction_output = 5;
|
||||||
|
|
||||||
|
|
||||||
repeated bytes block_hash = 5;
|
repeated bytes block_hash = 6;
|
||||||
// Sha256Hash of block in block chain in which this transaction appears
|
// Sha256Hash of block in block chain in which this transaction appears
|
||||||
}
|
}
|
||||||
repeated Transaction transaction = 4;
|
repeated Transaction transaction = 4;
|
||||||
|
@ -78,7 +78,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
// Data about how confirmed this tx is. Serialized, may be null.
|
// Data about how confirmed this tx is. Serialized, may be null.
|
||||||
private TransactionConfidence confidence;
|
private TransactionConfidence confidence;
|
||||||
|
|
||||||
Transaction(NetworkParameters params) {
|
public Transaction(NetworkParameters params) {
|
||||||
super(params);
|
super(params);
|
||||||
version = 1;
|
version = 1;
|
||||||
inputs = new ArrayList<TransactionInput>();
|
inputs = new ArrayList<TransactionInput>();
|
||||||
@ -87,6 +87,16 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
length = 10; // 8 for std fields + 1 for each 0 varint
|
length = 10; // 8 for std fields + 1 for each 0 varint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Transaction(NetworkParameters params, Sha256Hash hash) {
|
||||||
|
super(params);
|
||||||
|
version = 1;
|
||||||
|
inputs = new ArrayList<TransactionInput>();
|
||||||
|
outputs = new ArrayList<TransactionOutput>();
|
||||||
|
this.hash = hash;
|
||||||
|
// We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet.
|
||||||
|
length = 10; //8 for std fields + 1 for each 0 varint
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a transaction from the given serialized bytes, eg, from a block or a tx network message.
|
* Creates a transaction from the given serialized bytes, eg, from a block or a tx network message.
|
||||||
*/
|
*/
|
||||||
@ -337,6 +347,10 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
}
|
}
|
||||||
return updatedAt;
|
return updatedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUpdateTime(Date updatedAt) {
|
||||||
|
this.updatedAt = updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These constants are a part of a scriptSig signature on the inputs. They define the details of how a
|
* These constants are a part of a scriptSig signature on the inputs. They define the details of how a
|
||||||
|
@ -61,6 +61,18 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
|||||||
length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
|
length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransactionInput(NetworkParameters params, Transaction parentTransaction,
|
||||||
|
byte[] scriptBytes,
|
||||||
|
TransactionOutPoint outpoint) {
|
||||||
|
super(params);
|
||||||
|
this.scriptBytes = scriptBytes;
|
||||||
|
this.outpoint = outpoint;
|
||||||
|
this.sequence = 0xFFFFFFFFL;
|
||||||
|
this.parentTransaction = parentTransaction;
|
||||||
|
|
||||||
|
length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an UNSIGNED input that links to the given output
|
* Creates an UNSIGNED input that links to the given output
|
||||||
*/
|
*/
|
||||||
|
@ -38,7 +38,7 @@ public class TransactionOutPoint extends ChildMessage implements Serializable {
|
|||||||
// It points to the connected transaction.
|
// It points to the connected transaction.
|
||||||
Transaction fromTx;
|
Transaction fromTx;
|
||||||
|
|
||||||
TransactionOutPoint(NetworkParameters params, long index, Transaction fromTx) {
|
public TransactionOutPoint(NetworkParameters params, long index, Transaction fromTx) {
|
||||||
super(params);
|
super(params);
|
||||||
this.index = index;
|
this.index = index;
|
||||||
if (fromTx != null) {
|
if (fromTx != null) {
|
||||||
|
@ -92,6 +92,15 @@ public class TransactionOutput extends ChildMessage implements Serializable {
|
|||||||
length = 8 + VarInt.sizeOf(scriptBytes.length) + scriptBytes.length;
|
length = 8 + VarInt.sizeOf(scriptBytes.length) + scriptBytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, byte[] scriptBytes) {
|
||||||
|
super(params);
|
||||||
|
this.value = value;
|
||||||
|
this.scriptBytes = scriptBytes;
|
||||||
|
parentTransaction = parent;
|
||||||
|
availableForSpending = true;
|
||||||
|
length = 8 + VarInt.sizeOf(scriptBytes.length) + scriptBytes.length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used only in creation of the genesis blocks and in unit tests.
|
* Used only in creation of the genesis blocks and in unit tests.
|
||||||
*/
|
*/
|
||||||
@ -153,7 +162,7 @@ public class TransactionOutput extends ChildMessage implements Serializable {
|
|||||||
* Sets this objects availableToSpend flag to false and the spentBy pointer to the given input.
|
* Sets this objects availableToSpend flag to false and the spentBy pointer to the given input.
|
||||||
* If the input is null, it means this output was signed over to somebody else rather than one of our own keys.
|
* If the input is null, it means this output was signed over to somebody else rather than one of our own keys.
|
||||||
*/
|
*/
|
||||||
void markAsSpent(TransactionInput input) {
|
public void markAsSpent(TransactionInput input) {
|
||||||
assert availableForSpending;
|
assert availableForSpending;
|
||||||
availableForSpending = false;
|
availableForSpending = false;
|
||||||
spentBy = input;
|
spentBy = input;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.core;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Transaction in a Wallet - includes the pool ID
|
* A Transaction in a Wallet - includes the pool ID
|
||||||
*
|
*
|
||||||
@ -38,6 +39,17 @@ public class WalletTransaction {
|
|||||||
public int getValue() {
|
public int getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Pool valueOf(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0: return UNSPENT;
|
||||||
|
case 1: return SPENT;
|
||||||
|
case 2: return PENDING;
|
||||||
|
case 3: return INACTIVE;
|
||||||
|
case 4: return DEAD;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private Transaction transaction;
|
private Transaction transaction;
|
||||||
private Pool pool;
|
private Pool pool;
|
||||||
|
@ -16,10 +16,15 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.store;
|
package com.google.bitcoin.store;
|
||||||
|
|
||||||
|
import com.google.bitcoin.core.AddressFormatException;
|
||||||
|
import com.google.bitcoin.core.DumpedPrivateKey;
|
||||||
import com.google.bitcoin.core.ECKey;
|
import com.google.bitcoin.core.ECKey;
|
||||||
|
import com.google.bitcoin.core.NetworkParameters;
|
||||||
|
import com.google.bitcoin.core.Sha256Hash;
|
||||||
import com.google.bitcoin.core.StoredBlock;
|
import com.google.bitcoin.core.StoredBlock;
|
||||||
import com.google.bitcoin.core.Transaction;
|
import com.google.bitcoin.core.Transaction;
|
||||||
import com.google.bitcoin.core.TransactionInput;
|
import com.google.bitcoin.core.TransactionInput;
|
||||||
|
import com.google.bitcoin.core.TransactionOutPoint;
|
||||||
import com.google.bitcoin.core.TransactionOutput;
|
import com.google.bitcoin.core.TransactionOutput;
|
||||||
import com.google.bitcoin.core.Wallet;
|
import com.google.bitcoin.core.Wallet;
|
||||||
import com.google.bitcoin.core.WalletTransaction;
|
import com.google.bitcoin.core.WalletTransaction;
|
||||||
@ -28,7 +33,12 @@ import com.google.protobuf.ByteString;
|
|||||||
import org.bitcoinj.wallet.Protos;
|
import org.bitcoinj.wallet.Protos;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize and de-serialize a wallet to a protobuf stream.
|
* Serialize and de-serialize a wallet to a protobuf stream.
|
||||||
@ -36,7 +46,14 @@ import java.io.OutputStream;
|
|||||||
* @author Miron Cuperman
|
* @author Miron Cuperman
|
||||||
*/
|
*/
|
||||||
public class WalletProtobufSerializer {
|
public class WalletProtobufSerializer {
|
||||||
void writeWallet(Wallet wallet, OutputStream output) throws IOException {
|
// Used for de-serialization
|
||||||
|
private Map<ByteString, Transaction> txMap;
|
||||||
|
|
||||||
|
public WalletProtobufSerializer() {
|
||||||
|
txMap = new HashMap<ByteString, Transaction>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void writeWallet(Wallet wallet, OutputStream output) throws IOException {
|
||||||
Protos.Wallet.Builder walletBuilder = Protos.Wallet.newBuilder();
|
Protos.Wallet.Builder walletBuilder = Protos.Wallet.newBuilder();
|
||||||
walletBuilder
|
walletBuilder
|
||||||
.setNetworkIdentifier(wallet.getNetworkParameters().getId())
|
.setNetworkIdentifier(wallet.getNetworkParameters().getId())
|
||||||
@ -59,14 +76,16 @@ public class WalletProtobufSerializer {
|
|||||||
|
|
||||||
walletBuilder.build().writeTo(output);
|
walletBuilder.build().writeTo(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Protos.Wallet.Transaction makeTxProto(WalletTransaction wtx) {
|
private static Protos.Wallet.Transaction makeTxProto(WalletTransaction wtx) {
|
||||||
Transaction tx = wtx.getTransaction();
|
Transaction tx = wtx.getTransaction();
|
||||||
Protos.Wallet.Transaction.Builder txBuilder = Protos.Wallet.Transaction.newBuilder();
|
Protos.Wallet.Transaction.Builder txBuilder = Protos.Wallet.Transaction.newBuilder();
|
||||||
|
|
||||||
txBuilder
|
txBuilder
|
||||||
.setUpdatedAt(tx.getUpdateTime().getTime())
|
.setUpdatedAt(tx.getUpdateTime().getTime())
|
||||||
.setPool(Protos.Wallet.Transaction.Pool.valueOf(wtx.getPool().getValue()));
|
.setPool(Protos.Wallet.Transaction.Pool.valueOf(wtx.getPool().getValue()))
|
||||||
|
.setHash(ByteString.copyFrom(tx.getHash().getBytes()))
|
||||||
|
;
|
||||||
|
|
||||||
// Handle inputs
|
// Handle inputs
|
||||||
for (TransactionInput input : tx.getInputs()) {
|
for (TransactionInput input : tx.getInputs()) {
|
||||||
@ -81,15 +100,17 @@ public class WalletProtobufSerializer {
|
|||||||
|
|
||||||
// Handle outputs
|
// Handle outputs
|
||||||
for (TransactionOutput output : tx.getOutputs()) {
|
for (TransactionOutput output : tx.getOutputs()) {
|
||||||
final TransactionInput spentBy = output.getSpentBy();
|
Protos.Wallet.Transaction.TransactionOutput.Builder outputBuilder =
|
||||||
txBuilder.addTransactionOutput(
|
|
||||||
Protos.Wallet.Transaction.TransactionOutput.newBuilder()
|
Protos.Wallet.Transaction.TransactionOutput.newBuilder()
|
||||||
.setScriptBytes(ByteString.copyFrom(output.getScriptBytes()))
|
.setScriptBytes(ByteString.copyFrom(output.getScriptBytes()))
|
||||||
.setSpentByTransactionHash(ByteString.copyFrom(
|
.setValue(output.getValue().longValue());
|
||||||
spentBy.getHash().getBytes()))
|
final TransactionInput spentBy = output.getSpentBy();
|
||||||
.setSpentByTransactionIndex((int)spentBy.getOutpoint().getIndex()) // FIXME
|
if (spentBy != null) {
|
||||||
.setValue(output.getValue().longValue())
|
outputBuilder
|
||||||
);
|
.setSpentByTransactionHash(ByteString.copyFrom(spentBy.getHash().getBytes()))
|
||||||
|
.setSpentByTransactionIndex((int)spentBy.getOutpoint().getIndex()); // FIXME
|
||||||
|
}
|
||||||
|
txBuilder.addTransactionOutput(outputBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle which blocks tx was seen in
|
// Handle which blocks tx was seen in
|
||||||
@ -99,4 +120,100 @@ public class WalletProtobufSerializer {
|
|||||||
|
|
||||||
return txBuilder.build();
|
return txBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public Wallet readWallet(InputStream input, NetworkParameters params, BlockStore store)
|
||||||
|
throws IOException, AddressFormatException, BlockStoreException {
|
||||||
|
WalletProtobufSerializer serializer = new WalletProtobufSerializer();
|
||||||
|
Protos.Wallet walletProto = Protos.Wallet.parseFrom(input);
|
||||||
|
if (!params.getId().equals(walletProto.getNetworkIdentifier()))
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Trying to read a wallet with a different network id " +
|
||||||
|
walletProto.getNetworkIdentifier());
|
||||||
|
|
||||||
|
Wallet wallet = new Wallet(params);
|
||||||
|
|
||||||
|
// Read all keys
|
||||||
|
for (Protos.Wallet.Key keyProto : walletProto.getKeyList()) {
|
||||||
|
wallet.addKey(new DumpedPrivateKey(params, keyProto.getPrivateKey()).getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all transactions and create outputs
|
||||||
|
for (Protos.Wallet.Transaction txProto : walletProto.getTransactionList()) {
|
||||||
|
serializer.readTransaction(txProto, params, store);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create transactions inputs pointing to transactions
|
||||||
|
for (Protos.Wallet.Transaction txProto : walletProto.getTransactionList()) {
|
||||||
|
serializer.connectTransactionInputs(txProto, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update transaction outputs to point to inputs that spend them
|
||||||
|
for (Protos.Wallet.Transaction txProto : walletProto.getTransactionList()) {
|
||||||
|
WalletTransaction wtx = serializer.connectTransactionOutputs(txProto, params);
|
||||||
|
wallet.addWalletTransaction(wtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void readTransaction(Protos.Wallet.Transaction txProto,
|
||||||
|
NetworkParameters params, BlockStore store) throws BlockStoreException {
|
||||||
|
Transaction tx = new Transaction(params, new Sha256Hash(txProto.getHash().toByteArray()));
|
||||||
|
if (txProto.hasUpdatedAt())
|
||||||
|
tx.setUpdateTime(new Date(txProto.getUpdatedAt()));
|
||||||
|
|
||||||
|
for (Protos.Wallet.Transaction.TransactionOutput outputProto :
|
||||||
|
txProto.getTransactionOutputList()) {
|
||||||
|
TransactionOutput output = new TransactionOutput(params, tx,
|
||||||
|
BigInteger.valueOf(outputProto.getValue()),
|
||||||
|
outputProto.getScriptBytes().toByteArray());
|
||||||
|
tx.addOutput(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txMap.containsKey(tx.getHash())) {
|
||||||
|
throw new RuntimeException("Transaction " + tx.getHashAsString() + " appears twice");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ByteString blockHash : txProto.getBlockHashList()) {
|
||||||
|
tx.addBlockAppearance(store.get(new Sha256Hash(blockHash.toByteArray())), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
txMap.put(txProto.getHash(), tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectTransactionInputs(Protos.Wallet.Transaction txProto, NetworkParameters params) {
|
||||||
|
Transaction tx = txMap.get(txProto.getHash());
|
||||||
|
for (Protos.Wallet.Transaction.TransactionInput transactionInput : txProto.getTransactionInputList()) {
|
||||||
|
TransactionInput input =
|
||||||
|
new TransactionInput(params, tx,
|
||||||
|
transactionInput.getScriptBytes().toByteArray(),
|
||||||
|
new TransactionOutPoint(params,
|
||||||
|
transactionInput.getTransactionOutPointIndex(),
|
||||||
|
txMap.get(transactionInput.getTransactionOutPointHash())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
tx.addInput(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WalletTransaction connectTransactionOutputs(
|
||||||
|
org.bitcoinj.wallet.Protos.Wallet.Transaction txProto, NetworkParameters params) {
|
||||||
|
Transaction tx = txMap.get(txProto.getHash());
|
||||||
|
WalletTransaction.Pool pool =
|
||||||
|
WalletTransaction.Pool.valueOf(txProto.getPool().getNumber());
|
||||||
|
for (int i = 0 ; i < tx.getOutputs().size() ; i++) {
|
||||||
|
TransactionOutput output = tx.getOutputs().get(i);
|
||||||
|
final Protos.Wallet.Transaction.TransactionOutput transactionOutput =
|
||||||
|
txProto.getTransactionOutput(i);
|
||||||
|
if (transactionOutput.hasSpentByTransactionHash()) {
|
||||||
|
Transaction spendingTx =
|
||||||
|
txMap.get(transactionOutput.getSpentByTransactionHash());
|
||||||
|
final int spendingIndex = transactionOutput.getSpentByTransactionIndex();
|
||||||
|
output.markAsSpent(spendingTx.getInputs().get(spendingIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WalletTransaction(pool, tx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user