Save value of inputs when completing transactions and persist it to the wallet protobuf. Determine the fee of a transaction in case we have all the values.

This commit is contained in:
Andreas Schildbach
2014-05-29 13:11:33 +02:00
committed by Mike Hearn
parent 03e8934576
commit 780be05260
6 changed files with 243 additions and 68 deletions

View File

@@ -1,4 +1,5 @@
/** Copyright 2013 Google Inc.
* Copyright 2014 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +15,7 @@
*/
/*
* Authors: Jim Burton, Miron Cuperman
* Authors: Jim Burton, Miron Cuperman, Andreas Schildbach
*/
/* Notes:
@@ -94,6 +95,8 @@ message TransactionInput {
required bytes script_bytes = 3;
// Sequence number. Currently unused, but intended for contracts in future.
optional uint32 sequence = 4;
// Value of connected output, if known
optional int64 value = 5;
}
message TransactionOutput {

View File

@@ -1,5 +1,6 @@
/**
* Copyright 2011 Google Inc.
* Copyright 2014 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -375,6 +376,25 @@ public class Transaction extends ChildMessage implements Serializable {
return getValueSentToMe(wallet).subtract(getValueSentFromMe(wallet));
}
/**
* The transaction fee is the difference of the value of all inputs and the value of all outputs. Currently, the fee
* can only be determined for transactions created by us.
*
* @return fee, or null if it cannot be determined
*/
public BigInteger getFee() {
BigInteger fee = BigInteger.ZERO;
for (TransactionInput input : inputs) {
if (input.getValue() == null)
return null;
fee = fee.add(input.getValue());
}
for (TransactionOutput output : outputs) {
fee = fee.subtract(output.getValue());
}
return fee;
}
boolean disconnectInputs() {
boolean disconnected = false;
maybeParse();
@@ -635,6 +655,8 @@ public class Transaction extends ChildMessage implements Serializable {
try {
Script scriptSig = in.getScriptSig();
s.append(scriptSig);
if (in.getValue() != null)
s.append(" ").append(bitcoinValueToFriendlyString(in.getValue())).append(" BTC");
s.append("\n ");
s.append("outpoint:");
final TransactionOutPoint outpoint = in.getOutpoint();

View File

@@ -1,5 +1,6 @@
/**
* Copyright 2011 Google Inc.
* Copyright 2014 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@ import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Map;
@@ -54,28 +56,32 @@ public class TransactionInput extends ChildMessage implements Serializable {
// The Script object obtained from parsing scriptBytes. Only filled in on demand and if the transaction is not
// coinbase.
transient private WeakReference<Script> scriptSig;
/** Value of the output connected to the input, if known. This field does not participate in equals()/hashCode(). */
@Nullable
private final BigInteger value;
// A pointer to the transaction that owns this input.
private Transaction parentTransaction;
private final Transaction parentTransaction;
/**
* Creates an input that connects to nothing - used only in creation of coinbase transactions.
*/
public TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] scriptBytes) {
super(params);
this.scriptBytes = scriptBytes;
this.outpoint = new TransactionOutPoint(params, NO_SEQUENCE, (Transaction)null);
this.sequence = NO_SEQUENCE;
this.parentTransaction = parentTransaction;
length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
this(params, parentTransaction, scriptBytes, new TransactionOutPoint(params, NO_SEQUENCE, (Transaction) null));
}
public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes,
TransactionOutPoint outpoint) {
this(params, parentTransaction, scriptBytes, outpoint, null);
}
public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes,
TransactionOutPoint outpoint, @Nullable BigInteger value) {
super(params);
this.scriptBytes = scriptBytes;
this.outpoint = outpoint;
this.sequence = NO_SEQUENCE;
this.parentTransaction = parentTransaction;
this.value = value;
length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
}
@@ -89,6 +95,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
scriptBytes = EMPTY_ARRAY;
sequence = NO_SEQUENCE;
this.parentTransaction = parentTransaction;
this.value = output.getValue();
length = 41;
}
@@ -99,6 +106,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
byte[] payload, int offset) throws ProtocolException {
super(params, payload, offset);
this.parentTransaction = parentTransaction;
this.value = null;
}
/**
@@ -118,6 +126,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
throws ProtocolException {
super(params, msg, offset, parentTransaction, parseLazy, parseRetain, UNKNOWN_LENGTH);
this.parentTransaction = parentTransaction;
this.value = null;
}
protected void parseLite() throws ProtocolException {
@@ -254,6 +263,14 @@ public class TransactionInput extends ChildMessage implements Serializable {
return parentTransaction;
}
/**
* @return Value of the output connected to this input, if known. Null if unknown.
*/
@Nullable
public BigInteger getValue() {
return value;
}
/**
* Returns a human readable debug string.
*/

