transaction: cache tx size, don't serialize it over and over again

This commit is contained in:
Roman Khimov 2020-09-10 19:28:16 +03:00
parent a6a1df4e0d
commit 19c69618c5
5 changed files with 21 additions and 11 deletions

View file

@ -1181,7 +1181,7 @@ func (bc *Blockchain) ApplyPolicyToTxSet(txes []*transaction.Transaction) []*tra
)
blockSize = uint32(io.GetVarSize(new(block.Block)) + io.GetVarSize(len(txes)+1))
for i, tx := range txes {
blockSize += uint32(io.GetVarSize(tx))
blockSize += uint32(tx.Size())
sysFee += tx.SystemFee
if blockSize > maxBlockSize || sysFee > maxBlockSysFee {
txes = txes[:i]
@ -1234,7 +1234,7 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
// Only one %w can be used.
return fmt.Errorf("%w: %v", ErrPolicy, err)
}
size := io.GetVarSize(t)
size := t.Size()
if size > transaction.MaxTransactionSize {
return fmt.Errorf("%w: (%d > MaxTransactionSize %d)", ErrTxTooBig, size, transaction.MaxTransactionSize)
}

View file

@ -524,8 +524,8 @@ func TestGetTransaction(t *testing.T) {
tx, height, err := bc.GetTransaction(block.Transactions[0].Hash())
require.Nil(t, err)
assert.Equal(t, block.Index, height)
assert.Equal(t, txSize, tx.Size())
assert.Equal(t, block.Transactions[0], tx)
assert.Equal(t, txSize, io.GetVarSize(tx))
assert.Equal(t, 1, io.GetVarSize(tx.Attributes))
assert.Equal(t, 1, io.GetVarSize(tx.Scripts))
assert.NoError(t, bc.persist())

View file

@ -62,8 +62,8 @@ type Transaction struct {
// for correct signing/verification.
Network netmode.Magic
// feePerByte is the ratio of NetworkFee and tx size, used for calculating tx priority.
feePerByte int64
// size is transaction's serialized size.
size int
// Hash of the transaction (double SHA256).
hash util.Uint256
@ -158,6 +158,7 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) {
// to do it anymore.
if br.Err == nil {
br.Err = t.createHash()
_ = t.Size()
}
}
@ -252,18 +253,22 @@ func NewTransactionFromBytes(network netmode.Magic, b []byte) (*Transaction, err
if r.Err == nil {
return nil, errors.New("additional data after the transaction")
}
tx.feePerByte = tx.NetworkFee / int64(len(b))
tx.size = len(b)
return tx, nil
}
// FeePerByte returns NetworkFee of the transaction divided by
// its size
func (t *Transaction) FeePerByte() int64 {
if t.feePerByte != 0 {
return t.feePerByte
return t.NetworkFee / int64(t.Size())
}
// Size returns size of the serialized transaction.
func (t *Transaction) Size() int {
if t.size == 0 {
t.size = io.GetVarSize(t)
}
t.feePerByte = t.NetworkFee / int64(io.GetVarSize(t))
return t.feePerByte
return t.size
}
// Sender returns the sender of the transaction which is always on the first place
@ -296,7 +301,7 @@ type transactionJSON struct {
func (t *Transaction) MarshalJSON() ([]byte, error) {
tx := transactionJSON{
TxID: t.Hash(),
Size: io.GetVarSize(t),
Size: t.Size(),
Version: t.Version,
Nonce: t.Nonce,
Sender: address.Uint160ToString(t.Sender()),
@ -329,6 +334,9 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
if t.Hash() != tx.TxID {
return errors.New("txid doesn't match transaction hash")
}
if t.Size() != tx.Size {
return errors.New("'size' doesn't match transaction size")
}
return t.isValid()
}

View file

@ -73,6 +73,7 @@ func TestNew(t *testing.T) {
assert.Equal(t, script, tx.Script)
// Update hash fields to match tx2 that is gonna autoupdate them on decode.
_ = tx.Hash()
_ = tx.Size()
testserdes.EncodeDecodeBinary(t, tx, &Transaction{Network: netmode.UnitTestNet})
}

View file

@ -811,6 +811,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) {
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
TXHash := block.Transactions[0].Hash()
_ = block.Transactions[0].Size()
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 1]}"`, TXHash.StringLE())
body := doRPCCall(rpc, httpSrv.URL, t)
txOut := checkErrGetResult(t, body, false)