diff --git a/VERSION b/VERSION index faef31a43..a3df0a695 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.0 +0.8.0 diff --git a/pkg/core/block.go b/pkg/core/block.go index a46115527..bbd6b97d8 100644 --- a/pkg/core/block.go +++ b/pkg/core/block.go @@ -36,16 +36,32 @@ type BlockBase struct { // DecodeBinary implements the payload interface. func (b *BlockBase) DecodeBinary(r io.Reader) error { - binary.Read(r, binary.LittleEndian, &b.Version) - binary.Read(r, binary.LittleEndian, &b.PrevHash) - binary.Read(r, binary.LittleEndian, &b.MerkleRoot) - binary.Read(r, binary.LittleEndian, &b.Timestamp) - binary.Read(r, binary.LittleEndian, &b.Index) - binary.Read(r, binary.LittleEndian, &b.ConsensusData) - binary.Read(r, binary.LittleEndian, &b.NextConsensus) + 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 + } var padding uint8 - binary.Read(r, binary.LittleEndian, &padding) + if err := binary.Read(r, binary.LittleEndian, &padding); err != nil { + return err + } if padding != 1 { return fmt.Errorf("format error: padding must equal 1 got %d", padding) } @@ -86,6 +102,21 @@ func (b *BlockBase) encodeHashableFields(w io.Writer) error { return err } +// EncodeBinary implements the Payload interface +func (b *BlockBase) EncodeBinary(w io.Writer) error { + if err := b.encodeHashableFields(w); err != nil { + return err + } + + // padding + if err := binary.Write(w, binary.LittleEndian, uint8(1)); err != nil { + return err + } + + // script + return b.Script.EncodeBinary(w) +} + // Header holds the head info of a block type Header struct { BlockBase @@ -115,7 +146,12 @@ func (h *Header) DecodeBinary(r io.Reader) error { // EncodeBinary impelements the Payload interface. func (h *Header) EncodeBinary(w io.Writer) error { - return nil + if err := h.BlockBase.EncodeBinary(w); err != nil { + return err + } + + // padding + return binary.Write(w, binary.LittleEndian, uint8(0)) } // Block represents one block in the chain. diff --git a/pkg/core/block_test.go b/pkg/core/block_test.go index a6e0cca3d..68850ee19 100644 --- a/pkg/core/block_test.go +++ b/pkg/core/block_test.go @@ -82,7 +82,10 @@ func newBlockBase() BlockBase { Index: 1, ConsensusData: 1111, NextConsensus: util.Uint160{}, - Script: &Witness{}, + Script: &Witness{ + VerificationScript: []byte{0x0}, + InvocationScript: []byte{0x1}, + }, } } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 5e5f7d282..551d9692f 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -191,6 +191,11 @@ func (bc *Blockchain) CurrentBlockHash() (hash util.Uint256) { return bc.headerIndex[bc.currentBlockHeight] } +// CurrentHeaderHash returns the hash of the latest known header. +func (bc *Blockchain) CurrentHeaderHash() (hash util.Uint256) { + return bc.headerIndex[len(bc.headerIndex)-1] +} + // BlockHeight return the height/index of the latest block this node has. func (bc *Blockchain) BlockHeight() uint32 { return bc.currentBlockHeight diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index a1b25e661..0f095e5fa 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -31,9 +31,9 @@ func TestAddHeaders(t *testing.T) { startHash, _ := util.Uint256DecodeFromString("996e37358dc369912041f966f8c5d8d3a8255ba5dcbd3447f8a82b55db869099") bc := NewBlockchain(NewMemoryStore(), log.New(os.Stdout, "", 0), startHash) - h1 := &Header{BlockBase: BlockBase{Version: 0, Index: 1}} - h2 := &Header{BlockBase: BlockBase{Version: 0, Index: 2}} - h3 := &Header{BlockBase: BlockBase{Version: 0, Index: 3}} + h1 := &Header{BlockBase: BlockBase{Version: 0, Index: 1, Script: &Witness{}}} + h2 := &Header{BlockBase: BlockBase{Version: 0, Index: 2, Script: &Witness{}}} + h3 := &Header{BlockBase: BlockBase{Version: 0, Index: 3, Script: &Witness{}}} if err := bc.AddHeaders(h1, h2, h3); err != nil { t.Fatal(err) diff --git a/pkg/core/header_test.go b/pkg/core/header_test.go new file mode 100644 index 000000000..bafd2d3e9 --- /dev/null +++ b/pkg/core/header_test.go @@ -0,0 +1,61 @@ +package core + +import ( + "bytes" + "crypto/sha256" + "testing" + "time" + + "github.com/CityOfZion/neo-go/pkg/util" +) + +func TestHeaderEncodeDecode(t *testing.T) { + header := Header{BlockBase: BlockBase{ + Version: 0, + PrevHash: sha256.Sum256([]byte("prevhash")), + MerkleRoot: sha256.Sum256([]byte("merkleroot")), + Timestamp: uint32(time.Now().UTC().Unix()), + Index: 3445, + ConsensusData: 394949, + NextConsensus: util.Uint160{}, + Script: &Witness{ + InvocationScript: []byte{0x10}, + VerificationScript: []byte{0x11}, + }, + }} + + buf := new(bytes.Buffer) + if err := header.EncodeBinary(buf); err != nil { + t.Fatal(err) + } + + headerDecode := &Header{} + if err := headerDecode.DecodeBinary(buf); err != nil { + t.Fatal(err) + } + if header.Version != headerDecode.Version { + t.Fatal("expected both versions to be equal") + } + if !header.PrevHash.Equals(headerDecode.PrevHash) { + t.Fatal("expected both prev hashes to be equal") + } + if !header.MerkleRoot.Equals(headerDecode.MerkleRoot) { + t.Fatal("expected both merkle roots to be equal") + } + if header.Index != headerDecode.Index { + t.Fatal("expected both indexes to be equal") + } + if header.ConsensusData != headerDecode.ConsensusData { + t.Fatal("expected both consensus data fields to be equal") + } + if !header.NextConsensus.Equals(headerDecode.NextConsensus) { + t.Fatalf("expected both next consensus fields to be equal") + } + + if bytes.Compare(header.Script.InvocationScript, headerDecode.Script.InvocationScript) != 0 { + t.Fatalf("expected equal invocation scripts %v and %v", header.Script.InvocationScript, headerDecode.Script.InvocationScript) + } + if bytes.Compare(header.Script.VerificationScript, headerDecode.Script.VerificationScript) != 0 { + t.Fatalf("expected equal verification scripts %v and %v", header.Script.VerificationScript, headerDecode.Script.VerificationScript) + } +} diff --git a/pkg/core/witness.go b/pkg/core/witness.go index 7f253d69f..b59d80d7c 100644 --- a/pkg/core/witness.go +++ b/pkg/core/witness.go @@ -32,5 +32,10 @@ func (wit *Witness) DecodeBinary(r io.Reader) error { // EncodeBinary implements the payload interface. func (wit *Witness) EncodeBinary(w io.Writer) error { - return nil + util.WriteVarUint(w, uint64(len(wit.InvocationScript))) + if err := binary.Write(w, binary.LittleEndian, wit.InvocationScript); err != nil { + return err + } + util.WriteVarUint(w, uint64(len(wit.VerificationScript))) + return binary.Write(w, binary.LittleEndian, wit.VerificationScript) } diff --git a/pkg/network/payload/addr.go b/pkg/network/payload/addr.go index 49d9c2026..3feec11c7 100644 --- a/pkg/network/payload/addr.go +++ b/pkg/network/payload/addr.go @@ -49,7 +49,7 @@ func (p *AddrWithTime) EncodeBinary(w io.Writer) error { return err } -// AddressList holds a slice of AddrWithTime. +// AddressList is a list with AddrWithTime. type AddressList struct { Addrs []*AddrWithTime } diff --git a/pkg/network/payload/addr_test.go b/pkg/network/payload/addr_test.go index 8f2da310d..656d5bfbd 100644 --- a/pkg/network/payload/addr_test.go +++ b/pkg/network/payload/addr_test.go @@ -33,14 +33,13 @@ func TestEncodeDecodeAddr(t *testing.T) { func TestEncodeDecodeAddressList(t *testing.T) { var lenList uint8 = 4 - addrs := make([]*AddrWithTime, lenList) + addrList := &AddressList{make([]*AddrWithTime, lenList)} for i := 0; i < int(lenList); i++ { e, _ := util.EndpointFromString(fmt.Sprintf("127.0.0.1:200%d", i)) - addrs[i] = NewAddrWithTime(e) + addrList.Addrs[i] = NewAddrWithTime(e) } buf := new(bytes.Buffer) - addrList := &AddressList{addrs} if err := addrList.EncodeBinary(buf); err != nil { t.Fatal(err) } diff --git a/pkg/network/payload/getblocks.go b/pkg/network/payload/getblocks.go index 7e42e61e0..3c206d434 100644 --- a/pkg/network/payload/getblocks.go +++ b/pkg/network/payload/getblocks.go @@ -2,7 +2,6 @@ package payload import ( "encoding/binary" - "fmt" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -18,21 +17,23 @@ type GetBlocks struct { // NewGetBlocks return a pointer to a GetBlocks object. func NewGetBlocks(start []util.Uint256, stop util.Uint256) *GetBlocks { - p := &GetBlocks{} - p.HashStart = start - p.HashStop = stop - return p + return &GetBlocks{ + HashStart: start, + HashStop: stop, + } } // DecodeBinary implements the payload interface. func (p *GetBlocks) DecodeBinary(r io.Reader) error { lenStart := util.ReadVarUint(r) - fmt.Println(lenStart) p.HashStart = make([]util.Uint256, lenStart) - err := binary.Read(r, binary.LittleEndian, &p.HashStart) - err = binary.Read(r, binary.LittleEndian, &p.HashStop) - fmt.Println(p) + if err := binary.Read(r, binary.LittleEndian, &p.HashStart); err != nil { + return err + } + + // If the reader returns EOF we know the hashStop is not encoded. + err := binary.Read(r, binary.LittleEndian, &p.HashStop) if err == io.EOF { return nil } @@ -42,11 +43,20 @@ func (p *GetBlocks) DecodeBinary(r io.Reader) error { // EncodeBinary implements the payload interface. func (p *GetBlocks) EncodeBinary(w io.Writer) error { - err := util.WriteVarUint(w, uint64(len(p.HashStart))) - err = binary.Write(w, binary.LittleEndian, p.HashStart) - //err = binary.Write(w, binary.LittleEndian, p.HashStop) + 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 err + // Only write hashStop if its not filled with zero bytes. + var emtpy util.Uint256 + if p.HashStop != emtpy { + return binary.Write(w, binary.LittleEndian, p.HashStop) + } + + return nil } // Size implements the payload interface. diff --git a/pkg/network/payload/getblocks_test.go b/pkg/network/payload/getblocks_test.go index 7e80429b2..0ebda49ce 100644 --- a/pkg/network/payload/getblocks_test.go +++ b/pkg/network/payload/getblocks_test.go @@ -1,58 +1,60 @@ package payload -// TODO: Currently the hashstop is not encoded, therefore this test will fail. -// Need to figure some stuff how to handle this properly. -// - anthdm 04/02/2018 +import ( + "bytes" + "crypto/sha256" + "reflect" + "testing" -// func TestGetBlocksEncodeDecode(t *testing.T) { -// hash, _ := util.Uint256DecodeFromString("d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf") + "github.com/CityOfZion/neo-go/pkg/util" +) -// start := []util.Uint256{ -// hash, -// sha256.Sum256([]byte("a")), -// sha256.Sum256([]byte("b")), -// sha256.Sum256([]byte("c")), -// } -// stop := sha256.Sum256([]byte("d")) +func TestGetBlockEncodeDecode(t *testing.T) { + start := []util.Uint256{ + sha256.Sum256([]byte("a")), + sha256.Sum256([]byte("b")), + sha256.Sum256([]byte("c")), + sha256.Sum256([]byte("d")), + } -// p := NewGetBlocks(start, stop) -// buf := new(bytes.Buffer) -// if err := p.EncodeBinary(buf); err != nil { -// t.Fatal(err) -// } + p := NewGetBlocks(start, util.Uint256{}) + buf := new(bytes.Buffer) + if err := p.EncodeBinary(buf); err != nil { + t.Fatal(err) + } -// pDecode := &GetBlocks{} -// if err := pDecode.DecodeBinary(buf); err != nil { -// t.Fatal(err) -// } + pDecode := &GetBlocks{} + if err := pDecode.DecodeBinary(buf); err != nil { + t.Fatal(err) + } -// if !reflect.DeepEqual(p, pDecode) { -// t.Fatalf("expecting both getblocks payloads to be equal %v and %v", p, pDecode) -// } -// } + if !reflect.DeepEqual(p, pDecode) { + t.Fatalf("expected to have equal block payload %v and %v", p, pDecode) + } +} -// TODO: Currently the hashstop is not encoded, therefore this test will fail. -// Need to figure some stuff how to handle this properly. -// - anthdm 04/02/2018 -// -// func TestGetBlocksWithEmptyHashStop(t *testing.T) { -// start := []util.Uint256{ -// sha256.Sum256([]byte("a")), -// } -// stop := util.Uint256{} +func TestGetBlockEncodeDecodeWithHashStop(t *testing.T) { + var ( + start = []util.Uint256{ + sha256.Sum256([]byte("a")), + sha256.Sum256([]byte("b")), + sha256.Sum256([]byte("c")), + sha256.Sum256([]byte("d")), + } + stop = sha256.Sum256([]byte("e")) + ) + p := NewGetBlocks(start, stop) + buf := new(bytes.Buffer) + if err := p.EncodeBinary(buf); err != nil { + t.Fatal(err) + } -// buf := new(bytes.Buffer) -// p := NewGetBlocks(start, stop) -// if err := p.EncodeBinary(buf); err != nil { -// t.Fatal(err) -// } + pDecode := &GetBlocks{} + if err := pDecode.DecodeBinary(buf); err != nil { + t.Fatal(err) + } -// pDecode := &GetBlocks{} -// if err := pDecode.DecodeBinary(buf); err != nil { -// t.Fatal(err) -// } - -// if !reflect.DeepEqual(p, pDecode) { -// t.Fatalf("expecting both getblocks payloads to be equal %v and %v", p, pDecode) -// } -// } + if !reflect.DeepEqual(p, pDecode) { + t.Fatalf("expected to have equal block payload %v and %v", p, pDecode) + } +} diff --git a/pkg/network/payload/headers.go b/pkg/network/payload/headers.go index 1b9d1d736..373b149da 100644 --- a/pkg/network/payload/headers.go +++ b/pkg/network/payload/headers.go @@ -30,6 +30,13 @@ func (p *Headers) DecodeBinary(r io.Reader) error { } // EncodeBinary implements the Payload interface. -func (h *Headers) EncodeBinary(w io.Writer) error { +func (p *Headers) EncodeBinary(w io.Writer) error { + util.WriteVarUint(w, uint64(len(p.Hdrs))) + for _, header := range p.Hdrs { + if err := header.EncodeBinary(w); err != nil { + return err + } + } + return nil } diff --git a/pkg/network/payload/headers_test.go b/pkg/network/payload/headers_test.go new file mode 100644 index 000000000..d9918316b --- /dev/null +++ b/pkg/network/payload/headers_test.go @@ -0,0 +1,55 @@ +package payload + +import ( + "bytes" + "reflect" + "testing" + + "github.com/CityOfZion/neo-go/pkg/core" +) + +func TestHeadersEncodeDecode(t *testing.T) { + headers := &Headers{[]*core.Header{ + &core.Header{ + BlockBase: core.BlockBase{ + Version: 0, + Index: 1, + Script: &core.Witness{ + InvocationScript: []byte{0x0}, + VerificationScript: []byte{0x1}, + }, + }}, + &core.Header{ + BlockBase: core.BlockBase{ + Version: 0, + Index: 2, + Script: &core.Witness{ + InvocationScript: []byte{0x0}, + VerificationScript: []byte{0x1}, + }, + }}, + &core.Header{ + BlockBase: core.BlockBase{ + Version: 0, + Index: 3, + Script: &core.Witness{ + InvocationScript: []byte{0x0}, + VerificationScript: []byte{0x1}, + }, + }}, + }} + + buf := new(bytes.Buffer) + if err := headers.EncodeBinary(buf); err != nil { + t.Fatal(err) + } + + headersDecode := &Headers{} + if err := headersDecode.DecodeBinary(buf); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(headers, headersDecode) { + t.Fatalf("expected both header payload to be equal %+v and %+v", headers, headersDecode) + } +} diff --git a/pkg/network/peer.go b/pkg/network/peer.go index 228f1638f..c2aa0e913 100644 --- a/pkg/network/peer.go +++ b/pkg/network/peer.go @@ -41,7 +41,8 @@ func (p *LocalPeer) version() *payload.Version { } func (p *LocalPeer) callVersion(msg *Message) error { - return p.s.handleVersionCmd(msg, p) + version := msg.Payload.(*payload.Version) + return p.s.handleVersionCmd(version, p) } func (p *LocalPeer) callVerack(msg *Message) error { diff --git a/pkg/network/server.go b/pkg/network/server.go index 8fef91487..cb6a7d1a4 100644 --- a/pkg/network/server.go +++ b/pkg/network/server.go @@ -212,8 +212,7 @@ func (s *Server) handlePeerConnected(p Peer) error { return p.callVersion(msg) } -func (s *Server) handleVersionCmd(msg *Message, p Peer) error { - version := msg.Payload.(*payload.Version) +func (s *Server) handleVersionCmd(version *payload.Version, p Peer) error { if s.id == version.Nonce { return errors.New("identical nonce") } @@ -230,8 +229,7 @@ func (s *Server) handleGetaddrCmd(msg *Message, p Peer) error { // The node can broadcast the object information it owns by this message. // The message can be sent automatically or can be used to answer getbloks messages. -func (s *Server) handleInvCmd(msg *Message, p Peer) error { - inv := msg.Payload.(*payload.Inventory) +func (s *Server) handleInvCmd(inv *payload.Inventory, p Peer) error { if !inv.Type.Valid() { return fmt.Errorf("invalid inventory type %s", inv.Type) } @@ -248,8 +246,7 @@ func (s *Server) handleInvCmd(msg *Message, p Peer) error { } // handleBlockCmd processes the received block. -func (s *Server) handleBlockCmd(msg *Message, p Peer) error { - block := msg.Payload.(*core.Block) +func (s *Server) handleBlockCmd(block *core.Block, p Peer) error { hash, err := block.Hash() if err != nil { return err @@ -262,8 +259,7 @@ func (s *Server) handleBlockCmd(msg *Message, p Peer) error { // After receiving the getaddr message, the node returns an addr message as response // and provides information about the known nodes on the network. -func (s *Server) handleAddrCmd(msg *Message, p Peer) error { - addrList := msg.Payload.(*payload.AddressList) +func (s *Server) handleAddrCmd(addrList *payload.AddressList, p Peer) error { for _, addr := range addrList.Addrs { if !s.peerAlreadyConnected(addr.Addr) { // TODO: this is not transport abstracted. @@ -285,13 +281,11 @@ func (s *Server) handleHeadersCmd(headers *payload.Headers, p Peer) error { // Ask more headers if we are not in sync with the peer. if s.bc.HeaderHeight() < p.version().StartHeight { - s.logger.Printf("header height %d peer height %d", s.bc.HeaderHeight(), p.version().StartHeight) if err := s.askMoreHeaders(p); err != nil { s.logger.Printf("getheaders RPC failed: %s", err) return } } - }(context.TODO(), headers.Hdrs) return nil @@ -299,7 +293,7 @@ func (s *Server) handleHeadersCmd(headers *payload.Headers, p Peer) error { // Ask the peer for more headers We use the current block hash as start. func (s *Server) askMoreHeaders(p Peer) error { - start := []util.Uint256{s.bc.CurrentBlockHash()} + start := []util.Uint256{s.bc.CurrentHeaderHash()} payload := payload.NewGetBlocks(start, util.Uint256{}) msg := newMessage(s.net, cmdGetHeaders, payload) diff --git a/pkg/network/server_test.go b/pkg/network/server_test.go index 6056877c6..d072ba6c5 100644 --- a/pkg/network/server_test.go +++ b/pkg/network/server_test.go @@ -15,8 +15,7 @@ func TestHandleVersionFailWrongPort(t *testing.T) { p := NewLocalPeer(s) version := payload.NewVersion(1337, 1, "/NEO:0.0.0/", 0, true) - msg := newMessage(ModeDevNet, cmdVersion, version) - if err := s.handleVersionCmd(msg, p); err == nil { + if err := s.handleVersionCmd(version, p); err == nil { t.Fatal("expected error got nil") } } @@ -28,8 +27,7 @@ func TestHandleVersionFailIdenticalNonce(t *testing.T) { p := NewLocalPeer(s) version := payload.NewVersion(s.id, 1, "/NEO:0.0.0/", 0, true) - msg := newMessage(ModeDevNet, cmdVersion, version) - if err := s.handleVersionCmd(msg, p); err == nil { + if err := s.handleVersionCmd(version, p); err == nil { t.Fatal("expected error got nil") } } @@ -41,9 +39,7 @@ func TestHandleVersion(t *testing.T) { p := NewLocalPeer(s) version := payload.NewVersion(1337, p.addr().Port, "/NEO:0.0.0/", 0, true) - msg := newMessage(ModeDevNet, cmdVersion, version) - - if err := s.handleVersionCmd(msg, p); err != nil { + if err := s.handleVersionCmd(version, p); err != nil { t.Fatal(err) } } diff --git a/pkg/network/tcp.go b/pkg/network/tcp.go index 2fa23fec9..9f54de4e3 100644 --- a/pkg/network/tcp.go +++ b/pkg/network/tcp.go @@ -6,6 +6,7 @@ import ( "fmt" "net" + "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/network/payload" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -84,10 +85,10 @@ func handleMessage(s *Server, p *TCPPeer) { switch command { case cmdVersion: - if err = s.handleVersionCmd(msg, p); err != nil { + version := msg.Payload.(*payload.Version) + if err = s.handleVersionCmd(version, p); err != nil { break } - version := msg.Payload.(*payload.Version) p.nonce = version.Nonce p.pVersion = version @@ -106,19 +107,22 @@ func handleMessage(s *Server, p *TCPPeer) { // start the protocol go s.startProtocol(p) case cmdAddr: - err = s.handleAddrCmd(msg, p) + addrList := msg.Payload.(*payload.AddressList) + err = s.handleAddrCmd(addrList, p) case cmdGetAddr: err = s.handleGetaddrCmd(msg, p) case cmdInv: - err = s.handleInvCmd(msg, p) + inv := msg.Payload.(*payload.Inventory) + err = s.handleInvCmd(inv, p) case cmdBlock: - err = s.handleBlockCmd(msg, p) + block := msg.Payload.(*core.Block) + err = s.handleBlockCmd(block, p) case cmdConsensus: case cmdTX: case cmdVerack: // If we receive a verack here we disconnect. We already handled the verack // when we sended our version. - err = errors.New("received verack twice") + err = errors.New("verack already received") case cmdGetHeaders: case cmdGetBlocks: case cmdGetData: @@ -284,6 +288,7 @@ func (p *TCPPeer) writeLoop() { p.disconnect() }() + // resuse this buffer buf := new(bytes.Buffer) for { t := <-p.send diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go index 424cd6210..529678fa8 100644 --- a/pkg/util/uint160.go +++ b/pkg/util/uint160.go @@ -22,3 +22,8 @@ func (u Uint160) ToSlice() []byte { func (u Uint160) String() string { return hex.EncodeToString(u.ToSlice()) } + +// Equals returns true if both Uint256 values are the same. +func (u Uint160) Equals(other Uint160) bool { + return u.String() == other.String() +}