From e89e78159b39fc23a71266048e61708c80ac14da Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 27 Aug 2019 19:56:12 +0300 Subject: [PATCH 01/13] _pkg.dev: drop wire/protocol package Move service definitions from it into version (just to save them), but other than that it's useless for master that has configs ruling the networks. --- _pkg.dev/wire/protocol/protocol.go | 44 ------------------------------ pkg/network/payload/version.go | 11 +++++++- 2 files changed, 10 insertions(+), 45 deletions(-) delete mode 100644 _pkg.dev/wire/protocol/protocol.go diff --git a/_pkg.dev/wire/protocol/protocol.go b/_pkg.dev/wire/protocol/protocol.go deleted file mode 100644 index d8b223267..000000000 --- a/_pkg.dev/wire/protocol/protocol.go +++ /dev/null @@ -1,44 +0,0 @@ -package protocol - -//Version represents the latest protocol version for the neo node -type Version uint32 - -const ( - // DefaultVersion is the nodes default protocol version - DefaultVersion Version = 0 - // UserAgent is the nodes user agent or human-readable name - UserAgent = "/NEO-GO/" -) - -// ServiceFlag indicates the services provided by the node. 1 = P2P Full Node -type ServiceFlag uint64 - -// List of Services offered by the node -const ( - NodePeerService ServiceFlag = 1 - // BloomFilerService ServiceFlag = 2 // Not implemented - // PrunedNode ServiceFlag = 3 // Not implemented - // LightNode ServiceFlag = 4 // Not implemented - -) - -// Magic is the network that NEO is running on -type Magic uint32 - -// List of possible networks -const ( - MainNet Magic = 7630401 - TestNet Magic = 0x74746e41 -) - -// String implements the stringer interface -func (m Magic) String() string { - switch m { - case MainNet: - return "Mainnet" - case TestNet: - return "Testnet" - default: - return "UnknownNet" - } -} diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index 42d69f451..f5be4b615 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -8,6 +8,15 @@ import ( const minVersionSize = 27 +// List of Services offered by the node +const ( + nodePeerService uint64 = 1 + // BloomFilerService uint64 = 2 // Not implemented + // PrunedNode uint64 = 3 // Not implemented + // LightNode uint64 = 4 // Not implemented + +) + // Version payload. type Version struct { // currently the version of the protocol is 0 @@ -32,7 +41,7 @@ type Version struct { func NewVersion(id uint32, p uint16, ua string, h uint32, r bool) *Version { return &Version{ Version: 0, - Services: 1, + Services: nodePeerService, Timestamp: uint32(time.Now().UTC().Unix()), Port: p, Nonce: id, From 0f265a6a049a370fc013dbd67d063158041a842a Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 27 Aug 2019 20:08:48 +0300 Subject: [PATCH 02/13] _pkg.dev: drop wire/command Transferring some missing commands to pkg/network. --- _pkg.dev/wire/command/command.go | 29 ----------------------------- pkg/network/message.go | 9 +++++++++ 2 files changed, 9 insertions(+), 29 deletions(-) delete mode 100644 _pkg.dev/wire/command/command.go diff --git a/_pkg.dev/wire/command/command.go b/_pkg.dev/wire/command/command.go deleted file mode 100644 index d89dcd335..000000000 --- a/_pkg.dev/wire/command/command.go +++ /dev/null @@ -1,29 +0,0 @@ -package command - -// Size of command field in bytes -const ( - Size = 12 -) - -// Type represents the type of a message command. -type Type string - -// Valid protocol commands used to send between nodes. -const ( - Version Type = "version" - Mempool Type = "mempool" - Ping Type = "ping" - Pong Type = "pong" - Verack Type = "verack" - GetAddr Type = "getaddr" - Addr Type = "addr" - GetHeaders Type = "getheaders" - Headers Type = "headers" - GetBlocks Type = "getblocks" - Inv Type = "inv" - GetData Type = "getdata" - Block Type = "block" - TX Type = "tx" - Consensus Type = "consensus" - Unknown Type = "unknown" -) diff --git a/pkg/network/message.go b/pkg/network/message.go index 2154c848e..1af230ab0 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -66,6 +66,9 @@ const ( CMDFilterClear CommandType = "filterclear" CMDFilterLoad CommandType = "filterload" CMDMerkleBlock CommandType = "merkleblock" + CMDMempool CommandType = "mempool" + CMDPing CommandType = "ping" + CMDPong CommandType = "pong" ) // NewMessage returns a new message with the given payload. @@ -131,6 +134,12 @@ func (m *Message) CommandType() CommandType { return CMDFilterAdd case "filterclear": return CMDFilterClear + case "mempool": + return CMDMempool + case "ping": + return CMDPing + case "pong": + return CMDPong default: return CMDUnknown } From a436e22ec1664bb29303eec16e5bc1848ba6c5fe Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 27 Aug 2019 20:12:01 +0300 Subject: [PATCH 03/13] pkg/network: sort messages Just for convenience. --- pkg/network/message.go | 66 +++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/pkg/network/message.go b/pkg/network/message.go index 1af230ab0..fa39c0591 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -49,26 +49,26 @@ type CommandType string // Valid protocol commands used to send between nodes. const ( - CMDVersion CommandType = "version" - CMDVerack CommandType = "verack" - CMDGetAddr CommandType = "getaddr" CMDAddr CommandType = "addr" - CMDGetHeaders CommandType = "getheaders" - CMDHeaders CommandType = "headers" - CMDGetBlocks CommandType = "getblocks" - CMDInv CommandType = "inv" - CMDGetData CommandType = "getdata" CMDBlock CommandType = "block" - CMDTX CommandType = "tx" CMDConsensus CommandType = "consensus" - CMDUnknown CommandType = "unknown" CMDFilterAdd CommandType = "filteradd" CMDFilterClear CommandType = "filterclear" CMDFilterLoad CommandType = "filterload" - CMDMerkleBlock CommandType = "merkleblock" + CMDGetAddr CommandType = "getaddr" + CMDGetBlocks CommandType = "getblocks" + CMDGetData CommandType = "getdata" + CMDGetHeaders CommandType = "getheaders" + CMDHeaders CommandType = "headers" + CMDInv CommandType = "inv" CMDMempool CommandType = "mempool" + CMDMerkleBlock CommandType = "merkleblock" CMDPing CommandType = "ping" CMDPong CommandType = "pong" + CMDTX CommandType = "tx" + CMDUnknown CommandType = "unknown" + CMDVerack CommandType = "verack" + CMDVersion CommandType = "version" ) // NewMessage returns a new message with the given payload. @@ -102,44 +102,44 @@ func NewMessage(magic config.NetMode, cmd CommandType, p payload.Payload) *Messa func (m *Message) CommandType() CommandType { cmd := cmdByteArrayToString(m.Command) switch cmd { - case "version": - return CMDVersion - case "verack": - return CMDVerack - case "getaddr": - return CMDGetAddr case "addr": return CMDAddr - case "getheaders": - return CMDGetHeaders - case "headers": - return CMDHeaders - case "getblocks": - return CMDGetBlocks - case "inv": - return CMDInv - case "getdata": - return CMDGetData case "block": return CMDBlock - case "tx": - return CMDTX case "consensus": return CMDConsensus - case "merkleblock": - return CMDMerkleBlock - case "filterload": - return CMDFilterLoad case "filteradd": return CMDFilterAdd case "filterclear": return CMDFilterClear + case "filterload": + return CMDFilterLoad + case "getaddr": + return CMDGetAddr + case "getblocks": + return CMDGetBlocks + case "getdata": + return CMDGetData + case "getheaders": + return CMDGetHeaders + case "headers": + return CMDHeaders + case "inv": + return CMDInv case "mempool": return CMDMempool + case "merkleblock": + return CMDMerkleBlock case "ping": return CMDPing case "pong": return CMDPong + case "tx": + return CMDTX + case "verack": + return CMDVerack + case "version": + return CMDVersion default: return CMDUnknown } From 6409cc753caa7928e4522740bf8358fca0809411 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 12:44:23 +0300 Subject: [PATCH 04/13] util: move binaryReader/Writer from _pkg.dev These are useful and nice. --- {_pkg.dev/wire => pkg}/util/binaryReader.go | 0 {_pkg.dev/wire => pkg}/util/binaryWriter.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {_pkg.dev/wire => pkg}/util/binaryReader.go (100%) rename {_pkg.dev/wire => pkg}/util/binaryWriter.go (100%) diff --git a/_pkg.dev/wire/util/binaryReader.go b/pkg/util/binaryReader.go similarity index 100% rename from _pkg.dev/wire/util/binaryReader.go rename to pkg/util/binaryReader.go diff --git a/_pkg.dev/wire/util/binaryWriter.go b/pkg/util/binaryWriter.go similarity index 100% rename from _pkg.dev/wire/util/binaryWriter.go rename to pkg/util/binaryWriter.go From ad2cd15c6cf2579faea829ca10a29ef9f7eaccf0 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 13:09:03 +0300 Subject: [PATCH 05/13] _pkg.dev: drop the last wire/util code SumSHA256() and ReaderToBuffer() are not used, CalculateHash() shouldn't be used and BufferLength() is just to easy with only one user. --- _pkg.dev/wire/message.go | 2 +- _pkg.dev/wire/util/util.go | 49 -------------------------------------- 2 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 _pkg.dev/wire/util/util.go diff --git a/_pkg.dev/wire/message.go b/_pkg.dev/wire/message.go index da0742c63..64904479d 100644 --- a/_pkg.dev/wire/message.go +++ b/_pkg.dev/wire/message.go @@ -47,7 +47,7 @@ func WriteMessage(w io.Writer, magic protocol.Magic, message Messager) error { return err } - payloadLen := util.BufferLength(buf) + payloadLen := uint32(buf.Len()) checksum := checksum.FromBytes(buf.Bytes()) bw.Write(payloadLen) diff --git a/_pkg.dev/wire/util/util.go b/_pkg.dev/wire/util/util.go deleted file mode 100644 index 58499562c..000000000 --- a/_pkg.dev/wire/util/util.go +++ /dev/null @@ -1,49 +0,0 @@ -package util - -import ( - "bytes" - "crypto/sha256" - - "io" - "io/ioutil" -) - -// Convenience function - -// BufferLength returns the length of a buffer as uint32 -func BufferLength(buf *bytes.Buffer) uint32 { - - return uint32(buf.Len()) -} - -// SumSHA256 returns the sha256 sum of the data -func SumSHA256(b []byte) []byte { - h := sha256.New() - h.Write(b) - return h.Sum(nil) -} - -// CalculateHash takes a function with a binary writer and returns -// the double hash of the io.Writer -func CalculateHash(f func(bw *BinWriter)) (Uint256, error) { - buf := new(bytes.Buffer) - bw := &BinWriter{W: buf} - - f(bw) - - var hash Uint256 - hash = sha256.Sum256(buf.Bytes()) - hash = sha256.Sum256(hash.Bytes()) - return hash, bw.Err - -} - -//ReaderToBuffer converts a io.Reader into a bytes.Buffer -func ReaderToBuffer(r io.Reader) (*bytes.Buffer, error) { - byt, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - buf := bytes.NewBuffer(byt) - return buf, nil -} From 672668b9fbc265a0190d2ac1cf5d904869b8159a Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 13:24:06 +0300 Subject: [PATCH 06/13] util: use more consistent and explicit naming for BR/BW --- pkg/util/binaryReader.go | 32 ++++++++++++++++---------------- pkg/util/binaryWriter.go | 27 +++++++++++++-------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/pkg/util/binaryReader.go b/pkg/util/binaryReader.go index 529610f14..f13879549 100644 --- a/pkg/util/binaryReader.go +++ b/pkg/util/binaryReader.go @@ -12,27 +12,27 @@ type BinReader struct { Err error } -// Read reads from the underlying io.Reader -// into the interface v in LE -func (r *BinReader) Read(v interface{}) { +// ReadLE reads from the underlying io.Reader +// into the interface v in little-endian format +func (r *BinReader) ReadLE(v interface{}) { if r.Err != nil { return } r.Err = binary.Read(r.R, binary.LittleEndian, v) } -// ReadBigEnd reads from the underlying io.Reader -// into the interface v in BE -func (r *BinReader) ReadBigEnd(v interface{}) { +// ReadBE reads from the underlying io.Reader +// into the interface v in big-endian format +func (r *BinReader) ReadBE(v interface{}) { if r.Err != nil { return } r.Err = binary.Read(r.R, binary.BigEndian, v) } -//VarUint reads a variable integer from the +// ReadVarUint reads a variable-length-encoded integer from the // underlying reader -func (r *BinReader) VarUint() uint64 { +func (r *BinReader) ReadVarUint() uint64 { var b uint8 r.Err = binary.Read(r.R, binary.LittleEndian, &b) @@ -55,17 +55,17 @@ func (r *BinReader) VarUint() uint64 { return uint64(b) } -// VarBytes reads the next set of bytes from the underlying reader. -// VarUInt is used to determine how large that slice is -func (r *BinReader) VarBytes() []byte { - n := r.VarUint() +// ReadBytes 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 { + n := r.ReadVarUint() b := make([]byte, n) - r.Read(b) + r.ReadLE(b) return b } -// VarString calls VarBytes and casts the results as a string -func (r *BinReader) VarString() string { - b := r.VarBytes() +// ReadString calls ReadBytes and casts the results as a string +func (r *BinReader) ReadString() string { + b := r.ReadBytes() return string(b) } diff --git a/pkg/util/binaryWriter.go b/pkg/util/binaryWriter.go index d4fa34ae6..39acf9a41 100644 --- a/pkg/util/binaryWriter.go +++ b/pkg/util/binaryWriter.go @@ -14,25 +14,24 @@ type BinWriter struct { Err error } -// Write writes into the underlying io.Writer from an object v in LE format -func (w *BinWriter) Write(v interface{}) { +// WriteLE writes into the underlying io.Writer from an object v in little-endian format +func (w *BinWriter) WriteLE(v interface{}) { if w.Err != nil { return } w.Err = binary.Write(w.W, binary.LittleEndian, v) } -// WriteBigEnd writes into the underlying io.Writer from an object v in BE format -// Only used for IP and PORT. Additional method makes the default LittleEndian case clean -func (w *BinWriter) WriteBigEnd(v interface{}) { +// WriteBE writes into the underlying io.Writer from an object v in big-endian format +func (w *BinWriter) WriteBE(v interface{}) { if w.Err != nil { return } w.Err = binary.Write(w.W, binary.BigEndian, v) } -// VarUint writes a uint64 into the underlying writer -func (w *BinWriter) VarUint(val uint64) { +// WriteVarUint writes a uint64 into the underlying writer using variable-length encoding +func (w *BinWriter) WriteVarUint(val uint64) { if val < 0 { w.Err = errors.New("value out of range") return @@ -63,13 +62,13 @@ func (w *BinWriter) VarUint(val uint64) { } -// VarBytes writes a variable length byte array into the underlying io.Writer -func (w *BinWriter) VarBytes(b []byte) { - w.VarUint(uint64(len(b))) - w.Write(b) +// WriteBytes writes a variable length byte array into the underlying io.Writer +func (w *BinWriter) WriteBytes(b []byte) { + w.WriteVarUint(uint64(len(b))) + w.WriteLE(b) } -//VarString casts the string as a byte slice and calls VarBytes -func (w *BinWriter) VarString(s string) { - w.VarBytes([]byte(s)) +// WriteString writes a variable length string into the underlying io.Writer +func (w *BinWriter) WriteString(s string) { + w.WriteBytes([]byte(s)) } From 07096a551b62fec731b1ee3c40cc390397669f8f Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 14:38:57 +0300 Subject: [PATCH 07/13] util: move strange Read2000Uint256Hashes() into storage It's the only user of it. --- pkg/core/storage/helpers.go | 15 ++++++++++++++- pkg/util/io.go | 13 ------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/core/storage/helpers.go b/pkg/core/storage/helpers.go index d62f2bcfc..b98c7671c 100644 --- a/pkg/core/storage/helpers.go +++ b/pkg/core/storage/helpers.go @@ -1,6 +1,7 @@ package storage import ( + "bytes" "encoding/binary" "sort" @@ -55,7 +56,7 @@ func HeaderHashes(s Store) ([]util.Uint256, error) { hashMap := make(map[uint32][]util.Uint256) s.Seek(IXHeaderHashList.Bytes(), func(k, v []byte) { storedCount := binary.LittleEndian.Uint32(k[1:]) - hashes, err := util.Read2000Uint256Hashes(v) + hashes, err := read2000Uint256Hashes(v) if err != nil { panic(err) } @@ -78,3 +79,15 @@ func HeaderHashes(s Store) ([]util.Uint256, error) { return hashes, nil } + +// read2000Uint256Hashes attempts to read 2000 Uint256 hashes from +// the given byte array. +func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) { + r := bytes.NewReader(b) + lenHashes := util.ReadVarUint(r) + hashes := make([]util.Uint256, lenHashes) + if err := binary.Read(r, binary.LittleEndian, hashes); err != nil { + return nil, err + } + return hashes, nil +} diff --git a/pkg/util/io.go b/pkg/util/io.go index fcc737a73..d81491da3 100644 --- a/pkg/util/io.go +++ b/pkg/util/io.go @@ -1,7 +1,6 @@ package util import ( - "bytes" "encoding/binary" "io" ) @@ -89,15 +88,3 @@ func WriteVarBytes(w io.Writer, b []byte) error { } return binary.Write(w, binary.LittleEndian, b) } - -// Read2000Uint256Hashes attempt to read 2000 Uint256 hashes from -// the given byte array. -func Read2000Uint256Hashes(b []byte) ([]Uint256, error) { - r := bytes.NewReader(b) - lenHashes := ReadVarUint(r) - hashes := make([]Uint256, lenHashes) - if err := binary.Read(r, binary.LittleEndian, hashes); err != nil { - return nil, err - } - return hashes, nil -} From 459542a9782f2c0c818c4c1be176f0018afdc2e3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 15:43:56 +0300 Subject: [PATCH 08/13] pkg/network: convert to using binaryReader/Writer --- pkg/network/message.go | 37 ++++++--------- pkg/network/payload/address.go | 45 +++++++++--------- pkg/network/payload/getblocks.go | 23 ++++----- pkg/network/payload/headers.go | 13 +++-- pkg/network/payload/inventory.go | 31 +++++------- pkg/network/payload/merkleblock.go | 15 +++--- pkg/network/payload/version.go | 76 ++++++++++-------------------- 7 files changed, 98 insertions(+), 142 deletions(-) diff --git a/pkg/network/message.go b/pkg/network/message.go index fa39c0591..638289192 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -12,6 +12,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/network/payload" + "github.com/CityOfZion/neo-go/pkg/util" ) const ( @@ -147,17 +148,13 @@ func (m *Message) CommandType() CommandType { // Decode a Message from the given reader. func (m *Message) Decode(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &m.Magic); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &m.Command); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &m.Length); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &m.Checksum); err != nil { - return err + br := util.BinReader{R: r} + br.ReadLE(&m.Magic) + br.ReadLE(&m.Command) + br.ReadLE(&m.Length) + br.ReadLE(&m.Checksum) + if br.Err != nil { + return br.Err } // return if their is no payload. if m.Length == 0 { @@ -233,17 +230,13 @@ func (m *Message) decodePayload(r io.Reader) error { // Encode a Message to any given io.Writer. func (m *Message) Encode(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, m.Magic); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, m.Command); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, m.Length); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, m.Checksum); err != nil { - return err + br := util.BinWriter{W: w} + br.WriteLE(m.Magic) + br.WriteLE(m.Command) + br.WriteLE(m.Length) + br.WriteLE(m.Checksum) + if br.Err != nil { + return br.Err } if m.Payload != nil { return m.Payload.EncodeBinary(w) diff --git a/pkg/network/payload/address.go b/pkg/network/payload/address.go index 044d652a8..723275338 100644 --- a/pkg/network/payload/address.go +++ b/pkg/network/payload/address.go @@ -1,7 +1,6 @@ package payload import ( - "encoding/binary" "io" "time" @@ -26,30 +25,22 @@ func NewAddressAndTime(e util.Endpoint, t time.Time) *AddressAndTime { // DecodeBinary implements the Payload interface. func (p *AddressAndTime) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &p.Timestamp); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &p.Services); err != nil { - return err - } - if err := binary.Read(r, binary.BigEndian, &p.Endpoint.IP); err != nil { - return err - } - return binary.Read(r, binary.BigEndian, &p.Endpoint.Port) + br := util.BinReader{R: r} + br.ReadLE(&p.Timestamp) + br.ReadLE(&p.Services) + br.ReadBE(&p.Endpoint.IP) + br.ReadBE(&p.Endpoint.Port) + return br.Err } // EncodeBinary implements the Payload interface. func (p *AddressAndTime) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, p.Timestamp); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.Services); err != nil { - return err - } - if err := binary.Write(w, binary.BigEndian, p.Endpoint.IP); err != nil { - return err - } - return binary.Write(w, binary.BigEndian, p.Endpoint.Port) + bw := util.BinWriter{W: w} + bw.WriteLE(p.Timestamp) + bw.WriteLE(p.Services) + bw.WriteBE(p.Endpoint.IP) + bw.WriteBE(p.Endpoint.Port) + return bw.Err } // AddressList is a list with AddrAndTime. @@ -59,7 +50,11 @@ type AddressList struct { // DecodeBinary implements the Payload interface. func (p *AddressList) DecodeBinary(r io.Reader) error { - listLen := util.ReadVarUint(r) + br := util.BinReader{R: r} + listLen := br.ReadVarUint() + if br.Err != nil { + return br.Err + } p.Addrs = make([]*AddressAndTime, listLen) for i := 0; i < int(listLen); i++ { @@ -73,8 +68,10 @@ func (p *AddressList) DecodeBinary(r io.Reader) error { // EncodeBinary implements the Payload interface. func (p *AddressList) EncodeBinary(w io.Writer) error { - if err := util.WriteVarUint(w, uint64(len(p.Addrs))); err != nil { - return err + bw := util.BinWriter{W: w} + bw.WriteVarUint(uint64(len(p.Addrs))) + if bw.Err != nil { + return bw.Err } for _, addr := range p.Addrs { if err := addr.EncodeBinary(w); err != nil { diff --git a/pkg/network/payload/getblocks.go b/pkg/network/payload/getblocks.go index 6614f2388..0e0a3419e 100644 --- a/pkg/network/payload/getblocks.go +++ b/pkg/network/payload/getblocks.go @@ -1,7 +1,6 @@ package payload import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -25,24 +24,22 @@ func NewGetBlocks(start []util.Uint256, stop util.Uint256) *GetBlocks { // DecodeBinary implements the payload interface. func (p *GetBlocks) DecodeBinary(r io.Reader) error { - lenStart := util.ReadVarUint(r) + br := util.BinReader{R: r} + lenStart := br.ReadVarUint() p.HashStart = make([]util.Uint256, lenStart) - if err := binary.Read(r, binary.LittleEndian, &p.HashStart); err != nil { - return err - } - return binary.Read(r, binary.LittleEndian, &p.HashStop) + br.ReadLE(&p.HashStart) + br.ReadLE(&p.HashStop) + return br.Err } // EncodeBinary implements the payload interface. func (p *GetBlocks) EncodeBinary(w io.Writer) error { - if err := util.WriteVarUint(w, uint64(len(p.HashStart))); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.HashStart); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, p.HashStop) + bw := util.BinWriter{W: w} + bw.WriteVarUint(uint64(len(p.HashStart))) + bw.WriteLE(p.HashStart) + bw.WriteLE(p.HashStop) + return bw.Err } // Size implements the payload interface. diff --git a/pkg/network/payload/headers.go b/pkg/network/payload/headers.go index 8699f7e9c..f4e63925a 100644 --- a/pkg/network/payload/headers.go +++ b/pkg/network/payload/headers.go @@ -14,7 +14,11 @@ type Headers struct { // DecodeBinary implements the Payload interface. func (p *Headers) DecodeBinary(r io.Reader) error { - lenHeaders := util.ReadVarUint(r) + br := util.BinReader{R: r} + lenHeaders := br.ReadVarUint() + if br.Err != nil { + return br.Err + } p.Hdrs = make([]*core.Header, lenHeaders) @@ -31,9 +35,12 @@ func (p *Headers) DecodeBinary(r io.Reader) error { // EncodeBinary implements the Payload interface. func (p *Headers) EncodeBinary(w io.Writer) error { - if err := util.WriteVarUint(w, uint64(len(p.Hdrs))); err != nil { - return err + bw := util.BinWriter{W: w} + bw.WriteVarUint(uint64(len(p.Hdrs))) + if bw.Err != nil { + return bw.Err } + for _, header := range p.Hdrs { if err := header.EncodeBinary(w); err != nil { return err diff --git a/pkg/network/payload/inventory.go b/pkg/network/payload/inventory.go index 9a9da7efd..7bed53eaa 100644 --- a/pkg/network/payload/inventory.go +++ b/pkg/network/payload/inventory.go @@ -1,7 +1,6 @@ package payload import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -58,36 +57,28 @@ func NewInventory(typ InventoryType, hashes []util.Uint256) *Inventory { // DecodeBinary implements the Payload interface. func (p *Inventory) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &p.Type); err != nil { - return err - } + br := util.BinReader{R: r} + br.ReadLE(&p.Type) - listLen := util.ReadVarUint(r) + listLen := br.ReadVarUint() p.Hashes = make([]util.Uint256, listLen) for i := 0; i < int(listLen); i++ { - if err := binary.Read(r, binary.LittleEndian, &p.Hashes[i]); err != nil { - return err - } + br.ReadLE(&p.Hashes[i]) } - return nil + return br.Err } // EncodeBinary implements the Payload interface. func (p *Inventory) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, p.Type); err != nil { - return err - } + bw := util.BinWriter{W: w} + bw.WriteLE(p.Type) listLen := len(p.Hashes) - if err := util.WriteVarUint(w, uint64(listLen)); err != nil { - return err - } - for i := 0; i < len(p.Hashes); i++ { - if err := binary.Write(w, binary.LittleEndian, p.Hashes[i]); err != nil { - return err - } + bw.WriteVarUint(uint64(listLen)) + for i := 0; i < listLen; i++ { + bw.WriteLE(p.Hashes[i]) } - return nil + return bw.Err } diff --git a/pkg/network/payload/merkleblock.go b/pkg/network/payload/merkleblock.go index bf46bbe81..f39b9789a 100644 --- a/pkg/network/payload/merkleblock.go +++ b/pkg/network/payload/merkleblock.go @@ -1,7 +1,6 @@ package payload import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/core" @@ -20,18 +19,16 @@ func (m *MerkleBlock) DecodeBinary(r io.Reader) error { if err := m.BlockBase.DecodeBinary(r); err != nil { return err } + br := util.BinReader{R: r} - m.TxCount = int(util.ReadVarUint(r)) - n := util.ReadVarUint(r) + m.TxCount = int(br.ReadVarUint()) + n := br.ReadVarUint() m.Hashes = make([]util.Uint256, n) for i := 0; i < len(m.Hashes); i++ { - if err := binary.Read(r, binary.LittleEndian, &m.Hashes[i]); err != nil { - return err - } + br.ReadLE(&m.Hashes[i]) } - var err error - m.Flags, err = util.ReadVarBytes(r) - return err + m.Flags = br.ReadBytes() + return br.Err } func (m *MerkleBlock) EncodeBinary(w io.Writer) error { diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index f5be4b615..35f97a40f 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -1,9 +1,10 @@ package payload import ( - "encoding/binary" "io" "time" + + "github.com/CityOfZion/neo-go/pkg/util" ) const minVersionSize = 27 @@ -53,63 +54,36 @@ func NewVersion(id uint32, p uint16, ua string, h uint32, r bool) *Version { // DecodeBinary implements the Payload interface. func (p *Version) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &p.Version); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &p.Services); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &p.Timestamp); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &p.Port); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &p.Nonce); err != nil { - return err - } + br := util.BinReader{R: r} + br.ReadLE(&p.Version) + br.ReadLE(&p.Services) + br.ReadLE(&p.Timestamp) + br.ReadLE(&p.Port) + br.ReadLE(&p.Nonce) var lenUA uint8 - if err := binary.Read(r, binary.LittleEndian, &lenUA); err != nil { - return err - } + br.ReadLE(&lenUA) p.UserAgent = make([]byte, lenUA) - if err := binary.Read(r, binary.LittleEndian, &p.UserAgent); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &p.StartHeight); err != nil { - return err - } - return binary.Read(r, binary.LittleEndian, &p.Relay) + br.ReadLE(&p.UserAgent) + br.ReadLE(&p.StartHeight) + br.ReadLE(&p.Relay) + return br.Err } // EncodeBinary implements the Payload interface. func (p *Version) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, p.Version); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.Services); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.Timestamp); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.Port); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.Nonce); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, uint8(len(p.UserAgent))); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.UserAgent); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, p.StartHeight); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, p.Relay) + br := util.BinWriter{W: w} + br.WriteLE(p.Version) + br.WriteLE(p.Services) + br.WriteLE(p.Timestamp) + br.WriteLE(p.Port) + br.WriteLE(p.Nonce) + + br.WriteLE(uint8(len(p.UserAgent))) + br.WriteLE(p.UserAgent) + br.WriteLE(p.StartHeight) + br.WriteLE(&p.Relay) + return br.Err } // Size implements the payloader interface. From 361724a33e8f2072fb3fb63ccfa9b7e5ef4136a1 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 19:27:06 +0300 Subject: [PATCH 09/13] pkg/core: use util.binaryReader/Writer Simplify error handling. --- pkg/core/account_state.go | 61 ++++--------- pkg/core/asset_state.go | 108 +++++++---------------- pkg/core/block.go | 40 +++++---- pkg/core/block_base.go | 72 ++++++--------- pkg/core/header_hash_list.go | 12 +-- pkg/core/spent_coin_state.go | 45 +++------- pkg/core/storage/helpers.go | 8 +- pkg/core/transaction/attribute.go | 93 +++++++++---------- pkg/core/transaction/claim.go | 12 ++- pkg/core/transaction/input.go | 20 ++--- pkg/core/transaction/invocation.go | 22 ++--- pkg/core/transaction/output.go | 25 +++--- pkg/core/transaction/publish.go | 50 +++-------- pkg/core/transaction/register.go | 49 ++++------ pkg/core/transaction/state.go | 6 +- pkg/core/transaction/state_descriptor.go | 29 ++---- pkg/core/transaction/transaction.go | 55 +++++++----- pkg/core/transaction/witness.go | 30 +++---- pkg/core/unspent_coin_state.go | 21 ++--- 19 files changed, 291 insertions(+), 467 deletions(-) diff --git a/pkg/core/account_state.go b/pkg/core/account_state.go index 01fecc7a7..44d277e76 100644 --- a/pkg/core/account_state.go +++ b/pkg/core/account_state.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "encoding/binary" "fmt" "io" @@ -69,17 +68,11 @@ func NewAccountState(scriptHash util.Uint160) *AccountState { // DecodeBinary decodes AccountState from the given io.Reader. func (s *AccountState) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &s.Version); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &s.ScriptHash); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &s.IsFrozen); err != nil { - return err - } - - lenVotes := util.ReadVarUint(r) + br := util.BinReader{R: r} + br.ReadLE(&s.Version) + br.ReadLE(&s.ScriptHash) + br.ReadLE(&s.IsFrozen) + lenVotes := br.ReadVarUint() s.Votes = make([]*keys.PublicKey, lenVotes) for i := 0; i < int(lenVotes); i++ { s.Votes[i] = &keys.PublicKey{} @@ -89,37 +82,25 @@ func (s *AccountState) DecodeBinary(r io.Reader) error { } s.Balances = make(map[util.Uint256]util.Fixed8) - lenBalances := util.ReadVarUint(r) + lenBalances := br.ReadVarUint() for i := 0; i < int(lenBalances); i++ { key := util.Uint256{} - if err := binary.Read(r, binary.LittleEndian, &key); err != nil { - return err - } + br.ReadLE(&key) var val util.Fixed8 - if err := binary.Read(r, binary.LittleEndian, &val); err != nil { - return err - } + br.ReadLE(&val) s.Balances[key] = val } - return nil + return br.Err } // EncodeBinary encode AccountState to the given io.Writer. func (s *AccountState) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, s.Version); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, s.ScriptHash); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, s.IsFrozen); err != nil { - return err - } - - if err := util.WriteVarUint(w, uint64(len(s.Votes))); err != nil { - return err - } + bw := util.BinWriter{W: w} + bw.WriteLE(s.Version) + bw.WriteLE(s.ScriptHash) + bw.WriteLE(s.IsFrozen) + bw.WriteVarUint(uint64(len(s.Votes))) for _, point := range s.Votes { if err := point.EncodeBinary(w); err != nil { return err @@ -127,19 +108,13 @@ func (s *AccountState) EncodeBinary(w io.Writer) error { } balances := s.nonZeroBalances() - if err := util.WriteVarUint(w, uint64(len(balances))); err != nil { - return err - } + bw.WriteVarUint(uint64(len(balances))) for k, v := range balances { - if err := binary.Write(w, binary.LittleEndian, k); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, v); err != nil { - return err - } + bw.WriteLE(k) + bw.WriteLE(v) } - return nil + return bw.Err } // Returns only the non-zero balances for the account. diff --git a/pkg/core/asset_state.go b/pkg/core/asset_state.go index ee4b4407b..19f7db8c3 100644 --- a/pkg/core/asset_state.go +++ b/pkg/core/asset_state.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/core/storage" @@ -48,95 +47,56 @@ type AssetState struct { // DecodeBinary implements the Payload interface. func (a *AssetState) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &a.ID); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &a.AssetType); err != nil { - return err - } + br := util.BinReader{R: r} + br.ReadLE(&a.ID) + br.ReadLE(&a.AssetType) - var err error - a.Name, err = util.ReadVarString(r) - if err != nil { - return err - } + a.Name = br.ReadString() - if err := binary.Read(r, binary.LittleEndian, &a.Amount); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &a.Available); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &a.Precision); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &a.FeeMode); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &a.FeeAddress); err != nil { - return err - } + br.ReadLE(&a.Amount) + br.ReadLE(&a.Available) + br.ReadLE(&a.Precision) + br.ReadLE(&a.FeeMode) + br.ReadLE(&a.FeeAddress) + if br.Err != nil { + return br.Err + } a.Owner = &keys.PublicKey{} if err := a.Owner.DecodeBinary(r); err != nil { return err } - if err := binary.Read(r, binary.LittleEndian, &a.Admin); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &a.Issuer); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &a.Expiration); err != nil { - return err - } - return binary.Read(r, binary.LittleEndian, &a.IsFrozen) + br.ReadLE(&a.Admin) + br.ReadLE(&a.Issuer) + br.ReadLE(&a.Expiration) + br.ReadLE(&a.IsFrozen) + + return br.Err } // EncodeBinary implements the Payload interface. func (a *AssetState) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, a.ID); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, a.AssetType); err != nil { - return err - } - if err := util.WriteVarUint(w, uint64(len(a.Name))); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, []byte(a.Name)); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, a.Amount); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, a.Available); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, a.Precision); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, a.FeeMode); err != nil { - return err - } + bw := util.BinWriter{W: w} + bw.WriteLE(a.ID) + bw.WriteLE(a.AssetType) + bw.WriteString(a.Name) + bw.WriteLE(a.Amount) + bw.WriteLE(a.Available) + bw.WriteLE(a.Precision) + bw.WriteLE(a.FeeMode) + bw.WriteLE(a.FeeAddress) - if err := binary.Write(w, binary.LittleEndian, a.FeeAddress); err != nil { - return err + if bw.Err != nil { + return bw.Err } - if err := a.Owner.EncodeBinary(w); err != nil { return err } - if err := binary.Write(w, binary.LittleEndian, a.Admin); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, a.Issuer); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, a.Expiration); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, a.IsFrozen) + bw.WriteLE(a.Admin) + bw.WriteLE(a.Issuer) + bw.WriteLE(a.Expiration) + bw.WriteLE(a.IsFrozen) + return bw.Err } // GetName returns the asset name based on its type. diff --git a/pkg/core/block.go b/pkg/core/block.go index 762ba6787..7744314fd 100644 --- a/pkg/core/block.go +++ b/pkg/core/block.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/core/transaction" @@ -79,9 +78,11 @@ func NewBlockFromTrimmedBytes(b []byte) (*Block, error) { return block, err } + br := util.BinReader{R: r} var padding uint8 - if err := binary.Read(r, binary.LittleEndian, &padding); err != nil { - return block, err + br.ReadLE(&padding) + if br.Err != nil { + return block, br.Err } block.Script = &transaction.Witness{} @@ -89,17 +90,15 @@ func NewBlockFromTrimmedBytes(b []byte) (*Block, error) { return block, err } - lenTX := util.ReadVarUint(r) + lenTX := br.ReadVarUint() block.Transactions = make([]*transaction.Transaction, lenTX) for i := 0; i < int(lenTX); i++ { var hash util.Uint256 - if err := binary.Read(r, binary.LittleEndian, &hash); err != nil { - return block, err - } + br.ReadLE(&hash) block.Transactions[i] = transaction.NewTrimmedTX(hash) } - return block, nil + return block, br.Err } // Trim returns a subset of the block data to save up space @@ -110,23 +109,22 @@ func (b *Block) Trim() ([]byte, error) { if err := b.encodeHashableFields(buf); err != nil { return nil, err } - if err := binary.Write(buf, binary.LittleEndian, uint8(1)); err != nil { - return nil, err + bw := util.BinWriter{W: buf} + bw.WriteLE(uint8(1)) + if bw.Err != nil { + return nil, bw.Err } if err := b.Script.EncodeBinary(buf); err != nil { return nil, err } - lenTX := uint64(len(b.Transactions)) - if err := util.WriteVarUint(buf, lenTX); err != nil { - return nil, err - } + bw.WriteVarUint(uint64(len(b.Transactions))) for _, tx := range b.Transactions { - if err := binary.Write(buf, binary.LittleEndian, tx.Hash()); err != nil { - return nil, err - } + bw.WriteLE(tx.Hash()) + } + if bw.Err != nil { + return nil, bw.Err } - return buf.Bytes(), nil } @@ -136,7 +134,11 @@ func (b *Block) DecodeBinary(r io.Reader) error { return err } - lentx := util.ReadVarUint(r) + br := util.BinReader{R: r} + lentx := br.ReadVarUint() + if br.Err != nil { + return br.Err + } b.Transactions = make([]*transaction.Transaction, lentx) for i := 0; i < int(lentx); i++ { b.Transactions[i] = &transaction.Transaction{} diff --git a/pkg/core/block_base.go b/pkg/core/block_base.go index 608e3e706..00fd4d277 100644 --- a/pkg/core/block_base.go +++ b/pkg/core/block_base.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "encoding/binary" "fmt" "io" @@ -67,8 +66,10 @@ func (b *BlockBase) DecodeBinary(r io.Reader) error { } var padding uint8 - if err := binary.Read(r, binary.LittleEndian, &padding); err != nil { - return err + br := util.BinReader{R: r} + br.ReadLE(&padding) + if br.Err != nil { + return br.Err } if padding != 1 { return fmt.Errorf("format error: padding must equal 1 got %d", padding) @@ -83,8 +84,10 @@ func (b *BlockBase) EncodeBinary(w io.Writer) error { if err := b.encodeHashableFields(w); err != nil { return err } - if err := binary.Write(w, binary.LittleEndian, uint8(1)); err != nil { - return err + bw := util.BinWriter{W: w} + bw.WriteLE(uint8(1)) + if bw.Err != nil { + return bw.Err } return b.Script.EncodeBinary(w) } @@ -108,50 +111,31 @@ func (b *BlockBase) createHash() error { // encodeHashableFields will only encode the fields used for hashing. // see Hash() for more information about the fields. func (b *BlockBase) encodeHashableFields(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, b.Version); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, b.PrevHash); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, b.MerkleRoot); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, b.Timestamp); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, b.Index); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, b.ConsensusData); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, b.NextConsensus) + bw := util.BinWriter{W: w} + bw.WriteLE(b.Version) + bw.WriteLE(b.PrevHash) + bw.WriteLE(b.MerkleRoot) + bw.WriteLE(b.Timestamp) + bw.WriteLE(b.Index) + bw.WriteLE(b.ConsensusData) + bw.WriteLE(b.NextConsensus) + return bw.Err } // decodeHashableFields will only decode the fields used for hashing. // see Hash() for more information about the fields. func (b *BlockBase) decodeHashableFields(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &b.Version); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &b.PrevHash); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &b.MerkleRoot); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &b.Timestamp); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &b.Index); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &b.ConsensusData); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &b.NextConsensus); err != nil { - return err + br := util.BinReader{R: r} + br.ReadLE(&b.Version) + br.ReadLE(&b.PrevHash) + br.ReadLE(&b.MerkleRoot) + br.ReadLE(&b.Timestamp) + br.ReadLE(&b.Index) + br.ReadLE(&b.ConsensusData) + br.ReadLE(&b.NextConsensus) + + if br.Err != nil { + return br.Err } // Make the hash of the block here so we dont need to do this diff --git a/pkg/core/header_hash_list.go b/pkg/core/header_hash_list.go index ad97b526e..35c190afa 100644 --- a/pkg/core/header_hash_list.go +++ b/pkg/core/header_hash_list.go @@ -1,7 +1,6 @@ package core import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -60,14 +59,11 @@ func (l *HeaderHashList) Slice(start, end int) []util.Uint256 { // WriteTo will write n underlying hashes to the given io.Writer // starting from start. func (l *HeaderHashList) Write(w io.Writer, start, n int) error { - if err := util.WriteVarUint(w, uint64(n)); err != nil { - return err - } + bw := util.BinWriter{W: w} + bw.WriteVarUint(uint64(n)) hashes := l.Slice(start, start+n) for _, hash := range hashes { - if err := binary.Write(w, binary.LittleEndian, hash); err != nil { - return err - } + bw.WriteLE(hash) } - return nil + return bw.Err } diff --git a/pkg/core/spent_coin_state.go b/pkg/core/spent_coin_state.go index 9e590dd49..fcfde1ae7 100644 --- a/pkg/core/spent_coin_state.go +++ b/pkg/core/spent_coin_state.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "encoding/binary" "fmt" "io" @@ -68,49 +67,33 @@ func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState { // DecodeBinary implements the Payload interface. func (s *SpentCoinState) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &s.txHash); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &s.txHeight); err != nil { - return err - } + br := util.BinReader{R: r} + br.ReadLE(&s.txHash) + br.ReadLE(&s.txHeight) s.items = make(map[uint16]uint32) - lenItems := util.ReadVarUint(r) + lenItems := br.ReadVarUint() for i := 0; i < int(lenItems); i++ { var ( key uint16 value uint32 ) - if err := binary.Read(r, binary.LittleEndian, &key); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &value); err != nil { - return err - } + br.ReadLE(&key) + br.ReadLE(&value) s.items[key] = value } - return nil + return br.Err } // EncodeBinary implements the Payload interface. func (s *SpentCoinState) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, s.txHash); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, s.txHeight); err != nil { - return err - } - if err := util.WriteVarUint(w, uint64(len(s.items))); err != nil { - return err - } + bw := util.BinWriter{W: w} + bw.WriteLE(s.txHash) + bw.WriteLE(s.txHeight) + bw.WriteVarUint(uint64(len(s.items))) for k, v := range s.items { - if err := binary.Write(w, binary.LittleEndian, k); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, v); err != nil { - return err - } + bw.WriteLE(k) + bw.WriteLE(v) } - return nil + return bw.Err } diff --git a/pkg/core/storage/helpers.go b/pkg/core/storage/helpers.go index b98c7671c..2776da38c 100644 --- a/pkg/core/storage/helpers.go +++ b/pkg/core/storage/helpers.go @@ -84,10 +84,12 @@ func HeaderHashes(s Store) ([]util.Uint256, error) { // the given byte array. func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) { r := bytes.NewReader(b) - lenHashes := util.ReadVarUint(r) + br := util.BinReader{R: r} + lenHashes := br.ReadVarUint() hashes := make([]util.Uint256, lenHashes) - if err := binary.Read(r, binary.LittleEndian, hashes); err != nil { - return nil, err + br.ReadLE(hashes) + if br.Err != nil { + return nil, br.Err } return hashes, nil } diff --git a/pkg/core/transaction/attribute.go b/pkg/core/transaction/attribute.go index 783b1dcc3..34bf6443c 100644 --- a/pkg/core/transaction/attribute.go +++ b/pkg/core/transaction/attribute.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "encoding/hex" "encoding/json" "fmt" @@ -18,66 +17,58 @@ type Attribute struct { // DecodeBinary implements the Payload interface. func (attr *Attribute) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &attr.Usage); err != nil { - return err - } - if attr.Usage == ContractHash || - attr.Usage == Vote || - (attr.Usage >= Hash1 && attr.Usage <= Hash15) { - attr.Data = make([]byte, 32) - return binary.Read(r, binary.LittleEndian, attr.Data) - } + br := util.BinReader{R: r} + br.ReadLE(&attr.Usage) + + // very special case if attr.Usage == ECDH02 || attr.Usage == ECDH03 { attr.Data = make([]byte, 33) attr.Data[0] = byte(attr.Usage) - return binary.Read(r, binary.LittleEndian, attr.Data[1:]) + br.ReadLE(attr.Data[1:]) + return br.Err } - if attr.Usage == Script { - attr.Data = make([]byte, 20) - return binary.Read(r, binary.LittleEndian, attr.Data) + var datasize uint64 + switch attr.Usage { + case ContractHash, Vote, Hash1, Hash2, Hash3, Hash4, Hash5, + Hash6, Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, + Hash14, Hash15: + datasize = 32 + case Script: + datasize = 20 + case DescriptionURL: + datasize = 1 + case Description, Remark, Remark1, Remark2, Remark3, Remark4, + Remark5, Remark6, Remark7, Remark8, Remark9, Remark10, Remark11, + Remark12, Remark13, Remark14, Remark15: + datasize = br.ReadVarUint() + default: + return fmt.Errorf("failed decoding TX attribute usage: 0x%2x", attr.Usage) } - if attr.Usage == DescriptionURL { - attr.Data = make([]byte, 1) - return binary.Read(r, binary.LittleEndian, attr.Data) - } - if attr.Usage == Description || attr.Usage >= Remark { - lenData := util.ReadVarUint(r) - attr.Data = make([]byte, lenData) - return binary.Read(r, binary.LittleEndian, attr.Data) - } - return fmt.Errorf("failed decoding TX attribute usage: 0x%2x", attr.Usage) + attr.Data = make([]byte, datasize) + br.ReadLE(attr.Data) + return br.Err } // EncodeBinary implements the Payload interface. func (attr *Attribute) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, &attr.Usage); err != nil { - return err + bw := util.BinWriter{W: w} + bw.WriteLE(&attr.Usage) + switch attr.Usage { + case ECDH02, ECDH03: + bw.WriteLE(attr.Data[1:]) + case DescriptionURL, Description, Remark, Remark1, Remark2, Remark3, Remark4, + Remark5, Remark6, Remark7, Remark8, Remark9, Remark10, Remark11, + Remark12, Remark13, Remark14, Remark15: + bw.WriteVarUint(uint64(len(attr.Data))) + fallthrough + case Script, ContractHash, Vote, Hash1, Hash2, Hash3, Hash4, Hash5, Hash6, + Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15: + bw.WriteLE(attr.Data) + default: + return fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Usage) } - if attr.Usage == ContractHash || - attr.Usage == Vote || - (attr.Usage >= Hash1 && attr.Usage <= Hash15) { - return binary.Write(w, binary.LittleEndian, attr.Data) - } - if attr.Usage == ECDH02 || attr.Usage == ECDH03 { - attr.Data[0] = byte(attr.Usage) - return binary.Write(w, binary.LittleEndian, attr.Data[1:33]) - } - if attr.Usage == Script { - return binary.Write(w, binary.LittleEndian, attr.Data) - } - if attr.Usage == DescriptionURL { - if err := util.WriteVarUint(w, uint64(len(attr.Data))); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, attr.Data) - } - if attr.Usage == Description || attr.Usage >= Remark { - if err := util.WriteVarUint(w, uint64(len(attr.Data))); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, attr.Data) - } - return fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Usage) + + return bw.Err } // Size returns the size in number bytes of the Attribute diff --git a/pkg/core/transaction/claim.go b/pkg/core/transaction/claim.go index b620ae297..ecb7f0720 100644 --- a/pkg/core/transaction/claim.go +++ b/pkg/core/transaction/claim.go @@ -13,7 +13,11 @@ type ClaimTX struct { // DecodeBinary implements the Payload interface. func (tx *ClaimTX) DecodeBinary(r io.Reader) error { - lenClaims := util.ReadVarUint(r) + br := util.BinReader{R: r} + lenClaims := br.ReadVarUint() + if br.Err != nil { + return br.Err + } tx.Claims = make([]*Input, lenClaims) for i := 0; i < int(lenClaims); i++ { tx.Claims[i] = &Input{} @@ -26,8 +30,10 @@ func (tx *ClaimTX) DecodeBinary(r io.Reader) error { // EncodeBinary implements the Payload interface. func (tx *ClaimTX) EncodeBinary(w io.Writer) error { - if err := util.WriteVarUint(w, uint64(len(tx.Claims))); err != nil { - return err + bw := util.BinWriter{W: w} + bw.WriteVarUint(uint64(len(tx.Claims))) + if bw.Err != nil { + return bw.Err } for _, claim := range tx.Claims { if err := claim.EncodeBinary(w); err != nil { diff --git a/pkg/core/transaction/input.go b/pkg/core/transaction/input.go index 23ef80c03..ab923981f 100644 --- a/pkg/core/transaction/input.go +++ b/pkg/core/transaction/input.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -18,21 +17,18 @@ type Input struct { // DecodeBinary implements the Payload interface. func (in *Input) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &in.PrevHash); err != nil { - return err - } - return binary.Read(r, binary.LittleEndian, &in.PrevIndex) + br := util.BinReader{R: r} + br.ReadLE(&in.PrevHash) + br.ReadLE(&in.PrevIndex) + return br.Err } // EncodeBinary implements the Payload interface. func (in *Input) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, in.PrevHash); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, in.PrevIndex); err != nil { - return err - } - return nil + bw := util.BinWriter{W: w} + bw.WriteLE(in.PrevHash) + bw.WriteLE(in.PrevIndex) + return bw.Err } // Size returns the size in bytes of the Input diff --git a/pkg/core/transaction/invocation.go b/pkg/core/transaction/invocation.go index 216d141fb..f07233cfe 100644 --- a/pkg/core/transaction/invocation.go +++ b/pkg/core/transaction/invocation.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -34,21 +33,16 @@ func NewInvocationTX(script []byte) *Transaction { // DecodeBinary implements the Payload interface. func (tx *InvocationTX) DecodeBinary(r io.Reader) error { - lenScript := util.ReadVarUint(r) - tx.Script = make([]byte, lenScript) - if err := binary.Read(r, binary.LittleEndian, tx.Script); err != nil { - return err - } - return binary.Read(r, binary.LittleEndian, &tx.Gas) + br := util.BinReader{R: r} + tx.Script = br.ReadBytes() + br.ReadLE(&tx.Gas) + return br.Err } // EncodeBinary implements the Payload interface. func (tx *InvocationTX) EncodeBinary(w io.Writer) error { - if err := util.WriteVarUint(w, uint64(len(tx.Script))); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, tx.Script); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, tx.Gas) + bw := util.BinWriter{W: w} + bw.WriteBytes(tx.Script) + bw.WriteLE(tx.Gas) + return bw.Err } diff --git a/pkg/core/transaction/output.go b/pkg/core/transaction/output.go index 2898605aa..60a55b5cf 100644 --- a/pkg/core/transaction/output.go +++ b/pkg/core/transaction/output.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "encoding/json" "io" @@ -36,24 +35,20 @@ func NewOutput(assetID util.Uint256, amount util.Fixed8, scriptHash util.Uint160 // DecodeBinary implements the Payload interface. func (out *Output) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &out.AssetID); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &out.Amount); err != nil { - return err - } - return binary.Read(r, binary.LittleEndian, &out.ScriptHash) + br := util.BinReader{R: r} + br.ReadLE(&out.AssetID) + br.ReadLE(&out.Amount) + br.ReadLE(&out.ScriptHash) + return br.Err } // EncodeBinary implements the Payload interface. func (out *Output) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, out.AssetID); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, out.Amount); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, out.ScriptHash) + bw := util.BinWriter{W: w} + bw.WriteLE(out.AssetID) + bw.WriteLE(out.Amount) + bw.WriteLE(out.ScriptHash) + return bw.Err } // Size returns the size in bytes of the Output diff --git a/pkg/core/transaction/publish.go b/pkg/core/transaction/publish.go index ed92409c9..f6a247b92 100644 --- a/pkg/core/transaction/publish.go +++ b/pkg/core/transaction/publish.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/smartcontract" @@ -24,55 +23,30 @@ type PublishTX struct { // DecodeBinary implements the Payload interface. func (tx *PublishTX) DecodeBinary(r io.Reader) error { - var err error + br := util.BinReader{R: r} + tx.Script = br.ReadBytes() - tx.Script, err = util.ReadVarBytes(r) - if err != nil { - return err - } - - lenParams := util.ReadVarUint(r) + lenParams := br.ReadVarUint() tx.ParamList = make([]smartcontract.ParamType, lenParams) for i := 0; i < int(lenParams); i++ { var ptype uint8 - if err := binary.Read(r, binary.LittleEndian, &ptype); err != nil { - return err - } + br.ReadLE(&ptype) tx.ParamList[i] = smartcontract.ParamType(ptype) } var rtype uint8 - if err := binary.Read(r, binary.LittleEndian, &rtype); err != nil { - return err - } + br.ReadLE(&rtype) tx.ReturnType = smartcontract.ParamType(rtype) - if err := binary.Read(r, binary.LittleEndian, &tx.NeedStorage); err != nil { - return err - } + br.ReadLE(&tx.NeedStorage) - tx.Name, err = util.ReadVarString(r) - if err != nil { - return err - } - tx.CodeVersion, err = util.ReadVarString(r) - if err != nil { - return err - } - tx.Author, err = util.ReadVarString(r) - if err != nil { - return err - } - tx.Email, err = util.ReadVarString(r) - if err != nil { - return err - } - tx.Description, err = util.ReadVarString(r) - if err != nil { - return err - } + tx.Name = br.ReadString() + tx.CodeVersion = br.ReadString() + tx.Author = br.ReadString() + tx.Email = br.ReadString() + tx.Description = br.ReadString() - return nil + return br.Err } // EncodeBinary implements the Payload interface. diff --git a/pkg/core/transaction/register.go b/pkg/core/transaction/register.go index 5f2467a47..2d47fd442 100644 --- a/pkg/core/transaction/register.go +++ b/pkg/core/transaction/register.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -32,22 +31,15 @@ type RegisterTX struct { // DecodeBinary implements the Payload interface. func (tx *RegisterTX) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &tx.AssetType); err != nil { - return err - } + br := util.BinReader{R: r} + br.ReadLE(&tx.AssetType) - var err error - tx.Name, err = util.ReadVarString(r) - if err != nil { - return err - } + tx.Name = br.ReadString() - if err := binary.Read(r, binary.LittleEndian, &tx.Amount); err != nil { - return err - } - - if err := binary.Read(r, binary.LittleEndian, &tx.Precision); err != nil { - return err + br.ReadLE(&tx.Amount) + br.ReadLE(&tx.Precision) + if br.Err != nil { + return br.Err } tx.Owner = &keys.PublicKey{} @@ -55,25 +47,18 @@ func (tx *RegisterTX) DecodeBinary(r io.Reader) error { return err } - return binary.Read(r, binary.LittleEndian, &tx.Admin) + br.ReadLE(&tx.Admin) + return br.Err } // EncodeBinary implements the Payload interface. func (tx *RegisterTX) EncodeBinary(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, tx.AssetType); err != nil { - return err - } - if err := util.WriteVarString(w, tx.Name); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, tx.Amount); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, tx.Precision); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, tx.Owner.Bytes()); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, tx.Admin) + bw := util.BinWriter{W: w} + bw.WriteLE(tx.AssetType) + bw.WriteString(tx.Name) + bw.WriteLE(tx.Amount) + bw.WriteLE(tx.Precision) + bw.WriteLE(tx.Owner.Bytes()) + bw.WriteLE(tx.Admin) + return bw.Err } diff --git a/pkg/core/transaction/state.go b/pkg/core/transaction/state.go index 3b61e7f84..6bcbf8e2a 100644 --- a/pkg/core/transaction/state.go +++ b/pkg/core/transaction/state.go @@ -13,7 +13,11 @@ type StateTX struct { // DecodeBinary implements the Payload interface. func (tx *StateTX) DecodeBinary(r io.Reader) error { - lenDesc := util.ReadVarUint(r) + br := util.BinReader{R: r} + lenDesc := br.ReadVarUint() + if br.Err != nil { + return br.Err + } for i := 0; i < int(lenDesc); i++ { tx.Descriptors[i] = &StateDescriptor{} if err := tx.Descriptors[i].DecodeBinary(r); err != nil { diff --git a/pkg/core/transaction/state_descriptor.go b/pkg/core/transaction/state_descriptor.go index b45b250a3..95f41be52 100644 --- a/pkg/core/transaction/state_descriptor.go +++ b/pkg/core/transaction/state_descriptor.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -26,30 +25,14 @@ type StateDescriptor struct { // DecodeBinary implements the Payload interface. func (s *StateDescriptor) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &s.Type); err != nil { - return err - } + br := util.BinReader{R: r} + br.ReadLE(&s.Type) - keyLen := util.ReadVarUint(r) - s.Key = make([]byte, keyLen) - if err := binary.Read(r, binary.LittleEndian, s.Key); err != nil { - return err - } + s.Key = br.ReadBytes() + s.Value = br.ReadBytes() + s.Field = br.ReadString() - valLen := util.ReadVarUint(r) - s.Value = make([]byte, valLen) - if err := binary.Read(r, binary.LittleEndian, s.Value); err != nil { - return err - } - - fieldLen := util.ReadVarUint(r) - field := make([]byte, fieldLen) - if err := binary.Read(r, binary.LittleEndian, field); err != nil { - return err - } - s.Field = string(field) - - return nil + return br.Err } // EncodeBinary implements the Payload interface. diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index d95f73dd9..fb83461a2 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -2,7 +2,6 @@ package transaction import ( "bytes" - "encoding/binary" "io" "github.com/CityOfZion/neo-go/pkg/crypto/hash" @@ -79,17 +78,17 @@ func (t *Transaction) AddInput(in *Input) { // DecodeBinary implements the payload interface. func (t *Transaction) DecodeBinary(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &t.Type); err != nil { - return err - } - if err := binary.Read(r, binary.LittleEndian, &t.Version); err != nil { - return err + br := util.BinReader{R: r} + br.ReadLE(&t.Type) + br.ReadLE(&t.Version) + if br.Err != nil { + return br.Err } if err := t.decodeData(r); err != nil { return err } - lenAttrs := util.ReadVarUint(r) + lenAttrs := br.ReadVarUint() t.Attributes = make([]*Attribute, lenAttrs) for i := 0; i < int(lenAttrs); i++ { t.Attributes[i] = &Attribute{} @@ -100,7 +99,7 @@ func (t *Transaction) DecodeBinary(r io.Reader) error { } } - lenInputs := util.ReadVarUint(r) + lenInputs := br.ReadVarUint() t.Inputs = make([]*Input, lenInputs) for i := 0; i < int(lenInputs); i++ { t.Inputs[i] = &Input{} @@ -109,7 +108,7 @@ func (t *Transaction) DecodeBinary(r io.Reader) error { } } - lenOutputs := util.ReadVarUint(r) + lenOutputs := br.ReadVarUint() t.Outputs = make([]*Output, lenOutputs) for i := 0; i < int(lenOutputs); i++ { t.Outputs[i] = &Output{} @@ -118,7 +117,7 @@ func (t *Transaction) DecodeBinary(r io.Reader) error { } } - lenScripts := util.ReadVarUint(r) + lenScripts := br.ReadVarUint() t.Scripts = make([]*Witness, lenScripts) for i := 0; i < int(lenScripts); i++ { t.Scripts[i] = &Witness{} @@ -127,6 +126,9 @@ func (t *Transaction) DecodeBinary(r io.Reader) error { } } + if br.Err != nil { + return br.Err + } // Create the hash of the transaction at decode, so we dont need // to do it anymore. return t.createHash() @@ -172,8 +174,10 @@ func (t *Transaction) EncodeBinary(w io.Writer) error { if err := t.encodeHashableFields(w); err != nil { return err } - if err := util.WriteVarUint(w, uint64(len(t.Scripts))); err != nil { - return err + bw := util.BinWriter{W: w} + bw.WriteVarUint(uint64(len(t.Scripts))) + if bw.Err != nil { + return bw.Err } for _, s := range t.Scripts { if err := s.EncodeBinary(w); err != nil { @@ -186,11 +190,12 @@ func (t *Transaction) EncodeBinary(w io.Writer) error { // encodeHashableFields will only encode the fields that are not used for // signing the transaction, which are all fields except the scripts. func (t *Transaction) encodeHashableFields(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, t.Type); err != nil { - return err - } - if err := binary.Write(w, binary.LittleEndian, t.Version); err != nil { - return err + bw := util.BinWriter{W: w} + + bw.WriteLE(t.Type) + bw.WriteLE(t.Version) + if bw.Err != nil { + return bw.Err } // Underlying TXer. @@ -201,9 +206,9 @@ func (t *Transaction) encodeHashableFields(w io.Writer) error { } // Attributes - lenAttrs := uint64(len(t.Attributes)) - if err := util.WriteVarUint(w, lenAttrs); err != nil { - return err + bw.WriteVarUint(uint64(len(t.Attributes))) + if bw.Err != nil { + return bw.Err } for _, attr := range t.Attributes { if err := attr.EncodeBinary(w); err != nil { @@ -212,8 +217,9 @@ func (t *Transaction) encodeHashableFields(w io.Writer) error { } // Inputs - if err := util.WriteVarUint(w, uint64(len(t.Inputs))); err != nil { - return err + bw.WriteVarUint(uint64(len(t.Inputs))) + if bw.Err != nil { + return bw.Err } for _, in := range t.Inputs { if err := in.EncodeBinary(w); err != nil { @@ -222,8 +228,9 @@ func (t *Transaction) encodeHashableFields(w io.Writer) error { } // Outputs - if err := util.WriteVarUint(w, uint64(len(t.Outputs))); err != nil { - return err + bw.WriteVarUint(uint64(len(t.Outputs))) + if bw.Err != nil { + return bw.Err } for _, out := range t.Outputs { if err := out.EncodeBinary(w); err != nil { diff --git a/pkg/core/transaction/witness.go b/pkg/core/transaction/witness.go index cf10d8bb2..125fede63 100644 --- a/pkg/core/transaction/witness.go +++ b/pkg/core/transaction/witness.go @@ -1,7 +1,6 @@ package transaction import ( - "encoding/binary" "encoding/hex" "encoding/json" "io" @@ -18,28 +17,21 @@ type Witness struct { // DecodeBinary implements the payload interface. func (w *Witness) DecodeBinary(r io.Reader) error { - lenb := util.ReadVarUint(r) - w.InvocationScript = make([]byte, lenb) - if err := binary.Read(r, binary.LittleEndian, w.InvocationScript); err != nil { - return err - } - lenb = util.ReadVarUint(r) - w.VerificationScript = make([]byte, lenb) - return binary.Read(r, binary.LittleEndian, w.VerificationScript) + br := util.BinReader{R: r} + + w.InvocationScript = br.ReadBytes() + w.VerificationScript = br.ReadBytes() + return br.Err } // EncodeBinary implements the payload interface. func (w *Witness) EncodeBinary(writer io.Writer) error { - if err := util.WriteVarUint(writer, uint64(len(w.InvocationScript))); err != nil { - return err - } - if err := binary.Write(writer, binary.LittleEndian, w.InvocationScript); err != nil { - return err - } - if err := util.WriteVarUint(writer, uint64(len(w.VerificationScript))); err != nil { - return err - } - return binary.Write(writer, binary.LittleEndian, w.VerificationScript) + bw := util.BinWriter{W: writer} + + bw.WriteBytes(w.InvocationScript) + bw.WriteBytes(w.VerificationScript) + + return bw.Err } // MarshalJSON implements the json marshaller interface. diff --git a/pkg/core/unspent_coin_state.go b/pkg/core/unspent_coin_state.go index e588e0575..e8972934f 100644 --- a/pkg/core/unspent_coin_state.go +++ b/pkg/core/unspent_coin_state.go @@ -2,7 +2,6 @@ package core import ( "bytes" - "encoding/binary" "fmt" "io" @@ -68,29 +67,25 @@ func (u UnspentCoins) commit(b storage.Batch) error { // EncodeBinary encodes UnspentCoinState to the given io.Writer. func (s *UnspentCoinState) EncodeBinary(w io.Writer) error { - if err := util.WriteVarUint(w, uint64(len(s.states))); err != nil { - return err - } + bw := util.BinWriter{W: w} + bw.WriteVarUint(uint64(len(s.states))) for _, state := range s.states { - if err := binary.Write(w, binary.LittleEndian, byte(state)); err != nil { - return err - } + bw.WriteLE(byte(state)) } - return nil + return bw.Err } // DecodeBinary decodes UnspentCoinState from the given io.Reader. func (s *UnspentCoinState) DecodeBinary(r io.Reader) error { - lenStates := util.ReadVarUint(r) + br := util.BinReader{R: r} + lenStates := br.ReadVarUint() s.states = make([]CoinState, lenStates) for i := 0; i < int(lenStates); i++ { var state uint8 - if err := binary.Read(r, binary.LittleEndian, &state); err != nil { - return err - } + br.ReadLE(&state) s.states[i] = CoinState(state) } - return nil + return br.Err } // IsDoubleSpend verifies that the input transactions are not double spent. From eba83a0e31289b510bdad1f585acd5eb9c8d2625 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 19:34:38 +0300 Subject: [PATCH 10/13] smartcontract: use new binaryReader/Writer API from util --- pkg/smartcontract/contract_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go index 2708e4f0a..37c76d575 100644 --- a/pkg/smartcontract/contract_test.go +++ b/pkg/smartcontract/contract_test.go @@ -23,19 +23,21 @@ func TestCreateMultiSigRedeemScript(t *testing.T) { } buf := bytes.NewBuffer(out) - b, _ := buf.ReadByte() + br := util.BinReader{R: buf} + var b uint8 + br.ReadLE(&b) assert.Equal(t, vm.PUSH3, vm.Instruction(b)) for i := 0; i < len(validators); i++ { - b, err := util.ReadVarBytes(buf) + bb := br.ReadBytes() if err != nil { t.Fatal(err) } - assert.Equal(t, validators[i].Bytes(), b) + assert.Equal(t, validators[i].Bytes(), bb) } - b, _ = buf.ReadByte() + br.ReadLE(&b) assert.Equal(t, vm.PUSH3, vm.Instruction(b)) - b, _ = buf.ReadByte() + br.ReadLE(&b) assert.Equal(t, vm.CHECKMULTISIG, vm.Instruction(b)) } From 15311f202bdfd9f484de85c3d7c95d294e14e46b Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 28 Aug 2019 19:41:56 +0300 Subject: [PATCH 11/13] util: drop io, reuse its tests for binaryRW --- pkg/util/{io_test.go => binaryrw_test.go} | 39 +++++----- pkg/util/io.go | 90 ----------------------- 2 files changed, 21 insertions(+), 108 deletions(-) rename pkg/util/{io_test.go => binaryrw_test.go} (62%) delete mode 100644 pkg/util/io.go diff --git a/pkg/util/io_test.go b/pkg/util/binaryrw_test.go similarity index 62% rename from pkg/util/io_test.go rename to pkg/util/binaryrw_test.go index 7d0258eda..7bf320f20 100644 --- a/pkg/util/io_test.go +++ b/pkg/util/binaryrw_test.go @@ -12,9 +12,9 @@ func TestWriteVarUint1(t *testing.T) { val = uint64(1) buf = new(bytes.Buffer) ) - if err := WriteVarUint(buf, val); err != nil { - t.Fatal(err) - } + bw := BinWriter{W: buf} + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) assert.Equal(t, 1, buf.Len()) } @@ -23,13 +23,14 @@ func TestWriteVarUint1000(t *testing.T) { val = uint64(1000) buf = new(bytes.Buffer) ) - - if err := WriteVarUint(buf, val); err != nil { - t.Fatal(err) - } + bw := BinWriter{W: buf} + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) assert.Equal(t, 3, buf.Len()) assert.Equal(t, byte(0xfd), buf.Bytes()[0]) - res := ReadVarUint(buf) + br := BinReader{R: buf} + res := br.ReadVarUint() + assert.Nil(t, br.Err) assert.Equal(t, val, res) } @@ -38,13 +39,14 @@ func TestWriteVarUint100000(t *testing.T) { val = uint64(100000) buf = new(bytes.Buffer) ) - - if err := WriteVarUint(buf, val); err != nil { - t.Fatal(err) - } + bw := BinWriter{W: buf} + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) assert.Equal(t, 5, buf.Len()) assert.Equal(t, byte(0xfe), buf.Bytes()[0]) - res := ReadVarUint(buf) + br := BinReader{R: buf} + res := br.ReadVarUint() + assert.Nil(t, br.Err) assert.Equal(t, val, res) } @@ -53,12 +55,13 @@ func TestWriteVarUint100000000000(t *testing.T) { val = uint64(1000000000000) buf = new(bytes.Buffer) ) - - if err := WriteVarUint(buf, val); err != nil { - t.Fatal(err) - } + bw := BinWriter{W: buf} + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) assert.Equal(t, 9, buf.Len()) assert.Equal(t, byte(0xff), buf.Bytes()[0]) - res := ReadVarUint(buf) + br := BinReader{R: buf} + res := br.ReadVarUint() + assert.Nil(t, br.Err) assert.Equal(t, val, res) } diff --git a/pkg/util/io.go b/pkg/util/io.go deleted file mode 100644 index d81491da3..000000000 --- a/pkg/util/io.go +++ /dev/null @@ -1,90 +0,0 @@ -package util - -import ( - "encoding/binary" - "io" -) - -// Variable length integer, can be encoded to save space according to the value typed. -// len 1 uint8 -// len 3 0xfd + uint16 -// len 5 0xfe = uint32 -// len 9 0xff = uint64 -// For more information about this: -// https://github.com/neo-project/neo/wiki/Network-Protocol - -// ReadVarUint reads a variable unsigned integer and returns it as a uint64. -func ReadVarUint(r io.Reader) uint64 { - var b uint8 - binary.Read(r, binary.LittleEndian, &b) - switch b { - case 0xfd: - var v uint16 - binary.Read(r, binary.LittleEndian, &v) - return uint64(v) - case 0xfe: - var v uint32 - binary.Read(r, binary.LittleEndian, &v) - return uint64(v) - case 0xff: - var v uint64 - binary.Read(r, binary.LittleEndian, &v) - return v - default: - return uint64(b) - } -} - -// WriteVarUint writes a variable unsigned integer. -func WriteVarUint(w io.Writer, val uint64) error { - if val < 0xfd { - return binary.Write(w, binary.LittleEndian, uint8(val)) - } - if val < 0xFFFF { - if err := binary.Write(w, binary.LittleEndian, byte(0xfd)); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, uint16(val)) - } - if val < 0xFFFFFFFF { - if err := binary.Write(w, binary.LittleEndian, byte(0xfe)); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, uint32(val)) - } - - if err := binary.Write(w, binary.LittleEndian, byte(0xff)); err != nil { - return err - } - - return binary.Write(w, binary.LittleEndian, val) -} - -// ReadVarBytes reads a variable length byte array. -func ReadVarBytes(r io.Reader) ([]byte, error) { - n := ReadVarUint(r) - b := make([]byte, n) - if err := binary.Read(r, binary.LittleEndian, b); err != nil { - return nil, err - } - return b, nil -} - -// ReadVarString reads a variable length string. -func ReadVarString(r io.Reader) (string, error) { - b, err := ReadVarBytes(r) - return string(b), err -} - -// WriteVarString writes a variable length string. -func WriteVarString(w io.Writer, s string) error { - return WriteVarBytes(w, []byte(s)) -} - -// WriteVarBytes writes a variable length byte array. -func WriteVarBytes(w io.Writer, b []byte) error { - if err := WriteVarUint(w, uint64(len(b))); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, b) -} From 7b46f9bb860853a2fb44518c260f745c5e012667 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 29 Aug 2019 13:24:03 +0300 Subject: [PATCH 12/13] util: add error check to Read/WriteVarUint Fixes possibility of bogus reads/writes. Suggested by @im-kulikov. --- pkg/util/binaryReader.go | 4 ++++ pkg/util/binaryWriter.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/pkg/util/binaryReader.go b/pkg/util/binaryReader.go index f13879549..fe92ae61a 100644 --- a/pkg/util/binaryReader.go +++ b/pkg/util/binaryReader.go @@ -33,6 +33,10 @@ func (r *BinReader) ReadBE(v interface{}) { // ReadVarUint reads a variable-length-encoded integer from the // underlying reader func (r *BinReader) ReadVarUint() uint64 { + if r.Err != nil { + return 0 + } + var b uint8 r.Err = binary.Read(r.R, binary.LittleEndian, &b) diff --git a/pkg/util/binaryWriter.go b/pkg/util/binaryWriter.go index 39acf9a41..8f2c5229d 100644 --- a/pkg/util/binaryWriter.go +++ b/pkg/util/binaryWriter.go @@ -32,6 +32,10 @@ func (w *BinWriter) WriteBE(v interface{}) { // WriteVarUint writes a uint64 into the underlying writer using variable-length encoding func (w *BinWriter) WriteVarUint(val uint64) { + if w.Err != nil { + return + } + if val < 0 { w.Err = errors.New("value out of range") return From 4f23117d32c87cd5c5d6b7e3906ce83902e6e76f Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 29 Aug 2019 13:42:16 +0300 Subject: [PATCH 13/13] payload: fix wrong useragent parsing in version C# code reads is as a proper variable-length string, so it's not limited to 252 bytes. --- pkg/network/payload/version.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index 35f97a40f..6545b19f5 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -60,11 +60,7 @@ func (p *Version) DecodeBinary(r io.Reader) error { br.ReadLE(&p.Timestamp) br.ReadLE(&p.Port) br.ReadLE(&p.Nonce) - - var lenUA uint8 - br.ReadLE(&lenUA) - p.UserAgent = make([]byte, lenUA) - br.ReadLE(&p.UserAgent) + p.UserAgent = br.ReadBytes() br.ReadLE(&p.StartHeight) br.ReadLE(&p.Relay) return br.Err @@ -79,8 +75,7 @@ func (p *Version) EncodeBinary(w io.Writer) error { br.WriteLE(p.Port) br.WriteLE(p.Nonce) - br.WriteLE(uint8(len(p.UserAgent))) - br.WriteLE(p.UserAgent) + br.WriteBytes(p.UserAgent) br.WriteLE(p.StartHeight) br.WriteLE(&p.Relay) return br.Err