From ffeb3b8473ee49dc4b7516add1f291139958b720 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 2 Dec 2021 14:48:35 +0300 Subject: [PATCH 1/5] transaction: microoptimize Hash() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't cost much, but it's used _a lot_, so optimizing it makes sense. name old time/op new time/op delta TxHash-8 4.89ns ± 5% 0.54ns ± 2% -88.86% (p=0.008 n=5+5) name old alloc/op new alloc/op delta TxHash-8 0.00B 0.00B ~ (all equal) name old allocs/op new allocs/op delta TxHash-8 0.00 0.00 ~ (all equal) --- pkg/core/transaction/transaction.go | 8 +++++++- pkg/core/transaction/transaction_test.go | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index f4c267b0e..af57f4e3f 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -68,6 +68,9 @@ type Transaction struct { // Hash of the transaction (double SHA256). hash util.Uint256 + // Whether hash is correct. + hashed bool + // Trimmed indicates this is a transaction from trimmed // data. Trimmed bool @@ -78,6 +81,7 @@ type Transaction struct { func NewTrimmedTX(hash util.Uint256) *Transaction { return &Transaction{ hash: hash, + hashed: true, Trimmed: true, } } @@ -98,7 +102,7 @@ func New(script []byte, gas int64) *Transaction { // Hash returns the hash of the transaction. func (t *Transaction) Hash() util.Uint256 { - if t.hash.Equals(util.Uint256{}) { + if !t.hashed { if t.createHash() != nil { panic("failed to compute hash!") } @@ -172,6 +176,7 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader, buf []byte) { if buf != nil { end = len(buf) - br.Len() t.hash = hash.Sha256(buf[start:end]) + t.hashed = true } } @@ -261,6 +266,7 @@ func (t *Transaction) createHash() error { } shaHash.Sum(t.hash[:0]) + t.hashed = true return nil } diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go index 52318fdd8..1aeca4211 100644 --- a/pkg/core/transaction/transaction_test.go +++ b/pkg/core/transaction/transaction_test.go @@ -308,3 +308,17 @@ func TestTransaction_HasSigner(t *testing.T) { require.True(t, tx.HasSigner(u1)) require.False(t, tx.HasSigner(util.Uint160{})) } + +func BenchmarkTxHash(b *testing.B) { + script := []byte{0x51} + tx := New(script, 1) + tx.NetworkFee = 123 + tx.Signers = []Signer{{Account: util.Uint160{1, 2, 3}}} + tx.Scripts = []Witness{{InvocationScript: []byte{}, VerificationScript: []byte{}}} + + // Prime cache. + tx.Hash() + for i := 0; i < b.N; i++ { + _ = tx.Hash() + } +} From a003879b843a177196a6c706b40ae0f2479d2842 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 2 Dec 2021 15:30:20 +0300 Subject: [PATCH 2/5] native: avoid allocations in increaseBalance --- pkg/core/native/native_gas.go | 2 +- pkg/core/native/native_neo.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 2903e10f9..e06925741 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -54,7 +54,7 @@ func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.Stor err = errors.New("insufficient funds") } return err - } else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 { + } else if sign == -1 && acc.Balance.CmpAbs(amount) == -1 { return errors.New("insufficient funds") } acc.Balance.Add(&acc.Balance, amount) diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index eb5291d87..163525080 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -405,7 +405,7 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto if err != nil { return err } - if (amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1) || + if (amount.Sign() == -1 && acc.Balance.CmpAbs(amount) == -1) || (amount.Sign() == 0 && checkBal != nil && acc.Balance.Cmp(checkBal) == -1) { return errors.New("insufficient funds") } From ac7e922606175f4ea34fbaac87afd81ff82e8eb3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 2 Dec 2021 15:40:43 +0300 Subject: [PATCH 3/5] native: avoid big.Int allocation in burn() addTokens and incBalance only read the amount, so it's not a problem. --- pkg/core/native/native_nep17.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/core/native/native_nep17.go b/pkg/core/native/native_nep17.go index ca5a5bc7d..0f88df1ad 100644 --- a/pkg/core/native/native_nep17.go +++ b/pkg/core/native/native_nep17.go @@ -262,7 +262,9 @@ func (c *nep17TokenNative) burn(ic *interop.Context, h util.Uint160, amount *big if amount.Sign() == 0 { return } - c.addTokens(ic, h, new(big.Int).Neg(amount)) + amount.Neg(amount) + c.addTokens(ic, h, amount) + amount.Neg(amount) c.postTransfer(ic, &h, nil, amount, stackitem.Null{}, false) } From 53cfde62deba4108eacf54cf5b48be8b8f99be8b Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 2 Dec 2021 15:59:51 +0300 Subject: [PATCH 4/5] vm: don't allocate another stack for entry scripts vm.New() creates a new stack, then we load an entry script with LoadScriptWithFlags and it creates another one which doesn't make much sense. rvcount is -1 for it, so all elements are to be copied down anyway and it's clear so a new loaded script can't dig down to anything it shouldn't be able to. --- pkg/vm/vm.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 33859b91c..5cc6b872a 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -317,7 +317,9 @@ func (v *VM) loadScriptWithCallingHash(b []byte, exe *nef.File, caller util.Uint v.checkInvocationStackSize() ctx := NewContextWithParams(b, rvcount, offset) - v.estack = newStack("evaluation", &v.refs) + if rvcount != -1 || v.estack.Len() != 0 { + v.estack = newStack("evaluation", &v.refs) + } ctx.estack = v.estack initStack(&ctx.tryStack, "exception", nil) ctx.callFlag = f From 41ab7a85a70ef70037ac5ee94f0f7e112ada8c54 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 2 Dec 2021 16:11:09 +0300 Subject: [PATCH 5/5] hash: avoid allocation in RipeMD160 calculation --- pkg/crypto/hash/hash.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/crypto/hash/hash.go b/pkg/crypto/hash/hash.go index 45f18c7fd..c56600c3c 100644 --- a/pkg/crypto/hash/hash.go +++ b/pkg/crypto/hash/hash.go @@ -53,7 +53,7 @@ func RipeMD160(data []byte) util.Uint160 { hasher := ripemd160.New() _, _ = hasher.Write(data) - hash, _ = util.Uint160DecodeBytesBE(hasher.Sum(nil)) + hasher.Sum(hash[:0]) return hash }