diff --git a/pkg/core/account_state.go b/pkg/core/account_state.go index 44d277e76..87c9771a0 100644 --- a/pkg/core/account_state.go +++ b/pkg/core/account_state.go @@ -1,12 +1,11 @@ package core import ( - "bytes" "fmt" - "io" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -21,8 +20,10 @@ func (a Accounts) getAndUpdate(s storage.Store, hash util.Uint160) (*AccountStat account := &AccountState{} key := storage.AppendPrefix(storage.STAccount, hash.Bytes()) if b, err := s.Get(key); err == nil { - if err := account.DecodeBinary(bytes.NewReader(b)); err != nil { - return nil, fmt.Errorf("failed to decode (AccountState): %s", err) + r := io.NewBinReaderFromBuf(b) + account.DecodeBinary(r) + if r.Err != nil { + return nil, fmt.Errorf("failed to decode (AccountState): %s", r.Err) } } else { account = NewAccountState(hash) @@ -34,10 +35,11 @@ func (a Accounts) getAndUpdate(s storage.Store, hash util.Uint160) (*AccountStat // commit writes all account states to the given Batch. func (a Accounts) commit(b storage.Batch) error { - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() for hash, state := range a { - if err := state.EncodeBinary(buf); err != nil { - return err + state.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err } key := storage.AppendPrefix(storage.STAccount, hash.Bytes()) b.Put(key, buf.Bytes()) @@ -66,9 +68,8 @@ func NewAccountState(scriptHash util.Uint160) *AccountState { } } -// DecodeBinary decodes AccountState from the given io.Reader. -func (s *AccountState) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary decodes AccountState from the given BinReader. +func (s *AccountState) DecodeBinary(br *io.BinReader) { br.ReadLE(&s.Version) br.ReadLE(&s.ScriptHash) br.ReadLE(&s.IsFrozen) @@ -76,9 +77,7 @@ func (s *AccountState) DecodeBinary(r io.Reader) error { s.Votes = make([]*keys.PublicKey, lenVotes) for i := 0; i < int(lenVotes); i++ { s.Votes[i] = &keys.PublicKey{} - if err := s.Votes[i].DecodeBinary(r); err != nil { - return err - } + s.Votes[i].DecodeBinary(br) } s.Balances = make(map[util.Uint256]util.Fixed8) @@ -90,21 +89,16 @@ func (s *AccountState) DecodeBinary(r io.Reader) error { br.ReadLE(&val) s.Balances[key] = val } - - return br.Err } -// EncodeBinary encode AccountState to the given io.Writer. -func (s *AccountState) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary encodes AccountState to the given BinWriter. +func (s *AccountState) EncodeBinary(bw *io.BinWriter) { 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 - } + point.EncodeBinary(bw) } balances := s.nonZeroBalances() @@ -113,8 +107,6 @@ func (s *AccountState) EncodeBinary(w io.Writer) error { bw.WriteLE(k) bw.WriteLE(v) } - - return bw.Err } // Returns only the non-zero balances for the account. diff --git a/pkg/core/account_state_test.go b/pkg/core/account_state_test.go index 82009be0d..b00225984 100644 --- a/pkg/core/account_state_test.go +++ b/pkg/core/account_state_test.go @@ -1,10 +1,10 @@ package core import ( - "bytes" "testing" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) @@ -30,15 +30,14 @@ func TestDecodeEncodeAccountState(t *testing.T) { Balances: balances, } - buf := new(bytes.Buffer) - if err := a.EncodeBinary(buf); err != nil { - t.Fatal(err) - } + buf := io.NewBufBinWriter() + a.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) aDecode := &AccountState{} - if err := aDecode.DecodeBinary(buf); err != nil { - t.Fatal(err) - } + r := io.NewBinReaderFromBuf(buf.Bytes()) + aDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, a.Version, aDecode.Version) assert.Equal(t, a.ScriptHash, aDecode.ScriptHash) diff --git a/pkg/core/asset_state.go b/pkg/core/asset_state.go index 19f7db8c3..3a553d7ef 100644 --- a/pkg/core/asset_state.go +++ b/pkg/core/asset_state.go @@ -1,12 +1,10 @@ package core import ( - "bytes" - "io" - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -16,10 +14,11 @@ const feeMode = 0x0 type Assets map[util.Uint256]*AssetState func (a Assets) commit(b storage.Batch) error { - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() for hash, state := range a { - if err := state.EncodeBinary(buf); err != nil { - return err + state.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err } key := storage.AppendPrefix(storage.STAsset, hash.Bytes()) b.Put(key, buf.Bytes()) @@ -45,9 +44,8 @@ type AssetState struct { IsFrozen bool } -// DecodeBinary implements the Payload interface. -func (a *AssetState) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (a *AssetState) DecodeBinary(br *io.BinReader) { br.ReadLE(&a.ID) br.ReadLE(&a.AssetType) @@ -59,24 +57,16 @@ func (a *AssetState) DecodeBinary(r io.Reader) error { 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 - } + a.Owner.DecodeBinary(br) 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 { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (a *AssetState) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(a.ID) bw.WriteLE(a.AssetType) bw.WriteString(a.Name) @@ -86,17 +76,12 @@ func (a *AssetState) EncodeBinary(w io.Writer) error { bw.WriteLE(a.FeeMode) bw.WriteLE(a.FeeAddress) - if bw.Err != nil { - return bw.Err - } - if err := a.Owner.EncodeBinary(w); err != nil { - return err - } + a.Owner.EncodeBinary(bw) + 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/asset_state_test.go b/pkg/core/asset_state_test.go index 952c81fc4..203897c47 100644 --- a/pkg/core/asset_state_test.go +++ b/pkg/core/asset_state_test.go @@ -1,11 +1,11 @@ package core import ( - "bytes" "testing" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) @@ -26,9 +26,12 @@ func TestEncodeDecodeAssetState(t *testing.T) { IsFrozen: false, } - buf := new(bytes.Buffer) - assert.Nil(t, asset.EncodeBinary(buf)) + buf := io.NewBufBinWriter() + asset.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) assetDecode := &AssetState{} - assert.Nil(t, assetDecode.DecodeBinary(buf)) + r := io.NewBinReaderFromBuf(buf.Bytes()) + assetDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, asset, assetDecode) } diff --git a/pkg/core/block.go b/pkg/core/block.go index 38d8a9122..27ec6b225 100644 --- a/pkg/core/block.go +++ b/pkg/core/block.go @@ -1,11 +1,9 @@ package core import ( - "bytes" - "io" - "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" log "github.com/sirupsen/logrus" ) @@ -73,22 +71,14 @@ func NewBlockFromTrimmedBytes(b []byte) (*Block, error) { Trimmed: true, } - r := bytes.NewReader(b) - if err := block.decodeHashableFields(r); err != nil { - return block, err - } + br := io.NewBinReaderFromBuf(b) + block.decodeHashableFields(br) - br := util.BinReader{R: r} var padding uint8 br.ReadLE(&padding) - if br.Err != nil { - return block, br.Err - } block.Script = &transaction.Witness{} - if err := block.Script.DecodeBinary(r); err != nil { - return block, err - } + block.Script.DecodeBinary(br) lenTX := br.ReadVarUint() block.Transactions = make([]*transaction.Transaction, lenTX) @@ -105,67 +95,40 @@ func NewBlockFromTrimmedBytes(b []byte) (*Block, error) { // in storage. // Notice that only the hashes of the transactions are stored. func (b *Block) Trim() ([]byte, error) { - buf := new(bytes.Buffer) - if err := b.encodeHashableFields(buf); 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 - } + buf := io.NewBufBinWriter() + b.encodeHashableFields(buf.BinWriter) + buf.WriteLE(uint8(1)) + b.Script.EncodeBinary(buf.BinWriter) - bw.WriteVarUint(uint64(len(b.Transactions))) + buf.WriteVarUint(uint64(len(b.Transactions))) for _, tx := range b.Transactions { - bw.WriteLE(tx.Hash()) + buf.WriteLE(tx.Hash()) } - if bw.Err != nil { - return nil, bw.Err + if buf.Err != nil { + return nil, buf.Err } return buf.Bytes(), nil } -// DecodeBinary decodes the block from the given reader. -func (b *Block) DecodeBinary(r io.Reader) error { - if err := b.BlockBase.DecodeBinary(r); err != nil { - return err - } +// DecodeBinary decodes the block from the given BinReader, implementing +// Serializable interface. +func (b *Block) DecodeBinary(br *io.BinReader) { + b.BlockBase.DecodeBinary(br) - 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{} - if err := b.Transactions[i].DecodeBinary(r); err != nil { - return err - } + b.Transactions[i].DecodeBinary(br) } - - return nil } -// EncodeBinary encodes the block to the given writer. -func (b *Block) EncodeBinary(w io.Writer) error { - err := b.BlockBase.EncodeBinary(w) - if err != nil { - return err - } - bw := util.BinWriter{W: w} +// EncodeBinary encodes the block to the given BinWriter, implementing +// Serializable interface. +func (b *Block) EncodeBinary(bw *io.BinWriter) { + b.BlockBase.EncodeBinary(bw) bw.WriteVarUint(uint64(len(b.Transactions))) - if bw.Err != nil { - return err - } for _, tx := range b.Transactions { - err := tx.EncodeBinary(w) - if err != nil { - return err - } + tx.EncodeBinary(bw) } - return nil } diff --git a/pkg/core/block_base.go b/pkg/core/block_base.go index 00fd4d277..6b77e1b5b 100644 --- a/pkg/core/block_base.go +++ b/pkg/core/block_base.go @@ -1,12 +1,11 @@ package core import ( - "bytes" "fmt" - "io" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -59,37 +58,26 @@ func (b *BlockBase) Hash() util.Uint256 { return b.hash } -// DecodeBinary implements the payload interface. -func (b *BlockBase) DecodeBinary(r io.Reader) error { - if err := b.decodeHashableFields(r); err != nil { - return err - } +// DecodeBinary implements Serializable interface. +func (b *BlockBase) DecodeBinary(br *io.BinReader) { + b.decodeHashableFields(br) var padding uint8 - 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) + br.Err = fmt.Errorf("format error: padding must equal 1 got %d", padding) + return } b.Script = &transaction.Witness{} - return b.Script.DecodeBinary(r) + b.Script.DecodeBinary(br) } -// EncodeBinary implements the Payload interface -func (b *BlockBase) EncodeBinary(w io.Writer) error { - if err := b.encodeHashableFields(w); err != nil { - return err - } - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface +func (b *BlockBase) EncodeBinary(bw *io.BinWriter) { + b.encodeHashableFields(bw) bw.WriteLE(uint8(1)) - if bw.Err != nil { - return bw.Err - } - return b.Script.EncodeBinary(w) + b.Script.EncodeBinary(bw) } // createHash creates the hash of the block. @@ -99,9 +87,10 @@ func (b *BlockBase) EncodeBinary(w io.Writer) error { // Since MerkleRoot already contains the hash value of all transactions, // the modification of transaction will influence the hash value of the block. func (b *BlockBase) createHash() error { - buf := new(bytes.Buffer) - if err := b.encodeHashableFields(buf); err != nil { - return err + buf := io.NewBufBinWriter() + b.encodeHashableFields(buf.BinWriter) + if buf.Err != nil { + return buf.Err } b.hash = hash.DoubleSha256(buf.Bytes()) @@ -110,8 +99,7 @@ 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 { - bw := util.BinWriter{W: w} +func (b *BlockBase) encodeHashableFields(bw *io.BinWriter) { bw.WriteLE(b.Version) bw.WriteLE(b.PrevHash) bw.WriteLE(b.MerkleRoot) @@ -119,13 +107,11 @@ func (b *BlockBase) encodeHashableFields(w io.Writer) error { 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 { - br := util.BinReader{R: r} +func (b *BlockBase) decodeHashableFields(br *io.BinReader) { br.ReadLE(&b.Version) br.ReadLE(&b.PrevHash) br.ReadLE(&b.MerkleRoot) @@ -134,11 +120,9 @@ func (b *BlockBase) decodeHashableFields(r io.Reader) error { 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 // again. - return b.createHash() + if br.Err == nil { + br.Err = b.createHash() + } } diff --git a/pkg/core/block_test.go b/pkg/core/block_test.go index 3191e959d..a26007d7e 100644 --- a/pkg/core/block_test.go +++ b/pkg/core/block_test.go @@ -1,12 +1,12 @@ package core import ( - "bytes" "encoding/hex" "testing" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -24,9 +24,9 @@ func TestDecodeBlock1(t *testing.T) { } block := &Block{} - if err := block.DecodeBinary(bytes.NewReader(b)); err != nil { - t.Fatal(err) - } + r := io.NewBinReaderFromBuf(b) + block.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, uint32(data["index"].(float64)), block.Index) assert.Equal(t, uint32(data["version"].(float64)), block.Version) @@ -109,9 +109,9 @@ func TestBinBlockDecodeEncode(t *testing.T) { b := Block{} - r := bytes.NewReader(rawtxBytes) - err := b.DecodeBinary(r) - assert.Nil(t, err) + r := io.NewBinReaderFromBuf(rawtxBytes) + b.DecodeBinary(r) + assert.Nil(t, r.Err) expected := map[string]bool{ // 18 trans "009f61f481f47eb7478e887871e4e744669d461b13d68e04250035260171d706": false, @@ -165,10 +165,10 @@ func TestBinBlockDecodeEncode(t *testing.T) { } assert.Equal(t, true, val) - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() - err = b.EncodeBinary(buf) - assert.Nil(t, err) + b.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes())) } @@ -185,9 +185,9 @@ func TestBlockSizeCalculation(t *testing.T) { b := Block{} - r := bytes.NewReader(rawBlockBytes) - err := b.DecodeBinary(r) - assert.Nil(t, err) + r := io.NewBinReaderFromBuf(rawBlockBytes) + b.DecodeBinary(r) + assert.Nil(t, r.Err) expected := []struct { ID string @@ -225,7 +225,7 @@ func TestBlockSizeCalculation(t *testing.T) { txID := tx.Hash() assert.Equal(t, expected[i].ID, txID.ReverseString()) - assert.Equal(t, expected[i].Size, tx.Size()) + assert.Equal(t, expected[i].Size, io.GetVarSize(tx)) assert.Equal(t, expected[i].Type, tx.Type.String()) assert.Equal(t, expected[i].Version, int(tx.Version)) assert.Equal(t, expected[i].InputsLen, len(tx.Inputs)) @@ -250,11 +250,12 @@ func TestBlockSizeCalculation(t *testing.T) { assert.Equal(t, "552102486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a7021024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d2102aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e2103b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c2103b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a2102ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba5542102df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e89509357ae", hex.EncodeToString(b.Script.VerificationScript)) assert.Equal(t, "0006d3ff96e269f599eb1b5c5a527c218439e498dcc65b63794591bbcdc0516b", b.Hash().ReverseString()) - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() - err = b.EncodeBinary(buf) - assert.Nil(t, err) + b.EncodeBinary(buf.BinWriter) + assert.Nil(t, r.Err) + benc := buf.Bytes() // test size of the block - assert.Equal(t, 7360, buf.Len()) - assert.Equal(t, rawBlock, hex.EncodeToString(buf.Bytes())) + assert.Equal(t, 7360, len(benc)) + assert.Equal(t, rawBlock, hex.EncodeToString(benc)) } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index f751fe2e8..21ca332a4 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1,9 +1,7 @@ package core import ( - "bytes" "context" - "encoding/binary" "fmt" "math" "sync/atomic" @@ -12,6 +10,7 @@ import ( "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -243,9 +242,9 @@ func (bc *Blockchain) AddHeaders(headers ...*Header) (err error) { func (bc *Blockchain) processHeader(h *Header, batch storage.Batch, headerList *HeaderHashList) error { headerList.Add(h.Hash()) - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() for int(h.Index)-headerBatchCount >= int(bc.storedHeaderCount) { - if err := headerList.Write(buf, int(bc.storedHeaderCount), headerBatchCount); err != nil { + if err := headerList.Write(buf.BinWriter, int(bc.storedHeaderCount), headerBatchCount); err != nil { return err } key := storage.AppendPrefixInt(storage.IXHeaderHashList, int(bc.storedHeaderCount)) @@ -255,8 +254,9 @@ func (bc *Blockchain) processHeader(h *Header, batch storage.Batch, headerList * } buf.Reset() - if err := h.EncodeBinary(buf); err != nil { - return err + h.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err } key := storage.AppendPrefix(storage.DataBlock, h.Hash().BytesReverse()) @@ -467,17 +467,17 @@ func (bc *Blockchain) GetTransaction(hash util.Uint256) (*transaction.Transactio if err != nil { return nil, 0, err } - r := bytes.NewReader(b) + r := io.NewBinReaderFromBuf(b) var height uint32 - if err := binary.Read(r, binary.LittleEndian, &height); err != nil { - return nil, 0, err - } + r.ReadLE(&height) tx := &transaction.Transaction{} - if err := tx.DecodeBinary(r); err != nil { - return nil, 0, err + tx.DecodeBinary(r) + if r.Err != nil { + return nil, 0, r.Err } + return tx, height, nil } @@ -578,7 +578,9 @@ func (bc *Blockchain) GetAssetState(assetID util.Uint256) *AssetState { var as *AssetState bc.Store.Seek(storage.STAsset.Bytes(), func(k, v []byte) { var a AssetState - if err := a.DecodeBinary(bytes.NewReader(v)); err == nil && a.ID == assetID { + r := io.NewBinReaderFromBuf(v) + a.DecodeBinary(r) + if r.Err == nil && a.ID == assetID { as = &a } }) @@ -591,7 +593,9 @@ func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *AccountState { var as *AccountState bc.Store.Seek(storage.STAccount.Bytes(), func(k, v []byte) { var a AccountState - if err := a.DecodeBinary(bytes.NewReader(v)); err == nil && a.ScriptHash == scriptHash { + r := io.NewBinReaderFromBuf(v) + a.DecodeBinary(r) + if r.Err == nil && a.ScriptHash == scriptHash { as = &a } }) @@ -628,7 +632,7 @@ func (bc *Blockchain) References(t *transaction.Transaction) map[util.Uint256]*t // FeePerByte returns network fee divided by the size of the transaction func (bc *Blockchain) FeePerByte(t *transaction.Transaction) util.Fixed8 { - return bc.NetworkFee(t).Div(int64(t.Size())) + return bc.NetworkFee(t).Div(int64(io.GetVarSize(t))) } // NetworkFee returns network fee @@ -669,8 +673,8 @@ func (bc *Blockchain) GetMemPool() MemPool { // Verify verifies whether a transaction is bonafide or not. // Golang implementation of Verify method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L270). func (bc *Blockchain) Verify(t *transaction.Transaction) error { - if t.Size() > transaction.MaxTransactionSize { - return errors.Errorf("invalid transaction size = %d. It shoud be less then MaxTransactionSize = %d", t.Size(), transaction.MaxTransactionSize) + if io.GetVarSize(t) > transaction.MaxTransactionSize { + return errors.Errorf("invalid transaction size = %d. It shoud be less then MaxTransactionSize = %d", io.GetVarSize(t), transaction.MaxTransactionSize) } if ok := bc.verifyInputs(t); !ok { return errors.New("invalid transaction's inputs") @@ -921,9 +925,10 @@ func (bc *Blockchain) VerifyWitnesses(t *transaction.Transaction) error { } func hashAndIndexToBytes(h util.Uint256, index uint32) []byte { - buf := make([]byte, 4) - binary.LittleEndian.PutUint32(buf, index) - return append(h.BytesReverse(), buf...) + buf := io.NewBufBinWriter() + buf.WriteLE(h.BytesReverse()) + buf.WriteLE(index) + return buf.Bytes() } func (bc *Blockchain) secondsPerBlock() int { diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 9a1c38562..8f35861c2 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -6,7 +6,7 @@ import ( "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -141,11 +141,11 @@ func TestGetTransaction(t *testing.T) { } assert.Equal(t, block.Index, height) assert.Equal(t, block.Transactions[0], tx) - assert.Equal(t, 10, tx.Size()) - assert.Equal(t, 1, util.GetVarSize(tx.Attributes)) - assert.Equal(t, 1, util.GetVarSize(tx.Inputs)) - assert.Equal(t, 1, util.GetVarSize(tx.Outputs)) - assert.Equal(t, 1, util.GetVarSize(tx.Scripts)) + assert.Equal(t, 10, io.GetVarSize(tx)) + assert.Equal(t, 1, io.GetVarSize(tx.Attributes)) + assert.Equal(t, 1, io.GetVarSize(tx.Inputs)) + assert.Equal(t, 1, io.GetVarSize(tx.Outputs)) + assert.Equal(t, 1, io.GetVarSize(tx.Scripts)) } func newTestChain(t *testing.T) *Blockchain { diff --git a/pkg/core/header.go b/pkg/core/header.go index 79b0e56ed..0ce253ddb 100644 --- a/pkg/core/header.go +++ b/pkg/core/header.go @@ -1,9 +1,9 @@ package core import ( - "encoding/binary" "fmt" - "io" + + "github.com/CityOfZion/neo-go/pkg/io" ) // Header holds the head info of a block. @@ -14,28 +14,20 @@ type Header struct { _ uint8 } -// DecodeBinary implements the Payload interface. -func (h *Header) DecodeBinary(r io.Reader) error { - if err := h.BlockBase.DecodeBinary(r); err != nil { - return err - } +// DecodeBinary implements Serializable interface. +func (h *Header) DecodeBinary(r *io.BinReader) { + h.BlockBase.DecodeBinary(r) var padding uint8 - if err := binary.Read(r, binary.LittleEndian, &padding); err != nil { - return err - } + r.ReadLE(&padding) if padding != 0 { - return fmt.Errorf("format error: padding must equal 0 got %d", padding) + r.Err = fmt.Errorf("format error: padding must equal 0 got %d", padding) } - - return nil } -// EncodeBinary implements the Payload interface. -func (h *Header) EncodeBinary(w io.Writer) error { - if err := h.BlockBase.EncodeBinary(w); err != nil { - return err - } - return binary.Write(w, binary.LittleEndian, uint8(0)) +// EncodeBinary implements Serializable interface. +func (h *Header) EncodeBinary(w *io.BinWriter) { + h.BlockBase.EncodeBinary(w) + w.WriteLE(uint8(0)) } diff --git a/pkg/core/header_hash_list.go b/pkg/core/header_hash_list.go index 35c190afa..9f6ffbdf2 100644 --- a/pkg/core/header_hash_list.go +++ b/pkg/core/header_hash_list.go @@ -1,8 +1,7 @@ package core import ( - "io" - + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -56,10 +55,9 @@ func (l *HeaderHashList) Slice(start, end int) []util.Uint256 { return l.hashes[start:end] } -// WriteTo will write n underlying hashes to the given io.Writer +// WriteTo will write n underlying hashes to the given BinWriter // starting from start. -func (l *HeaderHashList) Write(w io.Writer, start, n int) error { - bw := util.BinWriter{W: w} +func (l *HeaderHashList) Write(bw *io.BinWriter, start, n int) error { bw.WriteVarUint(uint64(n)) hashes := l.Slice(start, start+n) for _, hash := range hashes { diff --git a/pkg/core/header_test.go b/pkg/core/header_test.go index ef7dcbec0..81cd2a242 100644 --- a/pkg/core/header_test.go +++ b/pkg/core/header_test.go @@ -1,13 +1,14 @@ package core import ( - "bytes" "testing" "time" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/stretchr/testify/assert" ) func TestHeaderEncodeDecode(t *testing.T) { @@ -25,37 +26,20 @@ func TestHeaderEncodeDecode(t *testing.T) { }, }} - buf := new(bytes.Buffer) - if err := header.EncodeBinary(buf); err != nil { - t.Fatal(err) - } + buf := io.NewBufBinWriter() + header.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.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.Equal(header.Script.InvocationScript, headerDecode.Script.InvocationScript) { - t.Fatalf("expected equal invocation scripts %v and %v", header.Script.InvocationScript, headerDecode.Script.InvocationScript) - } - if !bytes.Equal(header.Script.VerificationScript, headerDecode.Script.VerificationScript) { - t.Fatalf("expected equal verification scripts %v and %v", header.Script.VerificationScript, headerDecode.Script.VerificationScript) - } + r := io.NewBinReaderFromBuf(buf.Bytes()) + headerDecode.DecodeBinary(r) + assert.Nil(t, r.Err) + assert.Equal(t, header.Version, headerDecode.Version, "expected both versions to be equal") + assert.Equal(t, header.PrevHash, headerDecode.PrevHash, "expected both prev hashes to be equal") + assert.Equal(t, header.MerkleRoot, headerDecode.MerkleRoot, "expected both merkle roots to be equal") + assert.Equal(t, header.Index, headerDecode.Index, "expected both indexes to be equal") + assert.Equal(t, header.ConsensusData, headerDecode.ConsensusData, "expected both consensus data fields to be equal") + assert.Equal(t, header.NextConsensus, headerDecode.NextConsensus, "expected both next consensus fields to be equal") + assert.Equal(t, header.Script.InvocationScript, headerDecode.Script.InvocationScript, "expected equal invocation scripts") + assert.Equal(t, header.Script.VerificationScript, headerDecode.Script.VerificationScript, "expected equal verification scripts") } diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 27317f64a..65c44a8c0 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -1,7 +1,6 @@ package core import ( - "bytes" "encoding/hex" "encoding/json" "fmt" @@ -11,6 +10,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -63,8 +63,10 @@ func getDecodedBlock(t *testing.T, i int) *Block { } block := &Block{} - if err := block.DecodeBinary(bytes.NewReader(b)); err != nil { - t.Fatal(err) + r := io.NewBinReaderFromBuf(b) + block.DecodeBinary(r) + if r.Err != nil { + t.Fatal(r.Err) } return block diff --git a/pkg/core/spent_coin_state.go b/pkg/core/spent_coin_state.go index fcfde1ae7..978ff2a02 100644 --- a/pkg/core/spent_coin_state.go +++ b/pkg/core/spent_coin_state.go @@ -1,11 +1,10 @@ package core import ( - "bytes" "fmt" - "io" "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -21,8 +20,10 @@ func (s SpentCoins) getAndUpdate(store storage.Store, hash util.Uint256) (*Spent spent := &SpentCoinState{} key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesReverse()) if b, err := store.Get(key); err == nil { - if err := spent.DecodeBinary(bytes.NewReader(b)); err != nil { - return nil, fmt.Errorf("failed to decode (UnspentCoinState): %s", err) + r := io.NewBinReaderFromBuf(b) + spent.DecodeBinary(r) + if r.Err != nil { + return nil, fmt.Errorf("failed to decode (UnspentCoinState): %s", r.Err) } } else { spent = &SpentCoinState{ @@ -35,10 +36,11 @@ func (s SpentCoins) getAndUpdate(store storage.Store, hash util.Uint256) (*Spent } func (s SpentCoins) commit(b storage.Batch) error { - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() for hash, state := range s { - if err := state.EncodeBinary(buf); err != nil { - return err + state.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err } key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesReverse()) b.Put(key, buf.Bytes()) @@ -65,9 +67,8 @@ func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState { } } -// DecodeBinary implements the Payload interface. -func (s *SpentCoinState) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (s *SpentCoinState) DecodeBinary(br *io.BinReader) { br.ReadLE(&s.txHash) br.ReadLE(&s.txHeight) @@ -82,12 +83,10 @@ func (s *SpentCoinState) DecodeBinary(r io.Reader) error { br.ReadLE(&value) s.items[key] = value } - return br.Err } -// EncodeBinary implements the Payload interface. -func (s *SpentCoinState) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (s *SpentCoinState) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(s.txHash) bw.WriteLE(s.txHeight) bw.WriteVarUint(uint64(len(s.items))) @@ -95,5 +94,4 @@ func (s *SpentCoinState) EncodeBinary(w io.Writer) error { bw.WriteLE(k) bw.WriteLE(v) } - return bw.Err } diff --git a/pkg/core/spent_coin_state_test.go b/pkg/core/spent_coin_state_test.go index 8a2b03981..0736196ed 100644 --- a/pkg/core/spent_coin_state_test.go +++ b/pkg/core/spent_coin_state_test.go @@ -1,10 +1,10 @@ package core import ( - "bytes" "testing" "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) @@ -20,10 +20,13 @@ func TestEncodeDecodeSpentCoinState(t *testing.T) { }, } - buf := new(bytes.Buffer) - assert.Nil(t, spent.EncodeBinary(buf)) + buf := io.NewBufBinWriter() + spent.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) spentDecode := new(SpentCoinState) - assert.Nil(t, spentDecode.DecodeBinary(buf)) + r := io.NewBinReaderFromBuf(buf.Bytes()) + spentDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, spent, spentDecode) } diff --git a/pkg/core/storage/helpers.go b/pkg/core/storage/helpers.go index 4a35d9ad7..8916a5615 100644 --- a/pkg/core/storage/helpers.go +++ b/pkg/core/storage/helpers.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "sort" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -84,7 +85,7 @@ func HeaderHashes(s Store) ([]util.Uint256, error) { // the given byte array. func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) { r := bytes.NewReader(b) - br := util.BinReader{R: r} + br := io.NewBinReaderFromIO(r) lenHashes := br.ReadVarUint() hashes := make([]util.Uint256, lenHashes) br.ReadLE(hashes) diff --git a/pkg/core/transaction/attribute.go b/pkg/core/transaction/attribute.go index 1b3ca0c3b..40b4ff7fb 100644 --- a/pkg/core/transaction/attribute.go +++ b/pkg/core/transaction/attribute.go @@ -4,9 +4,8 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io" - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" ) // Attribute represents a Transaction attribute. @@ -15,9 +14,8 @@ type Attribute struct { Data []byte } -// DecodeBinary implements the Payload interface. -func (attr *Attribute) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (attr *Attribute) DecodeBinary(br *io.BinReader) { br.ReadLE(&attr.Usage) // very special case @@ -25,7 +23,7 @@ func (attr *Attribute) DecodeBinary(r io.Reader) error { attr.Data = make([]byte, 33) attr.Data[0] = byte(attr.Usage) br.ReadLE(attr.Data[1:]) - return br.Err + return } var datasize uint64 switch attr.Usage { @@ -45,16 +43,15 @@ func (attr *Attribute) DecodeBinary(r io.Reader) error { Remark12, Remark13, Remark14, Remark15: datasize = br.ReadVarUint() default: - return fmt.Errorf("failed decoding TX attribute usage: 0x%2x", int(attr.Usage)) + br.Err = fmt.Errorf("failed decoding TX attribute usage: 0x%2x", int(attr.Usage)) + return } 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 { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (attr *Attribute) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(&attr.Usage) switch attr.Usage { case ECDH02, ECDH03: @@ -71,27 +68,8 @@ func (attr *Attribute) EncodeBinary(w io.Writer) error { 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) + bw.Err = fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Usage) } - - return bw.Err -} - -// Size returns the size in number bytes of the Attribute -func (attr *Attribute) Size() int { - sz := 1 // usage - switch attr.Usage { - case ContractHash, ECDH02, ECDH03, Vote, - Hash1, Hash2, Hash3, Hash4, Hash5, Hash6, Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15: - sz += 32 // uint8 + 32 = size(attrUsage) + 32 - case Script: - sz += 20 // uint8 + 20 = size(attrUsage) + 20 - case DescriptionURL: - sz += 1 + len(attr.Data) - default: - sz += util.GetVarSize(attr.Data) - } - return sz } // MarshalJSON implements the json Marschaller interface diff --git a/pkg/core/transaction/claim.go b/pkg/core/transaction/claim.go index 468ce0f19..7140f6aad 100644 --- a/pkg/core/transaction/claim.go +++ b/pkg/core/transaction/claim.go @@ -1,9 +1,7 @@ package transaction import ( - "io" - - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" ) // ClaimTX represents a claim transaction. @@ -11,43 +9,20 @@ type ClaimTX struct { Claims []*Input } -// DecodeBinary implements the Payload interface. -func (tx *ClaimTX) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (tx *ClaimTX) DecodeBinary(br *io.BinReader) { 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{} - if err := tx.Claims[i].DecodeBinary(r); err != nil { - return err - } + tx.Claims[i].DecodeBinary(br) } - return nil } -// EncodeBinary implements the Payload interface. -func (tx *ClaimTX) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (tx *ClaimTX) EncodeBinary(bw *io.BinWriter) { 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 { - return err - } + claim.EncodeBinary(bw) } - return nil -} - -// Size returns serialized binary size for this transaction. -func (tx *ClaimTX) Size() int { - sz := util.GetVarSize(uint64(len(tx.Claims))) - for _, claim := range tx.Claims { - sz += claim.Size() - } - return sz } diff --git a/pkg/core/transaction/contract.go b/pkg/core/transaction/contract.go index e36a1ded4..32e66388f 100644 --- a/pkg/core/transaction/contract.go +++ b/pkg/core/transaction/contract.go @@ -1,7 +1,7 @@ package transaction import ( - "io" + "github.com/CityOfZion/neo-go/pkg/io" ) // ContractTX represents a contract transaction. @@ -15,17 +15,10 @@ func NewContractTX() *Transaction { } } -// DecodeBinary implements the Payload interface. -func (tx *ContractTX) DecodeBinary(r io.Reader) error { - return nil +// DecodeBinary implements Serializable interface. +func (tx *ContractTX) DecodeBinary(r *io.BinReader) { } -// EncodeBinary implements the Payload interface. -func (tx *ContractTX) EncodeBinary(w io.Writer) error { - return nil -} - -// Size returns serialized binary size for this transaction. -func (tx *ContractTX) Size() int { - return 0 +// EncodeBinary implements Serializable interface. +func (tx *ContractTX) EncodeBinary(w *io.BinWriter) { } diff --git a/pkg/core/transaction/contract_test.go b/pkg/core/transaction/contract_test.go index cd3e512b9..6d6ed4ef0 100644 --- a/pkg/core/transaction/contract_test.go +++ b/pkg/core/transaction/contract_test.go @@ -1,10 +1,10 @@ package transaction import ( - "bytes" "encoding/hex" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -29,9 +29,9 @@ func TestEncodeDecodeContract(t *testing.T) { assert.Equal(t, "bdf6cc3b9af12a7565bda80933a75ee8cef1bc771d0d58effc08e4c8b436da79", tx.Hash().ReverseString()) // Encode - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() - err := tx.EncodeBinary(buf) - assert.Equal(t, nil, err) + tx.EncodeBinary(buf.BinWriter) + assert.Equal(t, nil, buf.Err) assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes())) } diff --git a/pkg/core/transaction/enrollment.go b/pkg/core/transaction/enrollment.go index 752f6b9ee..79e3c3b2e 100644 --- a/pkg/core/transaction/enrollment.go +++ b/pkg/core/transaction/enrollment.go @@ -1,9 +1,8 @@ package transaction import ( - "io" - "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" ) // EnrollmentTX transaction represents an enrollment form, which indicates @@ -16,18 +15,13 @@ type EnrollmentTX struct { PublicKey *keys.PublicKey } -// DecodeBinary implements the Payload interface. -func (tx *EnrollmentTX) DecodeBinary(r io.Reader) error { +// DecodeBinary implements Serializable interface. +func (tx *EnrollmentTX) DecodeBinary(r *io.BinReader) { tx.PublicKey = &keys.PublicKey{} - return tx.PublicKey.DecodeBinary(r) + tx.PublicKey.DecodeBinary(r) } -// EncodeBinary implements the Payload interface. -func (tx *EnrollmentTX) EncodeBinary(w io.Writer) error { - return tx.PublicKey.EncodeBinary(w) -} - -// Size returns serialized binary size for this transaction. -func (tx *EnrollmentTX) Size() int { - return len(tx.PublicKey.Bytes()) +// EncodeBinary implements Serializable interface. +func (tx *EnrollmentTX) EncodeBinary(w *io.BinWriter) { + tx.PublicKey.EncodeBinary(w) } diff --git a/pkg/core/transaction/enrollment_test.go b/pkg/core/transaction/enrollment_test.go index efec21d71..beb4f46f9 100644 --- a/pkg/core/transaction/enrollment_test.go +++ b/pkg/core/transaction/enrollment_test.go @@ -1,10 +1,10 @@ package transaction import ( - "bytes" "encoding/hex" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -16,9 +16,9 @@ func TestEncodeDecodeEnrollment(t *testing.T) { assert.IsType(t, tx.Data, &EnrollmentTX{}) assert.Equal(t, 0, int(tx.Version)) - buf := new(bytes.Buffer) - err := tx.EncodeBinary(buf) + buf := io.NewBufBinWriter() + tx.EncodeBinary(buf.BinWriter) - assert.Equal(t, nil, err) + assert.Equal(t, nil, buf.Err) assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes())) } diff --git a/pkg/core/transaction/helper_test.go b/pkg/core/transaction/helper_test.go index d727ca73d..beca5cf6a 100644 --- a/pkg/core/transaction/helper_test.go +++ b/pkg/core/transaction/helper_test.go @@ -1,10 +1,10 @@ package transaction import ( - "bytes" "encoding/hex" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -21,7 +21,8 @@ func decodeTransaction(rawTX string, t *testing.T) *Transaction { b, err1 := hex.DecodeString(rawTX) assert.Nil(t, err1) tx := &Transaction{} - err2 := tx.DecodeBinary(bytes.NewReader(b)) - assert.Nil(t, err2) + r := io.NewBinReaderFromBuf(b) + tx.DecodeBinary(r) + assert.Nil(t, r.Err) return tx } diff --git a/pkg/core/transaction/input.go b/pkg/core/transaction/input.go index ab923981f..f5e25dcc7 100644 --- a/pkg/core/transaction/input.go +++ b/pkg/core/transaction/input.go @@ -1,8 +1,7 @@ package transaction import ( - "io" - + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -15,23 +14,14 @@ type Input struct { PrevIndex uint16 `json:"vout"` } -// DecodeBinary implements the Payload interface. -func (in *Input) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (in *Input) DecodeBinary(br *io.BinReader) { br.ReadLE(&in.PrevHash) br.ReadLE(&in.PrevIndex) - return br.Err } -// EncodeBinary implements the Payload interface. -func (in *Input) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (in *Input) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(in.PrevHash) bw.WriteLE(in.PrevIndex) - return bw.Err -} - -// Size returns the size in bytes of the Input -func (in Input) Size() int { - return in.PrevHash.Size() + 2 // 2 = sizeOf uint16 } diff --git a/pkg/core/transaction/invocation.go b/pkg/core/transaction/invocation.go index 0b255426c..2ac3550e0 100644 --- a/pkg/core/transaction/invocation.go +++ b/pkg/core/transaction/invocation.go @@ -1,8 +1,7 @@ package transaction import ( - "io" - + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -32,33 +31,20 @@ func NewInvocationTX(script []byte) *Transaction { } } -// DecodeBinary implements the Payload interface. -func (tx *InvocationTX) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (tx *InvocationTX) DecodeBinary(br *io.BinReader) { tx.Script = br.ReadBytes() if tx.Version >= 1 { br.ReadLE(&tx.Gas) } else { tx.Gas = util.Fixed8FromInt64(0) } - return br.Err } -// EncodeBinary implements the Payload interface. -func (tx *InvocationTX) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (tx *InvocationTX) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(tx.Script) if tx.Version >= 1 { bw.WriteLE(tx.Gas) } - return bw.Err -} - -// Size returns serialized binary size for this transaction. -func (tx *InvocationTX) Size() int { - sz := util.GetVarSize(tx.Script) - if tx.Version >= 1 { - sz += tx.Gas.Size() - } - return sz } diff --git a/pkg/core/transaction/issue.go b/pkg/core/transaction/issue.go index 1a82d2df6..5e09bb171 100644 --- a/pkg/core/transaction/issue.go +++ b/pkg/core/transaction/issue.go @@ -1,24 +1,17 @@ package transaction import ( - "io" + "github.com/CityOfZion/neo-go/pkg/io" ) // IssueTX represents a issue transaction. // This TX has not special attributes. type IssueTX struct{} -// DecodeBinary implements the Payload interface. -func (tx *IssueTX) DecodeBinary(r io.Reader) error { - return nil +// DecodeBinary implements Serializable interface. +func (tx *IssueTX) DecodeBinary(r *io.BinReader) { } -// EncodeBinary implements the Payload interface. -func (tx *IssueTX) EncodeBinary(w io.Writer) error { - return nil -} - -// Size returns serialized binary size for this transaction. -func (tx *IssueTX) Size() int { - return 0 +// EncodeBinary implements Serializable interface. +func (tx *IssueTX) EncodeBinary(w *io.BinWriter) { } diff --git a/pkg/core/transaction/miner.go b/pkg/core/transaction/miner.go index 3f99ad125..1e1ab3753 100644 --- a/pkg/core/transaction/miner.go +++ b/pkg/core/transaction/miner.go @@ -1,8 +1,7 @@ package transaction import ( - "encoding/binary" - "io" + "github.com/CityOfZion/neo-go/pkg/io" ) // MinerTX represents a miner transaction. @@ -11,17 +10,12 @@ type MinerTX struct { Nonce uint32 } -// DecodeBinary implements the Payload interface. -func (tx *MinerTX) DecodeBinary(r io.Reader) error { - return binary.Read(r, binary.LittleEndian, &tx.Nonce) +// DecodeBinary implements Serializable interface. +func (tx *MinerTX) DecodeBinary(r *io.BinReader) { + r.ReadLE(&tx.Nonce) } -// EncodeBinary implements the Payload interface. -func (tx *MinerTX) EncodeBinary(w io.Writer) error { - return binary.Write(w, binary.LittleEndian, tx.Nonce) -} - -// Size returns serialized binary size for this transaction. -func (tx *MinerTX) Size() int { - return 4 // Nonce +// EncodeBinary implements Serializable interface. +func (tx *MinerTX) EncodeBinary(w *io.BinWriter) { + w.WriteLE(tx.Nonce) } diff --git a/pkg/core/transaction/miner_test.go b/pkg/core/transaction/miner_test.go index b26096ba5..4ef3e2334 100644 --- a/pkg/core/transaction/miner_test.go +++ b/pkg/core/transaction/miner_test.go @@ -1,10 +1,10 @@ package transaction import ( - "bytes" "encoding/hex" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -21,10 +21,10 @@ func TestEncodeDecodeMiner(t *testing.T) { assert.Equal(t, "a1f219dc6be4c35eca172e65e02d4591045220221b1543f1a4b67b9e9442c264", tx.Hash().ReverseString()) // Encode - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() - err := tx.EncodeBinary(buf) - assert.Equal(t, nil, err) + tx.EncodeBinary(buf.BinWriter) + assert.Equal(t, nil, buf.Err) assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes())) } diff --git a/pkg/core/transaction/output.go b/pkg/core/transaction/output.go index 60a55b5cf..489059839 100644 --- a/pkg/core/transaction/output.go +++ b/pkg/core/transaction/output.go @@ -2,9 +2,9 @@ package transaction import ( "encoding/json" - "io" "github.com/CityOfZion/neo-go/pkg/crypto" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -33,27 +33,18 @@ func NewOutput(assetID util.Uint256, amount util.Fixed8, scriptHash util.Uint160 } } -// DecodeBinary implements the Payload interface. -func (out *Output) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (out *Output) DecodeBinary(br *io.BinReader) { 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 { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (out *Output) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(out.AssetID) bw.WriteLE(out.Amount) bw.WriteLE(out.ScriptHash) - return bw.Err -} - -// Size returns the size in bytes of the Output -func (out *Output) Size() int { - return out.AssetID.Size() + out.Amount.Size() + out.ScriptHash.Size() } // MarshalJSON implements the Marshaler interface diff --git a/pkg/core/transaction/publish.go b/pkg/core/transaction/publish.go index 3a3c735f3..e46ce6a56 100644 --- a/pkg/core/transaction/publish.go +++ b/pkg/core/transaction/publish.go @@ -1,10 +1,8 @@ package transaction import ( - "io" - + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" - "github.com/CityOfZion/neo-go/pkg/util" ) // PublishTX represents a publish transaction. @@ -22,9 +20,8 @@ type PublishTX struct { Version uint8 // Version of the parent struct Transaction. Used in reading NeedStorage flag. } -// DecodeBinary implements the Payload interface. -func (tx *PublishTX) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (tx *PublishTX) DecodeBinary(br *io.BinReader) { tx.Script = br.ReadBytes() lenParams := br.ReadVarUint() @@ -50,13 +47,10 @@ func (tx *PublishTX) DecodeBinary(r io.Reader) error { tx.Author = br.ReadString() tx.Email = br.ReadString() tx.Description = br.ReadString() - - return br.Err } -// EncodeBinary implements the Payload interface. -func (tx *PublishTX) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (tx *PublishTX) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(tx.Script) bw.WriteVarUint(uint64(len(tx.ParamList))) for _, param := range tx.ParamList { @@ -71,19 +65,4 @@ func (tx *PublishTX) EncodeBinary(w io.Writer) error { bw.WriteString(tx.Author) bw.WriteString(tx.Email) bw.WriteString(tx.Description) - return bw.Err -} - -// Size returns serialized binary size for this transaction. -func (tx *PublishTX) Size() int { - sz := util.GetVarSize(tx.Script) + util.GetVarSize(uint64(len(tx.ParamList))) - sz += 1 * len(tx.ParamList) - sz++ - if tx.Version >= 1 { - sz++ - } - sz += util.GetVarSize(tx.Name) + util.GetVarSize(tx.CodeVersion) - sz += util.GetVarSize(tx.Author) + util.GetVarSize(tx.Email) - sz += util.GetVarSize(tx.Description) - return sz } diff --git a/pkg/core/transaction/register.go b/pkg/core/transaction/register.go index 96e5e1c6c..fde7687ef 100644 --- a/pkg/core/transaction/register.go +++ b/pkg/core/transaction/register.go @@ -1,9 +1,8 @@ package transaction import ( - "io" - "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -29,41 +28,27 @@ type RegisterTX struct { Admin util.Uint160 } -// DecodeBinary implements the Payload interface. -func (tx *RegisterTX) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (tx *RegisterTX) DecodeBinary(br *io.BinReader) { br.ReadLE(&tx.AssetType) tx.Name = br.ReadString() br.ReadLE(&tx.Amount) br.ReadLE(&tx.Precision) - if br.Err != nil { - return br.Err - } tx.Owner = &keys.PublicKey{} - if err := tx.Owner.DecodeBinary(r); err != nil { - return err - } + tx.Owner.DecodeBinary(br) br.ReadLE(&tx.Admin) - return br.Err } -// EncodeBinary implements the Payload interface. -func (tx *RegisterTX) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (tx *RegisterTX) EncodeBinary(bw *io.BinWriter) { 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 -} - -// Size returns serialized binary size for this transaction. -func (tx *RegisterTX) Size() int { - return 1 + util.GetVarSize(tx.Name) + tx.Amount.Size() + 1 + len(tx.Owner.Bytes()) + tx.Admin.Size() } diff --git a/pkg/core/transaction/register_test.go b/pkg/core/transaction/register_test.go index 710979e3e..77ee583ac 100644 --- a/pkg/core/transaction/register_test.go +++ b/pkg/core/transaction/register_test.go @@ -1,12 +1,12 @@ package transaction import ( - "bytes" "encoding/hex" "testing" "github.com/CityOfZion/neo-go/pkg/crypto" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) @@ -26,11 +26,15 @@ func TestRegisterTX(t *testing.T) { }, } - buf := new(bytes.Buffer) - assert.Nil(t, tx.EncodeBinary(buf)) + buf := io.NewBufBinWriter() + tx.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() txDecode := &Transaction{} - assert.Nil(t, txDecode.DecodeBinary(buf)) + r := io.NewBinReaderFromBuf(b) + txDecode.DecodeBinary(r) + assert.Nil(t, r.Err) txData := tx.Data.(*RegisterTX) txDecodeData := txDecode.Data.(*RegisterTX) assert.Equal(t, txData, txDecodeData) @@ -45,7 +49,9 @@ func TestDecodeRegisterTXFromRawString(t *testing.T) { } tx := &Transaction{} - assert.Nil(t, tx.DecodeBinary(bytes.NewReader(b))) + r := io.NewBinReaderFromBuf(b) + tx.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, RegisterType, tx.Type) txData := tx.Data.(*RegisterTX) assert.Equal(t, GoverningToken, txData.AssetType) @@ -56,10 +62,14 @@ func TestDecodeRegisterTXFromRawString(t *testing.T) { assert.Equal(t, "Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt", crypto.AddressFromUint160(txData.Admin)) assert.Equal(t, "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", tx.Hash().ReverseString()) - buf := new(bytes.Buffer) - assert.Nil(t, tx.EncodeBinary(buf)) + buf := io.NewBufBinWriter() + tx.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + benc := buf.Bytes() txDecode := &Transaction{} - assert.Nil(t, txDecode.DecodeBinary(buf)) + encreader := io.NewBinReaderFromBuf(benc) + txDecode.DecodeBinary(encreader) + assert.Nil(t, encreader.Err) assert.Equal(t, tx, txDecode) } diff --git a/pkg/core/transaction/state.go b/pkg/core/transaction/state.go index 5ee0c4a0d..22a214e99 100644 --- a/pkg/core/transaction/state.go +++ b/pkg/core/transaction/state.go @@ -1,9 +1,7 @@ package transaction import ( - "io" - - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" ) // StateTX represents a state transaction. @@ -11,44 +9,20 @@ type StateTX struct { Descriptors []*StateDescriptor } -// DecodeBinary implements the Payload interface. -func (tx *StateTX) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} - lenDesc := br.ReadVarUint() - if br.Err != nil { - return br.Err - } +// DecodeBinary implements Serializable interface. +func (tx *StateTX) DecodeBinary(r *io.BinReader) { + lenDesc := r.ReadVarUint() tx.Descriptors = make([]*StateDescriptor, lenDesc) for i := 0; i < int(lenDesc); i++ { tx.Descriptors[i] = &StateDescriptor{} - if err := tx.Descriptors[i].DecodeBinary(r); err != nil { - return err - } + tx.Descriptors[i].DecodeBinary(r) } - return nil } -// EncodeBinary implements the Payload interface. -func (tx *StateTX) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} - bw.WriteVarUint(uint64(len(tx.Descriptors))) - if bw.Err != nil { - return bw.Err - } +// EncodeBinary implements Serializable interface. +func (tx *StateTX) EncodeBinary(w *io.BinWriter) { + w.WriteVarUint(uint64(len(tx.Descriptors))) for _, desc := range tx.Descriptors { - err := desc.EncodeBinary(w) - if err != nil { - return err - } + desc.EncodeBinary(w) } - return nil -} - -// Size returns serialized binary size for this transaction. -func (tx *StateTX) Size() int { - sz := util.GetVarSize(uint64(len(tx.Descriptors))) - for _, desc := range tx.Descriptors { - sz += desc.Size() - } - return sz } diff --git a/pkg/core/transaction/state_descriptor.go b/pkg/core/transaction/state_descriptor.go index 80a30ab04..087ea5baf 100644 --- a/pkg/core/transaction/state_descriptor.go +++ b/pkg/core/transaction/state_descriptor.go @@ -1,9 +1,7 @@ package transaction import ( - "io" - - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" ) // DescStateType represents the type of StateDescriptor. @@ -23,29 +21,19 @@ type StateDescriptor struct { Field string } -// DecodeBinary implements the Payload interface. -func (s *StateDescriptor) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} - br.ReadLE(&s.Type) +// DecodeBinary implements Serializable interface. +func (s *StateDescriptor) DecodeBinary(r *io.BinReader) { + r.ReadLE(&s.Type) - s.Key = br.ReadBytes() - s.Value = br.ReadBytes() - s.Field = br.ReadString() - - return br.Err + s.Key = r.ReadBytes() + s.Value = r.ReadBytes() + s.Field = r.ReadString() } -// EncodeBinary implements the Payload interface. -func (s *StateDescriptor) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} - bw.WriteLE(s.Type) - bw.WriteBytes(s.Key) - bw.WriteBytes(s.Value) - bw.WriteString(s.Field) - return bw.Err -} - -// Size returns serialized binary size for state descriptor. -func (s *StateDescriptor) Size() int { - return 1 + util.GetVarSize(s.Key) + util.GetVarSize(s.Value) + util.GetVarSize(s.Field) +// EncodeBinary implements Serializable interface. +func (s *StateDescriptor) EncodeBinary(w *io.BinWriter) { + w.WriteLE(s.Type) + w.WriteBytes(s.Key) + w.WriteBytes(s.Value) + w.WriteString(s.Field) } diff --git a/pkg/core/transaction/state_test.go b/pkg/core/transaction/state_test.go index 65052058c..290bfe744 100644 --- a/pkg/core/transaction/state_test.go +++ b/pkg/core/transaction/state_test.go @@ -1,10 +1,10 @@ package transaction import ( - "bytes" "encoding/hex" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -30,11 +30,9 @@ func TestEncodeDecodeState(t *testing.T) { assert.Equal(t, Validator, descriptor.Type) // Encode + buf := io.NewBufBinWriter() + tx.EncodeBinary(buf.BinWriter) - buf := new(bytes.Buffer) - - err := tx.EncodeBinary(buf) - - assert.Equal(t, nil, err) + assert.Equal(t, nil, buf.Err) assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes())) } diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 90efcc777..13bdca753 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -1,10 +1,8 @@ package transaction import ( - "bytes" - "io" - "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" log "github.com/sirupsen/logrus" ) @@ -76,174 +74,126 @@ func (t *Transaction) AddInput(in *Input) { t.Inputs = append(t.Inputs, in) } -// DecodeBinary implements the payload interface. -func (t *Transaction) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (t *Transaction) DecodeBinary(br *io.BinReader) { br.ReadLE(&t.Type) br.ReadLE(&t.Version) - if br.Err != nil { - return br.Err - } - if err := t.decodeData(r); err != nil { - return err - } + t.decodeData(br) lenAttrs := br.ReadVarUint() t.Attributes = make([]*Attribute, lenAttrs) for i := 0; i < int(lenAttrs); i++ { t.Attributes[i] = &Attribute{} - if err := t.Attributes[i].DecodeBinary(r); err != nil { - log.Warnf("failed to decode TX %s", t.hash) - return err - } + t.Attributes[i].DecodeBinary(br) } lenInputs := br.ReadVarUint() t.Inputs = make([]*Input, lenInputs) for i := 0; i < int(lenInputs); i++ { t.Inputs[i] = &Input{} - if err := t.Inputs[i].DecodeBinary(r); err != nil { - return err - } + t.Inputs[i].DecodeBinary(br) } lenOutputs := br.ReadVarUint() t.Outputs = make([]*Output, lenOutputs) for i := 0; i < int(lenOutputs); i++ { t.Outputs[i] = &Output{} - if err := t.Outputs[i].DecodeBinary(r); err != nil { - return err - } + t.Outputs[i].DecodeBinary(br) } lenScripts := br.ReadVarUint() t.Scripts = make([]*Witness, lenScripts) for i := 0; i < int(lenScripts); i++ { t.Scripts[i] = &Witness{} - if err := t.Scripts[i].DecodeBinary(r); err != nil { - return err - } + t.Scripts[i].DecodeBinary(br) } - 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() + if br.Err == nil { + br.Err = t.createHash() + } } -func (t *Transaction) decodeData(r io.Reader) error { +func (t *Transaction) decodeData(r *io.BinReader) { switch t.Type { case InvocationType: t.Data = &InvocationTX{Version: t.Version} - return t.Data.(*InvocationTX).DecodeBinary(r) + t.Data.(*InvocationTX).DecodeBinary(r) case MinerType: t.Data = &MinerTX{} - return t.Data.(*MinerTX).DecodeBinary(r) + t.Data.(*MinerTX).DecodeBinary(r) case ClaimType: t.Data = &ClaimTX{} - return t.Data.(*ClaimTX).DecodeBinary(r) + t.Data.(*ClaimTX).DecodeBinary(r) case ContractType: t.Data = &ContractTX{} - return t.Data.(*ContractTX).DecodeBinary(r) + t.Data.(*ContractTX).DecodeBinary(r) case RegisterType: t.Data = &RegisterTX{} - return t.Data.(*RegisterTX).DecodeBinary(r) + t.Data.(*RegisterTX).DecodeBinary(r) case IssueType: t.Data = &IssueTX{} - return t.Data.(*IssueTX).DecodeBinary(r) + t.Data.(*IssueTX).DecodeBinary(r) case EnrollmentType: t.Data = &EnrollmentTX{} - return t.Data.(*EnrollmentTX).DecodeBinary(r) + t.Data.(*EnrollmentTX).DecodeBinary(r) case PublishType: t.Data = &PublishTX{Version: t.Version} - return t.Data.(*PublishTX).DecodeBinary(r) + t.Data.(*PublishTX).DecodeBinary(r) case StateType: t.Data = &StateTX{} - return t.Data.(*StateTX).DecodeBinary(r) + t.Data.(*StateTX).DecodeBinary(r) default: log.Warnf("invalid TX type %s", t.Type) } - return nil } -// EncodeBinary implements the payload interface. -func (t *Transaction) EncodeBinary(w io.Writer) error { - if err := t.encodeHashableFields(w); err != nil { - return err - } - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (t *Transaction) EncodeBinary(bw *io.BinWriter) { + t.encodeHashableFields(bw) 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 { - return err - } + s.EncodeBinary(bw) } - return nil } // 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 { - bw := util.BinWriter{W: w} - +func (t *Transaction) encodeHashableFields(bw *io.BinWriter) { bw.WriteLE(t.Type) bw.WriteLE(t.Version) - if bw.Err != nil { - return bw.Err - } // Underlying TXer. if t.Data != nil { - if err := t.Data.EncodeBinary(w); err != nil { - return err - } + t.Data.EncodeBinary(bw) } // Attributes 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 { - return err - } + attr.EncodeBinary(bw) } // Inputs 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 { - return err - } + in.EncodeBinary(bw) } // Outputs 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 { - return err - } + out.EncodeBinary(bw) } - return nil } // createHash creates the hash of the transaction. func (t *Transaction) createHash() error { - buf := new(bytes.Buffer) - if err := t.encodeHashableFields(buf); err != nil { - return err + buf := io.NewBufBinWriter() + t.encodeHashableFields(buf.BinWriter) + if buf.Err != nil { + return buf.Err } t.hash = hash.DoubleSha256(buf.Bytes()) @@ -269,22 +219,12 @@ func (t Transaction) GroupOutputByAssetID() map[util.Uint256][]*Output { return m } -// Size returns the size of the transaction in term of bytes -func (t *Transaction) Size() int { - attrSize := util.GetVarSize(t.Attributes) - inputSize := util.GetVarSize(t.Inputs) - outputSize := util.GetVarSize(t.Outputs) - witnesSize := util.GetVarSize(t.Scripts) - // uint8 + uint8 + attrSize + inputSize + outputSize + witnesSize - return 2 + attrSize + inputSize + outputSize + witnesSize + t.Data.Size() -} - // Bytes convert the transaction to []byte func (t *Transaction) Bytes() []byte { - buf := new(bytes.Buffer) - if err := t.EncodeBinary(buf); err != nil { + buf := io.NewBufBinWriter() + t.EncodeBinary(buf.BinWriter) + if buf.Err != nil { return nil } return buf.Bytes() - } diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go index 3bfeb94da..2c416c570 100644 --- a/pkg/core/transaction/transaction_test.go +++ b/pkg/core/transaction/transaction_test.go @@ -1,11 +1,11 @@ package transaction import ( - "bytes" "encoding/hex" "testing" "github.com/CityOfZion/neo-go/pkg/crypto" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" @@ -27,15 +27,15 @@ func TestWitnessEncodeDecode(t *testing.T) { VerificationScript: verif, } - buf := new(bytes.Buffer) - if err := wit.EncodeBinary(buf); err != nil { - t.Fatal(err) - } + buf := io.NewBufBinWriter() + wit.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + benc := buf.Bytes() witDecode := &Witness{} - if err := witDecode.DecodeBinary(buf); err != nil { - t.Fatal(err) - } + encreader := io.NewBinReaderFromBuf(benc) + witDecode.DecodeBinary(encreader) + assert.Nil(t, encreader.Err) t.Log(len(witDecode.VerificationScript)) t.Log(len(witDecode.InvocationScript)) @@ -61,10 +61,9 @@ func TestDecodeEncodeClaimTX(t *testing.T) { assert.Equal(t, invoc, hex.EncodeToString(tx.Scripts[0].InvocationScript)) assert.Equal(t, verif, hex.EncodeToString(tx.Scripts[0].VerificationScript)) - buf := new(bytes.Buffer) - if err := tx.EncodeBinary(buf); err != nil { - t.Fatal(err) - } + buf := io.NewBufBinWriter() + tx.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) assert.Equal(t, rawClaimTX, hex.EncodeToString(buf.Bytes())) hash := "2c6a45547b3898318e400e541628990a07acb00f3b9a15a8e966ae49525304da" @@ -90,10 +89,10 @@ func TestDecodeEncodeInvocationTX(t *testing.T) { assert.Equal(t, invoc, hex.EncodeToString(tx.Scripts[0].InvocationScript)) assert.Equal(t, verif, hex.EncodeToString(tx.Scripts[0].VerificationScript)) - buf := new(bytes.Buffer) - if err := tx.EncodeBinary(buf); err != nil { - t.Fatal(err) - } + buf := io.NewBufBinWriter() + tx.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + assert.Equal(t, rawInvocationTX, hex.EncodeToString(buf.Bytes())) } @@ -145,8 +144,8 @@ func TestDecodePublishTX(t *testing.T) { assert.Equal(t, expectedTX.Type, actualTX.Type) assert.Equal(t, expectedTX.Version, actualTX.Version) - buf := new(bytes.Buffer) - err := actualTX.EncodeBinary(buf) - assert.Nil(t, err) + buf := io.NewBufBinWriter() + actualTX.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) assert.Equal(t, rawPublishTX, hex.EncodeToString(buf.Bytes())) } diff --git a/pkg/core/transaction/txer.go b/pkg/core/transaction/txer.go index e87187bfd..ca612c7fa 100644 --- a/pkg/core/transaction/txer.go +++ b/pkg/core/transaction/txer.go @@ -1,11 +1,9 @@ package transaction -import "io" +import "github.com/CityOfZion/neo-go/pkg/io" // TXer is interface that can act as the underlying data of // a transaction. type TXer interface { - DecodeBinary(io.Reader) error - EncodeBinary(io.Writer) error - Size() int + io.Serializable } diff --git a/pkg/core/transaction/witness.go b/pkg/core/transaction/witness.go index 125fede63..8640dca74 100644 --- a/pkg/core/transaction/witness.go +++ b/pkg/core/transaction/witness.go @@ -3,9 +3,9 @@ package transaction import ( "encoding/hex" "encoding/json" - "io" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -15,23 +15,16 @@ type Witness struct { VerificationScript []byte } -// DecodeBinary implements the payload interface. -func (w *Witness) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} - +// DecodeBinary implements Serializable interface. +func (w *Witness) DecodeBinary(br *io.BinReader) { w.InvocationScript = br.ReadBytes() w.VerificationScript = br.ReadBytes() - return br.Err } -// EncodeBinary implements the payload interface. -func (w *Witness) EncodeBinary(writer io.Writer) error { - bw := util.BinWriter{W: writer} - +// EncodeBinary implements Serializable interface. +func (w *Witness) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(w.InvocationScript) bw.WriteBytes(w.VerificationScript) - - return bw.Err } // MarshalJSON implements the json marshaller interface. @@ -44,11 +37,6 @@ func (w *Witness) MarshalJSON() ([]byte, error) { return json.Marshal(data) } -// Size returns the size in bytes of the Witness. -func (w *Witness) Size() int { - return util.GetVarSize(w.InvocationScript) + util.GetVarSize(w.VerificationScript) -} - // ScriptHash returns the hash of the VerificationScript. func (w Witness) ScriptHash() util.Uint160 { return hash.Hash160(w.VerificationScript) diff --git a/pkg/core/unspent_coin_state.go b/pkg/core/unspent_coin_state.go index e8972934f..9f8026578 100644 --- a/pkg/core/unspent_coin_state.go +++ b/pkg/core/unspent_coin_state.go @@ -1,12 +1,11 @@ package core import ( - "bytes" "fmt" - "io" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -22,8 +21,10 @@ func (u UnspentCoins) getAndUpdate(s storage.Store, hash util.Uint256) (*Unspent unspent := &UnspentCoinState{} key := storage.AppendPrefix(storage.STCoin, hash.BytesReverse()) if b, err := s.Get(key); err == nil { - if err := unspent.DecodeBinary(bytes.NewReader(b)); err != nil { - return nil, fmt.Errorf("failed to decode (UnspentCoinState): %s", err) + r := io.NewBinReaderFromBuf(b) + unspent.DecodeBinary(r) + if r.Err != nil { + return nil, fmt.Errorf("failed to decode (UnspentCoinState): %s", r.Err) } } else { unspent = &UnspentCoinState{ @@ -53,10 +54,11 @@ func NewUnspentCoinState(n int) *UnspentCoinState { // commit writes all unspent coin states to the given Batch. func (u UnspentCoins) commit(b storage.Batch) error { - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() for hash, state := range u { - if err := state.EncodeBinary(buf); err != nil { - return err + state.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err } key := storage.AppendPrefix(storage.STCoin, hash.BytesReverse()) b.Put(key, buf.Bytes()) @@ -65,19 +67,16 @@ func (u UnspentCoins) commit(b storage.Batch) error { return nil } -// EncodeBinary encodes UnspentCoinState to the given io.Writer. -func (s *UnspentCoinState) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary encodes UnspentCoinState to the given BinWriter. +func (s *UnspentCoinState) EncodeBinary(bw *io.BinWriter) { bw.WriteVarUint(uint64(len(s.states))) for _, state := range s.states { bw.WriteLE(byte(state)) } - return bw.Err } -// DecodeBinary decodes UnspentCoinState from the given io.Reader. -func (s *UnspentCoinState) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary decodes UnspentCoinState from the given BinReader. +func (s *UnspentCoinState) DecodeBinary(br *io.BinReader) { lenStates := br.ReadVarUint() s.states = make([]CoinState, lenStates) for i := 0; i < int(lenStates); i++ { @@ -85,7 +84,6 @@ func (s *UnspentCoinState) DecodeBinary(r io.Reader) error { br.ReadLE(&state) s.states[i] = CoinState(state) } - return br.Err } // IsDoubleSpend verifies that the input transactions are not double spent. @@ -98,7 +96,9 @@ func IsDoubleSpend(s storage.Store, tx *transaction.Transaction) bool { unspent := &UnspentCoinState{} key := storage.AppendPrefix(storage.STCoin, prevHash.BytesReverse()) if b, err := s.Get(key); err == nil { - if err := unspent.DecodeBinary(bytes.NewReader(b)); err != nil { + r := io.NewBinReaderFromBuf(b) + unspent.DecodeBinary(r) + if r.Err != nil { return false } if unspent == nil { diff --git a/pkg/core/unspent_coint_state_test.go b/pkg/core/unspent_coint_state_test.go index 3338f6b8a..658233ebc 100644 --- a/pkg/core/unspent_coint_state_test.go +++ b/pkg/core/unspent_coint_state_test.go @@ -1,10 +1,10 @@ package core import ( - "bytes" "testing" "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -19,10 +19,13 @@ func TestDecodeEncodeUnspentCoinState(t *testing.T) { }, } - buf := new(bytes.Buffer) - assert.Nil(t, unspent.EncodeBinary(buf)) + buf := io.NewBufBinWriter() + unspent.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) unspentDecode := &UnspentCoinState{} - assert.Nil(t, unspentDecode.DecodeBinary(buf)) + r := io.NewBinReaderFromBuf(buf.Bytes()) + unspentDecode.DecodeBinary(r) + assert.Nil(t, r.Err) } func TestCommitUnspentCoins(t *testing.T) { diff --git a/pkg/core/util.go b/pkg/core/util.go index de359a38f..05a189d26 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -1,8 +1,6 @@ package core import ( - "bytes" - "encoding/binary" "time" "github.com/CityOfZion/neo-go/config" @@ -10,6 +8,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/vm" @@ -181,11 +180,9 @@ func headerSliceReverse(dest []*Header) { // storeAsCurrentBlock stores the given block witch prefix // SYSCurrentBlock. func storeAsCurrentBlock(batch storage.Batch, block *Block) { - buf := new(bytes.Buffer) - buf.Write(block.Hash().BytesReverse()) - b := make([]byte, 4) - binary.LittleEndian.PutUint32(b, block.Index) - buf.Write(b) + buf := io.NewBufBinWriter() + buf.WriteLE(block.Hash().BytesReverse()) + buf.WriteLE(block.Index) batch.Put(storage.SYSCurrentBlock.Bytes(), buf.Bytes()) } @@ -193,17 +190,18 @@ func storeAsCurrentBlock(batch storage.Batch, block *Block) { func storeAsBlock(batch storage.Batch, block *Block, sysFee uint32) error { var ( key = storage.AppendPrefix(storage.DataBlock, block.Hash().BytesReverse()) - buf = new(bytes.Buffer) + buf = io.NewBufBinWriter() ) - - b := make([]byte, 4) - binary.LittleEndian.PutUint32(b, sysFee) - + // sysFee needs to be handled somehow + // buf.WriteLE(sysFee) b, err := block.Trim() if err != nil { return err } - buf.Write(b) + buf.WriteLE(b) + if buf.Err != nil { + return buf.Err + } batch.Put(key, buf.Bytes()) return nil } @@ -211,15 +209,12 @@ func storeAsBlock(batch storage.Batch, block *Block, sysFee uint32) error { // storeAsTransaction stores the given TX as DataTransaction. func storeAsTransaction(batch storage.Batch, tx *transaction.Transaction, index uint32) error { key := storage.AppendPrefix(storage.DataTransaction, tx.Hash().BytesReverse()) - buf := new(bytes.Buffer) - if err := tx.EncodeBinary(buf); err != nil { - return err + buf := io.NewBufBinWriter() + buf.WriteLE(index) + tx.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err } - - dest := make([]byte, buf.Len()+4) - binary.LittleEndian.PutUint32(dest[:4], index) - copy(dest[4:], buf.Bytes()) - batch.Put(key, dest) - + batch.Put(key, buf.Bytes()) return nil } diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index a03734688..bcb6cf68a 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -5,14 +5,13 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/x509" - "encoding/binary" "encoding/hex" "fmt" - "io" "math/big" "github.com/CityOfZion/neo-go/pkg/crypto" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/pkg/errors" ) @@ -51,8 +50,10 @@ func NewPublicKeyFromString(s string) (*PublicKey, error) { } pubKey := new(PublicKey) - if err := pubKey.DecodeBinary(bytes.NewReader(b)); err != nil { - return nil, err + r := io.NewBinReaderFromBuf(b) + pubKey.DecodeBinary(r) + if r.Err != nil { + return nil, r.Err } return pubKey, nil @@ -122,69 +123,70 @@ func decodeCompressedY(x *big.Int, ylsb uint) (*big.Int, error) { // DecodeBytes decodes a PublicKey from the given slice of bytes. func (p *PublicKey) DecodeBytes(data []byte) error { - var datab []byte - copy(datab, data) - b := bytes.NewBuffer(datab) - return p.DecodeBinary(b) + b := io.NewBinReaderFromBuf(data) + p.DecodeBinary(b) + return b.Err } -// DecodeBinary decodes a PublicKey from the given io.Reader. -func (p *PublicKey) DecodeBinary(r io.Reader) error { +// DecodeBinary decodes a PublicKey from the given BinReader. +func (p *PublicKey) DecodeBinary(r *io.BinReader) { var prefix uint8 var x, y *big.Int var err error - if err = binary.Read(r, binary.LittleEndian, &prefix); err != nil { - return err + r.ReadLE(&prefix) + if r.Err != nil { + return } // Infinity switch prefix { case 0x00: // noop, initialized to nil - return nil + return case 0x02, 0x03: // Compressed public keys xbytes := make([]byte, 32) - if _, err := io.ReadFull(r, xbytes); err != nil { - return err + r.ReadLE(xbytes) + if r.Err != nil { + return } x = new(big.Int).SetBytes(xbytes) ylsb := uint(prefix & 0x1) y, err = decodeCompressedY(x, ylsb) if err != nil { - return err + return } case 0x04: xbytes := make([]byte, 32) ybytes := make([]byte, 32) - if _, err = io.ReadFull(r, xbytes); err != nil { - return err - } - if _, err = io.ReadFull(r, ybytes); err != nil { - return err + r.ReadLE(xbytes) + r.ReadLE(ybytes) + if r.Err != nil { + return } x = new(big.Int).SetBytes(xbytes) y = new(big.Int).SetBytes(ybytes) default: - return errors.Errorf("invalid prefix %d", prefix) + r.Err = errors.Errorf("invalid prefix %d", prefix) + return } c := elliptic.P256() cp := c.Params() if !c.IsOnCurve(x, y) { - return errors.New("enccoded point is not on the P256 curve") + r.Err = errors.New("enccoded point is not on the P256 curve") + return } if x.Cmp(cp.P) >= 0 || y.Cmp(cp.P) >= 0 { - return errors.New("enccoded point is not correct (X or Y is bigger than P") + r.Err = errors.New("enccoded point is not correct (X or Y is bigger than P") + return } p.X, p.Y = x, y - - return nil } -// EncodeBinary encodes a PublicKey to the given io.Writer. -func (p *PublicKey) EncodeBinary(w io.Writer) error { - return binary.Write(w, binary.LittleEndian, p.Bytes()) +// EncodeBinary encodes a PublicKey to the given BinWriter. +func (p *PublicKey) EncodeBinary(w *io.BinWriter) { + w.WriteLE(p.Bytes()) } // Signature returns a NEO-specific hash of the key. diff --git a/pkg/crypto/keys/publickey_test.go b/pkg/crypto/keys/publickey_test.go index 34c0b334d..199577d6e 100644 --- a/pkg/crypto/keys/publickey_test.go +++ b/pkg/crypto/keys/publickey_test.go @@ -1,21 +1,23 @@ package keys import ( - "bytes" "encoding/hex" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) func TestEncodeDecodeInfinity(t *testing.T) { key := &PublicKey{} - buf := new(bytes.Buffer) - assert.Nil(t, key.EncodeBinary(buf)) - assert.Equal(t, 1, buf.Len()) + buf := io.NewBufBinWriter() + key.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + assert.Equal(t, 1, len(b)) keyDecode := &PublicKey{} - assert.Nil(t, keyDecode.DecodeBinary(buf)) + assert.Nil(t, keyDecode.DecodeBytes(b)) assert.Equal(t, []byte{0x00}, keyDecode.Bytes()) } @@ -24,11 +26,13 @@ func TestEncodeDecodePublicKey(t *testing.T) { k, err := NewPrivateKey() assert.Nil(t, err) p := k.PublicKey() - buf := new(bytes.Buffer) - assert.Nil(t, p.EncodeBinary(buf)) + buf := io.NewBufBinWriter() + p.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() pDecode := &PublicKey{} - assert.Nil(t, pDecode.DecodeBinary(buf)) + assert.Nil(t, pDecode.DecodeBytes(b)) assert.Equal(t, p.X, pDecode.X) } } diff --git a/pkg/io/binaryBufWriter.go b/pkg/io/binaryBufWriter.go new file mode 100644 index 000000000..31ba262ac --- /dev/null +++ b/pkg/io/binaryBufWriter.go @@ -0,0 +1,39 @@ +package io + +import ( + "bytes" + "errors" +) + +// BufBinWriter is an additional layer on top of BinWriter that +// automatically creates buffer to write into that you can get after all +// writes via Bytes(). +type BufBinWriter struct { + *BinWriter + buf *bytes.Buffer +} + +// NewBufBinWriter makes a BufBinWriter with an empty byte buffer. +func NewBufBinWriter() *BufBinWriter { + b := new(bytes.Buffer) + return &BufBinWriter{BinWriter: NewBinWriterFromIO(b), buf: b} +} + +// Bytes returns resulting buffer and makes future writes return an error. +func (bw *BufBinWriter) Bytes() []byte { + if bw.Err != nil { + return nil + } + bw.Err = errors.New("buffer already drained") + return bw.buf.Bytes() +} + +// Reset resets the state of the buffer, making it usable again. It can +// make buffer usage somewhat more efficient, because you don't need to +// create it again, but beware that the buffer is gonna be the same as the one +// returned by Bytes(), so if you need that data after Reset() you have to copy +// it yourself. +func (bw *BufBinWriter) Reset() { + bw.Err = nil + bw.buf.Reset() +} diff --git a/pkg/util/binaryReader.go b/pkg/io/binaryReader.go similarity index 66% rename from pkg/util/binaryReader.go rename to pkg/io/binaryReader.go index fe92ae61a..27845b21d 100644 --- a/pkg/util/binaryReader.go +++ b/pkg/io/binaryReader.go @@ -1,6 +1,7 @@ -package util +package io import ( + "bytes" "encoding/binary" "io" ) @@ -8,17 +9,28 @@ import ( //BinReader is a convenient wrapper around a io.Reader and err object // Used to simplify error handling when reading into a struct with many fields type BinReader struct { - R io.Reader + r io.Reader Err error } +// NewBinReaderFromIO makes a BinReader from io.Reader. +func NewBinReaderFromIO(ior io.Reader) *BinReader { + return &BinReader{r: ior} +} + +// NewBinReaderFromBuf makes a BinReader from byte buffer. +func NewBinReaderFromBuf(b []byte) *BinReader { + r := bytes.NewReader(b) + return NewBinReaderFromIO(r) +} + // 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) + r.Err = binary.Read(r.r, binary.LittleEndian, v) } // ReadBE reads from the underlying io.Reader @@ -27,7 +39,7 @@ func (r *BinReader) ReadBE(v interface{}) { if r.Err != nil { return } - r.Err = binary.Read(r.R, binary.BigEndian, v) + r.Err = binary.Read(r.r, binary.BigEndian, v) } // ReadVarUint reads a variable-length-encoded integer from the @@ -38,21 +50,21 @@ func (r *BinReader) ReadVarUint() uint64 { } var b uint8 - r.Err = binary.Read(r.R, binary.LittleEndian, &b) + r.Err = binary.Read(r.r, binary.LittleEndian, &b) if b == 0xfd { var v uint16 - r.Err = binary.Read(r.R, binary.LittleEndian, &v) + r.Err = binary.Read(r.r, binary.LittleEndian, &v) return uint64(v) } if b == 0xfe { var v uint32 - r.Err = binary.Read(r.R, binary.LittleEndian, &v) + r.Err = binary.Read(r.r, binary.LittleEndian, &v) return uint64(v) } if b == 0xff { var v uint64 - r.Err = binary.Read(r.R, binary.LittleEndian, &v) + r.Err = binary.Read(r.r, binary.LittleEndian, &v) return v } diff --git a/pkg/util/binaryWriter.go b/pkg/io/binaryWriter.go similarity index 63% rename from pkg/util/binaryWriter.go rename to pkg/io/binaryWriter.go index 8f2c5229d..b85cda48b 100644 --- a/pkg/util/binaryWriter.go +++ b/pkg/io/binaryWriter.go @@ -1,8 +1,7 @@ -package util +package io import ( "encoding/binary" - "errors" "io" ) @@ -10,16 +9,21 @@ import ( // Used to simplify error handling when writing into a io.Writer // from a struct with many fields type BinWriter struct { - W io.Writer + w io.Writer Err error } +// NewBinWriterFromIO makes a BinWriter from io.Writer. +func NewBinWriterFromIO(iow io.Writer) *BinWriter { + return &BinWriter{w: iow} +} + // 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) + w.Err = binary.Write(w.w, binary.LittleEndian, v) } // WriteBE writes into the underlying io.Writer from an object v in big-endian format @@ -27,7 +31,7 @@ func (w *BinWriter) WriteBE(v interface{}) { if w.Err != nil { return } - w.Err = binary.Write(w.W, binary.BigEndian, v) + w.Err = binary.Write(w.w, binary.BigEndian, v) } // WriteVarUint writes a uint64 into the underlying writer using variable-length encoding @@ -36,33 +40,24 @@ func (w *BinWriter) WriteVarUint(val uint64) { return } - if val < 0 { - w.Err = errors.New("value out of range") - return - } - - if w.Err != nil { - return - } - if val < 0xfd { - w.Err = binary.Write(w.W, binary.LittleEndian, uint8(val)) + w.Err = binary.Write(w.w, binary.LittleEndian, uint8(val)) return } if val < 0xFFFF { - w.Err = binary.Write(w.W, binary.LittleEndian, byte(0xfd)) - w.Err = binary.Write(w.W, binary.LittleEndian, uint16(val)) + w.Err = binary.Write(w.w, binary.LittleEndian, byte(0xfd)) + w.Err = binary.Write(w.w, binary.LittleEndian, uint16(val)) return } if val < 0xFFFFFFFF { - w.Err = binary.Write(w.W, binary.LittleEndian, byte(0xfe)) - w.Err = binary.Write(w.W, binary.LittleEndian, uint32(val)) + w.Err = binary.Write(w.w, binary.LittleEndian, byte(0xfe)) + w.Err = binary.Write(w.w, binary.LittleEndian, uint32(val)) return } - w.Err = binary.Write(w.W, binary.LittleEndian, byte(0xff)) - w.Err = binary.Write(w.W, binary.LittleEndian, val) + w.Err = binary.Write(w.w, binary.LittleEndian, byte(0xff)) + w.Err = binary.Write(w.w, binary.LittleEndian, val) } diff --git a/pkg/io/binaryrw_test.go b/pkg/io/binaryrw_test.go new file mode 100644 index 000000000..dc69a92c4 --- /dev/null +++ b/pkg/io/binaryrw_test.go @@ -0,0 +1,193 @@ +package io + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +// mocks io.Reader and io.Writer, always fails to Write() or Read(). +type badRW struct{} + +func (w *badRW) Write(p []byte) (int, error) { + return 0, errors.New("it always fails") +} + +func (w *badRW) Read(p []byte) (int, error) { + return w.Write(p) +} + +func TestWriteLE(t *testing.T) { + var ( + val uint32 = 0xdeadbeef + readval uint32 + bin []byte = []byte{0xef, 0xbe, 0xad, 0xde} + ) + bw := NewBufBinWriter() + bw.WriteLE(val) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + br.ReadLE(&readval) + assert.Nil(t, br.Err) + assert.Equal(t, val, readval) +} + +func TestWriteBE(t *testing.T) { + var ( + val uint32 = 0xdeadbeef + readval uint32 + bin []byte = []byte{0xde, 0xad, 0xbe, 0xef} + ) + bw := NewBufBinWriter() + bw.WriteBE(val) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + assert.Equal(t, wrotebin, bin) + br := NewBinReaderFromBuf(bin) + br.ReadBE(&readval) + assert.Nil(t, br.Err) + assert.Equal(t, val, readval) +} + +func TestWriterErrHandling(t *testing.T) { + var badio = &badRW{} + bw := NewBinWriterFromIO(badio) + bw.WriteLE(uint32(0)) + assert.NotNil(t, bw.Err) + // these should work (without panic), preserving the Err + bw.WriteLE(uint32(0)) + bw.WriteBE(uint32(0)) + bw.WriteVarUint(0) + bw.WriteBytes([]byte{0x55, 0xaa}) + bw.WriteString("neo") + assert.NotNil(t, bw.Err) +} + +func TestReaderErrHandling(t *testing.T) { + var ( + i uint32 = 0xdeadbeef + iorig = i + badio = &badRW{} + ) + br := NewBinReaderFromIO(badio) + br.ReadLE(&i) + assert.NotNil(t, br.Err) + // i shouldn't change + assert.Equal(t, i, iorig) + // these should work (without panic), preserving the Err + br.ReadLE(&i) + br.ReadBE(&i) + assert.Equal(t, i, iorig) + val := br.ReadVarUint() + assert.Equal(t, val, uint64(0)) + b := br.ReadBytes() + assert.Equal(t, b, []byte{}) + s := br.ReadString() + assert.Equal(t, s, "") + assert.NotNil(t, br.Err) +} + +func TestBufBinWriterErr(t *testing.T) { + bw := NewBufBinWriter() + bw.WriteLE(uint32(0)) + assert.Nil(t, bw.Err) + // inject error + bw.Err = errors.New("oopsie") + res := bw.Bytes() + assert.NotNil(t, bw.Err) + assert.Nil(t, res) +} + +func TestBufBinWriterReset(t *testing.T) { + bw := NewBufBinWriter() + for i := 0; i < 3; i++ { + bw.WriteLE(uint32(i)) + assert.Nil(t, bw.Err) + _ = bw.Bytes() + assert.NotNil(t, bw.Err) + bw.Reset() + assert.Nil(t, bw.Err) + } +} + +func TestWriteString(t *testing.T) { + var ( + str string = "teststring" + ) + bw := NewBufBinWriter() + bw.WriteString(str) + assert.Nil(t, bw.Err) + wrotebin := bw.Bytes() + // +1 byte for length + assert.Equal(t, len(wrotebin), len(str)+1) + br := NewBinReaderFromBuf(wrotebin) + readstr := br.ReadString() + assert.Nil(t, br.Err) + assert.Equal(t, str, readstr) +} + +func TestWriteVarUint1(t *testing.T) { + var ( + val = uint64(1) + ) + bw := NewBufBinWriter() + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) + buf := bw.Bytes() + assert.Equal(t, 1, len(buf)) + br := NewBinReaderFromBuf(buf) + res := br.ReadVarUint() + assert.Nil(t, br.Err) + assert.Equal(t, val, res) +} + +func TestWriteVarUint1000(t *testing.T) { + var ( + val = uint64(1000) + ) + bw := NewBufBinWriter() + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) + buf := bw.Bytes() + assert.Equal(t, 3, len(buf)) + assert.Equal(t, byte(0xfd), buf[0]) + br := NewBinReaderFromBuf(buf) + res := br.ReadVarUint() + assert.Nil(t, br.Err) + assert.Equal(t, val, res) +} + +func TestWriteVarUint100000(t *testing.T) { + var ( + val = uint64(100000) + ) + bw := NewBufBinWriter() + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) + buf := bw.Bytes() + assert.Equal(t, 5, len(buf)) + assert.Equal(t, byte(0xfe), buf[0]) + br := NewBinReaderFromBuf(buf) + res := br.ReadVarUint() + assert.Nil(t, br.Err) + assert.Equal(t, val, res) +} + +func TestWriteVarUint100000000000(t *testing.T) { + var ( + val = uint64(1000000000000) + ) + bw := NewBufBinWriter() + bw.WriteVarUint(val) + assert.Nil(t, bw.Err) + buf := bw.Bytes() + assert.Equal(t, 9, len(buf)) + assert.Equal(t, byte(0xff), buf[0]) + br := NewBinReaderFromBuf(buf) + res := br.ReadVarUint() + assert.Nil(t, br.Err) + assert.Equal(t, val, res) +} diff --git a/pkg/io/serializable.go b/pkg/io/serializable.go index 359c8513e..8e132b86b 100644 --- a/pkg/io/serializable.go +++ b/pkg/io/serializable.go @@ -1,10 +1,12 @@ package io -import "io" - -// Serializable defines the binary encoding/decoding interface. +// Serializable defines the binary encoding/decoding interface. Errors are +// returned via BinReader/BinWriter Err field. These functions must have safe +// behavior when passed BinReader/BinWriter with Err already set. Invocations +// to these functions tend to be nested, with this mechanism only the top-level +// caller should handle the error once and all the other code should just not +// panic in presence of error. type Serializable interface { - Size() int - DecodeBinary(io.Reader) error - EncodeBinary(io.Writer) error + DecodeBinary(*BinReader) + EncodeBinary(*BinWriter) } diff --git a/pkg/util/size.go b/pkg/io/size.go similarity index 71% rename from pkg/util/size.go rename to pkg/io/size.go index 33b7b60fc..ea8bcc562 100644 --- a/pkg/util/size.go +++ b/pkg/io/size.go @@ -1,10 +1,8 @@ -package util +package io import ( "fmt" "reflect" - - "github.com/CityOfZion/neo-go/pkg/io" ) var ( @@ -19,6 +17,20 @@ var ( i64 int64 ) +// This structure is used to calculate the wire size of the serializable +// structure. It's an io.Writer that doesn't do any real writes, but instead +// just counts the number of bytes to be written. +type counterWriter struct { + counter int +} + +// Write implements the io.Writer interface +func (cw *counterWriter) Write(p []byte) (int, error) { + n := len(p) + cw.counter += n + return n, nil +} + // GetVarIntSize returns the size in number of bytes of a variable integer // (reference: GetVarSize(int value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs) func GetVarIntSize(value int) int { @@ -61,16 +73,27 @@ func GetVarSize(value interface{}) int { reflect.Uint32, reflect.Uint64: return GetVarIntSize(int(v.Uint())) + case reflect.Ptr: + vser, ok := v.Interface().(Serializable) + if !ok { + panic(fmt.Sprintf("unable to calculate GetVarSize for a non-Serializable pointer")) + } + cw := counterWriter{} + w := NewBinWriterFromIO(&cw) + vser.EncodeBinary(w) + if w.Err != nil { + panic(fmt.Sprintf("error serializing %s: %s", reflect.TypeOf(value), w.Err.Error())) + } + return cw.counter case reflect.Slice, reflect.Array: valueLength := v.Len() valueSize := 0 if valueLength != 0 { switch reflect.ValueOf(value).Index(0).Interface().(type) { - case io.Serializable: + case Serializable: for i := 0; i < valueLength; i++ { - elem := v.Index(i).Interface().(io.Serializable) - valueSize += elem.Size() + valueSize += GetVarSize(v.Index(i).Interface()) } case uint8, int8: valueSize = valueLength diff --git a/pkg/util/size_test.go b/pkg/io/size_test.go similarity index 76% rename from pkg/util/size_test.go rename to pkg/io/size_test.go index 9d1217424..dddce59ad 100644 --- a/pkg/util/size_test.go +++ b/pkg/io/size_test.go @@ -1,27 +1,31 @@ -package util +package io import ( "fmt" - "io" "testing" + "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) // Mock structure to test getting size of an array of serializable things type smthSerializable struct { + some [42]byte } -func (*smthSerializable) DecodeBinary(io.Reader) error { - return nil +func (*smthSerializable) DecodeBinary(*BinReader) {} + +func (ss *smthSerializable) EncodeBinary(bw *BinWriter) { + bw.WriteLE(ss.some) } -func (*smthSerializable) EncodeBinary(io.Writer) error { - return nil -} +// Mock structure that gives error in EncodeBinary(). +type smthNotReallySerializable struct{} -func (*smthSerializable) Size() int { - return 42 +func (*smthNotReallySerializable) DecodeBinary(*BinReader) {} + +func (*smthNotReallySerializable) EncodeBinary(bw *BinWriter) { + bw.Err = fmt.Errorf("smth bad happened in smthNotReallySerializable") } func TestVarSize(t *testing.T) { @@ -87,7 +91,7 @@ func TestVarSize(t *testing.T) { }, { // The neo C# implementation doe not allowed this! - Uint160{1, 2, 4, 5, 6}, + util.Uint160{1, 2, 4, 5, 6}, "test_Uint160_1", 21, }, @@ -153,7 +157,7 @@ func TestVarSize(t *testing.T) { 241, }, // The neo C# implementation doe not allowed this! - {Uint256{1, 2, 3, 4, 5, 6}, + {util.Uint256{1, 2, 3, 4, 5, 6}, "test_Uint256_1", 33, }, @@ -184,11 +188,19 @@ func TestVarSize(t *testing.T) { } } -func TestVarSizePanic(t *testing.T) { +func panicVarSize(t *testing.T, v interface{}) { defer func() { r := recover() assert.NotNil(t, r) }() - _ = GetVarSize(t) + _ = GetVarSize(v) + // this should never execute + assert.Nil(t, t) +} + +func TestVarSizePanic(t *testing.T) { + panicVarSize(t, t) + panicVarSize(t, struct{}{}) + panicVarSize(t, &smthNotReallySerializable{}) } diff --git a/pkg/network/message.go b/pkg/network/message.go index 34bf4b137..1f702fc9f 100644 --- a/pkg/network/message.go +++ b/pkg/network/message.go @@ -1,18 +1,15 @@ package network import ( - "bytes" "encoding/binary" "errors" - "fmt" - "io" "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/network/payload" - "github.com/CityOfZion/neo-go/pkg/util" ) const ( @@ -80,12 +77,14 @@ func NewMessage(magic config.NetMode, cmd CommandType, p payload.Payload) *Messa ) if p != nil { - buf := new(bytes.Buffer) - if err := p.EncodeBinary(buf); err != nil { - panic(err) + buf := io.NewBufBinWriter() + p.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + panic(buf.Err) } - size = uint32(buf.Len()) - checksum = hash.Checksum(buf.Bytes()) + b := buf.Bytes() + size = uint32(len(b)) + checksum = hash.Checksum(b) } else { checksum = hash.Checksum([]byte{}) } @@ -147,8 +146,7 @@ func (m *Message) CommandType() CommandType { } // Decode a Message from the given reader. -func (m *Message) Decode(r io.Reader) error { - br := util.BinReader{R: r} +func (m *Message) Decode(br *io.BinReader) error { br.ReadLE(&m.Magic) br.ReadLE(&m.Command) br.ReadLE(&m.Length) @@ -160,69 +158,45 @@ func (m *Message) Decode(r io.Reader) error { if m.Length == 0 { return nil } - return m.decodePayload(r) + return m.decodePayload(br) } -func (m *Message) decodePayload(r io.Reader) error { - buf := new(bytes.Buffer) - n, err := io.CopyN(buf, r, int64(m.Length)) - if err != nil { - return err +func (m *Message) decodePayload(br *io.BinReader) error { + buf := make([]byte, m.Length) + br.ReadLE(buf) + if br.Err != nil { + return br.Err } - - if uint32(n) != m.Length { - return fmt.Errorf("expected to have read exactly %d bytes got %d", m.Length, n) - } - // Compare the checksum of the payload. - if !compareChecksum(m.Checksum, buf.Bytes()) { + if !compareChecksum(m.Checksum, buf) { return errChecksumMismatch } + r := io.NewBinReaderFromBuf(buf) var p payload.Payload switch m.CommandType() { case CMDVersion: p = &payload.Version{} - if err := p.DecodeBinary(buf); err != nil { - return err - } case CMDInv, CMDGetData: p = &payload.Inventory{} - if err := p.DecodeBinary(buf); err != nil { - return err - } case CMDAddr: p = &payload.AddressList{} - if err := p.DecodeBinary(buf); err != nil { - return err - } case CMDBlock: p = &core.Block{} - if err := p.DecodeBinary(buf); err != nil { - return err - } case CMDGetBlocks: fallthrough case CMDGetHeaders: p = &payload.GetBlocks{} - if err := p.DecodeBinary(buf); err != nil { - return err - } case CMDHeaders: p = &payload.Headers{} - if err := p.DecodeBinary(buf); err != nil { - return err - } case CMDTX: p = &transaction.Transaction{} - if err := p.DecodeBinary(buf); err != nil { - return err - } case CMDMerkleBlock: p = &payload.MerkleBlock{} - if err := p.DecodeBinary(buf); err != nil { - return err - } + } + p.DecodeBinary(r) + if r.Err != nil { + return r.Err } m.Payload = p @@ -230,19 +204,19 @@ func (m *Message) decodePayload(r io.Reader) error { return nil } -// Encode a Message to any given io.Writer. -func (m *Message) Encode(w io.Writer) error { - br := util.BinWriter{W: w} +// Encode a Message to any given BinWriter. +func (m *Message) Encode(br *io.BinWriter) error { br.WriteLE(m.Magic) br.WriteLE(m.Command) br.WriteLE(m.Length) br.WriteLE(m.Checksum) + if m.Payload != nil { + m.Payload.EncodeBinary(br) + + } if br.Err != nil { return br.Err } - if m.Payload != nil { - return m.Payload.EncodeBinary(w) - } return nil } diff --git a/pkg/network/payload/address.go b/pkg/network/payload/address.go index a83498a7d..eae7bc670 100644 --- a/pkg/network/payload/address.go +++ b/pkg/network/payload/address.go @@ -1,12 +1,11 @@ package payload import ( - "io" "net" "strconv" "time" - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" ) // AddressAndTime payload. @@ -28,24 +27,20 @@ func NewAddressAndTime(e *net.TCPAddr, t time.Time) *AddressAndTime { return &aat } -// DecodeBinary implements the Payload interface. -func (p *AddressAndTime) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (p *AddressAndTime) DecodeBinary(br *io.BinReader) { br.ReadLE(&p.Timestamp) br.ReadLE(&p.Services) br.ReadBE(&p.IP) br.ReadBE(&p.Port) - return br.Err } -// EncodeBinary implements the Payload interface. -func (p *AddressAndTime) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (p *AddressAndTime) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(p.Timestamp) bw.WriteLE(p.Services) bw.WriteBE(p.IP) bw.WriteBE(p.Port) - return bw.Err } // IPPortString makes a string from IP and port specified. @@ -70,35 +65,21 @@ func NewAddressList(n int) *AddressList { return &alist } -// DecodeBinary implements the Payload interface. -func (p *AddressList) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (p *AddressList) DecodeBinary(br *io.BinReader) { listLen := br.ReadVarUint() - if br.Err != nil { - return br.Err - } p.Addrs = make([]*AddressAndTime, listLen) for i := 0; i < int(listLen); i++ { p.Addrs[i] = &AddressAndTime{} - if err := p.Addrs[i].DecodeBinary(r); err != nil { - return err - } + p.Addrs[i].DecodeBinary(br) } - return nil } -// EncodeBinary implements the Payload interface. -func (p *AddressList) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (p *AddressList) EncodeBinary(bw *io.BinWriter) { 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 { - return err - } + addr.EncodeBinary(bw) } - return nil } diff --git a/pkg/network/payload/address_test.go b/pkg/network/payload/address_test.go index afece415a..bfd2f2ded 100644 --- a/pkg/network/payload/address_test.go +++ b/pkg/network/payload/address_test.go @@ -1,12 +1,12 @@ package payload import ( - "bytes" "fmt" "net" "testing" "time" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -15,7 +15,7 @@ func TestEncodeDecodeAddress(t *testing.T) { e, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:2000") ts = time.Now() addr = NewAddressAndTime(e, ts) - buf = new(bytes.Buffer) + buf = io.NewBufBinWriter() ) assert.Equal(t, ts.UTC().Unix(), int64(addr.Timestamp)) @@ -23,12 +23,14 @@ func TestEncodeDecodeAddress(t *testing.T) { copy(aatip, addr.IP[:]) assert.Equal(t, e.IP, aatip) assert.Equal(t, e.Port, int(addr.Port)) - err := addr.EncodeBinary(buf) - assert.Nil(t, err) + addr.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + r := io.NewBinReaderFromBuf(b) addrDecode := &AddressAndTime{} - err = addrDecode.DecodeBinary(buf) - assert.Nil(t, err) + addrDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, addr, addrDecode) } @@ -41,13 +43,15 @@ func TestEncodeDecodeAddressList(t *testing.T) { addrList.Addrs[i] = NewAddressAndTime(e, time.Now()) } - buf := new(bytes.Buffer) - err := addrList.EncodeBinary(buf) - assert.Nil(t, err) + buf := io.NewBufBinWriter() + addrList.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + r := io.NewBinReaderFromBuf(b) addrListDecode := &AddressList{} - err = addrListDecode.DecodeBinary(buf) - assert.Nil(t, err) + addrListDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, addrList, addrListDecode) } diff --git a/pkg/network/payload/getblocks.go b/pkg/network/payload/getblocks.go index 0e0a3419e..0029cb26c 100644 --- a/pkg/network/payload/getblocks.go +++ b/pkg/network/payload/getblocks.go @@ -1,8 +1,7 @@ package payload import ( - "io" - + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -22,25 +21,18 @@ func NewGetBlocks(start []util.Uint256, stop util.Uint256) *GetBlocks { } } -// DecodeBinary implements the payload interface. -func (p *GetBlocks) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (p *GetBlocks) DecodeBinary(br *io.BinReader) { lenStart := br.ReadVarUint() p.HashStart = make([]util.Uint256, lenStart) br.ReadLE(&p.HashStart) br.ReadLE(&p.HashStop) - return br.Err } -// EncodeBinary implements the payload interface. -func (p *GetBlocks) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (p *GetBlocks) EncodeBinary(bw *io.BinWriter) { bw.WriteVarUint(uint64(len(p.HashStart))) bw.WriteLE(p.HashStart) bw.WriteLE(p.HashStop) - return bw.Err } - -// Size implements the payload interface. -func (p *GetBlocks) Size() uint32 { return 0 } diff --git a/pkg/network/payload/getblocks_test.go b/pkg/network/payload/getblocks_test.go index e0745a3fb..dfa35bef0 100644 --- a/pkg/network/payload/getblocks_test.go +++ b/pkg/network/payload/getblocks_test.go @@ -1,10 +1,10 @@ package payload import ( - "bytes" "testing" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) @@ -18,13 +18,15 @@ func TestGetBlockEncodeDecode(t *testing.T) { } p := NewGetBlocks(start, util.Uint256{}) - buf := new(bytes.Buffer) - err := p.EncodeBinary(buf) - assert.Nil(t, err) + buf := io.NewBufBinWriter() + p.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + r := io.NewBinReaderFromBuf(b) pDecode := &GetBlocks{} - err = pDecode.DecodeBinary(buf) - assert.Nil(t, err) + pDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, p, pDecode) } @@ -39,12 +41,14 @@ func TestGetBlockEncodeDecodeWithHashStop(t *testing.T) { stop = hash.Sha256([]byte("e")) ) p := NewGetBlocks(start, stop) - buf := new(bytes.Buffer) - err := p.EncodeBinary(buf) - assert.Nil(t, err) + buf := io.NewBufBinWriter() + p.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + r := io.NewBinReaderFromBuf(b) pDecode := &GetBlocks{} - err = pDecode.DecodeBinary(buf) - assert.Nil(t, err) + pDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, p, pDecode) } diff --git a/pkg/network/payload/headers.go b/pkg/network/payload/headers.go index b0f45de9b..c85ecab2e 100644 --- a/pkg/network/payload/headers.go +++ b/pkg/network/payload/headers.go @@ -1,10 +1,8 @@ package payload import ( - "io" - "github.com/CityOfZion/neo-go/pkg/core" - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" log "github.com/sirupsen/logrus" ) @@ -18,13 +16,10 @@ const ( maxHeadersAllowed = 2000 ) -// DecodeBinary implements the Payload interface. -func (p *Headers) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (p *Headers) DecodeBinary(br *io.BinReader) { lenHeaders := br.ReadVarUint() - if br.Err != nil { - return br.Err - } + // C# node does it silently if lenHeaders > maxHeadersAllowed { log.Warnf("received %d headers, capping to %d", lenHeaders, maxHeadersAllowed) @@ -35,27 +30,16 @@ func (p *Headers) DecodeBinary(r io.Reader) error { for i := 0; i < int(lenHeaders); i++ { header := &core.Header{} - if err := header.DecodeBinary(r); err != nil { - return err - } + header.DecodeBinary(br) p.Hdrs[i] = header } - - return nil } -// EncodeBinary implements the Payload interface. -func (p *Headers) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (p *Headers) EncodeBinary(bw *io.BinWriter) { 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 - } + header.EncodeBinary(bw) } - return nil } diff --git a/pkg/network/payload/headers_test.go b/pkg/network/payload/headers_test.go index 7b762d4b8..3c421077b 100644 --- a/pkg/network/payload/headers_test.go +++ b/pkg/network/payload/headers_test.go @@ -1,12 +1,12 @@ package payload import ( - "bytes" "encoding/hex" "testing" "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -41,13 +41,15 @@ func TestHeadersEncodeDecode(t *testing.T) { }}, }} - buf := new(bytes.Buffer) - err := headers.EncodeBinary(buf) - assert.Nil(t, err) + buf := io.NewBufBinWriter() + headers.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + r := io.NewBinReaderFromBuf(b) headersDecode := &Headers{} - err = headersDecode.DecodeBinary(buf) - assert.Nil(t, err) + headersDecode.DecodeBinary(r) + assert.Nil(t, r.Err) for i := 0; i < len(headers.Hdrs); i++ { assert.Equal(t, headers.Hdrs[i].Version, headersDecode.Hdrs[i].Version) @@ -63,10 +65,10 @@ func TestBinEncodeDecode(t *testing.T) { rawBlockBytes, _ := hex.DecodeString(rawBlockHeaders) - r := bytes.NewReader(rawBlockBytes) + r := io.NewBinReaderFromBuf(rawBlockBytes) - err := headerMsg.DecodeBinary(r) - assert.Nil(t, err) + headerMsg.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, 1, len(headerMsg.Hdrs)) header := headerMsg.Hdrs[0] @@ -74,9 +76,9 @@ func TestBinEncodeDecode(t *testing.T) { assert.Equal(t, "f3c4ec44c07eccbda974f1ee34bc6654ab6d3f22cd89c2e5c593a16d6cc7e6e8", hash.ReverseString()) - buf := new(bytes.Buffer) + buf := io.NewBufBinWriter() - err = headerMsg.EncodeBinary(buf) - assert.Equal(t, nil, err) + headerMsg.EncodeBinary(buf.BinWriter) + assert.Equal(t, nil, buf.Err) assert.Equal(t, hex.EncodeToString(rawBlockBytes), hex.EncodeToString(buf.Bytes())) } diff --git a/pkg/network/payload/inventory.go b/pkg/network/payload/inventory.go index 012a9cf82..4ce9ca939 100644 --- a/pkg/network/payload/inventory.go +++ b/pkg/network/payload/inventory.go @@ -1,8 +1,7 @@ package payload import ( - "io" - + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -55,9 +54,8 @@ func NewInventory(typ InventoryType, hashes []util.Uint256) *Inventory { } } -// DecodeBinary implements the Payload interface. -func (p *Inventory) DecodeBinary(r io.Reader) error { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (p *Inventory) DecodeBinary(br *io.BinReader) { br.ReadLE(&p.Type) listLen := br.ReadVarUint() @@ -65,13 +63,10 @@ func (p *Inventory) DecodeBinary(r io.Reader) error { for i := 0; i < int(listLen); i++ { br.ReadLE(&p.Hashes[i]) } - - return br.Err } -// EncodeBinary implements the Payload interface. -func (p *Inventory) EncodeBinary(w io.Writer) error { - bw := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (p *Inventory) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(p.Type) listLen := len(p.Hashes) @@ -79,6 +74,4 @@ func (p *Inventory) EncodeBinary(w io.Writer) error { for i := 0; i < listLen; i++ { bw.WriteLE(p.Hashes[i]) } - - return bw.Err } diff --git a/pkg/network/payload/inventory_test.go b/pkg/network/payload/inventory_test.go index f9fffcc79..5e1d8ea92 100644 --- a/pkg/network/payload/inventory_test.go +++ b/pkg/network/payload/inventory_test.go @@ -1,10 +1,10 @@ package payload import ( - "bytes" "testing" "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" . "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) @@ -16,22 +16,24 @@ func TestInventoryEncodeDecode(t *testing.T) { } inv := NewInventory(BlockType, hashes) - buf := new(bytes.Buffer) - err := inv.EncodeBinary(buf) - assert.Nil(t, err) + buf := io.NewBufBinWriter() + inv.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + r := io.NewBinReaderFromBuf(b) invDecode := &Inventory{} - err = invDecode.DecodeBinary(buf) - assert.Nil(t, err) + invDecode.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, inv, invDecode) } func TestEmptyInv(t *testing.T) { msgInv := NewInventory(TXType, []Uint256{}) - buf := new(bytes.Buffer) - err := msgInv.EncodeBinary(buf) - assert.Nil(t, err) + buf := io.NewBufBinWriter() + msgInv.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) assert.Equal(t, []byte{byte(TXType), 0}, buf.Bytes()) assert.Equal(t, 0, len(msgInv.Hashes)) } diff --git a/pkg/network/payload/merkleblock.go b/pkg/network/payload/merkleblock.go index 3b1e90eae..996f32431 100644 --- a/pkg/network/payload/merkleblock.go +++ b/pkg/network/payload/merkleblock.go @@ -1,9 +1,8 @@ package payload import ( - "io" - "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -15,13 +14,10 @@ type MerkleBlock struct { Flags []byte } -// DecodeBinary implements the Payload interface. -func (m *MerkleBlock) DecodeBinary(r io.Reader) error { +// DecodeBinary implements Serializable interface. +func (m *MerkleBlock) DecodeBinary(br *io.BinReader) { m.BlockBase = &core.BlockBase{} - if err := m.BlockBase.DecodeBinary(r); err != nil { - return err - } - br := util.BinReader{R: r} + m.BlockBase.DecodeBinary(br) m.TxCount = int(br.ReadVarUint()) n := br.ReadVarUint() @@ -30,10 +26,17 @@ func (m *MerkleBlock) DecodeBinary(r io.Reader) error { br.ReadLE(&m.Hashes[i]) } m.Flags = br.ReadBytes() - return br.Err } -// EncodeBinary implements the Payload interface. -func (m *MerkleBlock) EncodeBinary(w io.Writer) error { - return nil +// EncodeBinary implements Serializable interface. +func (m *MerkleBlock) EncodeBinary(bw *io.BinWriter) { + m.BlockBase = &core.BlockBase{} + m.BlockBase.EncodeBinary(bw) + + bw.WriteVarUint(uint64(m.TxCount)) + bw.WriteVarUint(uint64(len(m.Hashes))) + for i := 0; i < len(m.Hashes); i++ { + bw.WriteLE(m.Hashes[i]) + } + bw.WriteBytes(m.Flags) } diff --git a/pkg/network/payload/payload.go b/pkg/network/payload/payload.go index e23f9955a..112008ffd 100644 --- a/pkg/network/payload/payload.go +++ b/pkg/network/payload/payload.go @@ -1,11 +1,10 @@ package payload -import "io" +import "github.com/CityOfZion/neo-go/pkg/io" // Payload is anything that can be binary encoded/decoded. type Payload interface { - EncodeBinary(io.Writer) error - DecodeBinary(io.Reader) error + io.Serializable } // NullPayload is a dummy payload with no fields. @@ -17,12 +16,8 @@ func NewNullPayload() *NullPayload { return &NullPayload{} } -// DecodeBinary implements the Payload interface. -func (p *NullPayload) DecodeBinary(r io.Reader) error { - return nil -} +// DecodeBinary implements Serializable interface. +func (p *NullPayload) DecodeBinary(r *io.BinReader) {} -// EncodeBinary implements the Payload interface. -func (p *NullPayload) EncodeBinary(r io.Writer) error { - return nil -} +// EncodeBinary implements Serializable interface. +func (p *NullPayload) EncodeBinary(w *io.BinWriter) {} diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index 8f050d9f7..1343298b2 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -1,10 +1,9 @@ package payload import ( - "io" "time" - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" ) // Size of the payload not counting UserAgent encoding (which is at least 1 byte @@ -54,9 +53,8 @@ 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 { - br := util.BinReader{R: r} +// DecodeBinary implements Serializable interface. +func (p *Version) DecodeBinary(br *io.BinReader) { br.ReadLE(&p.Version) br.ReadLE(&p.Services) br.ReadLE(&p.Timestamp) @@ -65,12 +63,10 @@ func (p *Version) DecodeBinary(r io.Reader) error { p.UserAgent = br.ReadBytes() br.ReadLE(&p.StartHeight) br.ReadLE(&p.Relay) - return br.Err } -// EncodeBinary implements the Payload interface. -func (p *Version) EncodeBinary(w io.Writer) error { - br := util.BinWriter{W: w} +// EncodeBinary implements Serializable interface. +func (p *Version) EncodeBinary(br *io.BinWriter) { br.WriteLE(p.Version) br.WriteLE(p.Services) br.WriteLE(p.Timestamp) @@ -80,10 +76,4 @@ func (p *Version) EncodeBinary(w io.Writer) error { br.WriteBytes(p.UserAgent) br.WriteLE(p.StartHeight) br.WriteLE(&p.Relay) - return br.Err -} - -// Size implements the payloader interface. -func (p *Version) Size() uint32 { - return uint32(minVersionSize + util.GetVarSize(p.UserAgent)) } diff --git a/pkg/network/payload/version_test.go b/pkg/network/payload/version_test.go index ca4ed156d..4af2e2425 100644 --- a/pkg/network/payload/version_test.go +++ b/pkg/network/payload/version_test.go @@ -1,9 +1,9 @@ package payload import ( - "bytes" "testing" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -16,14 +16,16 @@ func TestVersionEncodeDecode(t *testing.T) { version := NewVersion(id, port, useragent, height, relay) - buf := new(bytes.Buffer) - err := version.EncodeBinary(buf) - assert.Nil(t, err) - assert.Equal(t, int(version.Size()), buf.Len()) + buf := io.NewBufBinWriter() + version.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + b := buf.Bytes() + assert.Equal(t, io.GetVarSize(version), len(b)) + r := io.NewBinReaderFromBuf(b) versionDecoded := &Version{} - err = versionDecoded.DecodeBinary(buf) - assert.Nil(t, err) + versionDecoded.DecodeBinary(r) + assert.Nil(t, r.Err) assert.Equal(t, versionDecoded.Nonce, id) assert.Equal(t, versionDecoded.Port, port) assert.Equal(t, versionDecoded.UserAgent, []byte(useragent)) diff --git a/pkg/network/tcp_peer.go b/pkg/network/tcp_peer.go index 10dac0a82..a9a5b5a27 100644 --- a/pkg/network/tcp_peer.go +++ b/pkg/network/tcp_peer.go @@ -6,6 +6,7 @@ import ( "net" "sync" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/network/payload" ) @@ -68,7 +69,8 @@ func (p *TCPPeer) writeMsg(msg *Message) error { case err := <-p.done: return err default: - return msg.Encode(p.conn) + w := io.NewBinWriterFromIO(p.conn) + return msg.Encode(w) } } diff --git a/pkg/network/tcp_transport.go b/pkg/network/tcp_transport.go index 8f61b7607..b99c060dd 100644 --- a/pkg/network/tcp_transport.go +++ b/pkg/network/tcp_transport.go @@ -5,6 +5,7 @@ import ( "regexp" "time" + "github.com/CityOfZion/neo-go/pkg/io" log "github.com/sirupsen/logrus" ) @@ -77,9 +78,10 @@ func (t *TCPTransport) handleConn(conn net.Conn) { t.server.register <- p + r := io.NewBinReaderFromIO(p.conn) for { msg := &Message{} - if err = msg.Decode(p.conn); err != nil { + if err = msg.Decode(r); err != nil { break } if err = t.server.handleMessage(p, msg); err != nil { diff --git a/pkg/rpc/rpc.go b/pkg/rpc/rpc.go index 73d62c421..fae42b549 100644 --- a/pkg/rpc/rpc.go +++ b/pkg/rpc/rpc.go @@ -1,10 +1,10 @@ package rpc import ( - "bytes" "encoding/hex" "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" "github.com/pkg/errors" @@ -113,7 +113,7 @@ func (c *Client) sendRawTransaction(rawTX string) (*response, error) { func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.Fixed8) (*SendToAddressResponse, error) { var ( err error - buf = &bytes.Buffer{} + buf = io.NewBufBinWriter() rawTx *transaction.Transaction rawTxStr string txParams = ContractTxParams{ @@ -130,8 +130,9 @@ func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.F if rawTx, err = CreateRawContractTransaction(txParams); err != nil { return nil, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`") } - if err = rawTx.EncodeBinary(buf); err != nil { - return nil, errors.Wrap(err, "failed to encode raw transaction to binary for `sendtoaddress`") + rawTx.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return nil, errors.Wrap(buf.Err, "failed to encode raw transaction to binary for `sendtoaddress`") } rawTxStr = hex.EncodeToString(buf.Bytes()) if resp, err = c.sendRawTransaction(rawTxStr); err != nil { diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index f8fcb836b..9d5070e61 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -1,7 +1,6 @@ package rpc import ( - "bytes" "context" "encoding/hex" "fmt" @@ -11,6 +10,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/network" "github.com/CityOfZion/neo-go/pkg/rpc/result" "github.com/CityOfZion/neo-go/pkg/rpc/wrappers" @@ -294,29 +294,29 @@ func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) { } else if byteTx, err := hex.DecodeString(param.StringVal); err != nil { resultsErr = errInvalidParams } else { - r := bytes.NewReader(byteTx) + r := io.NewBinReaderFromBuf(byteTx) tx := &transaction.Transaction{} - err = tx.DecodeBinary(r) - if err != nil { - err = errors.Wrap(err, "transaction DecodeBinary failed") - } - relayReason := s.coreServer.RelayTxn(tx) - switch relayReason { - case network.RelaySucceed: - results = true - case network.RelayAlreadyExists: - err = errors.New("block or transaction already exists and cannot be sent repeatedly") - case network.RelayOutOfMemory: - err = errors.New("the memory pool is full and no more transactions can be sent") - case network.RelayUnableToVerify: - err = errors.New("the block cannot be validated") - case network.RelayInvalid: - err = errors.New("block or transaction validation failed") - case network.RelayPolicyFail: - err = errors.New("one of the Policy filters failed") - default: - err = errors.New("unknown error") - + tx.DecodeBinary(r) + if r.Err != nil { + err = errors.Wrap(r.Err, "transaction DecodeBinary failed") + } else { + relayReason := s.coreServer.RelayTxn(tx) + switch relayReason { + case network.RelaySucceed: + results = true + case network.RelayAlreadyExists: + err = errors.New("block or transaction already exists and cannot be sent repeatedly") + case network.RelayOutOfMemory: + err = errors.New("the memory pool is full and no more transactions can be sent") + case network.RelayUnableToVerify: + err = errors.New("the block cannot be validated") + case network.RelayInvalid: + err = errors.New("block or transaction validation failed") + case network.RelayPolicyFail: + err = errors.New("one of the Policy filters failed") + default: + err = errors.New("unknown error") + } } if err != nil { resultsErr = NewInternalServerError(err.Error(), err) diff --git a/pkg/rpc/txBuilder.go b/pkg/rpc/txBuilder.go index 3109c1424..72a2298af 100644 --- a/pkg/rpc/txBuilder.go +++ b/pkg/rpc/txBuilder.go @@ -1,11 +1,10 @@ package rpc import ( - "bytes" - "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" errs "github.com/pkg/errors" ) @@ -71,11 +70,12 @@ func GetInvocationScript(tx *transaction.Transaction, wif keys.WIF) ([]byte, err ) var ( err error - buf = new(bytes.Buffer) + buf = io.NewBufBinWriter() signature []byte ) - if err = tx.EncodeBinary(buf); err != nil { - return nil, errs.Wrap(err, "Failed to encode transaction to binary") + tx.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return nil, errs.Wrap(buf.Err, "Failed to encode transaction to binary") } data := buf.Bytes() signature, err = wif.PrivateKey.Sign(data[:(len(data) - 1)]) diff --git a/pkg/rpc/wrappers/tx_raw_output.go b/pkg/rpc/wrappers/tx_raw_output.go index d3ebbd33a..7a8a6fcf7 100644 --- a/pkg/rpc/wrappers/tx_raw_output.go +++ b/pkg/rpc/wrappers/tx_raw_output.go @@ -3,6 +3,7 @@ package wrappers import ( "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -30,7 +31,7 @@ func NewTransactionOutputRaw(tx *transaction.Transaction, header *core.Header, c return TransactionOutputRaw{ Transaction: tx, TxHash: tx.Hash(), - Size: tx.Size(), + Size: io.GetVarSize(tx), SysFee: chain.SystemFee(tx), NetFee: chain.NetworkFee(tx), Blockhash: header.Hash(), diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go index 37c76d575..1cca3d229 100644 --- a/pkg/smartcontract/contract_test.go +++ b/pkg/smartcontract/contract_test.go @@ -1,11 +1,10 @@ package smartcontract import ( - "bytes" "testing" "github.com/CityOfZion/neo-go/pkg/crypto/keys" - "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/vm" "github.com/stretchr/testify/assert" ) @@ -22,8 +21,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) { t.Fatal(err) } - buf := bytes.NewBuffer(out) - br := util.BinReader{R: buf} + br := io.NewBinReaderFromBuf(out) var b uint8 br.ReadLE(&b) assert.Equal(t, vm.PUSH3, vm.Instruction(b)) diff --git a/pkg/util/binaryrw_test.go b/pkg/util/binaryrw_test.go deleted file mode 100644 index 7bf320f20..000000000 --- a/pkg/util/binaryrw_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package util - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestWriteVarUint1(t *testing.T) { - var ( - val = uint64(1) - buf = new(bytes.Buffer) - ) - bw := BinWriter{W: buf} - bw.WriteVarUint(val) - assert.Nil(t, bw.Err) - assert.Equal(t, 1, buf.Len()) -} - -func TestWriteVarUint1000(t *testing.T) { - var ( - val = uint64(1000) - buf = new(bytes.Buffer) - ) - 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]) - br := BinReader{R: buf} - res := br.ReadVarUint() - assert.Nil(t, br.Err) - assert.Equal(t, val, res) -} - -func TestWriteVarUint100000(t *testing.T) { - var ( - val = uint64(100000) - buf = new(bytes.Buffer) - ) - 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]) - br := BinReader{R: buf} - res := br.ReadVarUint() - assert.Nil(t, br.Err) - assert.Equal(t, val, res) -} - -func TestWriteVarUint100000000000(t *testing.T) { - var ( - val = uint64(1000000000000) - buf = new(bytes.Buffer) - ) - 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]) - br := BinReader{R: buf} - res := br.ReadVarUint() - assert.Nil(t, br.Err) - assert.Equal(t, val, res) -} diff --git a/pkg/util/fixed8.go b/pkg/util/fixed8.go index ec7b96d61..1364a0601 100644 --- a/pkg/util/fixed8.go +++ b/pkg/util/fixed8.go @@ -104,11 +104,6 @@ func (f *Fixed8) UnmarshalJSON(data []byte) error { return nil } -// Size returns the size in number of bytes of Fixed8. -func (f *Fixed8) Size() int { - return 8 -} - // MarshalJSON implements the json marshaller interface. func (f Fixed8) MarshalJSON() ([]byte, error) { return []byte(`"` + f.String() + `"`), nil diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go index 996bd9334..8c9202935 100644 --- a/pkg/util/uint160.go +++ b/pkg/util/uint160.go @@ -70,11 +70,6 @@ func (u *Uint160) UnmarshalJSON(data []byte) (err error) { return err } -// Size returns the length of the bytes representation of Uint160 -func (u Uint160) Size() int { - return uint160Size -} - // MarshalJSON implements the json marshaller interface. func (u Uint160) MarshalJSON() ([]byte, error) { return []byte(`"0x` + u.String() + `"`), nil diff --git a/pkg/util/uint256.go b/pkg/util/uint256.go index 0dbad3f89..4de10b135 100644 --- a/pkg/util/uint256.go +++ b/pkg/util/uint256.go @@ -82,11 +82,6 @@ func (u *Uint256) UnmarshalJSON(data []byte) (err error) { return err } -// Size returns the length of the bytes representation of Uint256 -func (u Uint256) Size() int { - return uint256Size -} - // MarshalJSON implements the json marshaller interface. func (u Uint256) MarshalJSON() ([]byte, error) { return []byte(`"0x` + u.ReverseString() + `"`), nil