Merge pull request #400 from nspcc-dev/move-rw-to-io

Move BinReader/BinWriter to io and simplify things

BinReader and BinWriter don't belong to util, actually util shouldn't
exist at all. Moving them to io and using them for all encoding/decoding
purposes allows to simplify a lot of code, especially in the error
handling space. These interfaces are designed to absorb errors until
someone can do something meaningful with them (usually that's the top
caller of encode/decode functions) and our current use of them is
inconsistent.

This patchset moves BinReader/BinWriter and size calculations (that
are mostly about Serializable things) to io package and makes all the
other code use them for encoding/decoding purposes.
This commit is contained in:
Roman Khimov 2019-09-17 15:08:15 +03:00 committed by GitHub
commit 39a024eb03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 981 additions and 1189 deletions

View file

@ -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.

View file

@ -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)

View file

@ -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.

View file

@ -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)
}

View file

@ -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
}

View file

@ -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()
}
}

View file

@ -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))
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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))
}

View file

@ -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 {

View file

@ -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")
}

View file

@ -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

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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) {
}

View file

@ -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()))
}

View file

@ -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)
}

View file

@ -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()))
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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) {
}

View file

@ -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)
}

View file

@ -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()))
}

View file

@ -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

View file

@ -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
}

View file

@ -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()
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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()))
}

View file

@ -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()
}

View file

@ -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()))
}

View file

@ -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
}

View file

@ -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)

View file

@ -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 {

View file

@ -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) {

View file

@ -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
}

View file

@ -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.

View file

@ -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)
}
}

39
pkg/io/binaryBufWriter.go Normal file
View file

@ -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()
}

View file

@ -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
}

View file

@ -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)
}

193
pkg/io/binaryrw_test.go Normal file
View file

@ -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)
}

View file

@ -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)
}

View file

@ -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

View file

@ -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{})
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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 }

View file

@ -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)
}

View file

@ -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
}

View file

@ -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()))
}

View file

@ -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
}

View file

@ -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))
}

View file

@ -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)
}

View file

@ -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) {}

View file

@ -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))
}

View file

@ -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))

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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)

View file

@ -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)])

View file

@ -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(),

View file

@ -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))

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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