From 54d888ba702cd28cc3fac4ada47e556882bfcb4c Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 12 Dec 2019 18:52:23 +0300 Subject: [PATCH] io: add type-specific read/write methods This seriously improves the serialization/deserialization performance for several reasons: * no time spent in `binary` reflection * no memory allocations being made on every read/write * uses fast ReadBytes everywhere it's appropriate It also makes Fixed8 Serializable just for convenience. --- cli/server/server.go | 10 +- pkg/consensus/change_view.go | 4 +- pkg/consensus/payload.go | 27 +++--- pkg/consensus/prepare_request.go | 8 +- pkg/consensus/recovery_message.go | 29 +++--- pkg/consensus/recovery_request.go | 4 +- pkg/core/block.go | 7 +- pkg/core/block_base.go | 16 ++-- pkg/core/blockchain.go | 2 +- pkg/core/dao.go | 19 ++-- pkg/core/spent_coin_state.go | 12 +-- pkg/core/state/account.go | 16 ++-- pkg/core/state/asset.go | 28 +++--- pkg/core/state/contract.go | 8 +- pkg/core/state/notification_event.go | 8 +- pkg/core/state/storage_item.go | 4 +- pkg/core/state/validator.go | 8 +- pkg/core/transaction/attribute.go | 10 +- pkg/core/transaction/input.go | 4 +- pkg/core/transaction/invocation.go | 4 +- pkg/core/transaction/miner.go | 4 +- pkg/core/transaction/output.go | 4 +- pkg/core/transaction/publish.go | 16 ++-- pkg/core/transaction/register.go | 12 +-- pkg/core/transaction/state_descriptor.go | 4 +- pkg/core/transaction/transaction.go | 8 +- pkg/core/unspent_coin_state.go | 6 +- pkg/crypto/keys/publickey.go | 2 +- pkg/io/binaryReader.go | 66 ++++++++++++- pkg/io/binaryWriter.go | 54 ++++++++++- pkg/io/binaryrw_test.go | 117 +++++++++++++++++++++++ pkg/io/size_test.go | 2 +- pkg/network/message.go | 14 +-- pkg/network/payload/address.go | 12 +-- pkg/network/payload/inventory.go | 4 +- pkg/network/payload/version.go | 28 +++--- pkg/rpc/server_helper_test.go | 2 +- pkg/smartcontract/contract_test.go | 10 +- pkg/smartcontract/param_context.go | 4 +- pkg/util/fixed8.go | 12 +++ pkg/util/fixed8_test.go | 15 +++ pkg/vm/context.go | 14 +-- pkg/vm/serialization.go | 8 +- 43 files changed, 441 insertions(+), 205 deletions(-) diff --git a/cli/server/server.go b/cli/server/server.go index d11c5ce7b..9061a832c 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -167,7 +167,7 @@ func dumpDB(ctx *cli.Context) error { if count == 0 { count = chainHeight - skip } - writer.WriteLE(count) + writer.WriteU32LE(count) for i := skip + 1; i <= skip+count; i++ { bh := chain.GetHeaderHash(int(i)) b, err := chain.GetBlock(bh) @@ -211,8 +211,7 @@ func restoreDB(ctx *cli.Context) error { } go chain.Run() - var allBlocks uint32 - reader.ReadLE(&allBlocks) + var allBlocks = reader.ReadU32LE() if reader.Err != nil { return cli.NewExitError(err, 1) } @@ -249,10 +248,9 @@ func restoreDB(ctx *cli.Context) error { // readBlock performs reading of block size and then bytes with the length equal to that size. func readBlock(reader *io.BinReader) ([]byte, error) { - var size uint32 - reader.ReadLE(&size) + var size = reader.ReadU32LE() bytes := make([]byte, size) - reader.ReadLE(bytes) + reader.ReadBytes(bytes) if reader.Err != nil { return nil, reader.Err } diff --git a/pkg/consensus/change_view.go b/pkg/consensus/change_view.go index c47b42485..6c7702c45 100644 --- a/pkg/consensus/change_view.go +++ b/pkg/consensus/change_view.go @@ -15,12 +15,12 @@ var _ payload.ChangeView = (*changeView)(nil) // EncodeBinary implements io.Serializable interface. func (c *changeView) EncodeBinary(w *io.BinWriter) { - w.WriteLE(c.timestamp) + w.WriteU32LE(c.timestamp) } // DecodeBinary implements io.Serializable interface. func (c *changeView) DecodeBinary(r *io.BinReader) { - r.ReadLE(&c.timestamp) + c.timestamp = r.ReadU32LE() } // NewViewNumber implements payload.ChangeView interface. diff --git a/pkg/consensus/payload.go b/pkg/consensus/payload.go index 52ba5c4b8..631440659 100644 --- a/pkg/consensus/payload.go +++ b/pkg/consensus/payload.go @@ -161,11 +161,11 @@ func (p *Payload) SetHeight(h uint32) { // EncodeBinaryUnsigned writes payload to w excluding signature. func (p *Payload) EncodeBinaryUnsigned(w *io.BinWriter) { - w.WriteLE(p.version) + w.WriteU32LE(p.version) w.WriteBytes(p.prevHash[:]) - w.WriteLE(p.height) - w.WriteLE(p.validatorIndex) - w.WriteLE(p.timestamp) + w.WriteU32LE(p.height) + w.WriteU16LE(p.validatorIndex) + w.WriteU32LE(p.timestamp) ww := io.NewBufBinWriter() p.message.EncodeBinary(ww.BinWriter) @@ -176,7 +176,7 @@ func (p *Payload) EncodeBinaryUnsigned(w *io.BinWriter) { func (p *Payload) EncodeBinary(w *io.BinWriter) { p.EncodeBinaryUnsigned(w) - w.WriteLE(byte(1)) + w.WriteByte(1) p.Witness.EncodeBinary(w) } @@ -211,11 +211,11 @@ func (p *Payload) Verify() bool { // DecodeBinaryUnsigned reads payload from w excluding signature. func (p *Payload) DecodeBinaryUnsigned(r *io.BinReader) { - r.ReadLE(&p.version) + p.version = r.ReadU32LE() r.ReadBytes(p.prevHash[:]) - r.ReadLE(&p.height) - r.ReadLE(&p.validatorIndex) - r.ReadLE(&p.timestamp) + p.height = r.ReadU32LE() + p.validatorIndex = r.ReadU16LE() + p.timestamp = r.ReadU32LE() data := r.ReadVarBytes() if r.Err != nil { @@ -242,8 +242,7 @@ func (p *Payload) DecodeBinary(r *io.BinReader) { return } - var b byte - r.ReadLE(&b) + var b = r.ReadByte() if b != 1 { r.Err = errors.New("invalid format") return @@ -255,14 +254,14 @@ func (p *Payload) DecodeBinary(r *io.BinReader) { // EncodeBinary implements io.Serializable interface. func (m *message) EncodeBinary(w *io.BinWriter) { w.WriteBytes([]byte{byte(m.Type)}) - w.WriteLE(m.ViewNumber) + w.WriteByte(m.ViewNumber) m.payload.EncodeBinary(w) } // DecodeBinary implements io.Serializable interface. func (m *message) DecodeBinary(r *io.BinReader) { - r.ReadLE((*byte)(&m.Type)) - r.ReadLE(&m.ViewNumber) + m.Type = messageType(r.ReadByte()) + m.ViewNumber = r.ReadByte() switch m.Type { case changeViewType: diff --git a/pkg/consensus/prepare_request.go b/pkg/consensus/prepare_request.go index a8ab4436f..efc8d53b2 100644 --- a/pkg/consensus/prepare_request.go +++ b/pkg/consensus/prepare_request.go @@ -20,8 +20,8 @@ var _ payload.PrepareRequest = (*prepareRequest)(nil) // EncodeBinary implements io.Serializable interface. func (p *prepareRequest) EncodeBinary(w *io.BinWriter) { - w.WriteLE(p.timestamp) - w.WriteLE(p.nonce) + w.WriteU32LE(p.timestamp) + w.WriteU64LE(p.nonce) w.WriteBytes(p.nextConsensus[:]) w.WriteArray(p.transactionHashes) p.minerTx.EncodeBinary(w) @@ -29,8 +29,8 @@ func (p *prepareRequest) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements io.Serializable interface. func (p *prepareRequest) DecodeBinary(r *io.BinReader) { - r.ReadLE(&p.timestamp) - r.ReadLE(&p.nonce) + p.timestamp = r.ReadU32LE() + p.nonce = r.ReadU64LE() r.ReadBytes(p.nextConsensus[:]) r.ReadArray(&p.transactionHashes) p.minerTx.DecodeBinary(r) diff --git a/pkg/consensus/recovery_message.go b/pkg/consensus/recovery_message.go index 365e1917f..5b17275d0 100644 --- a/pkg/consensus/recovery_message.go +++ b/pkg/consensus/recovery_message.go @@ -44,8 +44,7 @@ var _ payload.RecoveryMessage = (*recoveryMessage)(nil) func (m *recoveryMessage) DecodeBinary(r *io.BinReader) { r.ReadArray(&m.changeViewPayloads) - var hasReq bool - r.ReadLE(&hasReq) + var hasReq = r.ReadBool() if hasReq { m.prepareRequest = new(prepareRequest) m.prepareRequest.DecodeBinary(r) @@ -72,7 +71,7 @@ func (m *recoveryMessage) EncodeBinary(w *io.BinWriter) { w.WriteArray(m.changeViewPayloads) hasReq := m.prepareRequest != nil - w.WriteLE(hasReq) + w.WriteBool(hasReq) if hasReq { m.prepareRequest.EncodeBinary(w) } else { @@ -90,45 +89,45 @@ func (m *recoveryMessage) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements io.Serializable interface. func (p *changeViewCompact) DecodeBinary(r *io.BinReader) { - r.ReadLE(&p.ValidatorIndex) - r.ReadLE(&p.OriginalViewNumber) - r.ReadLE(&p.Timestamp) + p.ValidatorIndex = r.ReadU16LE() + p.OriginalViewNumber = r.ReadByte() + p.Timestamp = r.ReadU32LE() p.InvocationScript = r.ReadVarBytes() } // EncodeBinary implements io.Serializable interface. func (p *changeViewCompact) EncodeBinary(w *io.BinWriter) { - w.WriteLE(p.ValidatorIndex) - w.WriteLE(p.OriginalViewNumber) - w.WriteLE(p.Timestamp) + w.WriteU16LE(p.ValidatorIndex) + w.WriteByte(p.OriginalViewNumber) + w.WriteU32LE(p.Timestamp) w.WriteVarBytes(p.InvocationScript) } // DecodeBinary implements io.Serializable interface. func (p *commitCompact) DecodeBinary(r *io.BinReader) { - r.ReadLE(&p.ViewNumber) - r.ReadLE(&p.ValidatorIndex) + p.ViewNumber = r.ReadByte() + p.ValidatorIndex = r.ReadU16LE() r.ReadBytes(p.Signature[:]) p.InvocationScript = r.ReadVarBytes() } // EncodeBinary implements io.Serializable interface. func (p *commitCompact) EncodeBinary(w *io.BinWriter) { - w.WriteLE(p.ViewNumber) - w.WriteLE(p.ValidatorIndex) + w.WriteByte(p.ViewNumber) + w.WriteU16LE(p.ValidatorIndex) w.WriteBytes(p.Signature[:]) w.WriteVarBytes(p.InvocationScript) } // DecodeBinary implements io.Serializable interface. func (p *preparationCompact) DecodeBinary(r *io.BinReader) { - r.ReadLE(&p.ValidatorIndex) + p.ValidatorIndex = r.ReadU16LE() p.InvocationScript = r.ReadVarBytes() } // EncodeBinary implements io.Serializable interface. func (p *preparationCompact) EncodeBinary(w *io.BinWriter) { - w.WriteLE(p.ValidatorIndex) + w.WriteU16LE(p.ValidatorIndex) w.WriteVarBytes(p.InvocationScript) } diff --git a/pkg/consensus/recovery_request.go b/pkg/consensus/recovery_request.go index d73f34c08..d492279f2 100644 --- a/pkg/consensus/recovery_request.go +++ b/pkg/consensus/recovery_request.go @@ -14,12 +14,12 @@ var _ payload.RecoveryRequest = (*recoveryRequest)(nil) // DecodeBinary implements io.Serializable interface. func (m *recoveryRequest) DecodeBinary(r *io.BinReader) { - r.ReadLE(&m.timestamp) + m.timestamp = r.ReadU32LE() } // EncodeBinary implements io.Serializable interface. func (m *recoveryRequest) EncodeBinary(w *io.BinWriter) { - w.WriteLE(m.timestamp) + w.WriteU32LE(m.timestamp) } // Timestamp implements payload.RecoveryRequest interface. diff --git a/pkg/core/block.go b/pkg/core/block.go index 2a6af8d27..4b34ae1a6 100644 --- a/pkg/core/block.go +++ b/pkg/core/block.go @@ -88,8 +88,7 @@ func NewBlockFromTrimmedBytes(b []byte) (*Block, error) { br := io.NewBinReaderFromBuf(b) block.decodeHashableFields(br) - var padding uint8 - br.ReadLE(&padding) + _ = br.ReadByte() block.Script.DecodeBinary(br) @@ -97,7 +96,7 @@ func NewBlockFromTrimmedBytes(b []byte) (*Block, error) { block.Transactions = make([]*transaction.Transaction, lenTX) for i := 0; i < int(lenTX); i++ { var hash util.Uint256 - br.ReadLE(&hash) + hash.DecodeBinary(br) block.Transactions[i] = transaction.NewTrimmedTX(hash) } @@ -110,7 +109,7 @@ func NewBlockFromTrimmedBytes(b []byte) (*Block, error) { func (b *Block) Trim() ([]byte, error) { buf := io.NewBufBinWriter() b.encodeHashableFields(buf.BinWriter) - buf.WriteBytes([]byte{1}) + buf.WriteByte(1) b.Script.EncodeBinary(buf.BinWriter) buf.WriteVarUint(uint64(len(b.Transactions))) diff --git a/pkg/core/block_base.go b/pkg/core/block_base.go index d67970009..9e6c1d0b5 100644 --- a/pkg/core/block_base.go +++ b/pkg/core/block_base.go @@ -114,24 +114,24 @@ func (b *BlockBase) createHash() { // encodeHashableFields will only encode the fields used for hashing. // see Hash() for more information about the fields. func (b *BlockBase) encodeHashableFields(bw *io.BinWriter) { - bw.WriteLE(b.Version) + bw.WriteU32LE(b.Version) bw.WriteBytes(b.PrevHash[:]) bw.WriteBytes(b.MerkleRoot[:]) - bw.WriteLE(b.Timestamp) - bw.WriteLE(b.Index) - bw.WriteLE(b.ConsensusData) + bw.WriteU32LE(b.Timestamp) + bw.WriteU32LE(b.Index) + bw.WriteU64LE(b.ConsensusData) bw.WriteBytes(b.NextConsensus[:]) } // decodeHashableFields decodes the fields used for hashing. // see Hash() for more information about the fields. func (b *BlockBase) decodeHashableFields(br *io.BinReader) { - br.ReadLE(&b.Version) + b.Version = br.ReadU32LE() br.ReadBytes(b.PrevHash[:]) br.ReadBytes(b.MerkleRoot[:]) - br.ReadLE(&b.Timestamp) - br.ReadLE(&b.Index) - br.ReadLE(&b.ConsensusData) + b.Timestamp = br.ReadU32LE() + b.Index = br.ReadU32LE() + b.ConsensusData = br.ReadU64LE() br.ReadBytes(b.NextConsensus[:]) // Make the hash of the block here so we dont need to do this diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 8d43ff553..ab72791fc 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1451,7 +1451,7 @@ func (bc *Blockchain) verifyBlockWitnesses(block *Block, prevHeader *Header) err func hashAndIndexToBytes(h util.Uint256, index uint32) []byte { buf := io.NewBufBinWriter() buf.WriteBytes(h.BytesLE()) - buf.WriteLE(index) + buf.WriteU32LE(index) return buf.Bytes() } diff --git a/pkg/core/dao.go b/pkg/core/dao.go index 76a27ab3a..1a0c8cea3 100644 --- a/pkg/core/dao.go +++ b/pkg/core/dao.go @@ -449,8 +449,7 @@ func (dao *dao) GetTransaction(hash util.Uint256) (*transaction.Transaction, uin } r := io.NewBinReaderFromBuf(b) - var height uint32 - r.ReadLE(&height) + var height = r.ReadU32LE() tx := &transaction.Transaction{} tx.DecodeBinary(r) @@ -476,9 +475,8 @@ func (dao *dao) PutCurrentHeader(hashAndIndex []byte) error { func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) { r := bytes.NewReader(b) br := io.NewBinReaderFromIO(r) - lenHashes := br.ReadVarUint() - hashes := make([]util.Uint256, lenHashes) - br.ReadLE(hashes) + hashes := make([]util.Uint256, 0) + br.ReadArray(&hashes) if br.Err != nil { return nil, br.Err } @@ -502,12 +500,12 @@ func (dao *dao) StoreAsBlock(block *Block, sysFee uint32) error { buf = io.NewBufBinWriter() ) // sysFee needs to be handled somehow - // buf.WriteLE(sysFee) + // buf.WriteU32LE(sysFee) b, err := block.Trim() if err != nil { return err } - buf.WriteLE(b) + buf.WriteBytes(b) if buf.Err != nil { return buf.Err } @@ -517,8 +515,9 @@ func (dao *dao) StoreAsBlock(block *Block, sysFee uint32) error { // StoreAsCurrentBlock stores the given block witch prefix SYSCurrentBlock. func (dao *dao) StoreAsCurrentBlock(block *Block) error { buf := io.NewBufBinWriter() - buf.WriteLE(block.Hash().BytesLE()) - buf.WriteLE(block.Index) + h := block.Hash() + h.EncodeBinary(buf.BinWriter) + buf.WriteU32LE(block.Index) return dao.store.Put(storage.SYSCurrentBlock.Bytes(), buf.Bytes()) } @@ -526,7 +525,7 @@ func (dao *dao) StoreAsCurrentBlock(block *Block) error { func (dao *dao) StoreAsTransaction(tx *transaction.Transaction, index uint32) error { key := storage.AppendPrefix(storage.DataTransaction, tx.Hash().BytesLE()) buf := io.NewBufBinWriter() - buf.WriteLE(index) + buf.WriteU32LE(index) tx.EncodeBinary(buf.BinWriter) if buf.Err != nil { return buf.Err diff --git a/pkg/core/spent_coin_state.go b/pkg/core/spent_coin_state.go index 67332f766..def3cbbaa 100644 --- a/pkg/core/spent_coin_state.go +++ b/pkg/core/spent_coin_state.go @@ -26,7 +26,7 @@ func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState { // DecodeBinary implements Serializable interface. func (s *SpentCoinState) DecodeBinary(br *io.BinReader) { br.ReadBytes(s.txHash[:]) - br.ReadLE(&s.txHeight) + s.txHeight = br.ReadU32LE() s.items = make(map[uint16]uint32) lenItems := br.ReadVarUint() @@ -35,8 +35,8 @@ func (s *SpentCoinState) DecodeBinary(br *io.BinReader) { key uint16 value uint32 ) - br.ReadLE(&key) - br.ReadLE(&value) + key = br.ReadU16LE() + value = br.ReadU32LE() s.items[key] = value } } @@ -44,10 +44,10 @@ func (s *SpentCoinState) DecodeBinary(br *io.BinReader) { // EncodeBinary implements Serializable interface. func (s *SpentCoinState) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(s.txHash[:]) - bw.WriteLE(s.txHeight) + bw.WriteU32LE(s.txHeight) bw.WriteVarUint(uint64(len(s.items))) for k, v := range s.items { - bw.WriteLE(k) - bw.WriteLE(v) + bw.WriteU16LE(k) + bw.WriteU32LE(v) } } diff --git a/pkg/core/state/account.go b/pkg/core/state/account.go index e72fdc875..a6462e474 100644 --- a/pkg/core/state/account.go +++ b/pkg/core/state/account.go @@ -39,9 +39,9 @@ func NewAccount(scriptHash util.Uint160) *Account { // DecodeBinary decodes Account from the given BinReader. func (s *Account) DecodeBinary(br *io.BinReader) { - br.ReadLE(&s.Version) + s.Version = uint8(br.ReadByte()) br.ReadBytes(s.ScriptHash[:]) - br.ReadLE(&s.IsFrozen) + s.IsFrozen = br.ReadBool() br.ReadArray(&s.Votes) s.Balances = make(map[util.Uint256][]UnspentBalance) @@ -57,9 +57,9 @@ func (s *Account) DecodeBinary(br *io.BinReader) { // EncodeBinary encodes Account to the given BinWriter. func (s *Account) EncodeBinary(bw *io.BinWriter) { - bw.WriteLE(s.Version) + bw.WriteByte(byte(s.Version)) bw.WriteBytes(s.ScriptHash[:]) - bw.WriteLE(s.IsFrozen) + bw.WriteBool(s.IsFrozen) bw.WriteArray(s.Votes) bw.WriteVarUint(uint64(len(s.Balances))) @@ -72,15 +72,15 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) { // DecodeBinary implements io.Serializable interface. func (u *UnspentBalance) DecodeBinary(r *io.BinReader) { u.Tx.DecodeBinary(r) - r.ReadLE(&u.Index) - r.ReadLE(&u.Value) + u.Index = r.ReadU16LE() + u.Value.DecodeBinary(r) } // EncodeBinary implements io.Serializable interface. func (u *UnspentBalance) EncodeBinary(w *io.BinWriter) { u.Tx.EncodeBinary(w) - w.WriteLE(u.Index) - w.WriteLE(u.Value) + w.WriteU16LE(u.Index) + u.Value.EncodeBinary(w) } // GetBalanceValues sums all unspent outputs and returns a map of asset IDs to diff --git a/pkg/core/state/asset.go b/pkg/core/state/asset.go index b5268ce31..bbca1eca2 100644 --- a/pkg/core/state/asset.go +++ b/pkg/core/state/asset.go @@ -29,40 +29,40 @@ type Asset struct { // DecodeBinary implements Serializable interface. func (a *Asset) DecodeBinary(br *io.BinReader) { br.ReadBytes(a.ID[:]) - br.ReadLE(&a.AssetType) + a.AssetType = transaction.AssetType(br.ReadByte()) a.Name = br.ReadString() - br.ReadLE(&a.Amount) - br.ReadLE(&a.Available) - br.ReadLE(&a.Precision) - br.ReadLE(&a.FeeMode) + a.Amount.DecodeBinary(br) + a.Available.DecodeBinary(br) + a.Precision = uint8(br.ReadByte()) + a.FeeMode = uint8(br.ReadByte()) br.ReadBytes(a.FeeAddress[:]) a.Owner.DecodeBinary(br) br.ReadBytes(a.Admin[:]) br.ReadBytes(a.Issuer[:]) - br.ReadLE(&a.Expiration) - br.ReadLE(&a.IsFrozen) + a.Expiration = br.ReadU32LE() + a.IsFrozen = br.ReadBool() } // EncodeBinary implements Serializable interface. func (a *Asset) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(a.ID[:]) - bw.WriteLE(a.AssetType) + bw.WriteByte(byte(a.AssetType)) bw.WriteString(a.Name) - bw.WriteLE(a.Amount) - bw.WriteLE(a.Available) - bw.WriteLE(a.Precision) - bw.WriteLE(a.FeeMode) + a.Amount.EncodeBinary(bw) + a.Available.EncodeBinary(bw) + bw.WriteByte(byte(a.Precision)) + bw.WriteByte(byte(a.FeeMode)) bw.WriteBytes(a.FeeAddress[:]) a.Owner.EncodeBinary(bw) bw.WriteBytes(a.Admin[:]) bw.WriteBytes(a.Issuer[:]) - bw.WriteLE(a.Expiration) - bw.WriteLE(a.IsFrozen) + bw.WriteU32LE(a.Expiration) + bw.WriteBool(a.IsFrozen) } // GetName returns the asset name based on its type. diff --git a/pkg/core/state/contract.go b/pkg/core/state/contract.go index 443bf918c..0c83aff20 100644 --- a/pkg/core/state/contract.go +++ b/pkg/core/state/contract.go @@ -26,8 +26,8 @@ type Contract struct { func (cs *Contract) DecodeBinary(br *io.BinReader) { cs.Script = br.ReadVarBytes() br.ReadArray(&cs.ParamList) - br.ReadLE(&cs.ReturnType) - br.ReadLE(&cs.Properties) + cs.ReturnType = smartcontract.ParamType(br.ReadByte()) + cs.Properties = smartcontract.PropertyState(br.ReadByte()) cs.Name = br.ReadString() cs.CodeVersion = br.ReadString() cs.Author = br.ReadString() @@ -40,8 +40,8 @@ func (cs *Contract) DecodeBinary(br *io.BinReader) { func (cs *Contract) EncodeBinary(bw *io.BinWriter) { bw.WriteVarBytes(cs.Script) bw.WriteArray(cs.ParamList) - bw.WriteLE(cs.ReturnType) - bw.WriteLE(cs.Properties) + bw.WriteByte(byte(cs.ReturnType)) + bw.WriteByte(byte(cs.Properties)) bw.WriteString(cs.Name) bw.WriteString(cs.CodeVersion) bw.WriteString(cs.Author) diff --git a/pkg/core/state/notification_event.go b/pkg/core/state/notification_event.go index d86165be9..e5ed52f61 100644 --- a/pkg/core/state/notification_event.go +++ b/pkg/core/state/notification_event.go @@ -39,9 +39,9 @@ func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) { // EncodeBinary implements the Serializable interface. func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { w.WriteBytes(aer.TxHash[:]) - w.WriteLE(aer.Trigger) + w.WriteByte(aer.Trigger) w.WriteString(aer.VMState) - w.WriteLE(aer.GasConsumed) + aer.GasConsumed.EncodeBinary(w) w.WriteString(aer.Stack) w.WriteArray(aer.Events) } @@ -49,9 +49,9 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements the Serializable interface. func (aer *AppExecResult) DecodeBinary(r *io.BinReader) { r.ReadBytes(aer.TxHash[:]) - r.ReadLE(&aer.Trigger) + aer.Trigger = r.ReadByte() aer.VMState = r.ReadString() - r.ReadLE(&aer.GasConsumed) + aer.GasConsumed.DecodeBinary(r) aer.Stack = r.ReadString() r.ReadArray(&aer.Events) } diff --git a/pkg/core/state/storage_item.go b/pkg/core/state/storage_item.go index 49845f07b..12cd24ad8 100644 --- a/pkg/core/state/storage_item.go +++ b/pkg/core/state/storage_item.go @@ -13,11 +13,11 @@ type StorageItem struct { // EncodeBinary implements Serializable interface. func (si *StorageItem) EncodeBinary(w *io.BinWriter) { w.WriteVarBytes(si.Value) - w.WriteLE(si.IsConst) + w.WriteBool(si.IsConst) } // DecodeBinary implements Serializable interface. func (si *StorageItem) DecodeBinary(r *io.BinReader) { si.Value = r.ReadVarBytes() - r.ReadLE(&si.IsConst) + si.IsConst = r.ReadBool() } diff --git a/pkg/core/state/validator.go b/pkg/core/state/validator.go index c9f5a5862..bd3414a1f 100644 --- a/pkg/core/state/validator.go +++ b/pkg/core/state/validator.go @@ -21,16 +21,16 @@ func (vs *Validator) RegisteredAndHasVotes() bool { // EncodeBinary encodes Validator to the given BinWriter. func (vs *Validator) EncodeBinary(bw *io.BinWriter) { vs.PublicKey.EncodeBinary(bw) - bw.WriteLE(vs.Registered) - bw.WriteLE(vs.Votes) + bw.WriteBool(vs.Registered) + vs.Votes.EncodeBinary(bw) } // DecodeBinary decodes Validator from the given BinReader. func (vs *Validator) DecodeBinary(reader *io.BinReader) { vs.PublicKey = &keys.PublicKey{} vs.PublicKey.DecodeBinary(reader) - reader.ReadLE(&vs.Registered) - reader.ReadLE(&vs.Votes) + vs.Registered = reader.ReadBool() + vs.Votes.DecodeBinary(reader) } // GetValidatorsWeightedAverage applies weighted filter based on votes for validator and returns number of validators. diff --git a/pkg/core/transaction/attribute.go b/pkg/core/transaction/attribute.go index 61f208d79..b2e2fb6e2 100644 --- a/pkg/core/transaction/attribute.go +++ b/pkg/core/transaction/attribute.go @@ -16,7 +16,7 @@ type Attribute struct { // DecodeBinary implements Serializable interface. func (attr *Attribute) DecodeBinary(br *io.BinReader) { - br.ReadLE(&attr.Usage) + attr.Usage = AttrUsage(br.ReadByte()) // very special case if attr.Usage == ECDH02 || attr.Usage == ECDH03 { @@ -35,8 +35,7 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) { datasize = 20 case DescriptionURL: // It's not VarUint as per C# implementation, dunno why - var urllen uint8 - br.ReadLE(&urllen) + var urllen = br.ReadByte() datasize = uint64(urllen) case Description, Remark, Remark1, Remark2, Remark3, Remark4, Remark5, Remark6, Remark7, Remark8, Remark9, Remark10, Remark11, @@ -52,7 +51,7 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) { // EncodeBinary implements Serializable interface. func (attr *Attribute) EncodeBinary(bw *io.BinWriter) { - bw.WriteLE(&attr.Usage) + bw.WriteByte(byte(attr.Usage)) switch attr.Usage { case ECDH02, ECDH03: bw.WriteBytes(attr.Data[1:]) @@ -61,8 +60,7 @@ func (attr *Attribute) EncodeBinary(bw *io.BinWriter) { Remark12, Remark13, Remark14, Remark15: bw.WriteVarBytes(attr.Data) case DescriptionURL: - var urllen = uint8(len(attr.Data)) - bw.WriteLE(urllen) + bw.WriteByte(byte(len(attr.Data))) fallthrough case Script, ContractHash, Vote, Hash1, Hash2, Hash3, Hash4, Hash5, Hash6, Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15: diff --git a/pkg/core/transaction/input.go b/pkg/core/transaction/input.go index e303e10e5..da390cfe5 100644 --- a/pkg/core/transaction/input.go +++ b/pkg/core/transaction/input.go @@ -17,11 +17,11 @@ type Input struct { // DecodeBinary implements Serializable interface. func (in *Input) DecodeBinary(br *io.BinReader) { br.ReadBytes(in.PrevHash[:]) - br.ReadLE(&in.PrevIndex) + in.PrevIndex = br.ReadU16LE() } // EncodeBinary implements Serializable interface. func (in *Input) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(in.PrevHash[:]) - bw.WriteLE(in.PrevIndex) + bw.WriteU16LE(in.PrevIndex) } diff --git a/pkg/core/transaction/invocation.go b/pkg/core/transaction/invocation.go index cf60cb403..58f317fd7 100644 --- a/pkg/core/transaction/invocation.go +++ b/pkg/core/transaction/invocation.go @@ -37,7 +37,7 @@ func NewInvocationTX(script []byte, gas util.Fixed8) *Transaction { func (tx *InvocationTX) DecodeBinary(br *io.BinReader) { tx.Script = br.ReadVarBytes() if tx.Version >= 1 { - br.ReadLE(&tx.Gas) + tx.Gas.DecodeBinary(br) } else { tx.Gas = util.Fixed8FromInt64(0) } @@ -47,6 +47,6 @@ func (tx *InvocationTX) DecodeBinary(br *io.BinReader) { func (tx *InvocationTX) EncodeBinary(bw *io.BinWriter) { bw.WriteVarBytes(tx.Script) if tx.Version >= 1 { - bw.WriteLE(tx.Gas) + tx.Gas.EncodeBinary(bw) } } diff --git a/pkg/core/transaction/miner.go b/pkg/core/transaction/miner.go index 1e1ab3753..5189d34b6 100644 --- a/pkg/core/transaction/miner.go +++ b/pkg/core/transaction/miner.go @@ -12,10 +12,10 @@ type MinerTX struct { // DecodeBinary implements Serializable interface. func (tx *MinerTX) DecodeBinary(r *io.BinReader) { - r.ReadLE(&tx.Nonce) + tx.Nonce = r.ReadU32LE() } // EncodeBinary implements Serializable interface. func (tx *MinerTX) EncodeBinary(w *io.BinWriter) { - w.WriteLE(tx.Nonce) + w.WriteU32LE(tx.Nonce) } diff --git a/pkg/core/transaction/output.go b/pkg/core/transaction/output.go index 34c836915..72dd74f2d 100644 --- a/pkg/core/transaction/output.go +++ b/pkg/core/transaction/output.go @@ -36,14 +36,14 @@ func NewOutput(assetID util.Uint256, amount util.Fixed8, scriptHash util.Uint160 // DecodeBinary implements Serializable interface. func (out *Output) DecodeBinary(br *io.BinReader) { br.ReadBytes(out.AssetID[:]) - br.ReadLE(&out.Amount) + out.Amount.DecodeBinary(br) br.ReadBytes(out.ScriptHash[:]) } // EncodeBinary implements Serializable interface. func (out *Output) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(out.AssetID[:]) - bw.WriteLE(out.Amount) + out.Amount.EncodeBinary(bw) bw.WriteBytes(out.ScriptHash[:]) } diff --git a/pkg/core/transaction/publish.go b/pkg/core/transaction/publish.go index fc76f3535..fc592b86b 100644 --- a/pkg/core/transaction/publish.go +++ b/pkg/core/transaction/publish.go @@ -27,17 +27,13 @@ func (tx *PublishTX) DecodeBinary(br *io.BinReader) { lenParams := br.ReadVarUint() tx.ParamList = make([]smartcontract.ParamType, lenParams) for i := 0; i < int(lenParams); i++ { - var ptype uint8 - br.ReadLE(&ptype) - tx.ParamList[i] = smartcontract.ParamType(ptype) + tx.ParamList[i] = smartcontract.ParamType(br.ReadByte()) } - var rtype uint8 - br.ReadLE(&rtype) - tx.ReturnType = smartcontract.ParamType(rtype) + tx.ReturnType = smartcontract.ParamType(br.ReadByte()) if tx.Version >= 1 { - br.ReadLE(&tx.NeedStorage) + tx.NeedStorage = br.ReadBool() } else { tx.NeedStorage = false } @@ -54,11 +50,11 @@ func (tx *PublishTX) EncodeBinary(bw *io.BinWriter) { bw.WriteVarBytes(tx.Script) bw.WriteVarUint(uint64(len(tx.ParamList))) for _, param := range tx.ParamList { - bw.WriteLE(uint8(param)) + bw.WriteByte(byte(param)) } - bw.WriteLE(uint8(tx.ReturnType)) + bw.WriteByte(byte(tx.ReturnType)) if tx.Version >= 1 { - bw.WriteLE(tx.NeedStorage) + bw.WriteBool(tx.NeedStorage) } bw.WriteString(tx.Name) bw.WriteString(tx.CodeVersion) diff --git a/pkg/core/transaction/register.go b/pkg/core/transaction/register.go index 2c7758e84..eaa4bc7fd 100644 --- a/pkg/core/transaction/register.go +++ b/pkg/core/transaction/register.go @@ -30,12 +30,12 @@ type RegisterTX struct { // DecodeBinary implements Serializable interface. func (tx *RegisterTX) DecodeBinary(br *io.BinReader) { - br.ReadLE(&tx.AssetType) + tx.AssetType = AssetType(br.ReadByte()) tx.Name = br.ReadString() - br.ReadLE(&tx.Amount) - br.ReadLE(&tx.Precision) + tx.Amount.DecodeBinary(br) + tx.Precision = uint8(br.ReadByte()) tx.Owner.DecodeBinary(br) @@ -44,10 +44,10 @@ func (tx *RegisterTX) DecodeBinary(br *io.BinReader) { // EncodeBinary implements Serializable interface. func (tx *RegisterTX) EncodeBinary(bw *io.BinWriter) { - bw.WriteLE(tx.AssetType) + bw.WriteByte(byte(tx.AssetType)) bw.WriteString(tx.Name) - bw.WriteLE(tx.Amount) - bw.WriteLE(tx.Precision) + tx.Amount.EncodeBinary(bw) + bw.WriteByte(byte(tx.Precision)) bw.WriteBytes(tx.Owner.Bytes()) bw.WriteBytes(tx.Admin[:]) } diff --git a/pkg/core/transaction/state_descriptor.go b/pkg/core/transaction/state_descriptor.go index e38889373..1f2e5a5d4 100644 --- a/pkg/core/transaction/state_descriptor.go +++ b/pkg/core/transaction/state_descriptor.go @@ -23,7 +23,7 @@ type StateDescriptor struct { // DecodeBinary implements Serializable interface. func (s *StateDescriptor) DecodeBinary(r *io.BinReader) { - r.ReadLE(&s.Type) + s.Type = DescStateType(r.ReadByte()) s.Key = r.ReadVarBytes() s.Value = r.ReadVarBytes() @@ -32,7 +32,7 @@ func (s *StateDescriptor) DecodeBinary(r *io.BinReader) { // EncodeBinary implements Serializable interface. func (s *StateDescriptor) EncodeBinary(w *io.BinWriter) { - w.WriteLE(s.Type) + w.WriteByte(byte(s.Type)) w.WriteVarBytes(s.Key) w.WriteVarBytes(s.Value) w.WriteString(s.Field) diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 2669c5e8a..42aa6953e 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -92,8 +92,8 @@ func (t *Transaction) AddInput(in *Input) { // DecodeBinary implements Serializable interface. func (t *Transaction) DecodeBinary(br *io.BinReader) { - br.ReadLE(&t.Type) - br.ReadLE(&t.Version) + t.Type = TXType(br.ReadByte()) + t.Version = uint8(br.ReadByte()) t.decodeData(br) br.ReadArray(&t.Attributes) @@ -151,8 +151,8 @@ func (t *Transaction) EncodeBinary(bw *io.BinWriter) { // encodeHashableFields encodes the fields that are not used for // signing the transaction, which are all fields except the scripts. func (t *Transaction) encodeHashableFields(bw *io.BinWriter) { - bw.WriteLE(t.Type) - bw.WriteLE(t.Version) + bw.WriteByte(byte(t.Type)) + bw.WriteByte(byte(t.Version)) // Underlying TXer. if t.Data != nil { diff --git a/pkg/core/unspent_coin_state.go b/pkg/core/unspent_coin_state.go index c4c8f9723..c354eefb9 100644 --- a/pkg/core/unspent_coin_state.go +++ b/pkg/core/unspent_coin_state.go @@ -25,7 +25,7 @@ func NewUnspentCoinState(n int) *UnspentCoinState { func (s *UnspentCoinState) EncodeBinary(bw *io.BinWriter) { bw.WriteVarUint(uint64(len(s.states))) for _, state := range s.states { - bw.WriteBytes([]byte{byte(state)}) + bw.WriteByte(byte(state)) } } @@ -34,8 +34,6 @@ func (s *UnspentCoinState) DecodeBinary(br *io.BinReader) { lenStates := br.ReadVarUint() s.states = make([]state.Coin, lenStates) for i := 0; i < int(lenStates); i++ { - var coinState uint8 - br.ReadLE(&coinState) - s.states[i] = state.Coin(coinState) + s.states[i] = state.Coin(br.ReadByte()) } } diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 46a58370d..b886abbcf 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -168,7 +168,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) { var x, y *big.Int var err error - r.ReadLE(&prefix) + prefix = uint8(r.ReadByte()) if r.Err != nil { return } diff --git a/pkg/io/binaryReader.go b/pkg/io/binaryReader.go index e7c07dcf8..b05846753 100644 --- a/pkg/io/binaryReader.go +++ b/pkg/io/binaryReader.go @@ -16,12 +16,20 @@ const maxArraySize = 0x1000000 // Used to simplify error handling when reading into a struct with many fields. type BinReader struct { r io.Reader + u64 []byte + u32 []byte + u16 []byte + u8 []byte Err error } // NewBinReaderFromIO makes a BinReader from io.Reader. func NewBinReaderFromIO(ior io.Reader) *BinReader { - return &BinReader{r: ior} + u64 := make([]byte, 8) + u32 := u64[:4] + u16 := u64[:2] + u8 := u64[:1] + return &BinReader{r: ior, u64: u64, u32: u32, u16: u16, u8: u8} } // NewBinReaderFromBuf makes a BinReader from byte buffer. @@ -39,6 +47,62 @@ func (r *BinReader) ReadLE(v interface{}) { r.Err = binary.Read(r.r, binary.LittleEndian, v) } +// ReadU64LE reads a little-endian encoded uint64 value from the underlying +// io.Reader. On read failures it returns zero. +func (r *BinReader) ReadU64LE() uint64 { + r.ReadBytes(r.u64) + if r.Err != nil { + return 0 + } + return binary.LittleEndian.Uint64(r.u64) +} + +// ReadU32LE reads a little-endian encoded uint32 value from the underlying +// io.Reader. On read failures it returns zero. +func (r *BinReader) ReadU32LE() uint32 { + r.ReadBytes(r.u32) + if r.Err != nil { + return 0 + } + return binary.LittleEndian.Uint32(r.u32) +} + +// ReadU16LE reads a little-endian encoded uint16 value from the underlying +// io.Reader. On read failures it returns zero. +func (r *BinReader) ReadU16LE() uint16 { + r.ReadBytes(r.u16) + if r.Err != nil { + return 0 + } + return binary.LittleEndian.Uint16(r.u16) +} + +// ReadU16BE reads a big-endian encoded uint16 value from the underlying +// io.Reader. On read failures it returns zero. +func (r *BinReader) ReadU16BE() uint16 { + r.ReadBytes(r.u16) + if r.Err != nil { + return 0 + } + return binary.BigEndian.Uint16(r.u16) +} + +// ReadByte reads a byte from the underlying io.Reader. On read failures it +// returns zero. +func (r *BinReader) ReadByte() byte { + r.ReadBytes(r.u8) + if r.Err != nil { + return 0 + } + return r.u8[0] +} + +// ReadBool reads a boolean value encoded in a zero/non-zero byte from the +// underlying io.Reader. On read failures it returns false. +func (r *BinReader) ReadBool() bool { + return r.ReadByte() != 0 +} + // ReadArray reads array into value which must be // a pointer to a slice. func (r *BinReader) ReadArray(t interface{}, maxSize ...int) { diff --git a/pkg/io/binaryWriter.go b/pkg/io/binaryWriter.go index 457f7f846..668b1b875 100644 --- a/pkg/io/binaryWriter.go +++ b/pkg/io/binaryWriter.go @@ -11,12 +11,20 @@ import ( // from a struct with many fields. type BinWriter struct { w io.Writer + u64 []byte + u32 []byte + u16 []byte + u8 []byte Err error } // NewBinWriterFromIO makes a BinWriter from io.Writer. func NewBinWriterFromIO(iow io.Writer) *BinWriter { - return &BinWriter{w: iow} + u64 := make([]byte, 8) + u32 := u64[:4] + u16 := u64[:2] + u8 := u64[:1] + return &BinWriter{w: iow, u64: u64, u32: u32, u16: u16, u8: u8} } // WriteLE writes into the underlying io.Writer from an object v in little-endian format. @@ -35,6 +43,50 @@ func (w *BinWriter) WriteBE(v interface{}) { w.Err = binary.Write(w.w, binary.BigEndian, v) } +// WriteU64LE writes an uint64 value into the underlying io.Writer in +// little-endian format. +func (w *BinWriter) WriteU64LE(u64 uint64) { + binary.LittleEndian.PutUint64(w.u64, u64) + w.WriteBytes(w.u64) +} + +// WriteU32LE writes an uint32 value into the underlying io.Writer in +// little-endian format. +func (w *BinWriter) WriteU32LE(u32 uint32) { + binary.LittleEndian.PutUint32(w.u32, u32) + w.WriteBytes(w.u32) +} + +// WriteU16LE writes an uint16 value into the underlying io.Writer in +// little-endian format. +func (w *BinWriter) WriteU16LE(u16 uint16) { + binary.LittleEndian.PutUint16(w.u16, u16) + w.WriteBytes(w.u16) +} + +// WriteU16BE writes an uint16 value into the underlying io.Writer in +// big-endian format. +func (w *BinWriter) WriteU16BE(u16 uint16) { + binary.BigEndian.PutUint16(w.u16, u16) + w.WriteBytes(w.u16) +} + +// WriteByte writes a byte into the underlying io.Writer. +func (w *BinWriter) WriteByte(u8 byte) { + w.u8[0] = u8 + w.WriteBytes(w.u8) +} + +// WriteBool writes a boolean value into the underlying io.Writer encoded as +// a byte with values of 0 or 1. +func (w *BinWriter) WriteBool(b bool) { + var i byte + if b { + i = 1 + } + w.WriteByte(i) +} + // WriteArray writes a slice or an array arr into w. Note that nil slices and // empty slices are gonna be treated the same resulting in equal zero-length // array encoded. diff --git a/pkg/io/binaryrw_test.go b/pkg/io/binaryrw_test.go index a3e55c0eb..d7f0f8ec4 100644 --- a/pkg/io/binaryrw_test.go +++ b/pkg/io/binaryrw_test.go @@ -53,6 +53,123 @@ func TestWriteBE(t *testing.T) { assert.Equal(t, val, readval) } +func TestWriteU64LE(t *testing.T) { + var ( + val uint64 = 0xbadc0de15a11dead + readval uint64 + bin = []byte{0xad, 0xde, 0x11, 0x5a, 0xe1, 0x0d, 0xdc, 0xba} + ) + bw := NewBufBinWriter() + bw.WriteU64LE(val) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + readval = br.ReadU64LE() + assert.Nil(t, br.Err) + assert.Equal(t, val, readval) +} + +func TestWriteU32LE(t *testing.T) { + var ( + val uint32 = 0xdeadbeef + readval uint32 + bin = []byte{0xef, 0xbe, 0xad, 0xde} + ) + bw := NewBufBinWriter() + bw.WriteU32LE(val) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + readval = br.ReadU32LE() + assert.Nil(t, br.Err) + assert.Equal(t, val, readval) +} + +func TestWriteU16LE(t *testing.T) { + var ( + val uint16 = 0xbabe + readval uint16 + bin = []byte{0xbe, 0xba} + ) + bw := NewBufBinWriter() + bw.WriteU16LE(val) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + readval = br.ReadU16LE() + assert.Nil(t, br.Err) + assert.Equal(t, val, readval) +} + +func TestWriteU16BE(t *testing.T) { + var ( + val uint16 = 0xbabe + readval uint16 + bin = []byte{0xba, 0xbe} + ) + bw := NewBufBinWriter() + bw.WriteU16BE(val) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + readval = br.ReadU16BE() + assert.Nil(t, br.Err) + assert.Equal(t, val, readval) +} + +func TestWriteByte(t *testing.T) { + var ( + val byte = 0xa5 + readval byte + bin = []byte{0xa5} + ) + bw := NewBufBinWriter() + bw.WriteByte(val) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + readval = br.ReadByte() + assert.Nil(t, br.Err) + assert.Equal(t, val, readval) +} + +func TestWriteBool(t *testing.T) { + var ( + bin = []byte{0x01, 0x00} + ) + bw := NewBufBinWriter() + bw.WriteBool(true) + bw.WriteBool(false) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + assert.Equal(t, true, br.ReadBool()) + assert.Equal(t, false, br.ReadBool()) + assert.Nil(t, br.Err) +} + +func TestReadLEErrors(t *testing.T) { + bin := []byte{0xad, 0xde, 0x11, 0x5a, 0xe1, 0x0d, 0xdc, 0xba} + br := NewBinReaderFromBuf(bin) + // Prime the buffers with something. + _ = br.ReadU64LE() + assert.Nil(t, br.Err) + + assert.Equal(t, uint64(0), br.ReadU64LE()) + assert.Equal(t, uint32(0), br.ReadU32LE()) + assert.Equal(t, uint16(0), br.ReadU16LE()) + assert.Equal(t, uint16(0), br.ReadU16BE()) + assert.Equal(t, byte(0), br.ReadByte()) + assert.Equal(t, false, br.ReadBool()) + assert.NotNil(t, br.Err) +} + func TestBufBinWriter_Len(t *testing.T) { val := []byte{0xde} bw := NewBufBinWriter() diff --git a/pkg/io/size_test.go b/pkg/io/size_test.go index 4e397894c..ea5175956 100644 --- a/pkg/io/size_test.go +++ b/pkg/io/size_test.go @@ -17,7 +17,7 @@ type smthSerializable struct { func (*smthSerializable) DecodeBinary(*io.BinReader) {} func (ss *smthSerializable) EncodeBinary(bw *io.BinWriter) { - bw.WriteLE(ss.some) + bw.WriteBytes(ss.some[:]) } // Mock structure that gives error in EncodeBinary(). diff --git a/pkg/network/message.go b/pkg/network/message.go index 0b656194d..16802a2ba 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -149,10 +149,10 @@ 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) + m.Magic = config.NetMode(br.ReadU32LE()) br.ReadBytes(m.Command[:]) - br.ReadLE(&m.Length) - br.ReadLE(&m.Checksum) + m.Length = br.ReadU32LE() + m.Checksum = br.ReadU32LE() if br.Err != nil { return br.Err } @@ -165,7 +165,7 @@ func (m *Message) Decode(br *io.BinReader) error { func (m *Message) decodePayload(br *io.BinReader) error { buf := make([]byte, m.Length) - br.ReadLE(buf) + br.ReadBytes(buf) if br.Err != nil { return br.Err } @@ -212,10 +212,10 @@ func (m *Message) decodePayload(br *io.BinReader) error { // Encode encodes a Message to any given BinWriter. func (m *Message) Encode(br *io.BinWriter) error { - br.WriteLE(m.Magic) + br.WriteU32LE(uint32(m.Magic)) br.WriteBytes(m.Command[:]) - br.WriteLE(m.Length) - br.WriteLE(m.Checksum) + br.WriteU32LE(m.Length) + br.WriteU32LE(m.Checksum) if m.Payload != nil { m.Payload.EncodeBinary(br) diff --git a/pkg/network/payload/address.go b/pkg/network/payload/address.go index 9c8d3444b..248c313e2 100644 --- a/pkg/network/payload/address.go +++ b/pkg/network/payload/address.go @@ -29,18 +29,18 @@ func NewAddressAndTime(e *net.TCPAddr, t time.Time) *AddressAndTime { // DecodeBinary implements Serializable interface. func (p *AddressAndTime) DecodeBinary(br *io.BinReader) { - br.ReadLE(&p.Timestamp) - br.ReadLE(&p.Services) + p.Timestamp = br.ReadU32LE() + p.Services = br.ReadU64LE() br.ReadBytes(p.IP[:]) - br.ReadBE(&p.Port) + p.Port = br.ReadU16BE() } // EncodeBinary implements Serializable interface. func (p *AddressAndTime) EncodeBinary(bw *io.BinWriter) { - bw.WriteLE(p.Timestamp) - bw.WriteLE(p.Services) + bw.WriteU32LE(p.Timestamp) + bw.WriteU64LE(p.Services) bw.WriteBytes(p.IP[:]) - bw.WriteBE(p.Port) + bw.WriteU16BE(p.Port) } // IPPortString makes a string from IP and port specified. diff --git a/pkg/network/payload/inventory.go b/pkg/network/payload/inventory.go index 036aa0279..06bc0f87d 100644 --- a/pkg/network/payload/inventory.go +++ b/pkg/network/payload/inventory.go @@ -56,12 +56,12 @@ func NewInventory(typ InventoryType, hashes []util.Uint256) *Inventory { // DecodeBinary implements Serializable interface. func (p *Inventory) DecodeBinary(br *io.BinReader) { - br.ReadLE(&p.Type) + p.Type = InventoryType(br.ReadByte()) br.ReadArray(&p.Hashes) } // EncodeBinary implements Serializable interface. func (p *Inventory) EncodeBinary(bw *io.BinWriter) { - bw.WriteLE(p.Type) + bw.WriteByte(byte(p.Type)) bw.WriteArray(p.Hashes) } diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index c8d62ab20..a6da82e02 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -55,25 +55,25 @@ func NewVersion(id uint32, p uint16, ua string, h uint32, r bool) *Version { // DecodeBinary implements Serializable interface. func (p *Version) DecodeBinary(br *io.BinReader) { - br.ReadLE(&p.Version) - br.ReadLE(&p.Services) - br.ReadLE(&p.Timestamp) - br.ReadLE(&p.Port) - br.ReadLE(&p.Nonce) + p.Version = br.ReadU32LE() + p.Services = br.ReadU64LE() + p.Timestamp = br.ReadU32LE() + p.Port = br.ReadU16LE() + p.Nonce = br.ReadU32LE() p.UserAgent = br.ReadVarBytes() - br.ReadLE(&p.StartHeight) - br.ReadLE(&p.Relay) + p.StartHeight = br.ReadU32LE() + p.Relay = br.ReadBool() } // EncodeBinary implements Serializable interface. func (p *Version) EncodeBinary(br *io.BinWriter) { - br.WriteLE(p.Version) - br.WriteLE(p.Services) - br.WriteLE(p.Timestamp) - br.WriteLE(p.Port) - br.WriteLE(p.Nonce) + br.WriteU32LE(p.Version) + br.WriteU64LE(p.Services) + br.WriteU32LE(p.Timestamp) + br.WriteU16LE(p.Port) + br.WriteU32LE(p.Nonce) br.WriteVarBytes(p.UserAgent) - br.WriteLE(p.StartHeight) - br.WriteLE(&p.Relay) + br.WriteU32LE(p.StartHeight) + br.WriteBool(p.Relay) } diff --git a/pkg/rpc/server_helper_test.go b/pkg/rpc/server_helper_test.go index 493abbce9..c1091d8d2 100644 --- a/pkg/rpc/server_helper_test.go +++ b/pkg/rpc/server_helper_test.go @@ -188,7 +188,7 @@ func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFu f, err := os.Open("testdata/50testblocks.acc") require.Nil(t, err) br := io.NewBinReaderFromIO(f) - br.ReadLE(&nBlocks) + nBlocks = br.ReadU32LE() require.Nil(t, br.Err) for i := 0; i < int(nBlocks); i++ { block := &core.Block{} diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go index b6ba9b39f..fba6de4fe 100644 --- a/pkg/smartcontract/contract_test.go +++ b/pkg/smartcontract/contract_test.go @@ -22,9 +22,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) { } br := io.NewBinReaderFromBuf(out) - var b uint8 - br.ReadLE(&b) - assert.Equal(t, opcode.PUSH3, opcode.Opcode(b)) + assert.Equal(t, opcode.PUSH3, opcode.Opcode(br.ReadByte())) for i := 0; i < len(validators); i++ { bb := br.ReadVarBytes() @@ -34,8 +32,6 @@ func TestCreateMultiSigRedeemScript(t *testing.T) { assert.Equal(t, validators[i].Bytes(), bb) } - br.ReadLE(&b) - assert.Equal(t, opcode.PUSH3, opcode.Opcode(b)) - br.ReadLE(&b) - assert.Equal(t, opcode.CHECKMULTISIG, opcode.Opcode(b)) + assert.Equal(t, opcode.PUSH3, opcode.Opcode(br.ReadByte())) + assert.Equal(t, opcode.CHECKMULTISIG, opcode.Opcode(br.ReadByte())) } diff --git a/pkg/smartcontract/param_context.go b/pkg/smartcontract/param_context.go index cd486afe0..01984b79c 100644 --- a/pkg/smartcontract/param_context.go +++ b/pkg/smartcontract/param_context.go @@ -80,12 +80,12 @@ func (pt ParamType) MarshalJSON() ([]byte, error) { // EncodeBinary implements io.Serializable interface. func (pt ParamType) EncodeBinary(w *io.BinWriter) { - w.WriteBytes([]byte{byte(pt)}) + w.WriteByte(byte(pt)) } // DecodeBinary implements io.Serializable interface. func (pt *ParamType) DecodeBinary(r *io.BinReader) { - r.ReadLE(pt) + *pt = ParamType(r.ReadByte()) } // NewParameter returns a Parameter with proper initialized Value diff --git a/pkg/util/fixed8.go b/pkg/util/fixed8.go index f28f74e45..617d0f48d 100644 --- a/pkg/util/fixed8.go +++ b/pkg/util/fixed8.go @@ -5,6 +5,8 @@ import ( "errors" "strconv" "strings" + + "github.com/CityOfZion/neo-go/pkg/io" ) const ( @@ -109,6 +111,16 @@ func (f Fixed8) MarshalJSON() ([]byte, error) { return []byte(`"` + f.String() + `"`), nil } +// DecodeBinary implements the io.Serializable interface. +func (f *Fixed8) DecodeBinary(r *io.BinReader) { + *f = Fixed8(r.ReadU64LE()) +} + +// EncodeBinary implements the io.Serializable interface. +func (f *Fixed8) EncodeBinary(w *io.BinWriter) { + w.WriteU64LE(uint64(*f)) +} + // Satoshi defines the value of a 'Satoshi'. func Satoshi() Fixed8 { return Fixed8(1) diff --git a/pkg/util/fixed8_test.go b/pkg/util/fixed8_test.go index 5841da3f2..30753a7f2 100644 --- a/pkg/util/fixed8_test.go +++ b/pkg/util/fixed8_test.go @@ -5,7 +5,9 @@ import ( "strconv" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestFixed8FromInt64(t *testing.T) { @@ -134,3 +136,16 @@ func TestFixed8_Arith(t *testing.T) { assert.Zero(t, u1.CompareTo(u1)) assert.EqualValues(t, Fixed8(2), u2.Div(3)) } + +func TestFixed8_Serializable(t *testing.T) { + a := Fixed8(0x0102030405060708) + + w := io.NewBufBinWriter() + a.EncodeBinary(w.BinWriter) + require.NoError(t, w.Err) + + var b Fixed8 + r := io.NewBinReaderFromBuf(w.Bytes()) + b.DecodeBinary(r) + require.Equal(t, a, b) +} diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 413fc1c3c..18118e61a 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -50,26 +50,22 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) { } r := io.NewBinReaderFromBuf(c.prog[c.ip:]) - var instrbyte byte - r.ReadLE(&instrbyte) + var instrbyte = r.ReadByte() instr := opcode.Opcode(instrbyte) c.nextip++ var numtoread int switch instr { case opcode.PUSHDATA1, opcode.SYSCALL: - var n byte - r.ReadLE(&n) + var n = r.ReadByte() numtoread = int(n) c.nextip++ case opcode.PUSHDATA2: - var n uint16 - r.ReadLE(&n) + var n = r.ReadU16LE() numtoread = int(n) c.nextip += 2 case opcode.PUSHDATA4: - var n uint32 - r.ReadLE(&n) + var n = r.ReadU32LE() if n > MaxItemSize { return instr, nil, errors.New("parameter is too big") } @@ -92,7 +88,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) { } } parameter := make([]byte, numtoread) - r.ReadLE(parameter) + r.ReadBytes(parameter) if r.Err != nil { return instr, nil, errors.New("failed to read instruction parameter") } diff --git a/pkg/vm/serialization.go b/pkg/vm/serialization.go index 79c7b7228..a3d47c0a2 100644 --- a/pkg/vm/serialization.go +++ b/pkg/vm/serialization.go @@ -47,7 +47,7 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) { w.WriteVarBytes(t.value) case *BoolItem: w.WriteBytes([]byte{byte(booleanT)}) - w.WriteLE(t.value) + w.WriteBool(t.value) case *BigIntegerItem: w.WriteBytes([]byte{byte(integerT)}) w.WriteVarBytes(t.Bytes()) @@ -94,8 +94,7 @@ func deserializeItem(data []byte) (StackItem, error) { // as a function because StackItem itself is an interface. Caveat: always check // reader's error value before using the returned StackItem. func DecodeBinaryStackItem(r *io.BinReader) StackItem { - var t byte - r.ReadLE(&t) + var t = r.ReadByte() if r.Err != nil { return nil } @@ -105,8 +104,7 @@ func DecodeBinaryStackItem(r *io.BinReader) StackItem { data := r.ReadVarBytes() return NewByteArrayItem(data) case booleanT: - var b bool - r.ReadLE(&b) + var b = r.ReadBool() return NewBoolItem(b) case integerT: data := r.ReadVarBytes()