View File

@@ -67,6 +67,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* by anyone else.<p>
*
* @author Miron Cuperman
* @author Andreas Schildbach
*/
public class WalletProtobufSerializer {
private static final Logger log = LoggerFactory.getLogger(WalletProtobufSerializer.class);
@@ -248,9 +249,10 @@ public class WalletProtobufSerializer {
.setScriptBytes(ByteString.copyFrom(input.getScriptBytes()))
.setTransactionOutPointHash(hashToByteString(input.getOutpoint().getHash()))
.setTransactionOutPointIndex((int) input.getOutpoint().getIndex());
if (input.hasSequence()) {
inputBuilder.setSequence((int)input.getSequenceNumber());
}
if (input.hasSequence())
inputBuilder.setSequence((int) input.getSequenceNumber());
if (input.getValue() != null)
inputBuilder.setValue(input.getValue().longValue());
txBuilder.addTransactionInput(inputBuilder);
}
@@ -541,15 +543,16 @@ public class WalletProtobufSerializer {
tx.addOutput(output);
}
for (Protos.TransactionInput transactionInput : txProto.getTransactionInputList()) {
byte[] scriptBytes = transactionInput.getScriptBytes().toByteArray();
for (Protos.TransactionInput inputProto : txProto.getTransactionInputList()) {
byte[] scriptBytes = inputProto.getScriptBytes().toByteArray();
TransactionOutPoint outpoint = new TransactionOutPoint(params,
transactionInput.getTransactionOutPointIndex() & 0xFFFFFFFFL,
byteStringToHash(transactionInput.getTransactionOutPointHash())
inputProto.getTransactionOutPointIndex() & 0xFFFFFFFFL,
byteStringToHash(inputProto.getTransactionOutPointHash())
);
TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint);
if (transactionInput.hasSequence()) {
input.setSequenceNumber(transactionInput.getSequence());
BigInteger value = inputProto.hasValue() ? BigInteger.valueOf(inputProto.getValue()) : null;
TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint, value);
if (inputProto.hasSequence()) {
input.setSequenceNumber(inputProto.getSequence());
}
tx.addInput(input);
}

View File

@@ -3160,6 +3160,24 @@ public final class Protos {
* </pre>
*/
int getSequence();
// optional int64 value = 5;
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
boolean hasValue();
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
long getValue();
}
/**
* Protobuf type {@code wallet.TransactionInput}
@@ -3232,6 +3250,11 @@ public final class Protos {
sequence_ = input.readUInt32();
break;
}
case 40: {
bitField0_ |= 0x00000010;
value_ = input.readInt64();
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -3368,11 +3391,36 @@ public final class Protos {
return sequence_;
}
// optional int64 value = 5;
public static final int VALUE_FIELD_NUMBER = 5;
private long value_;
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
public boolean hasValue() {
return ((bitField0_ & 0x00000010) == 0x00000010);
}
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
public long getValue() {
return value_;
}
private void initFields() {
transactionOutPointHash_ = com.google.protobuf.ByteString.EMPTY;
transactionOutPointIndex_ = 0;
scriptBytes_ = com.google.protobuf.ByteString.EMPTY;
sequence_ = 0;
value_ = 0L;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -3410,6 +3458,9 @@ public final class Protos {
if (((bitField0_ & 0x00000008) == 0x00000008)) {
output.writeUInt32(4, sequence_);
}
if (((bitField0_ & 0x00000010) == 0x00000010)) {
output.writeInt64(5, value_);
}
getUnknownFields().writeTo(output);
}
@@ -3435,6 +3486,10 @@ public final class Protos {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(4, sequence_);
}
if (((bitField0_ & 0x00000010) == 0x00000010)) {
size += com.google.protobuf.CodedOutputStream
.computeInt64Size(5, value_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -3559,6 +3614,8 @@ public final class Protos {
bitField0_ = (bitField0_ & ~0x00000004);
sequence_ = 0;
bitField0_ = (bitField0_ & ~0x00000008);
value_ = 0L;
bitField0_ = (bitField0_ & ~0x00000010);
return this;
}
@@ -3603,6 +3660,10 @@ public final class Protos {
to_bitField0_ |= 0x00000008;
}
result.sequence_ = sequence_;
if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
to_bitField0_ |= 0x00000010;
}
result.value_ = value_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -3631,6 +3692,9 @@ public final class Protos {
if (other.hasSequence()) {
setSequence(other.getSequence());
}
if (other.hasValue()) {
setValue(other.getValue());
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -3872,6 +3936,55 @@ public final class Protos {
return this;
}
// optional int64 value = 5;
private long value_ ;
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
public boolean hasValue() {
return ((bitField0_ & 0x00000010) == 0x00000010);
}
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
public long getValue() {
return value_;
}
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
public Builder setValue(long value) {
bitField0_ |= 0x00000010;
value_ = value;
onChanged();
return this;
}
/**
* <code>optional int64 value = 5;</code>
*
* <pre>
* Value of connected output, if known
* </pre>
*/
public Builder clearValue() {
bitField0_ = (bitField0_ & ~0x00000010);
value_ = 0L;
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:wallet.TransactionInput)
}
@@ -14769,57 +14882,57 @@ public final class Protos {
"label\030\004 \001(\t\022\032\n\022creation_timestamp\030\005 \001(\003\"" +
".\n\004Type\022\014\n\010ORIGINAL\020\001\022\030\n\024ENCRYPTED_SCRYP",
"T_AES\020\002\"5\n\006Script\022\017\n\007program\030\001 \002(\014\022\032\n\022cr" +
"eation_timestamp\030\002 \002(\003\"\203\001\n\020TransactionIn" +
"eation_timestamp\030\002 \002(\003\"\222\001\n\020TransactionIn" +
"put\022\"\n\032transaction_out_point_hash\030\001 \002(\014\022" +
"#\n\033transaction_out_point_index\030\002 \002(\r\022\024\n\014" +
"script_bytes\030\003 \002(\014\022\020\n\010sequence\030\004 \001(\r\"\177\n\021" +
"TransactionOutput\022\r\n\005value\030\001 \002(\003\022\024\n\014scri" +
"pt_bytes\030\002 \002(\014\022!\n\031spent_by_transaction_h" +
"ash\030\003 \001(\014\022\"\n\032spent_by_transaction_index\030" +
"\004 \001(\005\"\234\003\n\025TransactionConfidence\0220\n\004type\030" +
"\001 \001(\0162\".wallet.TransactionConfidence.Typ",
"e\022\032\n\022appeared_at_height\030\002 \001(\005\022\036\n\026overrid" +
"ing_transaction\030\003 \001(\014\022\r\n\005depth\030\004 \001(\005\022\021\n\t" +
"work_done\030\005 \001(\003\022)\n\014broadcast_by\030\006 \003(\0132\023." +
"wallet.PeerAddress\0224\n\006source\030\007 \001(\0162$.wal" +
"let.TransactionConfidence.Source\"O\n\004Type" +
"\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING\020\001\022\013\n\007PENDING\020\002" +
"\022\025\n\021NOT_IN_BEST_CHAIN\020\003\022\010\n\004DEAD\020\004\"A\n\006Sou" +
"rce\022\022\n\016SOURCE_UNKNOWN\020\000\022\022\n\016SOURCE_NETWOR" +
"K\020\001\022\017\n\013SOURCE_SELF\020\002\"\236\004\n\013Transaction\022\017\n\007" +
"version\030\001 \002(\005\022\014\n\004hash\030\002 \002(\014\022&\n\004pool\030\003 \001(",
"\0162\030.wallet.Transaction.Pool\022\021\n\tlock_time" +
"\030\004 \001(\r\022\022\n\nupdated_at\030\005 \001(\003\0223\n\021transactio" +
"n_input\030\006 \003(\0132\030.wallet.TransactionInput\022" +
"5\n\022transaction_output\030\007 \003(\0132\031.wallet.Tra" +
"nsactionOutput\022\022\n\nblock_hash\030\010 \003(\014\022 \n\030bl" +
"ock_relativity_offsets\030\013 \003(\005\0221\n\nconfiden" +
"ce\030\t \001(\0132\035.wallet.TransactionConfidence\022" +
"5\n\007purpose\030\n \001(\0162\033.wallet.Transaction.Pu" +
"rpose:\007UNKNOWN\"Y\n\004Pool\022\013\n\007UNSPENT\020\004\022\t\n\005S" +
"PENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004DEAD\020\n\022\013\n\007PENDIN",
"G\020\020\022\024\n\020PENDING_INACTIVE\020\022\":\n\007Purpose\022\013\n\007" +
"UNKNOWN\020\000\022\020\n\014USER_PAYMENT\020\001\022\020\n\014KEY_ROTAT" +
"ION\020\002\"N\n\020ScryptParameters\022\014\n\004salt\030\001 \002(\014\022" +
"\020\n\001n\030\002 \001(\003:\00516384\022\014\n\001r\030\003 \001(\005:\0018\022\014\n\001p\030\004 \001" +
"(\005:\0011\"8\n\tExtension\022\n\n\002id\030\001 \002(\t\022\014\n\004data\030\002" +
" \002(\014\022\021\n\tmandatory\030\003 \002(\010\" \n\003Tag\022\013\n\003tag\030\001 " +
"\002(\t\022\014\n\004data\030\002 \002(\014\"\256\004\n\006Wallet\022\032\n\022network_" +
"identifier\030\001 \002(\t\022\034\n\024last_seen_block_hash" +
"\030\002 \001(\014\022\036\n\026last_seen_block_height\030\014 \001(\r\022!" +
"\n\031last_seen_block_time_secs\030\016 \001(\003\022\030\n\003key",
"\030\003 \003(\0132\013.wallet.Key\022(\n\013transaction\030\004 \003(\013" +
"2\023.wallet.Transaction\022&\n\016watched_script\030" +
"\017 \003(\0132\016.wallet.Script\022C\n\017encryption_type" +
"\030\005 \001(\0162\035.wallet.Wallet.EncryptionType:\013U" +
"NENCRYPTED\0227\n\025encryption_parameters\030\006 \001(" +
"\0132\030.wallet.ScryptParameters\022\017\n\007version\030\007" +
" \001(\005\022$\n\textension\030\n \003(\0132\021.wallet.Extensi" +
"on\022\023\n\013description\030\013 \001(\t\022\031\n\021key_rotation_" +
"time\030\r \001(\004\022\031\n\004tags\030\020 \003(\0132\013.wallet.Tag\";\n" +
"\016EncryptionType\022\017\n\013UNENCRYPTED\020\001\022\030\n\024ENCR",
"YPTED_SCRYPT_AES\020\002B\035\n\023org.bitcoinj.walle" +
"tB\006Protos"
"script_bytes\030\003 \002(\014\022\020\n\010sequence\030\004 \001(\r\022\r\n\005" +
"value\030\005 \001(\003\"\177\n\021TransactionOutput\022\r\n\005valu" +
"e\030\001 \002(\003\022\024\n\014script_bytes\030\002 \002(\014\022!\n\031spent_b" +
"y_transaction_hash\030\003 \001(\014\022\"\n\032spent_by_tra" +
"nsaction_index\030\004 \001(\005\"\234\003\n\025TransactionConf" +
"idence\0220\n\004type\030\001 \001(\0162\".wallet.Transactio",
"nConfidence.Type\022\032\n\022appeared_at_height\030\002" +
" \001(\005\022\036\n\026overriding_transaction\030\003 \001(\014\022\r\n\005" +
"depth\030\004 \001(\005\022\021\n\twork_done\030\005 \001(\003\022)\n\014broadc" +
"ast_by\030\006 \003(\0132\023.wallet.PeerAddress\0224\n\006sou" +
"rce\030\007 \001(\0162$.wallet.TransactionConfidence" +
".Source\"O\n\004Type\022\013\n\007UNKNOWN\020\000\022\014\n\010BUILDING" +
"\020\001\022\013\n\007PENDING\020\002\022\025\n\021NOT_IN_BEST_CHAIN\020\003\022\010" +
"\n\004DEAD\020\004\"A\n\006Source\022\022\n\016SOURCE_UNKNOWN\020\000\022\022" +
"\n\016SOURCE_NETWORK\020\001\022\017\n\013SOURCE_SELF\020\002\"\236\004\n\013" +
"Transaction\022\017\n\007version\030\001 \002(\005\022\014\n\004hash\030\002 \002",
"(\014\022&\n\004pool\030\003 \001(\0162\030.wallet.Transaction.Po" +
"ol\022\021\n\tlock_time\030\004 \001(\r\022\022\n\nupdated_at\030\005 \001(" +
"\003\0223\n\021transaction_input\030\006 \003(\0132\030.wallet.Tr" +
"ansactionInput\0225\n\022transaction_output\030\007 \003" +
"(\0132\031.wallet.TransactionOutput\022\022\n\nblock_h" +
"ash\030\010 \003(\014\022 \n\030block_relativity_offsets\030\013 " +
"\003(\005\0221\n\nconfidence\030\t \001(\0132\035.wallet.Transac" +
"tionConfidence\0225\n\007purpose\030\n \001(\0162\033.wallet" +
".Transaction.Purpose:\007UNKNOWN\"Y\n\004Pool\022\013\n" +
"\007UNSPENT\020\004\022\t\n\005SPENT\020\005\022\014\n\010INACTIVE\020\002\022\010\n\004D",
"EAD\020\n\022\013\n\007PENDING\020\020\022\024\n\020PENDING_INACTIVE\020\022" +
"\":\n\007Purpose\022\013\n\007UNKNOWN\020\000\022\020\n\014USER_PAYMENT" +
"\020\001\022\020\n\014KEY_ROTATION\020\002\"N\n\020ScryptParameters" +
"\022\014\n\004salt\030\001 \002(\014\022\020\n\001n\030\002 \001(\003:\00516384\022\014\n\001r\030\003 " +
"\001(\005:\0018\022\014\n\001p\030\004 \001(\005:\0011\"8\n\tExtension\022\n\n\002id\030" +
"\001 \002(\t\022\014\n\004data\030\002 \002(\014\022\021\n\tmandatory\030\003 \002(\010\" " +
"\n\003Tag\022\013\n\003tag\030\001 \002(\t\022\014\n\004data\030\002 \002(\014\"\256\004\n\006Wal" +
"let\022\032\n\022network_identifier\030\001 \002(\t\022\034\n\024last_" +
"seen_block_hash\030\002 \001(\014\022\036\n\026last_seen_block" +
"_height\030\014 \001(\r\022!\n\031last_seen_block_time_se",
"cs\030\016 \001(\003\022\030\n\003key\030\003 \003(\0132\013.wallet.Key\022(\n\013tr" +
"ansaction\030\004 \003(\0132\023.wallet.Transaction\022&\n\016" +
"watched_script\030\017 \003(\0132\016.wallet.Script\022C\n\017" +
"encryption_type\030\005 \001(\0162\035.wallet.Wallet.En" +
"cryptionType:\013UNENCRYPTED\0227\n\025encryption_" +
"parameters\030\006 \001(\0132\030.wallet.ScryptParamete" +
"rs\022\017\n\007version\030\007 \001(\005\022$\n\textension\030\n \003(\0132\021" +
".wallet.Extension\022\023\n\013description\030\013 \001(\t\022\031" +
"\n\021key_rotation_time\030\r \001(\004\022\031\n\004tags\030\020 \003(\0132" +
"\013.wallet.Tag\";\n\016EncryptionType\022\017\n\013UNENCR",
"YPTED\020\001\022\030\n\024ENCRYPTED_SCRYPT_AES\020\002B\035\n\023org" +
".bitcoinj.walletB\006Protos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -14855,7 +14968,7 @@ public final class Protos {
internal_static_wallet_TransactionInput_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_wallet_TransactionInput_descriptor,
new java.lang.String[] { "TransactionOutPointHash", "TransactionOutPointIndex", "ScriptBytes", "Sequence", });
new java.lang.String[] { "TransactionOutPointHash", "TransactionOutPointIndex", "ScriptBytes", "Sequence", "Value", });
internal_static_wallet_TransactionOutput_descriptor =
getDescriptor().getMessageTypes().get(5);
internal_static_wallet_TransactionOutput_fieldAccessorTable = new

View File

@@ -1983,6 +1983,23 @@ public class WalletTest extends TestWithWallet {
assertEquals(2, request1.tx.getOutputs().size()); // and gotten change back
}
@Test
public void transactionGetFeeTest() throws Exception {
Address notMyAddr = new ECKey().toAddress(params);
// Prepare wallet to spend
StoredBlock block = new StoredBlock(makeSolvedTestBlock(blockStore, notMyAddr), BigInteger.ONE, 1);
Transaction tx = createFakeTx(params, Utils.COIN, myAddress);
wallet.receiveFromBlock(tx, block, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0);
// Create a transaction
SendRequest request = SendRequest.to(notMyAddr, Utils.CENT);
request.feePerKb = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
wallet.completeTx(request);
assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, request.fee);
assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, request.tx.getFee());
}
@Test
public void feePerKbCategoryJumpTest() throws Exception {
// Simple test of boundary condition on fee per kb in category fee solver