3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 15:22:16 +00:00

Married wallets: extending fee calculation

This commit is contained in:
Kosta Korenkov 2014-06-25 23:19:02 +04:00 committed by Mike Hearn
parent fd0c6a27f4
commit 12bfa5f5ee
2 changed files with 44 additions and 9 deletions

View File

@ -491,7 +491,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
/** Returns the address used for change outputs. Note: this will probably go away in future. */ /** Returns the address used for change outputs. Note: this will probably go away in future. */
public Address getChangeAddress() { public Address getChangeAddress() {
return currentKey(KeyChain.KeyPurpose.CHANGE).toAddress(params); return keychain.currentAddress(KeyChain.KeyPurpose.CHANGE);
} }
/** /**
@ -3895,14 +3895,17 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
int size = 0; int size = 0;
for (TransactionOutput output : selection.gathered) { for (TransactionOutput output : selection.gathered) {
try { try {
if (output.getScriptPubKey().isSentToAddress()) { Script script = output.getScriptPubKey();
// Send-to-address spends usually take maximum pubkey.length (as it may be compressed or not) + 75 bytes ECKey key = null;
final ECKey key = findKeyFromPubHash(output.getScriptPubKey().getPubKeyHash()); Script redeemScript = null;
size += checkNotNull(key, "Coin selection includes unspendable outputs").getPubKey().length + 75; if (script.isSentToAddress()) {
} else if (output.getScriptPubKey().isSentToRawPubKey()) key = findKeyFromPubHash(script.getPubKeyHash());
size += 74; // Send-to-pubkey spends usually take maximum 74 bytes to spend checkNotNull(key, "Coin selection includes unspendable outputs");
else } else if (script.isPayToScriptHash()) {
throw new IllegalStateException("Unknown output type returned in coin selection"); redeemScript = keychain.findRedeemScriptFromPubHash(script.getPubKeyHash());
checkNotNull(redeemScript, "Coin selection includes unspendable outputs");
}
size += script.getNumberOfBytesRequiredToSpend(key, redeemScript);
} catch (ScriptException e) { } catch (ScriptException e) {
// If this happens it means an output script in a wallet tx could not be understood. That should never // If this happens it means an output script in a wallet tx could not be understood. That should never
// happen, if it does it means the wallet has got into an inconsistent state. // happen, if it does it means the wallet has got into an inconsistent state.

View File

@ -25,6 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.digests.RIPEMD160Digest; import org.spongycastle.crypto.digests.RIPEMD160Digest;
import javax.annotation.Nullable;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -54,6 +55,7 @@ import static com.google.common.base.Preconditions.checkState;
public class Script { public class Script {
private static final Logger log = LoggerFactory.getLogger(Script.class); private static final Logger log = LoggerFactory.getLogger(Script.class);
public static final long MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes public static final long MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
public static final int SIG_SIZE = 75;
// The program is a set of chunks where each element is either [opcode] or [data, data, data ...] // The program is a set of chunks where each element is either [opcode] or [data, data, data ...]
protected List<ScriptChunk> chunks; protected List<ScriptChunk> chunks;
@ -446,6 +448,36 @@ public class Script {
return 0; return 0;
} }
/**
* Returns number of bytes required to spend this script. It accepts optional ECKey and redeemScript that may
* be required for certain types of script to estimate target size.
*/
public int getNumberOfBytesRequiredToSpend(@Nullable ECKey pubKey, @Nullable Script redeemScript) {
if (isPayToScriptHash()) {
// scriptSig: <sig> [sig] [sig...] <redeemscript>
checkArgument(redeemScript != null, "P2SH script requires redeemScript to be spent");
// for N of M CHECKMULTISIG redeem script we will need N signatures to spend
ScriptChunk nChunk = redeemScript.getChunks().get(0);
int n = Script.decodeFromOpN(nChunk.opcode);
return n * SIG_SIZE + getProgram().length;
} else if (isSentToMultiSig()) {
// scriptSig: OP_0 <sig> [sig] [sig...]
// for N of M CHECKMULTISIG script we will need N signatures to spend
ScriptChunk nChunk = chunks.get(0);
int n = Script.decodeFromOpN(nChunk.opcode);
return n * SIG_SIZE + 1;
} else if (isSentToRawPubKey()) {
// scriptSig: <sig>
return SIG_SIZE;
} else if (isSentToAddress()) {
// scriptSig: <sig> <pubkey>
int uncompressedPubKeySize = 65;
return SIG_SIZE + (pubKey != null ? pubKey.getPubKey().length : uncompressedPubKeySize);
} else {
throw new IllegalStateException("Unsupported script type");
}
}
/** /**
* <p>Whether or not this is a scriptPubKey representing a pay-to-script-hash output. In such outputs, the logic that * <p>Whether or not this is a scriptPubKey representing a pay-to-script-hash output. In such outputs, the logic that
* controls reclamation is not actually in the output at all. Instead there's just a hash, and it's up to the * controls reclamation is not actually in the output at all. Instead there's just a hash, and it's up to the