Skip to content

Commit

Permalink
Merge pull request #68 from rsksmart/segwit_fee_calculation
Browse files Browse the repository at this point in the history
Segwit fee calculation
  • Loading branch information
julia-zack authored Aug 28, 2023
2 parents a70f5ee + 25f9abe commit b30262c
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/main/java/co/rsk/bitcoinj/script/Script.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public enum VerifyFlag {

private static final Logger log = LoggerFactory.getLogger(Script.class);
public static final long MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
public static final int SIG_SIZE = 75;
public static final int SIG_SIZE = 73;
/** Max number of sigops allowed in a standard p2sh redeem script */
public static final int MAX_P2SH_SIGOPS = 15;

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/co/rsk/bitcoinj/wallet/SendRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public class SendRequest {
*/
public BtcTransaction tx;

public boolean isSegwit = false;

/**
* When emptyWallet is set, all coins selected by the coin selector are sent to the first output in tx
* (its value is ignored and set to {@link co.rsk.bitcoinj.wallet.Wallet#getBalance()} - the fees required
Expand Down
73 changes: 71 additions & 2 deletions src/main/java/co/rsk/bitcoinj/wallet/Wallet.java
Original file line number Diff line number Diff line change
Expand Up @@ -996,8 +996,22 @@ public FeeCalculation calculateFee(SendRequest req, Coin value, List<Transaction
checkState(input.getScriptBytes().length == 0);
}

int size = tx.unsafeBitcoinSerialize().length;
size += estimateBytesForSigning(selection);
int size;
int totalSize;
int baseSize;

baseSize = calculateTxBaseSize(tx, req.isSegwit);
totalSize = baseSize + estimateBytesForSigning(selection);
totalSize += bytesToAdd(tx);

if (req.isSegwit) {
size = calculateWitnessTxVirtualSize(baseSize, totalSize);
log.info("The tx is segwit so the size is {} ", size);
}
else {
size = totalSize;
log.info("The tx is not segwit so the size is {} ", size);
}

Coin feePerKb = req.feePerKb;
if (needAtLeastReferenceFee && feePerKb.compareTo(BtcTransaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) {
Expand All @@ -1017,6 +1031,61 @@ public FeeCalculation calculateFee(SendRequest req, Coin value, List<Transaction

}

public static int calculateTxBaseSize(BtcTransaction spendTx, boolean isSegwit) {
int baseSize = 0;

int inputsQuantity = spendTx.getInputs().size();
for (int i = 0; i < inputsQuantity; i++) {
byte[] input = spendTx.getInput(i).bitcoinSerialize();
baseSize += input.length;
// at this time the scriptSig for every input is empty = 00. so we should count its bytes manually and remove the byte from the empty one.
baseSize += 36 - 1;
}

int outputsQuantity = spendTx.getOutputs().size();
for (int i = 0; i < outputsQuantity; i++) {
byte[] output = spendTx.getOutput(i).bitcoinSerialize();
baseSize += output.length;
}

baseSize += 4; // version size
baseSize += 4; // locktime size

if (isSegwit) {
baseSize += 1; // marker size
baseSize += 1; // flag size
}

return baseSize;
}

public static int calculateWitnessTxWeight(int txBaseSize, int txTotalSize) {

// As described in https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations
int txWeight = txTotalSize + (3 * txBaseSize);
return txWeight;
}

public static int calculateWitnessTxVirtualSize(int txBaseSize, int txTotalSize) {
double txWeight = calculateWitnessTxWeight(txBaseSize, txTotalSize);

// As described in https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations
int txVirtualSize = (int) Math.ceil(txWeight / 4);
return txVirtualSize;
}

public static int bytesToAdd(BtcTransaction tx) {
int bytes = 0;

bytes += 1; // inputs quantity bytes size
bytes += 1; // outputs quantity bytes size
bytes += 1; // empty vector before signatures bytes size
bytes += 1; // op_notif value bytes size
bytes += 4; // 3 or 4 bytes before the redeem are added. count them like 4

return bytes;
}

private void addSuppliedInputs(BtcTransaction tx, List<TransactionInput> originalInputs) {
for (TransactionInput input : originalInputs)
tx.addInput(new TransactionInput(params, tx, input.bitcoinSerialize()));
Expand Down

0 comments on commit b30262c

Please sign in to comment.