// EstimateSerializeSize returns a worst case serialize size estimate for a // signed transaction that spends inputCount number of compressed P2PKH outputs // and contains each transaction output from txOuts. The estimated size is // incremented for an additional P2PKH change output if addChangeOutput is true. func EstimateSerializeSize(inputCount int, txOuts []*wire.TxOut, addChangeOutput bool) int { changeSize := 0 outputCount := len(txOuts) if addChangeOutput { changeSize = P2PKHOutputSize outputCount++ } // 12 additional bytes are for version, locktime and expiry. return 12 + (2 * wire.VarIntSerializeSize(uint64(inputCount))) + wire.VarIntSerializeSize(uint64(outputCount)) + inputCount*RedeemP2PKHInputSize + h.SumOutputSerializeSizes(txOuts) + changeSize }
// TestVarIntWire tests the serialize size for variable length integers. func TestVarIntSerializeSize(t *testing.T) { tests := []struct { val uint64 // Value to get the serialized size for size int // Expected serialized size }{ // Single byte {0, 1}, // Max single byte {0xfc, 1}, // Min 2-byte {0xfd, 3}, // Max 2-byte {0xffff, 3}, // Min 4-byte {0x10000, 5}, // Max 4-byte {0xffffffff, 5}, // Min 8-byte {0x100000000, 9}, // Max 8-byte {0xffffffffffffffff, 9}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { serializedSize := wire.VarIntSerializeSize(test.val) if serializedSize != test.size { t.Errorf("VarIntSerializeSize #%d got: %d, want: %d", i, serializedSize, test.size) continue } } }
// calculateTxSize returns an estimate of the serialized size (in bytes) of the // given transaction. It assumes all tx inputs are P2SH multi-sig. func calculateTxSize(tx *withdrawalTx) int { msgtx := tx.toMsgTx() // Assume that there will always be a change output, for simplicity. We // simulate that by simply copying the first output as all we care about is // the size of its serialized form, which should be the same for all of them // as they're either P2PKH or P2SH.. if !tx.hasChange() { msgtx.AddTxOut(msgtx.TxOut[0]) } // Craft a SignatureScript with dummy signatures for every input in this tx // so that we can use msgtx.SerializeSize() to get its size and don't need // to rely on estimations. for i, txin := range msgtx.TxIn { // 1 byte for the OP_FALSE opcode, then 73+1 bytes for each signature // with their OP_DATA opcode and finally the redeem script + 1 byte // for its OP_PUSHDATA opcode and N bytes for the redeem script's size. // Notice that we use 73 as the signature length as that's the maximum // length they may have: // https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm addr := tx.inputs[i].addr redeemScriptLen := len(addr.redeemScript()) n := wire.VarIntSerializeSize(uint64(redeemScriptLen)) sigScriptLen := 1 + (74 * int(addr.series().reqSigs)) + redeemScriptLen + 1 + n txin.SignatureScript = bytes.Repeat([]byte{1}, sigScriptLen) } return msgtx.SerializeSize() }
// IsDustAmount determines whether a transaction output value and script length would // cause the output to be considered dust. Transactions with dust outputs are // not standard and are rejected by mempools with default policies. func IsDustAmount(amount dcrutil.Amount, scriptSize int, relayFeePerKb dcrutil.Amount) bool { // Calculate the total (estimated) cost to the network. This is // calculated using the serialize size of the output plus the serial // size of a transaction input which redeems it. The output is assumed // to be compressed P2PKH as this is the most common script type. Use // the average size of a compressed P2PKH redeem input (165) rather than // the largest possible (txsizes.RedeemP2PKHInputSize). totalSize := 8 + 2 + wire.VarIntSerializeSize(uint64(scriptSize)) + scriptSize + 165 // Dust is defined as an output value where the total cost to the network // (output size + input size) is greater than 1/3 of the relay fee. return int64(amount)*1000/(3*int64(totalSize)) < int64(relayFeePerKb) }