From 1784a141482d92e9d3fa82396e2344fea6fba5fb Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 6 Dec 2019 18:13:52 +0300 Subject: [PATCH 1/5] io: optimize BinReader.ReadArray() reflect.MethodByName is a rather expensive function especially when called on hot path. This became obvious during profiling of db restore. This commit replaces reflection with a cast to an interface. --- pkg/io/binaryReader.go | 36 +++++++++++------------------------- pkg/io/binaryrw_test.go | 11 +++-------- pkg/io/serializable.go | 4 ++++ 3 files changed, 18 insertions(+), 33 deletions(-) diff --git a/pkg/io/binaryReader.go b/pkg/io/binaryReader.go index 645aee5c1..1283537b2 100644 --- a/pkg/io/binaryReader.go +++ b/pkg/io/binaryReader.go @@ -47,19 +47,14 @@ func (r *BinReader) ReadArray(t interface{}, maxSize ...int) { panic(value.Type().String() + " is not a pointer to a slice") } - sliceType := value.Elem().Type() - elemType := sliceType.Elem() - isPtr := elemType.Kind() == reflect.Ptr - if isPtr { - checkHasDecodeBinary(elemType) - } else { - checkHasDecodeBinary(reflect.PtrTo(elemType)) - } - if r.Err != nil { return } + sliceType := value.Elem().Type() + elemType := sliceType.Elem() + isPtr := elemType.Kind() == reflect.Ptr + ms := maxArraySize if len(maxSize) != 0 { ms = maxSize[0] @@ -82,27 +77,18 @@ func (r *BinReader) ReadArray(t interface{}, maxSize ...int) { } else { elem = arr.Index(i).Addr() } - method := elem.MethodByName("DecodeBinary") - method.Call([]reflect.Value{reflect.ValueOf(r)}) + + el, ok := elem.Interface().(decodable) + if !ok { + panic(elemType.String() + "is not decodable") + } + + el.DecodeBinary(r) } value.Elem().Set(arr) } -func checkHasDecodeBinary(v reflect.Type) { - method, ok := v.MethodByName("DecodeBinary") - if !ok || !isDecodeBinaryMethod(method) { - panic(v.String() + " does not have DecodeBinary(*io.BinReader)") - } -} - -func isDecodeBinaryMethod(method reflect.Method) bool { - t := method.Type - return t != nil && - t.NumIn() == 2 && t.In(1) == reflect.TypeOf((*BinReader)(nil)) && - t.NumOut() == 0 -} - // ReadBE reads from the underlying io.Reader // into the interface v in big-endian format. func (r *BinReader) ReadBE(v interface{}) { diff --git a/pkg/io/binaryrw_test.go b/pkg/io/binaryrw_test.go index 19a8c2a55..4ce265d2a 100644 --- a/pkg/io/binaryrw_test.go +++ b/pkg/io/binaryrw_test.go @@ -326,15 +326,10 @@ func TestBinReader_ReadArray(t *testing.T) { require.NoError(t, r.Err) require.Equal(t, []testSerializable{}, arrVal) - r = NewBinReaderFromBuf([]byte{0}) - r.Err = errors.New("error") - require.Panics(t, func() { r.ReadArray(&[]*int{}) }) + r = NewBinReaderFromBuf([]byte{1}) + require.Panics(t, func() { r.ReadArray(&[]int{1}) }) r = NewBinReaderFromBuf([]byte{0}) r.Err = errors.New("error") - require.Panics(t, func() { r.ReadArray(&[]int{}) }) - - r = NewBinReaderFromBuf([]byte{0}) - r.Err = errors.New("error") - require.Panics(t, func() { r.ReadArray(0) }) + require.Panics(t, func() { r.ReadArray(1) }) } diff --git a/pkg/io/serializable.go b/pkg/io/serializable.go index 8e132b86b..c878d8ddb 100644 --- a/pkg/io/serializable.go +++ b/pkg/io/serializable.go @@ -10,3 +10,7 @@ type Serializable interface { DecodeBinary(*BinReader) EncodeBinary(*BinWriter) } + +type decodable interface { + DecodeBinary(*BinReader) +} From f01fc1cc290655b97662ef2aa3187d0747784d0b Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 6 Dec 2019 18:15:33 +0300 Subject: [PATCH 2/5] io: optimize BinWriter.WriteArray() Replace reflect.MethodByName with a simple interface cast. --- pkg/io/binaryWriter.go | 23 ++++++++--------------- pkg/io/binaryrw_test.go | 3 +-- pkg/io/serializable.go | 4 ++++ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/pkg/io/binaryWriter.go b/pkg/io/binaryWriter.go index 16e9dfc77..5b6ccade9 100644 --- a/pkg/io/binaryWriter.go +++ b/pkg/io/binaryWriter.go @@ -39,33 +39,26 @@ func (w *BinWriter) WriteBE(v interface{}) { func (w *BinWriter) WriteArray(arr interface{}) { switch val := reflect.ValueOf(arr); val.Kind() { case reflect.Slice, reflect.Array: - typ := val.Type().Elem() - method, ok := typ.MethodByName("EncodeBinary") - if !ok || !isEncodeBinaryMethod(method) { - panic(typ.String() + " does not have EncodeBinary(*BinWriter)") - } - if w.Err != nil { return } + typ := val.Type().Elem() + w.WriteVarUint(uint64(val.Len())) for i := 0; i < val.Len(); i++ { - method := val.Index(i).MethodByName("EncodeBinary") - method.Call([]reflect.Value{reflect.ValueOf(w)}) + el, ok := val.Index(i).Interface().(encodable) + if !ok { + panic(typ.String() + "is not encodable") + } + + el.EncodeBinary(w) } default: panic("not an array") } } -func isEncodeBinaryMethod(method reflect.Method) bool { - t := method.Type - return t != nil && - t.NumIn() == 2 && t.In(1) == reflect.TypeOf((*BinWriter)(nil)) && - t.NumOut() == 0 -} - // WriteVarUint writes a uint64 into the underlying writer using variable-length encoding. func (w *BinWriter) WriteVarUint(val uint64) { if w.Err != nil { diff --git a/pkg/io/binaryrw_test.go b/pkg/io/binaryrw_test.go index 4ce265d2a..8c6d64c6e 100644 --- a/pkg/io/binaryrw_test.go +++ b/pkg/io/binaryrw_test.go @@ -267,8 +267,7 @@ func TestBinWriter_WriteArray(t *testing.T) { require.Equal(t, w.Bytes(), []byte(nil)) w.Reset() - w.Err = errors.New("error") - require.Panics(t, func() { w.WriteArray([]int{}) }) + require.Panics(t, func() { w.WriteArray([]int{1}) }) w.Reset() w.Err = errors.New("error") diff --git a/pkg/io/serializable.go b/pkg/io/serializable.go index c878d8ddb..43e659731 100644 --- a/pkg/io/serializable.go +++ b/pkg/io/serializable.go @@ -14,3 +14,7 @@ type Serializable interface { type decodable interface { DecodeBinary(*BinReader) } + +type encodable interface { + EncodeBinary(*BinWriter) +} From 838050f8b5702c4a0d8d485eecf35bbbe1c93652 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 6 Dec 2019 18:37:37 +0300 Subject: [PATCH 3/5] io: rename ReadBytes() to ReadVarBytes() --- pkg/consensus/payload.go | 2 +- pkg/consensus/recovery_message.go | 6 +++--- pkg/core/contract_state.go | 2 +- pkg/core/storage_item.go | 2 +- pkg/core/transaction/invocation.go | 2 +- pkg/core/transaction/publish.go | 2 +- pkg/core/transaction/state_descriptor.go | 4 ++-- pkg/core/transaction/witness.go | 4 ++-- pkg/io/binaryReader.go | 8 ++++---- pkg/io/binaryrw_test.go | 2 +- pkg/network/payload/merkleblock.go | 2 +- pkg/network/payload/version.go | 2 +- pkg/smartcontract/contract_test.go | 2 +- pkg/vm/serialization.go | 4 ++-- 14 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pkg/consensus/payload.go b/pkg/consensus/payload.go index f7ad50a07..80d73e2c7 100644 --- a/pkg/consensus/payload.go +++ b/pkg/consensus/payload.go @@ -217,7 +217,7 @@ func (p *Payload) DecodeBinaryUnsigned(r *io.BinReader) { r.ReadLE(&p.validatorIndex) r.ReadLE(&p.timestamp) - data := r.ReadBytes() + data := r.ReadVarBytes() if r.Err != nil { return } diff --git a/pkg/consensus/recovery_message.go b/pkg/consensus/recovery_message.go index c6bb1b57a..4e6094f66 100644 --- a/pkg/consensus/recovery_message.go +++ b/pkg/consensus/recovery_message.go @@ -93,7 +93,7 @@ func (p *changeViewCompact) DecodeBinary(r *io.BinReader) { r.ReadLE(&p.ValidatorIndex) r.ReadLE(&p.OriginalViewNumber) r.ReadLE(&p.Timestamp) - p.InvocationScript = r.ReadBytes() + p.InvocationScript = r.ReadVarBytes() } // EncodeBinary implements io.Serializable interface. @@ -109,7 +109,7 @@ func (p *commitCompact) DecodeBinary(r *io.BinReader) { r.ReadLE(&p.ViewNumber) r.ReadLE(&p.ValidatorIndex) r.ReadBE(p.Signature[:]) - p.InvocationScript = r.ReadBytes() + p.InvocationScript = r.ReadVarBytes() } // EncodeBinary implements io.Serializable interface. @@ -123,7 +123,7 @@ func (p *commitCompact) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements io.Serializable interface. func (p *preparationCompact) DecodeBinary(r *io.BinReader) { r.ReadLE(&p.ValidatorIndex) - p.InvocationScript = r.ReadBytes() + p.InvocationScript = r.ReadVarBytes() } // EncodeBinary implements io.Serializable interface. diff --git a/pkg/core/contract_state.go b/pkg/core/contract_state.go index 6d976b209..5af26e1a8 100644 --- a/pkg/core/contract_state.go +++ b/pkg/core/contract_state.go @@ -38,7 +38,7 @@ func (a Contracts) commit(store storage.Store) error { // DecodeBinary implements Serializable interface. func (cs *ContractState) DecodeBinary(br *io.BinReader) { - cs.Script = br.ReadBytes() + cs.Script = br.ReadVarBytes() br.ReadArray(&cs.ParamList) br.ReadLE(&cs.ReturnType) br.ReadLE(&cs.Properties) diff --git a/pkg/core/storage_item.go b/pkg/core/storage_item.go index 331104b46..b61b66a2d 100644 --- a/pkg/core/storage_item.go +++ b/pkg/core/storage_item.go @@ -59,6 +59,6 @@ func (si *StorageItem) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements Serializable interface. func (si *StorageItem) DecodeBinary(r *io.BinReader) { - si.Value = r.ReadBytes() + si.Value = r.ReadVarBytes() r.ReadLE(&si.IsConst) } diff --git a/pkg/core/transaction/invocation.go b/pkg/core/transaction/invocation.go index 6276a1a28..50ad3c78f 100644 --- a/pkg/core/transaction/invocation.go +++ b/pkg/core/transaction/invocation.go @@ -35,7 +35,7 @@ func NewInvocationTX(script []byte, gas util.Fixed8) *Transaction { // DecodeBinary implements Serializable interface. func (tx *InvocationTX) DecodeBinary(br *io.BinReader) { - tx.Script = br.ReadBytes() + tx.Script = br.ReadVarBytes() if tx.Version >= 1 { br.ReadLE(&tx.Gas) } else { diff --git a/pkg/core/transaction/publish.go b/pkg/core/transaction/publish.go index 131a4ac9a..fc76f3535 100644 --- a/pkg/core/transaction/publish.go +++ b/pkg/core/transaction/publish.go @@ -22,7 +22,7 @@ type PublishTX struct { // DecodeBinary implements Serializable interface. func (tx *PublishTX) DecodeBinary(br *io.BinReader) { - tx.Script = br.ReadBytes() + tx.Script = br.ReadVarBytes() lenParams := br.ReadVarUint() tx.ParamList = make([]smartcontract.ParamType, lenParams) diff --git a/pkg/core/transaction/state_descriptor.go b/pkg/core/transaction/state_descriptor.go index c5d3cc958..e38889373 100644 --- a/pkg/core/transaction/state_descriptor.go +++ b/pkg/core/transaction/state_descriptor.go @@ -25,8 +25,8 @@ type StateDescriptor struct { func (s *StateDescriptor) DecodeBinary(r *io.BinReader) { r.ReadLE(&s.Type) - s.Key = r.ReadBytes() - s.Value = r.ReadBytes() + s.Key = r.ReadVarBytes() + s.Value = r.ReadVarBytes() s.Field = r.ReadString() } diff --git a/pkg/core/transaction/witness.go b/pkg/core/transaction/witness.go index bdbecb80d..6292ca981 100644 --- a/pkg/core/transaction/witness.go +++ b/pkg/core/transaction/witness.go @@ -17,8 +17,8 @@ type Witness struct { // DecodeBinary implements Serializable interface. func (w *Witness) DecodeBinary(br *io.BinReader) { - w.InvocationScript = br.ReadBytes() - w.VerificationScript = br.ReadBytes() + w.InvocationScript = br.ReadVarBytes() + w.VerificationScript = br.ReadVarBytes() } // EncodeBinary implements Serializable interface. diff --git a/pkg/io/binaryReader.go b/pkg/io/binaryReader.go index 1283537b2..6bc67fd66 100644 --- a/pkg/io/binaryReader.go +++ b/pkg/io/binaryReader.go @@ -127,17 +127,17 @@ func (r *BinReader) ReadVarUint() uint64 { return uint64(b) } -// ReadBytes reads the next set of bytes from the underlying reader. +// ReadVarBytes reads the next set of bytes from the underlying reader. // ReadVarUInt() is used to determine how large that slice is -func (r *BinReader) ReadBytes() []byte { +func (r *BinReader) ReadVarBytes() []byte { n := r.ReadVarUint() b := make([]byte, n) r.ReadLE(b) return b } -// ReadString calls ReadBytes and casts the results as a string. +// ReadString calls ReadVarBytes and casts the results as a string. func (r *BinReader) ReadString() string { - b := r.ReadBytes() + b := r.ReadVarBytes() return string(b) } diff --git a/pkg/io/binaryrw_test.go b/pkg/io/binaryrw_test.go index 8c6d64c6e..7047357a8 100644 --- a/pkg/io/binaryrw_test.go +++ b/pkg/io/binaryrw_test.go @@ -91,7 +91,7 @@ func TestReaderErrHandling(t *testing.T) { assert.Equal(t, i, iorig) val := br.ReadVarUint() assert.Equal(t, val, uint64(0)) - b := br.ReadBytes() + b := br.ReadVarBytes() assert.Equal(t, b, []byte{}) s := br.ReadString() assert.Equal(t, s, "") diff --git a/pkg/network/payload/merkleblock.go b/pkg/network/payload/merkleblock.go index 67142c2b4..410e2b749 100644 --- a/pkg/network/payload/merkleblock.go +++ b/pkg/network/payload/merkleblock.go @@ -21,7 +21,7 @@ func (m *MerkleBlock) DecodeBinary(br *io.BinReader) { m.TxCount = int(br.ReadVarUint()) br.ReadArray(&m.Hashes) - m.Flags = br.ReadBytes() + m.Flags = br.ReadVarBytes() } // EncodeBinary implements Serializable interface. diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index b221eef63..c8d62ab20 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -60,7 +60,7 @@ func (p *Version) DecodeBinary(br *io.BinReader) { br.ReadLE(&p.Timestamp) br.ReadLE(&p.Port) br.ReadLE(&p.Nonce) - p.UserAgent = br.ReadBytes() + p.UserAgent = br.ReadVarBytes() br.ReadLE(&p.StartHeight) br.ReadLE(&p.Relay) } diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go index 3f9667e16..b6ba9b39f 100644 --- a/pkg/smartcontract/contract_test.go +++ b/pkg/smartcontract/contract_test.go @@ -27,7 +27,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) { assert.Equal(t, opcode.PUSH3, opcode.Opcode(b)) for i := 0; i < len(validators); i++ { - bb := br.ReadBytes() + bb := br.ReadVarBytes() if br.Err != nil { t.Fatal(err) } diff --git a/pkg/vm/serialization.go b/pkg/vm/serialization.go index 8f155a8d6..79c7b7228 100644 --- a/pkg/vm/serialization.go +++ b/pkg/vm/serialization.go @@ -102,14 +102,14 @@ func DecodeBinaryStackItem(r *io.BinReader) StackItem { switch stackItemType(t) { case byteArrayT: - data := r.ReadBytes() + data := r.ReadVarBytes() return NewByteArrayItem(data) case booleanT: var b bool r.ReadLE(&b) return NewBoolItem(b) case integerT: - data := r.ReadBytes() + data := r.ReadVarBytes() num := new(big.Int).SetBytes(util.ArrayReverse(data)) return &BigIntegerItem{ value: num, From fccb0085941ec67351435190e1632b44c640ddfe Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 6 Dec 2019 18:37:46 +0300 Subject: [PATCH 4/5] io: implement ReadBytes() --- pkg/consensus/commit.go | 2 +- pkg/consensus/payload.go | 2 +- pkg/consensus/prepare_request.go | 2 +- pkg/consensus/prepare_response.go | 2 +- pkg/consensus/recovery_message.go | 4 ++-- pkg/core/account_state.go | 4 ++-- pkg/core/asset_state.go | 8 ++++---- pkg/core/block_base.go | 12 ++++++------ pkg/core/header.go | 6 +++--- pkg/core/notification_event.go | 4 ++-- pkg/core/spent_coin_state.go | 2 +- pkg/core/transaction/attribute.go | 4 ++-- pkg/core/transaction/input.go | 2 +- pkg/core/transaction/output.go | 4 ++-- pkg/core/transaction/register.go | 2 +- pkg/crypto/keys/publickey.go | 6 +++--- pkg/io/binaryReader.go | 11 ++++++++++- pkg/io/binaryrw_test.go | 25 +++++++++++++++++++++++++ pkg/network/message.go | 2 +- pkg/network/payload/address.go | 2 +- pkg/network/payload/getblocks.go | 2 +- pkg/util/uint256.go | 2 +- 22 files changed, 72 insertions(+), 38 deletions(-) diff --git a/pkg/consensus/commit.go b/pkg/consensus/commit.go index da20be00f..2a3c39445 100644 --- a/pkg/consensus/commit.go +++ b/pkg/consensus/commit.go @@ -23,7 +23,7 @@ func (c *commit) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements io.Serializable interface. func (c *commit) DecodeBinary(r *io.BinReader) { - r.ReadBE(&c.signature) + r.ReadBytes(c.signature[:]) } // Signature implements payload.Commit interface. diff --git a/pkg/consensus/payload.go b/pkg/consensus/payload.go index 80d73e2c7..f8f635314 100644 --- a/pkg/consensus/payload.go +++ b/pkg/consensus/payload.go @@ -212,7 +212,7 @@ func (p *Payload) Verify() bool { // DecodeBinaryUnsigned reads payload from w excluding signature. func (p *Payload) DecodeBinaryUnsigned(r *io.BinReader) { r.ReadLE(&p.version) - r.ReadBE(p.prevHash[:]) + r.ReadBytes(p.prevHash[:]) r.ReadLE(&p.height) r.ReadLE(&p.validatorIndex) r.ReadLE(&p.timestamp) diff --git a/pkg/consensus/prepare_request.go b/pkg/consensus/prepare_request.go index ef98e6512..a8ab4436f 100644 --- a/pkg/consensus/prepare_request.go +++ b/pkg/consensus/prepare_request.go @@ -31,7 +31,7 @@ func (p *prepareRequest) EncodeBinary(w *io.BinWriter) { func (p *prepareRequest) DecodeBinary(r *io.BinReader) { r.ReadLE(&p.timestamp) r.ReadLE(&p.nonce) - r.ReadBE(p.nextConsensus[:]) + r.ReadBytes(p.nextConsensus[:]) r.ReadArray(&p.transactionHashes) p.minerTx.DecodeBinary(r) } diff --git a/pkg/consensus/prepare_response.go b/pkg/consensus/prepare_response.go index 760b9475c..08bbaac72 100644 --- a/pkg/consensus/prepare_response.go +++ b/pkg/consensus/prepare_response.go @@ -20,7 +20,7 @@ func (p *prepareResponse) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements io.Serializable interface. func (p *prepareResponse) DecodeBinary(r *io.BinReader) { - r.ReadBE(p.preparationHash[:]) + r.ReadBytes(p.preparationHash[:]) } // PreparationHash implements payload.PrepareResponse interface. diff --git a/pkg/consensus/recovery_message.go b/pkg/consensus/recovery_message.go index 4e6094f66..365e1917f 100644 --- a/pkg/consensus/recovery_message.go +++ b/pkg/consensus/recovery_message.go @@ -54,7 +54,7 @@ func (m *recoveryMessage) DecodeBinary(r *io.BinReader) { if l != 0 { if l == util.Uint256Size { m.preparationHash = new(util.Uint256) - r.ReadBE(m.preparationHash[:]) + r.ReadBytes(m.preparationHash[:]) } else { r.Err = errors.New("invalid data") } @@ -108,7 +108,7 @@ func (p *changeViewCompact) EncodeBinary(w *io.BinWriter) { func (p *commitCompact) DecodeBinary(r *io.BinReader) { r.ReadLE(&p.ViewNumber) r.ReadLE(&p.ValidatorIndex) - r.ReadBE(p.Signature[:]) + r.ReadBytes(p.Signature[:]) p.InvocationScript = r.ReadVarBytes() } diff --git a/pkg/core/account_state.go b/pkg/core/account_state.go index abec81994..daf07b805 100644 --- a/pkg/core/account_state.go +++ b/pkg/core/account_state.go @@ -103,7 +103,7 @@ func NewAccountState(scriptHash util.Uint160) *AccountState { // DecodeBinary decodes AccountState from the given BinReader. func (s *AccountState) DecodeBinary(br *io.BinReader) { br.ReadLE(&s.Version) - br.ReadLE(&s.ScriptHash) + br.ReadBytes(s.ScriptHash[:]) br.ReadLE(&s.IsFrozen) br.ReadArray(&s.Votes) @@ -111,7 +111,7 @@ func (s *AccountState) DecodeBinary(br *io.BinReader) { lenBalances := br.ReadVarUint() for i := 0; i < int(lenBalances); i++ { key := util.Uint256{} - br.ReadLE(&key) + br.ReadBytes(key[:]) ubs := make([]UnspentBalance, 0) br.ReadArray(&ubs) s.Balances[key] = ubs diff --git a/pkg/core/asset_state.go b/pkg/core/asset_state.go index 00cb7492a..fc0fe09e7 100644 --- a/pkg/core/asset_state.go +++ b/pkg/core/asset_state.go @@ -52,7 +52,7 @@ type AssetState struct { // DecodeBinary implements Serializable interface. func (a *AssetState) DecodeBinary(br *io.BinReader) { - br.ReadLE(&a.ID) + br.ReadBytes(a.ID[:]) br.ReadLE(&a.AssetType) a.Name = br.ReadString() @@ -61,12 +61,12 @@ func (a *AssetState) DecodeBinary(br *io.BinReader) { br.ReadLE(&a.Available) br.ReadLE(&a.Precision) br.ReadLE(&a.FeeMode) - br.ReadLE(&a.FeeAddress) + br.ReadBytes(a.FeeAddress[:]) a.Owner = &keys.PublicKey{} a.Owner.DecodeBinary(br) - br.ReadLE(&a.Admin) - br.ReadLE(&a.Issuer) + br.ReadBytes(a.Admin[:]) + br.ReadBytes(a.Issuer[:]) br.ReadLE(&a.Expiration) br.ReadLE(&a.IsFrozen) } diff --git a/pkg/core/block_base.go b/pkg/core/block_base.go index e4a4bd64f..7ca235ae2 100644 --- a/pkg/core/block_base.go +++ b/pkg/core/block_base.go @@ -73,9 +73,9 @@ func (b *BlockBase) VerificationHash() util.Uint256 { func (b *BlockBase) DecodeBinary(br *io.BinReader) { b.decodeHashableFields(br) - var padding uint8 - br.ReadLE(&padding) - if padding != 1 { + padding := []byte{0} + br.ReadBytes(padding) + if padding[0] != 1 { br.Err = fmt.Errorf("format error: padding must equal 1 got %d", padding) return } @@ -128,12 +128,12 @@ func (b *BlockBase) encodeHashableFields(bw *io.BinWriter) { // see Hash() for more information about the fields. func (b *BlockBase) decodeHashableFields(br *io.BinReader) { br.ReadLE(&b.Version) - br.ReadLE(&b.PrevHash) - br.ReadLE(&b.MerkleRoot) + br.ReadBytes(b.PrevHash[:]) + br.ReadBytes(b.MerkleRoot[:]) br.ReadLE(&b.Timestamp) br.ReadLE(&b.Index) br.ReadLE(&b.ConsensusData) - br.ReadLE(&b.NextConsensus) + br.ReadBytes(b.NextConsensus[:]) // Make the hash of the block here so we dont need to do this // again. diff --git a/pkg/core/header.go b/pkg/core/header.go index e21ce70ed..e3bf50824 100644 --- a/pkg/core/header.go +++ b/pkg/core/header.go @@ -18,10 +18,10 @@ type Header struct { func (h *Header) DecodeBinary(r *io.BinReader) { h.BlockBase.DecodeBinary(r) - var padding uint8 - r.ReadLE(&padding) + padding := []byte{0} + r.ReadBytes(padding) - if padding != 0 { + if padding[0] != 0 { r.Err = fmt.Errorf("format error: padding must equal 0 got %d", padding) } } diff --git a/pkg/core/notification_event.go b/pkg/core/notification_event.go index db30b1cfd..fc1c6e727 100644 --- a/pkg/core/notification_event.go +++ b/pkg/core/notification_event.go @@ -63,7 +63,7 @@ func (ne NotificationEvent) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements the Serializable interface. func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) { - r.ReadLE(&ne.ScriptHash) + r.ReadBytes(ne.ScriptHash[:]) ne.Item = vm.DecodeBinaryStackItem(r) } @@ -75,6 +75,6 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements the Serializable interface. func (aer *AppExecResult) DecodeBinary(r *io.BinReader) { - r.ReadLE(&aer.TxHash) + r.ReadBytes(aer.TxHash[:]) r.ReadArray(&aer.Events) } diff --git a/pkg/core/spent_coin_state.go b/pkg/core/spent_coin_state.go index c3fe07e0f..f298ff9f5 100644 --- a/pkg/core/spent_coin_state.go +++ b/pkg/core/spent_coin_state.go @@ -75,7 +75,7 @@ func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState { // DecodeBinary implements Serializable interface. func (s *SpentCoinState) DecodeBinary(br *io.BinReader) { - br.ReadLE(&s.txHash) + br.ReadBytes(s.txHash[:]) br.ReadLE(&s.txHeight) s.items = make(map[uint16]uint32) diff --git a/pkg/core/transaction/attribute.go b/pkg/core/transaction/attribute.go index 3cd92d333..61f208d79 100644 --- a/pkg/core/transaction/attribute.go +++ b/pkg/core/transaction/attribute.go @@ -22,7 +22,7 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) { if attr.Usage == ECDH02 || attr.Usage == ECDH03 { attr.Data = make([]byte, 33) attr.Data[0] = byte(attr.Usage) - br.ReadLE(attr.Data[1:]) + br.ReadBytes(attr.Data[1:]) return } var datasize uint64 @@ -47,7 +47,7 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) { return } attr.Data = make([]byte, datasize) - br.ReadLE(attr.Data) + br.ReadBytes(attr.Data) } // EncodeBinary implements Serializable interface. diff --git a/pkg/core/transaction/input.go b/pkg/core/transaction/input.go index 27d895afc..e303e10e5 100644 --- a/pkg/core/transaction/input.go +++ b/pkg/core/transaction/input.go @@ -16,7 +16,7 @@ type Input struct { // DecodeBinary implements Serializable interface. func (in *Input) DecodeBinary(br *io.BinReader) { - br.ReadLE(&in.PrevHash) + br.ReadBytes(in.PrevHash[:]) br.ReadLE(&in.PrevIndex) } diff --git a/pkg/core/transaction/output.go b/pkg/core/transaction/output.go index 1e31fa92a..34c836915 100644 --- a/pkg/core/transaction/output.go +++ b/pkg/core/transaction/output.go @@ -35,9 +35,9 @@ func NewOutput(assetID util.Uint256, amount util.Fixed8, scriptHash util.Uint160 // DecodeBinary implements Serializable interface. func (out *Output) DecodeBinary(br *io.BinReader) { - br.ReadLE(&out.AssetID) + br.ReadBytes(out.AssetID[:]) br.ReadLE(&out.Amount) - br.ReadLE(&out.ScriptHash) + br.ReadBytes(out.ScriptHash[:]) } // EncodeBinary implements Serializable interface. diff --git a/pkg/core/transaction/register.go b/pkg/core/transaction/register.go index cccb6f769..bba131263 100644 --- a/pkg/core/transaction/register.go +++ b/pkg/core/transaction/register.go @@ -40,7 +40,7 @@ func (tx *RegisterTX) DecodeBinary(br *io.BinReader) { tx.Owner = &keys.PublicKey{} tx.Owner.DecodeBinary(br) - br.ReadLE(&tx.Admin) + br.ReadBytes(tx.Admin[:]) } // EncodeBinary implements Serializable interface. diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 8f5f3fde9..46a58370d 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -181,7 +181,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) { case 0x02, 0x03: // Compressed public keys xbytes := make([]byte, 32) - r.ReadLE(xbytes) + r.ReadBytes(xbytes) if r.Err != nil { return } @@ -194,8 +194,8 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) { case 0x04: xbytes := make([]byte, 32) ybytes := make([]byte, 32) - r.ReadLE(xbytes) - r.ReadLE(ybytes) + r.ReadBytes(xbytes) + r.ReadBytes(ybytes) if r.Err != nil { return } diff --git a/pkg/io/binaryReader.go b/pkg/io/binaryReader.go index 6bc67fd66..e7c07dcf8 100644 --- a/pkg/io/binaryReader.go +++ b/pkg/io/binaryReader.go @@ -132,10 +132,19 @@ func (r *BinReader) ReadVarUint() uint64 { func (r *BinReader) ReadVarBytes() []byte { n := r.ReadVarUint() b := make([]byte, n) - r.ReadLE(b) + r.ReadBytes(b) return b } +// ReadBytes copies fixed-size buffer from the reader to provided slice. +func (r *BinReader) ReadBytes(buf []byte) { + if r.Err != nil { + return + } + + _, r.Err = io.ReadFull(r.r, buf) +} + // ReadString calls ReadVarBytes and casts the results as a string. func (r *BinReader) ReadString() string { b := r.ReadVarBytes() diff --git a/pkg/io/binaryrw_test.go b/pkg/io/binaryrw_test.go index 7047357a8..40a6d42f2 100644 --- a/pkg/io/binaryrw_test.go +++ b/pkg/io/binaryrw_test.go @@ -332,3 +332,28 @@ func TestBinReader_ReadArray(t *testing.T) { r.Err = errors.New("error") require.Panics(t, func() { r.ReadArray(1) }) } + +func TestBinReader_ReadBytes(t *testing.T) { + data := []byte{0, 1, 2, 3, 4, 5, 6, 7} + r := NewBinReaderFromBuf(data) + + buf := make([]byte, 4) + r.ReadBytes(buf) + require.NoError(t, r.Err) + require.Equal(t, data[:4], buf) + + r.ReadBytes([]byte{}) + require.NoError(t, r.Err) + + buf = make([]byte, 3) + r.ReadBytes(buf) + require.NoError(t, r.Err) + require.Equal(t, data[4:7], buf) + + buf = make([]byte, 2) + r.ReadBytes(buf) + require.Error(t, r.Err) + + r.ReadBytes([]byte{}) + require.Error(t, r.Err) +} diff --git a/pkg/network/message.go b/pkg/network/message.go index 9459079ff..0b656194d 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -150,7 +150,7 @@ func (m *Message) CommandType() CommandType { // Decode decodes a Message from the given reader. func (m *Message) Decode(br *io.BinReader) error { br.ReadLE(&m.Magic) - br.ReadLE(&m.Command) + br.ReadBytes(m.Command[:]) br.ReadLE(&m.Length) br.ReadLE(&m.Checksum) if br.Err != nil { diff --git a/pkg/network/payload/address.go b/pkg/network/payload/address.go index c8a4f9ece..9c8d3444b 100644 --- a/pkg/network/payload/address.go +++ b/pkg/network/payload/address.go @@ -31,7 +31,7 @@ func NewAddressAndTime(e *net.TCPAddr, t time.Time) *AddressAndTime { func (p *AddressAndTime) DecodeBinary(br *io.BinReader) { br.ReadLE(&p.Timestamp) br.ReadLE(&p.Services) - br.ReadBE(&p.IP) + br.ReadBytes(p.IP[:]) br.ReadBE(&p.Port) } diff --git a/pkg/network/payload/getblocks.go b/pkg/network/payload/getblocks.go index 5c5caaebc..45e567119 100644 --- a/pkg/network/payload/getblocks.go +++ b/pkg/network/payload/getblocks.go @@ -24,7 +24,7 @@ func NewGetBlocks(start []util.Uint256, stop util.Uint256) *GetBlocks { // DecodeBinary implements Serializable interface. func (p *GetBlocks) DecodeBinary(br *io.BinReader) { br.ReadArray(&p.HashStart) - br.ReadLE(&p.HashStop) + br.ReadBytes(p.HashStop[:]) } // EncodeBinary implements Serializable interface. diff --git a/pkg/util/uint256.go b/pkg/util/uint256.go index 2c7085e69..81521e1b8 100644 --- a/pkg/util/uint256.go +++ b/pkg/util/uint256.go @@ -123,5 +123,5 @@ func (u Uint256) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements io.Serializable interface. func (u *Uint256) DecodeBinary(r *io.BinReader) { - r.ReadBE(u[:]) + r.ReadBytes(u[:]) } From 0e1621a0dad92cb64437531d7062733d36c4b38c Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 6 Dec 2019 21:27:27 +0300 Subject: [PATCH 5/5] go.mod: update dbft and tidy --- go.mod | 8 +------- go.sum | 3 +++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 16d201058..0dafc50c7 100644 --- a/go.mod +++ b/go.mod @@ -6,21 +6,15 @@ require ( github.com/etcd-io/bbolt v1.3.3 github.com/go-redis/redis v6.10.2+incompatible github.com/go-yaml/yaml v2.1.0+incompatible - github.com/golang/snappy v0.0.1 // indirect - github.com/mattn/go-colorable v0.1.4 // indirect - github.com/mattn/go-isatty v0.0.10 // indirect github.com/mr-tron/base58 v1.1.2 - github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254 + github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae github.com/nspcc-dev/rfc6979 v0.1.0 - github.com/onsi/ginkgo v1.10.3 // indirect - github.com/onsi/gomega v1.7.1 // indirect github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.2.1 github.com/sirupsen/logrus v1.4.2 github.com/stretchr/testify v1.4.0 github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73 github.com/urfave/cli v1.20.0 - github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7 // indirect go.uber.org/atomic v1.4.0 go.uber.org/zap v1.10.0 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 diff --git a/go.sum b/go.sum index 5d727e76e..5226ee678 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= +github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw= @@ -90,6 +91,8 @@ github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjW github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254 h1:A4OkQDQOSPsJF8qUmqNvFDzmIGALrvOCZrMktllDoKc= github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= +github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae h1:T5V1QANlNMKun0EPB3eqg2PTXG4rmLhzDyEiV63kdB0= +github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= github.com/nspcc-dev/neofs-crypto v0.2.0 h1:ftN+59WqxSWz/RCgXYOfhmltOOqU+udsNQSvN6wkFck= github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= github.com/nspcc-dev/rfc6979 v0.1.0 h1:Lwg7esRRoyK1Up/IN1vAef1EmvrBeMHeeEkek2fAJ6c=