pkg: hide it by moving to _pkg.dev

The idea here is to preserve the history of `dev` branch development and its
code when merging with the `master`. Later this code could be moved into the
masters code where appropriate.
This commit is contained in:
Roman Khimov 2019-08-20 18:39:50 +03:00
parent bb2568cc53
commit ddd1d92ff1
183 changed files with 0 additions and 0 deletions

62
_pkg.dev/wire/Readme.md Normal file
View file

@ -0,0 +1,62 @@
# Package - Wire
The neo wire package will implement the network protocol displayed here: http://docs.neo.org/en-us/network/network-protocol.html
This package will act as a standalone package.
# Responsibility
This package will solely be responsible for Encoding and decoding a Message.
It will return a Messager interface, which means that the caller of the package will need to type assert it to the appropriate type.
# Usage
## Write Message
expectedIP := "127.0.0.1"
expectedPort := 8333
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP(expectedIP), Port: expectedPort}
message, err := NewVersionMessage(tcpAddrMe, 0, true, defaultVersion)
conn := new(bytes.Buffer)
if err := WriteMessage(con, Production, message); err != nil {
// handle Error
}
## Read Message
readmsg, err := ReadMessage(conn, Production)
if err != nil {
// Log error
}
version := readmsg.(*VersionMessage)
## RoadMap
These below commands are left to implement.
[ x ] CMDVersion (Tests added)
[ x ] CMDVerack (Tests Added)
[ x ] CMDGetAddr(Tests Added)
[ x ] CMDAddr (Tests Added)
[ x ] CMDGetHeaders (Tests Added)
[ x ] CMDHeaders (Tests Added)
[ x ] CMDGetBlocks (Tests Added)
[ x ] CMDInv (Tests Added)
[ x ] CMDGetData (Tests Added)
[ x ] CMDBlock (Tests Added)
[ x ] CMDTX // Each tx implments the messager interface
[ ] CMDConsensus
## Notes
Please not that this package will do sanity checks on the fields, however it will not verify if any of the items are valid for the current state of the system. Please see `Responbilities`.
The difference between Encode/Decode and EncodePayload/DecodePayload, is the parameter type.
In most cases, Encode/Decode is just a convenience method.
# Contributors
When modifying this package, please ensure that it does not depend on any other package and that it conforms to the Single Responsibility Principle. If you see somewhere in the current implementation that does not do this, then please tell me.

42
_pkg.dev/wire/base.go Normal file
View file

@ -0,0 +1,42 @@
package wire
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// Base is everything in the message except the payload
type Base struct {
Magic uint32
CMD command.Type
PayloadLength uint32
Checksum uint32
}
// DecodeBase will decode an io.Reader into a Base object
// Note, That there is no EncodeBase, As the header is implicitly inferred from the message on Encode To send
func (h *Base) DecodeBase(r io.Reader) (io.Reader, error) {
br := &util.BinReader{R: r}
br.Read(&h.Magic)
var cmd [12]byte
br.Read(&cmd)
h.CMD = command.Type(cmdByteArrayToString(cmd))
br.Read(&h.PayloadLength)
br.Read(&h.Checksum)
return br.R, br.Err
}
func cmdByteArrayToString(cmd [command.Size]byte) string {
buf := []byte{}
for i := 0; i < command.Size; i++ {
if cmd[i] != 0 {
buf = append(buf, cmd[i])
}
}
return string(buf)
}

View file

@ -0,0 +1,29 @@
package command
// Size of command field in bytes
const (
Size = 12
)
// Type represents the type of a message command.
type Type string
// Valid protocol commands used to send between nodes.
const (
Version Type = "version"
Mempool Type = "mempool"
Ping Type = "ping"
Pong Type = "pong"
Verack Type = "verack"
GetAddr Type = "getaddr"
Addr Type = "addr"
GetHeaders Type = "getheaders"
Headers Type = "headers"
GetBlocks Type = "getblocks"
Inv Type = "inv"
GetData Type = "getdata"
Block Type = "block"
TX Type = "tx"
Consensus Type = "consensus"
Unknown Type = "unknown"
)

156
_pkg.dev/wire/message.go Normal file
View file

@ -0,0 +1,156 @@
package wire
import (
"bufio"
"bytes"
"errors"
"io"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
checksum "github.com/CityOfZion/neo-go/pkg/wire/util/Checksum"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/payload"
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// Messager is implemented by any object that can
// Encode and Decode Payloads
type Messager interface {
// EncodePayload takes a message payload and encodes it
EncodePayload(w io.Writer) error
// DecodePayload takes an io.Reader and decodes it into
// a message payload
DecodePayload(r io.Reader) error
// Command returns the assosciated command type
Command() command.Type
}
const (
// Magic + cmd + length + checksum
minMsgSize = 4 + 12 + 4 + 4
)
var (
errChecksumMismatch = errors.New("checksum mismatch")
)
// WriteMessage will write a message to a given io.Writer
func WriteMessage(w io.Writer, magic protocol.Magic, message Messager) error {
bw := &util.BinWriter{W: w}
bw.Write(magic)
bw.Write(cmdToByteArray(message.Command()))
buf := new(bytes.Buffer)
if err := message.EncodePayload(buf); err != nil {
return err
}
payloadLen := util.BufferLength(buf)
checksum := checksum.FromBytes(buf.Bytes())
bw.Write(payloadLen)
bw.Write(checksum)
bw.WriteBigEnd(buf.Bytes())
return bw.Err
}
// ReadMessage will read a message from a given io.Reader
func ReadMessage(r io.Reader, magic protocol.Magic) (Messager, error) {
byt := make([]byte, minMsgSize)
if _, err := io.ReadFull(r, byt); err != nil {
return nil, err
}
reader := bytes.NewReader(byt)
var header Base
_, err := header.DecodeBase(reader)
if err != nil {
return nil, errors.New("Error decoding into the header base")
}
buf := new(bytes.Buffer)
_, err = io.CopyN(buf, r, int64(header.PayloadLength))
if err != nil {
return nil, err
}
// Compare the checksum of the payload.
if !checksum.Compare(header.Checksum, buf.Bytes()) {
return nil, errChecksumMismatch
}
switch header.CMD {
case command.Version:
v := &payload.VersionMessage{}
err := v.DecodePayload(buf)
return v, err
case command.Verack:
v, err := payload.NewVerackMessage()
err = v.DecodePayload(buf)
return v, err
case command.Inv:
v, err := payload.NewInvMessage(0)
err = v.DecodePayload(buf)
return v, err
case command.GetAddr:
v, err := payload.NewGetAddrMessage()
err = v.DecodePayload(buf)
return v, err
case command.Addr:
v, err := payload.NewAddrMessage()
err = v.DecodePayload(buf)
return v, err
case command.Block:
v, err := payload.NewBlockMessage()
err = v.DecodePayload(buf)
return v, err
case command.GetBlocks:
v, err := payload.NewGetBlocksMessage([]util.Uint256{}, util.Uint256{})
err = v.DecodePayload(buf)
return v, err
case command.GetData:
v, err := payload.NewGetDataMessage(payload.InvTypeTx)
err = v.DecodePayload(buf)
return v, err
case command.GetHeaders:
v, err := payload.NewGetHeadersMessage([]util.Uint256{}, util.Uint256{})
err = v.DecodePayload(buf)
return v, err
case command.Headers:
v, err := payload.NewHeadersMessage()
err = v.DecodePayload(buf)
return v, err
case command.TX:
reader := bufio.NewReader(buf)
tx, err := transaction.FromReader(reader)
if err != nil {
return nil, err
}
return payload.NewTXMessage(tx)
}
return nil, errors.New("Unknown Message found")
}
func cmdToByteArray(cmd command.Type) [command.Size]byte {
cmdLen := len(cmd)
if cmdLen > command.Size {
panic("exceeded command max length of size 12")
}
// The command can have max 12 bytes, rest is filled with 0.
b := [command.Size]byte{}
for i := 0; i < cmdLen; i++ {
b[i] = cmd[i]
}
return b
}

View file

@ -0,0 +1,55 @@
package wire
import (
"bytes"
"net"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload"
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
"github.com/stretchr/testify/assert"
)
// This is quite hard to test because the message uses time.Now()
// TODO: Test each field expect time.Now(), just make sure it is a uint32
func TestWriteMessageLen(t *testing.T) {
expectedIP := "127.0.0.1"
expectedPort := 8333
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP(expectedIP), Port: expectedPort}
message, err := payload.NewVersionMessage(tcpAddrMe, 0, true, protocol.DefaultVersion, protocol.UserAgent, 100, protocol.NodePeerService)
if err != nil {
assert.Fail(t, err.Error())
}
buf := new(bytes.Buffer)
if err := WriteMessage(buf, protocol.MainNet, message); err != nil {
assert.Fail(t, err.Error())
}
assert.Equal(t, 60, len(buf.Bytes()))
}
func TestReadMessage(t *testing.T) {
expectedIP := "127.0.0.1"
expectedPort := 8333
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP(expectedIP), Port: expectedPort}
message, err := payload.NewVersionMessage(tcpAddrMe, 23, true, protocol.DefaultVersion, protocol.UserAgent, 100, protocol.NodePeerService)
if err != nil {
assert.Fail(t, err.Error())
}
buf := new(bytes.Buffer)
if err := WriteMessage(buf, protocol.MainNet, message); err != nil {
assert.Fail(t, err.Error())
}
readmsg, err := ReadMessage(buf, protocol.MainNet)
if err != nil {
assert.Fail(t, err.Error())
}
version := readmsg.(*payload.VersionMessage)
assert.Equal(t, 23, int(version.StartHeight))
// If MessageReading was unsuccessfull it will return a nil object
}

View file

@ -0,0 +1,67 @@
package payload
import (
"bufio"
"bytes"
"io"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// Block representa a Block in the neo-network
type Block struct {
BlockBase
Txs []transaction.Transactioner
}
// Decode decodes an io.Reader into a Block
func (b *Block) Decode(r io.Reader) error {
br := &util.BinReader{R: r}
b.DecodePayload(br)
return br.Err
}
// Encode writes a block into a io.Writer
func (b *Block) Encode(w io.Writer) error {
bw := &util.BinWriter{W: w}
b.EncodePayload(bw)
return bw.Err
}
//EncodePayload implements Messager interface
func (b *Block) EncodePayload(bw *util.BinWriter) {
b.BlockBase.EncodePayload(bw)
bw.VarUint(uint64(len(b.Txs)))
for _, tx := range b.Txs {
tx.Encode(bw.W)
}
}
// DecodePayload implements Messager interface
func (b *Block) DecodePayload(br *util.BinReader) error {
b.BlockBase.DecodePayload(br)
lenTXs := br.VarUint()
b.Txs = make([]transaction.Transactioner, lenTXs)
reader := bufio.NewReader(br.R)
for i := 0; i < int(lenTXs); i++ {
tx, err := transaction.FromReader(reader)
if err != nil {
return err
}
b.Txs[i] = tx
}
return nil
}
// Bytes returns the Byte representation of Block
func (b *Block) Bytes() ([]byte, error) {
buf := new(bytes.Buffer)
err := b.Encode(buf)
return buf.Bytes(), err
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,132 @@
package payload
import (
"bytes"
"errors"
"io"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
var (
errPadding = errors.New("There is a padding mismatch")
)
//BlockBase represents the base of the block
// This is different than the block header. See HeadersMessage
type BlockBase struct {
// Version of the block.
Version uint32 `json:"version"`
// hash of the previous block.
PrevHash util.Uint256 `json:"previousblockhash"`
// Root hash of a transaction list.
MerkleRoot util.Uint256 `json:"merkleroot"`
// The time stamp of each block must be later than previous block's time stamp.
// Generally the difference of two block's time stamp is about 15 seconds and imprecision is allowed.
// The height of the block must be exactly equal to the height of the previous block plus 1.
Timestamp uint32 `json:"time"`
// index/height of the block
Index uint32 `json:"height"`
// Random number also called nonce
ConsensusData uint64 `json:"nonce"`
// Contract addresss of the next miner
NextConsensus util.Uint160 `json:"next_consensus"`
// Padding that is fixed to 1
_ uint8
// Script used to validate the block
Witness transaction.Witness `json:"script"`
// hash of this block, created when binary encoded.
Hash util.Uint256
}
// EncodePayload implements the Messager interface
func (b *BlockBase) EncodePayload(bw *util.BinWriter) error {
b.encodeHashableFields(bw)
bw.Write(uint8(1))
b.Witness.Encode(bw)
return bw.Err
}
// Decode decodes an io.Reader into a Blockbase
func (b *BlockBase) Decode(r io.Reader) error {
br := &util.BinReader{R: r}
b.DecodePayload(br)
return br.Err
}
// Encode encodes a blockbase into an io.Writer
func (b *BlockBase) Encode(w io.Writer) error {
bw := &util.BinWriter{W: w}
b.EncodePayload(bw)
return bw.Err
}
func (b *BlockBase) encodeHashableFields(bw *util.BinWriter) {
bw.Write(b.Version)
bw.Write(b.PrevHash)
bw.Write(b.MerkleRoot)
bw.Write(b.Timestamp)
bw.Write(b.Index)
bw.Write(b.ConsensusData)
bw.Write(b.NextConsensus)
}
// DecodePayload implements the messager interface
func (b *BlockBase) DecodePayload(br *util.BinReader) error {
b.decodeHashableFields(br)
var padding uint8
br.Read(&padding)
if padding != 1 {
return errPadding
}
b.Witness = transaction.Witness{}
b.Witness.Decode(br)
err := b.createHash()
if err != nil {
return err
}
return br.Err
}
func (b *BlockBase) decodeHashableFields(br *util.BinReader) {
br.Read(&b.Version)
br.Read(&b.PrevHash)
br.Read(&b.MerkleRoot)
br.Read(&b.Timestamp)
br.Read(&b.Index)
br.Read(&b.ConsensusData)
br.Read(&b.NextConsensus)
}
func (b *BlockBase) createHash() error {
hash, err := util.CalculateHash(b.encodeHashableFields)
b.Hash = hash
return err
}
// Bytes returns the byte representation of Blockbase
func (b *BlockBase) Bytes() ([]byte, error) {
buf := new(bytes.Buffer)
err := b.Encode(buf)
return buf.Bytes(), err
}

View file

@ -0,0 +1,8 @@
package payload
import "testing"
func Test(t *testing.T) {
//tests for this have been included in the mheaders_test file
}

View file

@ -0,0 +1,64 @@
package payload
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// AddrMessage represents an address message on the neo network
type AddrMessage struct {
AddrList []*NetAddr
}
// NewAddrMessage instantiates a new AddrMessage
func NewAddrMessage() (*AddrMessage, error) {
addrMess := &AddrMessage{
nil,
}
return addrMess, nil
}
// AddNetAddr will add a net address into the Address message
func (a *AddrMessage) AddNetAddr(n *NetAddr) error {
a.AddrList = append(a.AddrList, n)
// TODO:check if max reached, if so return err. What is max?
return nil
}
// DecodePayload Implements Messager interface
func (a *AddrMessage) DecodePayload(r io.Reader) error {
br := &util.BinReader{R: r}
listLen := br.VarUint()
a.AddrList = make([]*NetAddr, listLen)
for i := 0; i < int(listLen); i++ {
a.AddrList[i] = &NetAddr{}
a.AddrList[i].DecodePayload(br)
if br.Err != nil {
return br.Err
}
}
return br.Err
}
// EncodePayload Implements messager interface
func (a *AddrMessage) EncodePayload(w io.Writer) error {
bw := &util.BinWriter{W: w}
listLen := uint64(len(a.AddrList))
bw.VarUint(listLen)
for _, addr := range a.AddrList {
addr.EncodePayload(bw)
}
return bw.Err
}
// Command Implements messager interface
func (a *AddrMessage) Command() command.Type {
return command.Addr
}

View file

@ -0,0 +1,40 @@
package payload
import (
"bytes"
"net"
"testing"
"time"
"github.com/CityOfZion/neo-go/pkg/wire/util/Checksum"
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
"github.com/stretchr/testify/assert"
)
func TestAddrMessageEncodeDecode(t *testing.T) {
ip := []byte(net.ParseIP("127.0.0.1").To16())
var ipByte [16]byte
copy(ipByte[:], ip)
netaddr, err := NewNetAddr(uint32(time.Now().Unix()), ipByte, 8080, protocol.NodePeerService)
addrmsg, err := NewAddrMessage()
addrmsg.AddNetAddr(netaddr)
buf := new(bytes.Buffer)
err = addrmsg.EncodePayload(buf)
expected := checksum.FromBuf(buf)
addrmsgDec, err := NewAddrMessage()
r := bytes.NewReader(buf.Bytes())
err = addrmsgDec.DecodePayload(r)
buf = new(bytes.Buffer)
err = addrmsgDec.EncodePayload(buf)
have := checksum.FromBuf(buf)
assert.Equal(t, nil, err)
assert.Equal(t, expected, have)
}

View file

@ -0,0 +1,38 @@
package payload
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/util"
"github.com/CityOfZion/neo-go/pkg/wire/command"
)
// BlockMessage represents a block message on the neo-network
type BlockMessage struct {
Block
}
// NewBlockMessage will return a block message object
func NewBlockMessage() (*BlockMessage, error) {
return &BlockMessage{}, nil
}
// DecodePayload Implements Messager interface
func (b *BlockMessage) DecodePayload(r io.Reader) error {
br := &util.BinReader{R: r}
b.Block.DecodePayload(br)
return br.Err
}
// EncodePayload Implements messager interface
func (b *BlockMessage) EncodePayload(w io.Writer) error {
bw := &util.BinWriter{W: w}
b.Block.EncodePayload(bw)
return bw.Err
}
// Command Implements messager interface
func (b *BlockMessage) Command() command.Type {
return command.Block
}

View file

@ -0,0 +1,30 @@
package payload
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
)
//GetAddrMessage represents a GetAddress message on the neo-network
type GetAddrMessage struct{}
// NewGetAddrMessage returns a GetAddrMessage object
func NewGetAddrMessage() (*GetAddrMessage, error) {
return &GetAddrMessage{}, nil
}
// DecodePayload Implements Messager interface
func (v *GetAddrMessage) DecodePayload(r io.Reader) error {
return nil
}
// EncodePayload Implements messager interface
func (v *GetAddrMessage) EncodePayload(w io.Writer) error {
return nil
}
// Command Implements messager interface
func (v *GetAddrMessage) Command() command.Type {
return command.GetAddr
}

View file

@ -0,0 +1,24 @@
package payload
import (
"bytes"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/util/Checksum"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/stretchr/testify/assert"
)
func TestNewGetAddr(t *testing.T) {
getAddrMessage, err := NewGetAddrMessage()
assert.Equal(t, nil, err)
assert.Equal(t, command.GetAddr, getAddrMessage.Command())
buf := new(bytes.Buffer)
assert.Equal(t, int(3806393949), int(checksum.FromBuf(buf)))
assert.Equal(t, int(0), len(buf.Bytes()))
}

View file

@ -0,0 +1,24 @@
package payload
import (
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// GetBlocksMessage represnts a GetBlocks message on the neo-network
type GetBlocksMessage struct {
*GetHeadersMessage
}
// NewGetBlocksMessage returns a GetBlocksMessage object
func NewGetBlocksMessage(start []util.Uint256, stop util.Uint256) (*GetBlocksMessage, error) {
GetHeaders, err := newAbstractGetHeaders(start, stop, command.GetBlocks)
if err != nil {
return nil, err
}
return &GetBlocksMessage{
GetHeaders,
}, nil
}

View file

@ -0,0 +1,27 @@
package payload
import (
"crypto/sha256"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
"github.com/stretchr/testify/assert"
)
func TestGetBlocksCommandType(t *testing.T) {
var (
start = []util.Uint256{
sha256.Sum256([]byte("a")),
sha256.Sum256([]byte("b")),
sha256.Sum256([]byte("c")),
sha256.Sum256([]byte("d")),
}
stop = sha256.Sum256([]byte("e"))
)
getBlocks, err := NewGetBlocksMessage(start, stop)
assert.Equal(t, err, nil)
assert.Equal(t, command.GetBlocks, getBlocks.Command())
}

View file

@ -0,0 +1,18 @@
package payload
import (
"github.com/CityOfZion/neo-go/pkg/wire/command"
)
// GetDataMessage represents a GetData message on the neo-network
type GetDataMessage struct {
*InvMessage
}
//NewGetDataMessage returns a GetDataMessage object
func NewGetDataMessage(typ InvType) (*GetDataMessage, error) {
getData, err := newAbstractInv(typ, command.GetData)
return &GetDataMessage{
getData,
}, err
}

View file

@ -0,0 +1,19 @@
package payload
import (
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/stretchr/testify/assert"
)
func TestGetDataCommandType(t *testing.T) {
getData, err := NewGetDataMessage(InvTypeBlock)
assert.Equal(t, err, nil)
assert.Equal(t, command.GetData, getData.Command())
}
func TestOtherFunctions(t *testing.T) {
// Other capabilities are tested in the inherited struct
}

View file

@ -0,0 +1,61 @@
package payload
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//GetHeadersMessage represents a GetHeaders message on the neo-network
type GetHeadersMessage struct {
cmd command.Type
hashStart []util.Uint256
hashStop util.Uint256
}
// NewGetHeadersMessage returns a NewGetHeaders object
// Start contains the list of all headers you want to fetch
// End contains the list of the highest header hash you would like to fetch
func NewGetHeadersMessage(start []util.Uint256, stop util.Uint256) (*GetHeadersMessage, error) {
getHeaders := &GetHeadersMessage{command.GetHeaders, start, stop}
return getHeaders, nil
}
func newAbstractGetHeaders(start []util.Uint256, stop util.Uint256, cmd command.Type) (*GetHeadersMessage, error) {
getHeaders, err := NewGetHeadersMessage(start, stop)
if err != nil {
return nil, err
}
getHeaders.cmd = cmd
return getHeaders, nil
}
// DecodePayload Implements Messager interface
func (v *GetHeadersMessage) DecodePayload(r io.Reader) error {
br := util.BinReader{R: r}
lenStart := br.VarUint()
v.hashStart = make([]util.Uint256, lenStart)
br.Read(&v.hashStart)
br.Read(&v.hashStop)
return br.Err
}
// EncodePayload Implements messager interface
func (v *GetHeadersMessage) EncodePayload(w io.Writer) error {
bw := &util.BinWriter{W: w}
bw.VarUint(uint64(len(v.hashStart)))
bw.Write(v.hashStart)
bw.Write(v.hashStop)
return bw.Err
}
// Command Implements messager interface
func (v *GetHeadersMessage) Command() command.Type {
return v.cmd
}

View file

@ -0,0 +1,47 @@
package payload
import (
"bytes"
"crypto/sha256"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/util/Checksum"
"github.com/CityOfZion/neo-go/pkg/wire/util"
"github.com/stretchr/testify/assert"
)
// Test taken from neo-go v1
func TestGetHeadersEncodeDecode(t *testing.T) {
var (
start = []util.Uint256{
sha256.Sum256([]byte("a")),
sha256.Sum256([]byte("b")),
sha256.Sum256([]byte("c")),
sha256.Sum256([]byte("d")),
}
stop = sha256.Sum256([]byte("e"))
)
msgGetHeaders, err := NewGetHeadersMessage(start, stop)
assert.Equal(t, nil, err)
buf := new(bytes.Buffer)
err = msgGetHeaders.EncodePayload(buf)
assert.Equal(t, nil, err)
expected := checksum.FromBuf(buf)
msgGetHeadersDec, err := NewGetHeadersMessage([]util.Uint256{}, util.Uint256{})
assert.Equal(t, nil, err)
r := bytes.NewReader(buf.Bytes())
err = msgGetHeadersDec.DecodePayload(r)
assert.Equal(t, nil, err)
buf = new(bytes.Buffer)
err = msgGetHeadersDec.EncodePayload(buf)
have := checksum.FromBuf(buf)
assert.Equal(t, expected, have)
}

View file

@ -0,0 +1,82 @@
package payload
import (
"errors"
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// HeadersMessage represents a Header(s) Message on the neo-network
type HeadersMessage struct {
Headers []*BlockBase
// Padding that is fixed to 0
_ uint8
}
// Users can at most request 2k header
const (
maxHeadersAllowed = 2000
)
var (
errMaxHeaders = errors.New("Maximum amount of headers allowed is 2000")
)
//NewHeadersMessage returns a HeadersMessage Object
func NewHeadersMessage() (*HeadersMessage, error) {
headers := &HeadersMessage{nil, 0}
return headers, nil
}
// AddHeader adds a header into the list of Headers.
// Since a header is just blockbase with padding, we use BlockBase
func (h *HeadersMessage) AddHeader(head *BlockBase) error {
if len(h.Headers)+1 > maxHeadersAllowed {
return errMaxHeaders
}
h.Headers = append(h.Headers, head)
return nil
}
// DecodePayload Implements Messager interface
func (h *HeadersMessage) DecodePayload(r io.Reader) error {
br := &util.BinReader{R: r}
lenHeaders := br.VarUint()
h.Headers = make([]*BlockBase, lenHeaders)
for i := 0; i < int(lenHeaders); i++ {
header := &BlockBase{}
header.DecodePayload(br)
var padding uint8
br.Read(&padding)
if padding != 0 {
return errPadding
}
h.Headers[i] = header
}
return br.Err
}
// EncodePayload Implements messager interface
func (h *HeadersMessage) EncodePayload(w io.Writer) error {
bw := &util.BinWriter{W: w}
bw.VarUint(uint64(len(h.Headers)))
for _, header := range h.Headers {
header.EncodePayload(bw)
bw.Write(uint8(0))
}
return bw.Err
}
// Command Implements messager interface
func (h *HeadersMessage) Command() command.Type {
return command.Headers
}

View file

@ -0,0 +1,82 @@
package payload
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
"github.com/CityOfZion/neo-go/pkg/wire/util"
"github.com/CityOfZion/neo-go/pkg/wire/util/address"
"github.com/stretchr/testify/assert"
)
func TestNewHeaderMessage(t *testing.T) {
msgHeaders, err := NewHeadersMessage()
assert.Equal(t, nil, err)
assert.Equal(t, 0, len(msgHeaders.Headers))
}
func TestAddAndEncodeHeaders(t *testing.T) {
// uses block10 from mainnet
msgHeaders, _ := NewHeadersMessage()
prevH, _ := util.Uint256DecodeString("005fb74a6de169ce5daf59a114405e5b27238b2489690e3b2a60c14ddfc3b326")
merkleRoot, _ := util.Uint256DecodeString("ca6d58bcb837472c2f77877e68495b83fd5b714dfe0c8230a525f4511a3239f4")
invocationScript, _ := hex.DecodeString("4036fdd23248880c1c311bcd97df04fe6d740dc1bf340c26915f0466e31e81c039012eca7a760270389e04b58b99820fe49cf8c24c9afc65d696b4d3f406a1e6b5405172a9b461e68dd399c8716de11d31f7dd2ec3be327c636b024562db6ac5df1cffdbee74c994736fd49803234d2baffbc0054f28ba5ec76494a467b4106955bb4084af7746d269241628c667003e9d39288b190ad5cef218ada625cbba8be411bb153828d8d3634e8f586638e2448425bc5b671be69800392ccbdebc945a5099c7406f6a11824105ecad345e525957053e77fbc0119d6b3fa7f854527e816cfce0d95dac66888e07e8990c95103d8e46124aac16f152e088520d7ec8325e3a2456f840e5b77ef0e3c410b347ccaf8a87516d10b88d436563c80712153273993afc320ec49b638225f58de464a1345e62a564b398939f96f6f4b7cf21b583609f85495a")
verificationScript, _ := hex.DecodeString("552102486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a7021024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d2102aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e2103b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c2103b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a2102ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba5542102df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e89509357ae")
nextCon, _ := util.Uint160DecodeString(address.ToScriptHash("APyEx5f4Zm4oCHwFWiSTaph1fPBxZacYVR"))
msgHeaders.AddHeader(&BlockBase{
Version: 0,
Index: 10,
PrevHash: prevH.Reverse(),
MerkleRoot: merkleRoot.Reverse(),
Timestamp: 1476647551,
ConsensusData: 0xc0f0280216ff14bf,
NextConsensus: nextCon,
Witness: transaction.Witness{
InvocationScript: invocationScript,
VerificationScript: verificationScript,
},
})
assert.Equal(t, 1, len(msgHeaders.Headers))
err := msgHeaders.Headers[0].createHash()
assert.Equal(t, nil, err)
// Hash being correct, automatically verifies that the fields are encoded properly
assert.Equal(t, "f3c4ec44c07eccbda974f1ee34bc6654ab6d3f22cd89c2e5c593a16d6cc7e6e8", msgHeaders.Headers[0].Hash.ReverseString())
}
func TestEncodeDecode(t *testing.T) {
rawBlockHeaders := "010000000026b3c3df4dc1602a3b0e6989248b23275b5e4014a159af5dce69e16d4ab75f00f439321a51f425a530820cfe4d715bfd835b49687e87772f2c4737b8bc586dca7fda03580a000000bf14ff160228f0c059e75d652b5d3827bf04c165bbe9ef95cca4bf5501fd45014036fdd23248880c1c311bcd97df04fe6d740dc1bf340c26915f0466e31e81c039012eca7a760270389e04b58b99820fe49cf8c24c9afc65d696b4d3f406a1e6b5405172a9b461e68dd399c8716de11d31f7dd2ec3be327c636b024562db6ac5df1cffdbee74c994736fd49803234d2baffbc0054f28ba5ec76494a467b4106955bb4084af7746d269241628c667003e9d39288b190ad5cef218ada625cbba8be411bb153828d8d3634e8f586638e2448425bc5b671be69800392ccbdebc945a5099c7406f6a11824105ecad345e525957053e77fbc0119d6b3fa7f854527e816cfce0d95dac66888e07e8990c95103d8e46124aac16f152e088520d7ec8325e3a2456f840e5b77ef0e3c410b347ccaf8a87516d10b88d436563c80712153273993afc320ec49b638225f58de464a1345e62a564b398939f96f6f4b7cf21b583609f85495af1552102486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a7021024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d2102aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e2103b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c2103b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a2102ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba5542102df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e89509357ae00"
var headerMsg HeadersMessage
rawBlockBytes, _ := hex.DecodeString(rawBlockHeaders)
r := bytes.NewReader(rawBlockBytes)
err := headerMsg.DecodePayload(r)
assert.Equal(t, 1, len(headerMsg.Headers))
header := headerMsg.Headers[0]
err = header.createHash()
assert.Equal(t, "f3c4ec44c07eccbda974f1ee34bc6654ab6d3f22cd89c2e5c593a16d6cc7e6e8", header.Hash.ReverseString())
buf := new(bytes.Buffer)
err = headerMsg.EncodePayload(buf)
assert.Equal(t, nil, err)
assert.Equal(t, hex.EncodeToString(rawBlockBytes), hex.EncodeToString(buf.Bytes()))
}

View file

@ -0,0 +1,114 @@
package payload
import (
"errors"
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//InvType represents the enum of inventory types
type InvType uint8
const (
// InvTypeTx represents the transaction inventory type
InvTypeTx InvType = 0x01
// InvTypeBlock represents the block inventory type
InvTypeBlock InvType = 0x02
// InvTypeConsensus represents the consensus inventory type
InvTypeConsensus InvType = 0xe0
)
const maxHashes = 0x10000000
var errMaxHash = errors.New("max size For Hashes reached")
// InvMessage represents an Inventory message on the neo-network
type InvMessage struct {
cmd command.Type
Type InvType
Hashes []util.Uint256
}
//NewInvMessage returns an InvMessage object
func NewInvMessage(typ InvType) (*InvMessage, error) {
inv := &InvMessage{
command.Inv,
typ,
nil,
}
return inv, nil
}
func newAbstractInv(typ InvType, cmd command.Type) (*InvMessage, error) {
inv, err := NewInvMessage(typ)
if err != nil {
return nil, err
}
inv.cmd = cmd
return inv, nil
}
// AddHash adds a hash to the list of hashes
func (inv *InvMessage) AddHash(h util.Uint256) error {
if len(inv.Hashes)+1 > maxHashes {
return errMaxHash
}
inv.Hashes = append(inv.Hashes, h)
return nil
}
// AddHashes adds multiple hashes to the list of hashes
func (inv *InvMessage) AddHashes(hashes []util.Uint256) error {
var err error
for _, hash := range hashes {
err = inv.AddHash(hash)
if err != nil {
break
}
}
return err
}
// DecodePayload Implements Messager interface
func (inv *InvMessage) DecodePayload(r io.Reader) error {
br := &util.BinReader{R: r}
br.Read(&inv.Type)
listLen := br.VarUint()
inv.Hashes = make([]util.Uint256, listLen)
for i := 0; i < int(listLen); i++ {
br.Read(&inv.Hashes[i])
}
return nil
}
// EncodePayload Implements messager interface
func (inv *InvMessage) EncodePayload(w io.Writer) error {
bw := &util.BinWriter{W: w}
bw.Write(inv.Type)
lenhashes := len(inv.Hashes)
bw.VarUint(uint64(lenhashes))
for _, hash := range inv.Hashes {
bw.Write(hash)
}
return bw.Err
}
// Command Implements messager interface
func (inv *InvMessage) Command() command.Type {
return inv.cmd
}

View file

@ -0,0 +1,78 @@
package payload
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/util"
"github.com/stretchr/testify/assert"
)
func TestNewInventory(t *testing.T) {
msgInv, err := NewInvMessage(InvTypeBlock)
assert.Equal(t, nil, err)
assert.Equal(t, command.Inv, msgInv.Command())
hash, _ := util.Uint256DecodeBytes([]byte("hello"))
err = msgInv.AddHash(hash)
assert.Equal(t, nil, err)
}
// Adjust test time or it will timeout
// func TestMaxHashes(t *testing.T) {
// msgInv, err := NewInvMessage(InvTypeBlock)
// assert.Equal(t, nil, err)
// hash, _ := util.Uint256DecodeBytes([]byte("hello"))
// for i := 0; i <= maxHashes+1; i++ {
// err = msgInv.AddHash(hash)
// }
// if err == nil {
// assert.Fail(t, "Max Hashes Exceeded, only allowed %v but have %v", maxHashes, len(msgInv.Hashes))
// } else if err != MaxHashError {
// assert.Fail(t, "Expected a MaxHashError, however we got %s", err.Error())
// }
// }
func TestEncodeDecodePayload(t *testing.T) {
msgInv, err := NewInvMessage(InvTypeBlock)
assert.Equal(t, nil, err)
blockOneHash := "d782db8a38b0eea0d7394e0f007c61c71798867578c77c387c08113903946cc9"
hash, _ := util.Uint256DecodeString(blockOneHash)
err = msgInv.AddHash(hash)
assert.Equal(t, nil, err)
buf := new(bytes.Buffer)
err = msgInv.EncodePayload(buf)
assert.Equal(t, nil, err)
numOfHashes := []byte{1}
expected := append([]byte{uint8(InvTypeBlock)}, numOfHashes...)
expected = append(expected, hash.Bytes()...)
assert.Equal(t, hex.EncodeToString(expected), hex.EncodeToString(buf.Bytes()))
var InvDec InvMessage
r := bytes.NewReader(buf.Bytes())
err = InvDec.DecodePayload(r)
assert.Equal(t, nil, err)
assert.Equal(t, 1, len(InvDec.Hashes))
assert.Equal(t, blockOneHash, hex.EncodeToString(InvDec.Hashes[0].Bytes()))
}
func TestEmptyInv(t *testing.T) {
msgInv, err := NewInvMessage(InvTypeBlock)
assert.Equal(t, nil, err)
buf := new(bytes.Buffer)
msgInv.EncodePayload(buf)
assert.Equal(t, []byte{byte(InvTypeBlock), 0}, buf.Bytes())
assert.Equal(t, 0, len(msgInv.Hashes))
}

View file

@ -0,0 +1,30 @@
package payload
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
)
// GetMempool represents a GetMempool message on the neo-network
type GetMempool struct{}
//NewGetMempool returns a GetMempool message
func NewGetMempool() (*GetMempool, error) {
return &GetMempool{}, nil
}
// DecodePayload Implements Messager interface
func (v *GetMempool) DecodePayload(r io.Reader) error {
return nil
}
// EncodePayload Implements messager interface
func (v *GetMempool) EncodePayload(w io.Writer) error {
return nil
}
// Command Implements messager interface
func (v *GetMempool) Command() command.Type {
return command.Mempool
}

View file

@ -0,0 +1,35 @@
package payload
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction"
)
// TXMessage represents a transaction message on the neo-network
type TXMessage struct {
Tx transaction.Transactioner
}
//NewTXMessage returns a new tx object
func NewTXMessage(tx transaction.Transactioner) (*TXMessage, error) {
Tx := &TXMessage{tx}
return Tx, nil
}
// DecodePayload Implements Messager interface
func (t *TXMessage) DecodePayload(r io.Reader) error {
return t.Tx.Decode(r)
}
// EncodePayload Implements messager interface
func (t *TXMessage) EncodePayload(w io.Writer) error {
return t.Tx.Encode(w)
}
// Command Implements messager interface
func (t *TXMessage) Command() command.Type {
return command.TX
}

View file

@ -0,0 +1,30 @@
package payload
import (
"io"
"github.com/CityOfZion/neo-go/pkg/wire/command"
)
//VerackMessage represents a verack message on the neo-network
type VerackMessage struct{}
//NewVerackMessage returns a verack message
func NewVerackMessage() (*VerackMessage, error) {
return &VerackMessage{}, nil
}
// DecodePayload Implements Messager interface
func (v *VerackMessage) DecodePayload(r io.Reader) error {
return nil
}
// EncodePayload Implements messager interface
func (v *VerackMessage) EncodePayload(w io.Writer) error {
return nil
}
// Command Implements messager interface
func (v *VerackMessage) Command() command.Type {
return command.Verack
}

View file

@ -0,0 +1,17 @@
package payload
import (
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/stretchr/testify/assert"
)
func TestNewVerack(t *testing.T) {
verackMessage, err := NewVerackMessage()
assert.Equal(t, nil, err)
assert.Equal(t, command.Verack, verackMessage.Command())
}

View file

@ -0,0 +1,93 @@
// Copied and Modified for NEO from: https://github.com/decred/dcrd/blob/master/wire/VersionMessage.go
package payload
import (
"errors"
"io"
"net"
"time"
"github.com/CityOfZion/neo-go/pkg/wire/command"
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
const minMsgVersionSize = 28
var errInvalidNetAddr = errors.New("provided net.Addr is not a net.TCPAddr")
//VersionMessage represents a version message on the neo-network
type VersionMessage struct {
Version protocol.Version
Timestamp uint32
Services protocol.ServiceFlag
IP net.IP
Port uint16
Nonce uint32
UserAgent []byte
StartHeight uint32
Relay bool
}
//NewVersionMessage will return a VersionMessage object
func NewVersionMessage(addr net.Addr, startHeight uint32, relay bool, pver protocol.Version, userAgent string, nonce uint32, services protocol.ServiceFlag) (*VersionMessage, error) {
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
return nil, errInvalidNetAddr
}
version := &VersionMessage{
pver,
uint32(time.Now().Unix()),
services,
tcpAddr.IP,
uint16(tcpAddr.Port),
nonce,
[]byte(userAgent),
startHeight,
relay,
}
return version, nil
}
// DecodePayload Implements Messager interface
func (v *VersionMessage) DecodePayload(r io.Reader) error {
br := &util.BinReader{R: r}
br.Read(&v.Version)
br.Read(&v.Services)
br.Read(&v.Timestamp)
br.Read(&v.Port) // Port is not BigEndian as stated in the docs
br.Read(&v.Nonce)
var lenUA uint8
br.Read(&lenUA)
v.UserAgent = make([]byte, lenUA)
br.Read(&v.UserAgent)
br.Read(&v.StartHeight)
br.Read(&v.Relay)
return br.Err
}
// EncodePayload Implements messager interface
func (v *VersionMessage) EncodePayload(w io.Writer) error {
bw := &util.BinWriter{W: w}
bw.Write(v.Version)
bw.Write(v.Services)
bw.Write(v.Timestamp)
bw.Write(v.Port) // Not big End
bw.Write(v.Nonce)
bw.Write(uint8(len(v.UserAgent)))
bw.Write(v.UserAgent)
bw.Write(v.StartHeight)
bw.Write(v.Relay)
return bw.Err
}
// Command Implements messager interface
func (v *VersionMessage) Command() command.Type {
return command.Version
}

View file

@ -0,0 +1,59 @@
package payload
import (
"bytes"
"math/rand"
"net"
"testing"
"time"
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
"github.com/stretchr/testify/assert"
)
func TestValidNewVersionMessage(t *testing.T) {
expectedIP := "127.0.0.1"
expectedPort := 8333
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP(expectedIP), Port: expectedPort}
nonce := randRange(12949672, 42949672)
message, err := NewVersionMessage(tcpAddrMe, 0, true, protocol.DefaultVersion, protocol.UserAgent, nonce, protocol.NodePeerService)
assert.Equal(t, nil, err)
assert.Equal(t, expectedIP, message.IP.String())
assert.Equal(t, uint16(expectedPort), message.Port)
assert.Equal(t, protocol.DefaultVersion, message.Version)
}
func TestEncode(t *testing.T) {
expectedIP := "127.0.0.1"
expectedPort := 8333
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP(expectedIP), Port: expectedPort}
nonce := randRange(12949672, 42949672)
message, err := NewVersionMessage(tcpAddrMe, 0, true, protocol.DefaultVersion, protocol.UserAgent, nonce, protocol.NodePeerService)
buf := new(bytes.Buffer)
err = message.EncodePayload(buf)
assert.Equal(t, nil, err)
assert.Equal(t, len(message.UserAgent)+minMsgVersionSize, int(buf.Len()))
}
func TestLenIsCorrect(t *testing.T) {
expectedIP := "127.0.0.1"
expectedPort := 8333
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP(expectedIP), Port: expectedPort}
nonce := randRange(12949672, 42949672)
message, err := NewVersionMessage(tcpAddrMe, 0, true, protocol.DefaultVersion, protocol.UserAgent, nonce, protocol.NodePeerService)
buf := new(bytes.Buffer)
err = message.EncodePayload(buf)
assert.Equal(t, nil, err)
assert.Equal(t, len(message.UserAgent)+minMsgVersionSize, len(buf.Bytes()))
}
func randRange(min, max int) uint32 {
rand.Seed(time.Now().Unix() + int64(rand.Uint64()))
return uint32(rand.Intn(max-min) + min)
}

View file

@ -0,0 +1,59 @@
package payload
import (
"net"
"strconv"
"time"
"github.com/CityOfZion/neo-go/pkg/wire/protocol"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//NetAddr is an abstraction for the IP layer
type NetAddr struct {
Timestamp uint32
IP [16]byte
Port uint16
Service protocol.ServiceFlag
}
//NewNetAddr returns a NetAddr object
func NewNetAddr(time uint32, ip [16]byte, port uint16, service protocol.ServiceFlag) (*NetAddr, error) {
return &NetAddr{time, ip, port, service}, nil
}
//NewAddrFromVersionMessage returns a NetAddr object from a version message
func NewAddrFromVersionMessage(version VersionMessage) (*NetAddr, error) {
var ip [16]byte
copy(ip[:], []byte(version.IP)[:16])
return NewNetAddr(version.Timestamp, ip, version.Port, version.Services)
}
// EncodePayload Implements messager interface
func (n *NetAddr) EncodePayload(bw *util.BinWriter) {
bw.Write(uint32(time.Now().Unix()))
bw.Write(protocol.NodePeerService)
bw.WriteBigEnd(n.IP)
bw.WriteBigEnd(n.Port)
}
// DecodePayload Implements Messager interface
func (n *NetAddr) DecodePayload(br *util.BinReader) {
br.Read(&n.Timestamp)
br.Read(&n.Service)
br.ReadBigEnd(&n.IP)
br.ReadBigEnd(&n.Port)
}
//IPPort returns the IPPort from the NetAddr
func (n *NetAddr) IPPort() string {
ip := net.IP(n.IP[:]).String()
port := strconv.Itoa(int(n.Port))
ipport := ip + ":" + port
return ipport
}

View file

@ -0,0 +1,61 @@
package transaction
import (
"errors"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// Attribute represents a Transaction attribute.
type Attribute struct {
Usage AttrUsage
Data []byte
}
var errMaxData = errors.New("max Size of Attribute reached")
const maxAttrSize = 65535
// Encode encodes the given Attribute into the binary writer
func (a *Attribute) Encode(bw *util.BinWriter) {
if len(a.Data) > maxAttrSize {
bw.Err = errMaxData
return
}
bw.Write(uint8(a.Usage))
if a.Usage == ContractHash || a.Usage == Vote || (a.Usage >= Hash1 && a.Usage <= Hash15) {
bw.Write(a.Data[:32])
} else if a.Usage == ECDH02 || a.Usage == ECDH03 {
bw.Write(a.Data[1:33])
} else if a.Usage == Script {
bw.Write(a.Data[:20])
} else if a.Usage == DescriptionURL || a.Usage == Description || a.Usage >= Remark {
bw.VarUint(uint64(len(a.Data)))
bw.Write(a.Data)
} else {
bw.Write(a.Data)
}
}
// Decode decodes the binary reader into an Attribute object
func (a *Attribute) Decode(br *util.BinReader) {
br.Read(&a.Usage)
if a.Usage == ContractHash || a.Usage == Vote || a.Usage >= Hash1 && a.Usage <= Hash15 {
a.Data = make([]byte, 32)
br.Read(&a.Data)
} else if a.Usage == ECDH02 || a.Usage == ECDH03 {
a.Data = make([]byte, 32)
br.Read(&a.Data)
} else if a.Usage == Script {
a.Data = make([]byte, 20)
br.Read(&a.Data)
} else if a.Usage == DescriptionURL || a.Usage == Description || a.Usage >= Remark {
lenData := br.VarUint()
a.Data = make([]byte, lenData)
br.Read(&a.Data)
} else {
br.Read(&a.Data)
}
}

View file

@ -0,0 +1,32 @@
package transaction
import "github.com/CityOfZion/neo-go/pkg/wire/util"
// Input represents a Transaction input.
type Input struct {
// The hash of the previous transaction.
PrevHash util.Uint256
// The index of the previous transaction.
PrevIndex uint16
}
//NewInput returns a transaction input object
func NewInput(prevHash util.Uint256, prevIndex uint16) *Input {
return &Input{
prevHash,
prevIndex,
}
}
// Encode encodes the given input into a binary writer
func (i *Input) Encode(bw *util.BinWriter) {
bw.Write(i.PrevHash)
bw.Write(i.PrevIndex)
}
// Decode decodes a binary reader into an input object
func (i *Input) Decode(br *util.BinReader) {
br.Read(&i.PrevHash)
br.Read(&i.PrevIndex)
}

View file

@ -0,0 +1,38 @@
package transaction
import "github.com/CityOfZion/neo-go/pkg/wire/util"
// Output represents a transaction output in the neo-network
type Output struct {
// The NEO asset id used in the transaction.
AssetID util.Uint256
// Amount of AssetType send or received.
Amount int64
// The address of the remittee.
ScriptHash util.Uint160
}
//NewOutput returns an output object
func NewOutput(assetID util.Uint256, Amount int64, ScriptHash util.Uint160) *Output {
return &Output{
assetID,
Amount,
ScriptHash,
}
}
// Encode encodes the Output into a binary writer
func (o *Output) Encode(bw *util.BinWriter) {
bw.Write(o.AssetID)
bw.Write(o.Amount)
bw.Write(o.ScriptHash)
}
// Decode decodes a binary reader into an output object
func (o *Output) Decode(br *util.BinReader) {
br.Read(&o.AssetID)
br.Read(&o.Amount)
br.Read(&o.ScriptHash)
}

View file

@ -0,0 +1,37 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//Witness represents a Witness object in a neo transaction
type Witness struct {
InvocationScript []byte
VerificationScript []byte
}
// Encode encodes a Witness into a binary writer
func (s *Witness) Encode(bw *util.BinWriter) error {
bw.VarUint(uint64(len(s.InvocationScript)))
bw.Write(s.InvocationScript)
bw.VarUint(uint64(len(s.VerificationScript)))
bw.Write(s.VerificationScript)
return bw.Err
}
// Decode decodes a binary reader into a Witness object
func (s *Witness) Decode(br *util.BinReader) error {
lenb := br.VarUint()
s.InvocationScript = make([]byte, lenb)
br.Read(s.InvocationScript)
lenb = br.VarUint()
s.VerificationScript = make([]byte, lenb)
br.Read(s.VerificationScript)
return br.Err
}

View file

@ -0,0 +1,16 @@
package transaction
// AssetType represent a NEO asset type
type AssetType uint8
// Valid asset types.
const (
CreditFlag AssetType = 0x40
DutyFlag AssetType = 0x80
GoverningToken AssetType = 0x00
UtilityToken AssetType = 0x01
Currency AssetType = 0x08
Share AssetType = DutyFlag | 0x10
Invoice AssetType = DutyFlag | 0x18
Token AssetType = CreditFlag | 0x20
)

View file

@ -0,0 +1,49 @@
package transaction
// AttrUsage represents an attribute usage on the neo network
type AttrUsage uint8
// List of valid attribute usages.
const (
ContractHash AttrUsage = 0x00
ECDH02 AttrUsage = 0x02
ECDH03 AttrUsage = 0x03
Script AttrUsage = 0x20
Vote AttrUsage = 0x30
CertURL AttrUsage = 0x80
DescriptionURL AttrUsage = 0x81
Description AttrUsage = 0x90
Hash1 AttrUsage = 0xa1
Hash2 AttrUsage = 0xa2
Hash3 AttrUsage = 0xa3
Hash4 AttrUsage = 0xa4
Hash5 AttrUsage = 0xa5
Hash6 AttrUsage = 0xa6
Hash7 AttrUsage = 0xa7
Hash8 AttrUsage = 0xa8
Hash9 AttrUsage = 0xa9
Hash10 AttrUsage = 0xaa
Hash11 AttrUsage = 0xab
Hash12 AttrUsage = 0xac
Hash13 AttrUsage = 0xad
Hash14 AttrUsage = 0xae
Hash15 AttrUsage = 0xaf
Remark AttrUsage = 0xf0
Remark1 AttrUsage = 0xf1
Remark2 AttrUsage = 0xf2
Remark3 AttrUsage = 0xf3
Remark4 AttrUsage = 0xf4
Remark5 AttrUsage = 0xf5
Remark6 AttrUsage = 0xf6
Remark7 AttrUsage = 0xf7
Remark8 AttrUsage = 0xf8
Remark9 AttrUsage = 0xf9
Remark10 AttrUsage = 0xfa
Remark11 AttrUsage = 0xfb
Remark12 AttrUsage = 0xfc
Remark13 AttrUsage = 0xfd
Remark14 AttrUsage = 0xfe
Remark15 AttrUsage = 0xff
)

View file

@ -0,0 +1,202 @@
package transaction
import (
"bytes"
"io"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
type encodeExclusiveFields func(bw *util.BinWriter)
type decodeExclusiveFields func(br *util.BinReader)
// Transactioner is the interface that will unite the
// transaction types. Each transaction will implement this interface
// and so wil be a transactioner
type Transactioner interface {
Encode(w io.Writer) error
Decode(r io.Reader) error
BaseTx() *Base
ID() (util.Uint256, error)
}
// Base transaction is the template for all other transactions
// It contains all of the shared fields between transactions and
// the additional encodeExclusive and decodeExclusive methods, which
// can be overwitten in the other transactions to encode the non shared fields
type Base struct {
Type types.TX
Version version.TX
Inputs []*Input
Outputs []*Output
Attributes []*Attribute
Witnesses []*Witness
Hash util.Uint256
encodeExclusive encodeExclusiveFields
decodeExclusive decodeExclusiveFields
}
func createBaseTransaction(typ types.TX, ver version.TX) *Base {
return &Base{
Type: typ,
Version: ver,
Inputs: []*Input{},
Outputs: []*Output{},
Attributes: []*Attribute{},
Witnesses: []*Witness{},
}
}
// Decode implements the transactioner interface
func (b *Base) Decode(r io.Reader) error {
br := &util.BinReader{R: r}
return b.DecodePayload(br)
}
// Encode implements the transactioner interface
func (b *Base) Encode(w io.Writer) error {
bw := &util.BinWriter{W: w}
b.EncodePayload(bw)
return bw.Err
}
//EncodePayload implements the Messager interface
func (b *Base) EncodePayload(bw *util.BinWriter) {
b.encodeHashableFields(bw)
lenWitnesses := uint64(len(b.Witnesses))
bw.VarUint(lenWitnesses)
for _, witness := range b.Witnesses {
witness.Encode(bw)
}
}
// DecodePayload implements the messager interface
func (b *Base) DecodePayload(br *util.BinReader) error {
b.decodeHashableFields(br)
lenWitnesses := br.VarUint()
b.Witnesses = make([]*Witness, lenWitnesses)
for i := 0; i < int(lenWitnesses); i++ {
b.Witnesses[i] = &Witness{}
b.Witnesses[i].Decode(br)
}
if br.Err != nil {
return br.Err
}
return b.createHash()
}
func (b *Base) encodeHashableFields(bw *util.BinWriter) {
b.Type.Encode(bw)
b.Version.Encode(bw)
b.encodeExclusive(bw)
lenAttrs := uint64(len(b.Attributes))
lenInputs := uint64(len(b.Inputs))
lenOutputs := uint64(len(b.Outputs))
bw.VarUint(lenAttrs)
for _, attr := range b.Attributes {
attr.Encode(bw)
}
bw.VarUint(lenInputs)
for _, input := range b.Inputs {
input.Encode(bw)
}
bw.VarUint(lenOutputs)
for _, output := range b.Outputs {
output.Encode(bw)
}
}
func (b *Base) decodeHashableFields(br *util.BinReader) {
b.Type.Decode(br)
b.Version.Decode(br)
b.decodeExclusive(br)
lenAttrs := br.VarUint()
b.Attributes = make([]*Attribute, lenAttrs)
for i := 0; i < int(lenAttrs); i++ {
b.Attributes[i] = &Attribute{}
b.Attributes[i].Decode(br)
}
lenInputs := br.VarUint()
b.Inputs = make([]*Input, lenInputs)
for i := 0; i < int(lenInputs); i++ {
b.Inputs[i] = &Input{}
b.Inputs[i].Decode(br)
}
lenOutputs := br.VarUint()
b.Outputs = make([]*Output, lenOutputs)
for i := 0; i < int(lenOutputs); i++ {
b.Outputs[i] = &Output{}
b.Outputs[i].Decode(br)
}
}
// AddInput adds an input to the transaction
func (b *Base) AddInput(i *Input) {
b.Inputs = append(b.Inputs, i)
}
// AddOutput adds an output to the transaction
func (b *Base) AddOutput(o *Output) {
b.Outputs = append(b.Outputs, o)
}
// AddAttribute adds an attribute to the transaction
func (b *Base) AddAttribute(a *Attribute) {
b.Attributes = append(b.Attributes, a)
}
// AddWitness adds a witness object to the transaction
func (b *Base) AddWitness(w *Witness) {
b.Witnesses = append(b.Witnesses, w)
}
func (b *Base) createHash() error {
hash, err := util.CalculateHash(b.encodeHashableFields)
b.Hash = hash
return err
}
// ID returns the TXID of the transaction
func (b *Base) ID() (util.Uint256, error) {
var emptyHash util.Uint256
var err error
if b.Hash == emptyHash {
err = b.createHash()
}
return b.Hash, err
}
// Bytes returns the raw bytes of the tx
func (b *Base) Bytes() []byte {
buf := new(bytes.Buffer)
b.Encode(buf)
return buf.Bytes()
}
// BaseTx returns the Base object in a transaction
func (b *Base) BaseTx() *Base {
return b
}

View file

@ -0,0 +1,43 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//Claim represents a claim transaction on the neo network
type Claim struct {
*Base
Claims []*Input
}
//NewClaim returns a ClaimTransaction
func NewClaim(ver version.TX) *Claim {
basicTrans := createBaseTransaction(types.Contract, ver)
claim := &Claim{}
claim.Base = basicTrans
claim.encodeExclusive = claim.encodeExcl
claim.decodeExclusive = claim.decodeExcl
return claim
}
func (c *Claim) encodeExcl(bw *util.BinWriter) {
bw.VarUint(uint64(len(c.Claims)))
for _, claim := range c.Claims {
claim.Encode(bw)
}
}
func (c *Claim) decodeExcl(br *util.BinReader) {
lenClaims := br.VarUint()
c.Claims = make([]*Input, lenClaims)
for i := 0; i < int(lenClaims); i++ {
c.Claims[i] = &Input{}
c.Claims[i].Decode(br)
}
}

View file

@ -0,0 +1,38 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeClaim(t *testing.T) {
// test taken from mainnet: abf142faf539c340e42722b5b34b505cf4fd73185fed775784e37c2c5ef1b866
rawtx := "020001af1b3a0f3729572893ce4e82f2113d18ec9a5e9d6fe02117eaa9e0c5a43770490000000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6048ae5801000000001123b6b74273562540479eea5cd0139f88ac7dd301414085f6d9edc24ab68c5d15c8e164de6702106c53bc15fa2c45b575bd3543c19132de61dd1922407be56affbcea73e5f8878811549340fd3c951e8593d51f3c8a962321028cf5e5a4d430db0202755c2cf1b3c99efcb4da4e41e182450dc5e1ddffb54bbfac"
rawtxBytes, _ := hex.DecodeString(rawtx)
c := NewClaim(0)
r := bytes.NewReader(rawtxBytes)
err := c.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Claim, c.Type)
assert.Equal(t, 0, int(c.Version))
assert.Equal(t, 1, int(len(c.Claims)))
claim := c.Claims[0]
assert.Equal(t, "497037a4c5e0a9ea1721e06f9d5e9aec183d11f2824ece93285729370f3a1baf", claim.PrevHash.ReverseString())
assert.Equal(t, uint16(0), claim.PrevIndex)
assert.Equal(t, "abf142faf539c340e42722b5b34b505cf4fd73185fed775784e37c2c5ef1b866", c.Hash.ReverseString())
// Encode
buf := new(bytes.Buffer)
err = c.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes()))
}

View file

@ -0,0 +1,28 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//Contract represents a contract transaction on the neo network
type Contract struct {
*Base
}
//NewContract returns a contract transaction
func NewContract(ver version.TX) *Contract {
basicTrans := createBaseTransaction(types.Contract, ver)
contract := &Contract{
basicTrans,
}
contract.encodeExclusive = contract.encodeExcl
contract.decodeExclusive = contract.decodeExcl
return contract
}
func (c *Contract) encodeExcl(bw *util.BinWriter) {}
func (c *Contract) decodeExcl(br *util.BinReader) {}

View file

@ -0,0 +1,45 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeContract(t *testing.T) {
// mainnet transaction: bdf6cc3b9af12a7565bda80933a75ee8cef1bc771d0d58effc08e4c8b436da79
rawtx := "80000001888da99f8f497fd65c4325786a09511159c279af4e7eb532e9edd628c87cc1ee0000019b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50082167010000000a8666b4830229d6a1a9b80f6088059191c122d2b0141409e79e132290c82916a88f1a3db5cf9f3248b780cfece938ab0f0812d0e188f3a489c7d1a23def86bd69d863ae67de753b2c2392e9497eadc8eb9fc43aa52c645232103e2f6a334e05002624cf616f01a62cff2844c34a3b08ca16048c259097e315078ac"
rawtxBytes, _ := hex.DecodeString(rawtx)
c := NewContract(30)
r := bytes.NewReader(rawtxBytes)
err := c.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Contract, c.Type)
assert.Equal(t, 0, int(c.Version))
assert.Equal(t, 1, int(len(c.Inputs)))
input := c.Inputs[0]
assert.Equal(t, "eec17cc828d6ede932b57e4eaf79c2591151096a7825435cd67f498f9fa98d88", input.PrevHash.ReverseString())
assert.Equal(t, 0, int(input.PrevIndex))
assert.Equal(t, int64(70600000000), c.Outputs[0].Amount)
assert.Equal(t, "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", c.Outputs[0].AssetID.ReverseString())
assert.Equal(t, "a8666b4830229d6a1a9b80f6088059191c122d2b", c.Outputs[0].ScriptHash.String())
assert.Equal(t, "bdf6cc3b9af12a7565bda80933a75ee8cef1bc771d0d58effc08e4c8b436da79", c.Hash.ReverseString())
// Encode
buf := new(bytes.Buffer)
err = c.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtxBytes, buf.Bytes())
}

View file

@ -0,0 +1,33 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//Enrollment represents an Enrollment transaction on the neo network
type Enrollment struct {
*Base
Key PublicKey
}
//NewEnrollment returns an Enrollment transaction
func NewEnrollment(ver version.TX) *Enrollment {
basicTrans := createBaseTransaction(types.Enrollment, ver)
enrollment := &Enrollment{
Base: basicTrans,
}
enrollment.encodeExclusive = enrollment.encodeExcl
enrollment.decodeExclusive = enrollment.decodeExcl
return enrollment
}
func (e *Enrollment) encodeExcl(bw *util.BinWriter) {
e.Key.Encode(bw)
}
func (e *Enrollment) decodeExcl(br *util.BinReader) {
e.Key.Decode(br)
}

View file

@ -0,0 +1,30 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeEnrollment(t *testing.T) {
rawtx := "200002ff8ac54687f36bbc31a91b730cc385da8af0b581f2d59d82b5cfef824fd271f60001d3d3b7028d61fea3b7803fda3d7f0a1f7262d38e5e1c8987b0313e0a94574151000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c60005441d11600000050ac4949596f5b62fef7be4d1c3e494e6048ed4a01414079d78189d591097b17657a62240c93595e8233dc81157ea2cd477813f09a11fd72845e6bd97c5a3dda125985ea3d5feca387e9933649a9a671a69ab3f6301df6232102ff8ac54687f36bbc31a91b730cc385da8af0b581f2d59d82b5cfef824fd271f6ac"
rawtxBytes, _ := hex.DecodeString(rawtx)
enroll := NewEnrollment(30)
r := bytes.NewReader(rawtxBytes)
err := enroll.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Enrollment, enroll.Type)
buf := new(bytes.Buffer)
err = enroll.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes()))
assert.Equal(t, "988832f693785dcbcb8d5a0e9d5d22002adcbfb1eb6bbeebf8c494fff580e147", enroll.Hash.ReverseString())
}

View file

@ -0,0 +1,61 @@
package transaction
import (
"errors"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
"github.com/CityOfZion/neo-go/pkg/wire/util/fixed8"
)
//Invocation represents an invocation transaction on the neo network
type Invocation struct {
*Base
Script []byte
Gas fixed8.Fixed8
}
//NewInvocation returns an invocation transaction
func NewInvocation(ver version.TX) *Invocation {
basicTrans := createBaseTransaction(types.Invocation, ver)
invocation := &Invocation{}
invocation.Base = basicTrans
invocation.encodeExclusive = invocation.encodeExcl
invocation.decodeExclusive = invocation.decodeExcl
return invocation
}
func (c *Invocation) encodeExcl(bw *util.BinWriter) {
bw.VarUint(uint64(len(c.Script)))
bw.Write(c.Script)
switch c.Version {
case 0:
c.Gas = fixed8.Fixed8(0)
case 1:
bw.Write(&c.Gas)
default:
bw.Write(&c.Gas)
}
return
}
func (c *Invocation) decodeExcl(br *util.BinReader) {
lenScript := br.VarUint()
c.Script = make([]byte, lenScript)
br.Read(&c.Script)
switch c.Version {
case 0:
c.Gas = fixed8.Fixed8(0)
case 1:
br.Read(&c.Gas)
default:
br.Err = errors.New("invalid Version Number for Invocation Transaction")
}
return
}

View file

@ -0,0 +1,78 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeInvoc(t *testing.T) {
// taken from mainnet b2a22cd9dd7636ae23e25576866cd1d9e2f3d85a85e80874441f085cd60006d1
rawtx := "d10151050034e23004141ad842821c7341d5a32b17d7177a1750d30014ca14628c9e5bc6a9346ca6bcdf050ceabdeb2bdc774953c1087472616e736665726703e1df72015bdef1a1b9567d4700635f23b1f406f100000000000000000220628c9e5bc6a9346ca6bcdf050ceabdeb2bdc7749f02f31363a30373a3032203a2030333366616431392d643638322d343035382d626437662d31356339333132343433653800000141403ced56c16f933e0a0a7d37470e114f6a4216ef9b834d61db67b74b9bd117370d10870857c0ee8adcf9956bc9fc92c5158de0c2db34ef459c17de042f20ad8fe92321027392870a5994b090d1750dda173a54df8dad324ed6d9ed25290d17c59059a112ac"
rawtxBytes, _ := hex.DecodeString(rawtx)
i := NewInvocation(30)
r := bytes.NewReader(rawtxBytes)
err := i.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Invocation, i.Type)
assert.Equal(t, 2, len(i.Attributes))
attr1 := i.Attributes[0]
assert.Equal(t, Script, attr1.Usage)
assert.Equal(t, "628c9e5bc6a9346ca6bcdf050ceabdeb2bdc7749", hex.EncodeToString(attr1.Data))
attr2 := i.Attributes[1]
assert.Equal(t, Remark, attr2.Usage)
assert.Equal(t, "31363a30373a3032203a2030333366616431392d643638322d343035382d626437662d313563393331323434336538", hex.EncodeToString(attr2.Data))
assert.Equal(t, "050034e23004141ad842821c7341d5a32b17d7177a1750d30014ca14628c9e5bc6a9346ca6bcdf050ceabdeb2bdc774953c1087472616e736665726703e1df72015bdef1a1b9567d4700635f23b1f406f1", hex.EncodeToString(i.Script))
assert.Equal(t, "b2a22cd9dd7636ae23e25576866cd1d9e2f3d85a85e80874441f085cd60006d1", i.Hash.ReverseString())
// Encode
buf := new(bytes.Buffer)
err = i.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtxBytes, buf.Bytes())
}
func TestEncodeDecodeInvocAttributes(t *testing.T) {
// taken from mainnet cb0b5edc7e87b3b1bd9e029112fd3ce17c16d3de20c43ca1c0c26f3add578ecb
rawtx := "d1015308005b950f5e010000140000000000000000000000000000000000000000141a1e29d6232d2148e1e71e30249835ea41eb7a3d53c1087472616e7366657267fb1c540417067c270dee32f21023aa8b9b71abce000000000000000002201a1e29d6232d2148e1e71e30249835ea41eb7a3d8110f9f504da6334935a2db42b18296d88700000014140461370f6847c4abbdddff54a3e1337e453ecc8133c882ec5b9aabcf0f47dafd3432d47e449f4efc77447ef03519b7808c450a998cca3ecc10e6536ed9db862ba23210285264b6f349f0fe86e9bb3044fde8f705b016593cf88cd5e8a802b78c7d2c950ac"
rawtxBytes, _ := hex.DecodeString(rawtx)
i := NewInvocation(30)
r := bytes.NewReader(rawtxBytes)
err := i.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Invocation, i.Type)
assert.Equal(t, 1, int(i.Version))
assert.Equal(t, 2, len(i.Attributes))
assert.Equal(t, Script, i.Attributes[0].Usage)
assert.Equal(t, "1a1e29d6232d2148e1e71e30249835ea41eb7a3d", hex.EncodeToString(i.Attributes[0].Data))
assert.Equal(t, DescriptionURL, i.Attributes[1].Usage)
assert.Equal(t, "f9f504da6334935a2db42b18296d8870", hex.EncodeToString(i.Attributes[1].Data))
assert.Equal(t, "08005b950f5e010000140000000000000000000000000000000000000000141a1e29d6232d2148e1e71e30249835ea41eb7a3d53c1087472616e7366657267fb1c540417067c270dee32f21023aa8b9b71abce", hex.EncodeToString(i.Script))
assert.Equal(t, "cb0b5edc7e87b3b1bd9e029112fd3ce17c16d3de20c43ca1c0c26f3add578ecb", i.Hash.ReverseString())
// Encode
buf := new(bytes.Buffer)
err = i.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtxBytes, buf.Bytes())
}

View file

@ -0,0 +1,34 @@
package transaction
import (
"errors"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// Issue represents an issue transaction on the neo network
type Issue struct {
*Base
}
//NewIssue returns an issue transaction
func NewIssue(ver version.TX) *Issue {
basicTrans := createBaseTransaction(types.Issue, ver)
Issue := &Issue{
basicTrans,
}
Issue.encodeExclusive = Issue.encodeExcl
Issue.decodeExclusive = Issue.decodeExcl
return Issue
}
func (c *Issue) encodeExcl(bw *util.BinWriter) {
if c.Version > 1 {
bw.Err = errors.New("Version Number Invalid, Issue cannot be more than 0")
}
}
func (c *Issue) decodeExcl(br *util.BinReader) {}

View file

@ -0,0 +1,8 @@
package transaction
import "testing"
func TestEncodeDecodeIssue(t *testing.T) {
// This is the same as Contract, as it has no special fields.
}

View file

@ -0,0 +1,32 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//Miner represents a miner transaction on the neo network
type Miner struct {
*Base
Nonce uint32
}
//NewMiner returns a miner transaction
func NewMiner(ver version.TX) *Miner {
basicTrans := createBaseTransaction(types.Miner, ver)
Miner := &Miner{}
Miner.Base = basicTrans
Miner.encodeExclusive = Miner.encodeExcl
Miner.decodeExclusive = Miner.decodeExcl
return Miner
}
func (c *Miner) encodeExcl(bw *util.BinWriter) {
bw.Write(c.Nonce)
}
func (c *Miner) decodeExcl(br *util.BinReader) {
br.Read(&c.Nonce)
}

View file

@ -0,0 +1,37 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeMiner(t *testing.T) {
// transaction from mainnet a1f219dc6be4c35eca172e65e02d4591045220221b1543f1a4b67b9e9442c264
rawtx := "0000fcd30e22000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c60c8000000000000001f72e68b4e39602912106d53b229378a082784b200"
rawtxBytes, _ := hex.DecodeString(rawtx)
m := NewMiner(0)
r := bytes.NewReader(rawtxBytes)
err := m.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Miner, m.Type)
assert.Equal(t, uint32(571397116), m.Nonce)
assert.Equal(t, "a1f219dc6be4c35eca172e65e02d4591045220221b1543f1a4b67b9e9442c264", m.Hash.ReverseString())
// Encode
buf := new(bytes.Buffer)
err = m.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtxBytes, buf.Bytes())
}

View file

@ -0,0 +1,17 @@
package transaction
// ParamType represent the Type of the contract parameter
type ParamType uint8
// A list of supported smart contract parameter types.
const (
SignatureType ParamType = iota
BoolType
IntegerType
Hash160Type
Hash256Type
ByteArrayType
PublicKeyType
StringType
ArrayType
)

View file

@ -0,0 +1,38 @@
package transaction
import (
"errors"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// PublicKey represents a public key on the neo network
type PublicKey struct {
Key []byte
}
//Encode encodes a public key into a binary writer
func (p *PublicKey) Encode(bw *util.BinWriter) {
bw.Write(p.Key)
}
// Decode decodes a bianry reader into a public key
func (p *PublicKey) Decode(br *util.BinReader) {
var prefix uint8
br.Read(&prefix)
// Compressed public keys.
if prefix == 0x02 || prefix == 0x03 {
p.Key = make([]byte, 32)
br.Read(p.Key)
} else if prefix == 0x04 {
p.Key = make([]byte, 65)
br.Read(p.Key)
} else if prefix == 0x00 {
// do nothing, For infinity, the p.Key == 0x00, included in the prefix
} else {
br.Err = errors.New("Prefix not recognised for public key")
return
}
p.Key = append([]byte{prefix}, p.Key...)
}

View file

@ -0,0 +1,90 @@
package transaction
import (
"errors"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// Publish represents a publish transaction on the neo network
type Publish struct {
*Base
Script []byte
ParamList []ParamType
ReturnType ParamType
NeedStorage byte
Name string
CodeVersion string
Author string
Email string
Description string
}
//NewPublish returns a publish transaction
func NewPublish(ver version.TX) *Publish {
basicTrans := createBaseTransaction(types.Publish, ver)
Publish := &Publish{}
Publish.Base = basicTrans
Publish.encodeExclusive = Publish.encodeExcl
Publish.decodeExclusive = Publish.decodeExcl
return Publish
}
func (p *Publish) encodeExcl(bw *util.BinWriter) {
bw.VarBytes(p.Script)
bw.VarUint(uint64(len(p.ParamList)))
for _, param := range p.ParamList {
bw.Write(param)
}
bw.Write(p.ReturnType)
switch p.Version {
case 0:
p.NeedStorage = byte(0)
case 1:
bw.Write(p.NeedStorage)
default:
bw.Err = errors.New("Version Number unknown for Publish Transaction")
}
bw.VarString(p.Name)
bw.VarString(p.CodeVersion)
bw.VarString(p.Author)
bw.VarString(p.Email)
bw.VarString(p.Description)
}
func (p *Publish) decodeExcl(br *util.BinReader) {
p.Script = br.VarBytes()
lenParams := br.VarUint()
p.ParamList = make([]ParamType, lenParams)
for i := 0; i < int(lenParams); i++ {
var ptype uint8
br.Read(&ptype)
p.ParamList[i] = ParamType(ptype)
}
var rtype uint8
br.Read(&rtype)
p.ReturnType = ParamType(rtype)
switch p.Version {
case 0:
p.NeedStorage = byte(0)
case 1:
br.Read(&p.NeedStorage)
default:
br.Err = errors.New("Version Number unknown for Publish Transaction")
}
p.Name = br.VarString()
p.CodeVersion = br.VarString()
p.Author = br.VarString()
p.Email = br.VarString()
p.Description = br.VarString()
}

View file

@ -0,0 +1,33 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodePublish(t *testing.T) {
///transaction taken from neo-python; can be found on testnet 5467a1fc8723ceffa8e5ee59399b02eea1df6fbaa53768c6704b90b960d223fa
// taken from neo-python;
rawtx := "d000fd3f01746b4c04000000004c04000000004c040000000061681e416e745368617265732e426c6f636b636861696e2e476574486569676874681d416e745368617265732e426c6f636b636861696e2e476574426c6f636b744c0400000000948c6c766b947275744c0402000000936c766b9479744c0400000000948c6c766b9479681d416e745368617265732e4865616465722e47657454696d657374616d70a0744c0401000000948c6c766b947275744c0401000000948c6c766b9479641b004c0400000000744c0402000000948c6c766b947275623000744c0401000000936c766b9479744c0400000000936c766b9479ac744c0402000000948c6c766b947275620300744c0402000000948c6c766b947961748c6c766b946d748c6c766b946d748c6c766b946d746c768c6b946d746c768c6b946d746c768c6b946d6c75660302050001044c6f636b0c312e302d70726576696577310a4572696b205a68616e67126572696b40616e747368617265732e6f7267234c6f636b20796f75722061737365747320756e74696c20612074696d657374616d702e00014e23ac4c4851f93407d4c59e1673171f39859db9e7cac72540cd3cc1ae0cca87000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6000ebcaaa0d00000067f97110a66136d38badc7b9f88eab013027ce49014140c298da9f06d5687a0bb87ea3bba188b7dcc91b9667ea5cb71f6fdefe388f42611df29be9b2d6288655b9f2188f46796886afc3b37d8b817599365d9e161ecfb62321034b44ed9c8a88fb2497b6b57206cc08edd42c5614bd1fee790e5b795dee0f4e11ac"
rawtxBytes, _ := hex.DecodeString(rawtx)
publ := NewPublish(30)
r := bytes.NewReader(rawtxBytes)
err := publ.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Publish, publ.Type)
buf := new(bytes.Buffer)
err = publ.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes()))
assert.Equal(t, "5467a1fc8723ceffa8e5ee59399b02eea1df6fbaa53768c6704b90b960d223fa", publ.Hash.ReverseString())
}

View file

@ -0,0 +1,58 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
"github.com/CityOfZion/neo-go/pkg/wire/util/fixed8"
)
// Register represents a register transaction on the neo network
type Register struct {
*Base
// The type of the asset being registered.
AssetType AssetType
// Name of the asset being registered.
Name string
// Amount registered
// Unlimited mode -0.00000001
Amount fixed8.Fixed8
// Decimals
Precision uint8
// Public key of the owner
Owner PublicKey
Admin util.Uint160
}
//NewRegister returns a register transaction
func NewRegister(ver version.TX) *Register {
basicTrans := createBaseTransaction(types.Register, ver)
Register := &Register{}
Register.Base = basicTrans
Register.encodeExclusive = Register.encodeExcl
Register.decodeExclusive = Register.decodeExcl
return Register
}
func (r *Register) encodeExcl(bw *util.BinWriter) {
bw.Write(r.AssetType)
bw.VarString(r.Name)
bw.Write(r.Amount)
bw.Write(r.Precision)
r.Owner.Encode(bw)
bw.Write(r.Admin)
}
func (r *Register) decodeExcl(br *util.BinReader) {
br.Read(&r.AssetType)
r.Name = br.VarString()
br.Read(&r.Amount)
br.Read(&r.Precision)
r.Owner.Decode(br)
br.Read(&r.Admin)
}

View file

@ -0,0 +1,54 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeRegister(t *testing.T) {
// transaction taken from neo-python; can be found on testnet 0c092117b4ba47b81001712425e6e7f760a637695eaf23741ba335925b195ecd
rawtx := "400060245b7b226c616e67223a227a682d434e222c226e616d65223a2254657374436f696e227d5dffffffffffffffff08034b44ed9c8a88fb2497b6b57206cc08edd42c5614bd1fee790e5b795dee0f4e1167f97110a66136d38badc7b9f88eab013027ce4900014423a26aeca49cdeeb9522c720e1ae3a93bbe27d53662839b16a438305c20906010001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c60001e1a210b00000067f97110a66136d38badc7b9f88eab013027ce490141405d8223ec807e3416a220a75ef9805dfa2e36bd4f6dcc7372373aa45f15c7fadfc96a8642e52acf56c2c66d549be4ba820484873d5cada00b9c1ce9674fbf96382321034b44ed9c8a88fb2497b6b57206cc08edd42c5614bd1fee790e5b795dee0f4e11ac"
rawtxBytes, _ := hex.DecodeString(rawtx)
reg := NewRegister(0)
r := bytes.NewReader(rawtxBytes)
err := reg.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Register, reg.Type)
buf := new(bytes.Buffer)
err = reg.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes()))
assert.Equal(t, "0c092117b4ba47b81001712425e6e7f760a637695eaf23741ba335925b195ecd", reg.Hash.ReverseString())
}
func TestEncodeDecodeGenesisRegister(t *testing.T) {
// genesis transaction taken from mainnet; can be found on mainnet(Block 0) : c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b
rawtx := "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000"
rawtxBytes, _ := hex.DecodeString(rawtx)
reg := NewRegister(0)
r := bytes.NewReader(rawtxBytes)
err := reg.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.Register, reg.Type)
buf := new(bytes.Buffer)
err = reg.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtx, hex.EncodeToString(buf.Bytes()))
assert.Equal(t, "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", reg.Hash.ReverseString())
}

View file

@ -0,0 +1,43 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/version"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
//StateTX represents a state transaction on the neo network
// XXX: TX postfix here as `state` is ambiguous. We can remove it for consistency
type StateTX struct {
*Base
Descriptors []*StateDescriptor
}
//NewStateTX returns a state transaction
func NewStateTX(ver version.TX) *StateTX {
basicTrans := createBaseTransaction(types.State, ver)
StateTX := &StateTX{}
StateTX.Base = basicTrans
StateTX.encodeExclusive = StateTX.encodeExcl
StateTX.decodeExclusive = StateTX.decodeExcl
return StateTX
}
func (s *StateTX) encodeExcl(bw *util.BinWriter) {
bw.VarUint(uint64(len(s.Descriptors)))
for _, desc := range s.Descriptors {
desc.Encode(bw)
}
}
func (s *StateTX) decodeExcl(br *util.BinReader) {
lenDesc := br.VarUint()
s.Descriptors = make([]*StateDescriptor, lenDesc)
for i := 0; i < int(lenDesc); i++ {
s.Descriptors[i] = &StateDescriptor{}
s.Descriptors[i].Decode(br)
}
}

View file

@ -0,0 +1,47 @@
package transaction
import (
"bytes"
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
"github.com/stretchr/testify/assert"
)
func TestEncodeDecodeState(t *testing.T) {
// transaction taken from testnet 8abf5ebdb9a8223b12109513647f45bd3c0a6cf1a6346d56684cff71ba308724
rawtx := "900001482103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c10a5265676973746572656401010001cb4184f0a96e72656c1fbdd4f75cca567519e909fd43cefcec13d6c6abcb92a1000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6000b8fb050109000071f9cf7f0ec74ec0b0f28a92b12e1081574c0af00141408780d7b3c0aadc5398153df5e2f1cf159db21b8b0f34d3994d865433f79fafac41683783c48aef510b67660e3157b701b9ca4dd9946a385d578fba7dd26f4849232103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1ac"
rawtxBytes, _ := hex.DecodeString(rawtx)
s := NewStateTX(0)
r := bytes.NewReader(rawtxBytes)
err := s.Decode(r)
assert.Equal(t, nil, err)
assert.Equal(t, types.State, s.Type)
assert.Equal(t, 1, len(s.Inputs))
input := s.Inputs[0]
assert.Equal(t, "a192cbabc6d613ecfcce43fd09e9197556ca5cf7d4bd1f6c65726ea9f08441cb", input.PrevHash.ReverseString())
assert.Equal(t, uint16(0), input.PrevIndex)
assert.Equal(t, 1, len(s.Descriptors))
descriptor := s.Descriptors[0]
assert.Equal(t, "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1", hex.EncodeToString(descriptor.Key))
assert.Equal(t, "52656769737465726564", hex.EncodeToString(descriptor.Value))
assert.Equal(t, "\x01", descriptor.Field)
assert.Equal(t, Validator, descriptor.Type)
// Encode
buf := new(bytes.Buffer)
err = s.Encode(buf)
assert.Equal(t, nil, err)
assert.Equal(t, rawtxBytes, buf.Bytes())
assert.Equal(t, "8abf5ebdb9a8223b12109513647f45bd3c0a6cf1a6346d56684cff71ba308724", s.Hash.ReverseString())
}

View file

@ -0,0 +1,55 @@
package transaction
import (
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// DescStateType represents the type of StateDescriptor.
type DescStateType uint8
// Valid DescStateType constants.
const (
Account DescStateType = 0x40
Validator DescStateType = 0x48
)
// StateDescriptor represents a state descriptor on the neo network
// used in a state transaction
type StateDescriptor struct {
Type DescStateType
Key []byte
Value []byte
Field string
}
// Decode decodes a binary reader into a state descriptor
func (s *StateDescriptor) Decode(br *util.BinReader) {
br.Read(&s.Type)
keyLen := br.VarUint()
s.Key = make([]byte, keyLen)
br.Read(s.Key)
valLen := br.VarUint()
s.Value = make([]byte, valLen)
br.Read(s.Value)
fieldLen := br.VarUint()
field := make([]byte, fieldLen)
br.Read(field)
s.Field = string(field)
}
//Encode encodes a state descriptor into a binary writer
func (s *StateDescriptor) Encode(bw *util.BinWriter) {
bw.Write(s.Type)
bw.VarUint(uint64(len(s.Key)))
bw.Write(s.Key)
bw.VarUint(uint64(len(s.Value)))
bw.Write(s.Value)
bw.VarString(s.Field)
}

View file

@ -0,0 +1,63 @@
package types
import (
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// TX is the type of a transaction.
type TX uint8
// List of transaction types
const (
Miner TX = 0x00
Issue TX = 0x01
Claim TX = 0x02
Enrollment TX = 0x20
Voting TX = 0x24
Register TX = 0x40
Contract TX = 0x80
State TX = 0x90
Agency TX = 0xb0
Publish TX = 0xd0
Invocation TX = 0xd1
)
// Encode encodes a tx type into the binary writer
func (t *TX) Encode(bw *util.BinWriter) {
bw.Write(t)
}
// Decode decodes a binary reader into a tx type
func (t *TX) Decode(br *util.BinReader) {
br.Read(t)
}
// String implements the stringer interface.
func (t TX) String() string {
switch t {
case Miner:
return "MinerTransaction"
case Issue:
return "IssueTransaction"
case Claim:
return "ClaimTransaction"
case Enrollment:
return "EnrollmentTransaction"
case Voting:
return "VotingTransaction"
case Register:
return "RegisterTransaction"
case Contract:
return "ContractTransaction"
case State:
return "StateTransaction"
case Agency:
return "AgencyTransaction"
case Publish:
return "PublishTransaction"
case Invocation:
return "InvocationTransaction"
default:
return "UnkownTransaction"
}
}

View file

@ -0,0 +1,62 @@
package transaction
import (
"bufio"
"encoding/hex"
"errors"
"github.com/CityOfZion/neo-go/pkg/wire/payload/transaction/types"
)
// FromReader returns a transaction from a bufio.Reader
func FromReader(reader *bufio.Reader) (Transactioner, error) {
t, err := reader.Peek(1)
typ := types.TX(t[0])
var trans Transactioner
switch typ {
case types.Miner:
miner := NewMiner(0)
err = miner.Decode(reader)
trans = miner
case types.Contract:
contract := NewContract(0)
err = contract.Decode(reader)
trans = contract
case types.Invocation:
invoc := NewInvocation(0)
err = invoc.Decode(reader)
trans = invoc
case types.Claim:
claim := NewClaim(0)
err = claim.Decode(reader)
trans = claim
case types.Register:
reg := NewRegister(0)
err = reg.Decode(reader)
trans = reg
case types.Issue:
iss := NewIssue(0)
err = iss.Decode(reader)
trans = iss
case types.Publish:
pub := NewPublish(0)
err = pub.Decode(reader)
trans = pub
case types.State:
state := NewStateTX(0)
err = state.Decode(reader)
trans = state
case types.Enrollment:
enr := NewEnrollment(0)
err = enr.Decode(reader)
trans = enr
case types.Agency:
err = errors.New("unsupported transaction type: Agency")
default:
err = errors.New("unsupported transaction with byte type " + hex.EncodeToString([]byte{t[0]}))
}
return trans, err
}

View file

@ -0,0 +1,24 @@
package version
import (
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// TX represents a tx version
type TX uint8
// List of latest tx version
const (
Contract TX = 0
Invocation TX = 1
)
// Encode encodes the tx version into the binary writer
func (v *TX) Encode(bw *util.BinWriter) {
bw.Write(v)
}
// Decode decodes the binary reader into a tx type
func (v *TX) Decode(br *util.BinReader) {
br.Read(v)
}

View file

@ -0,0 +1,44 @@
package protocol
//Version represents the latest protocol version for the neo node
type Version uint32
const (
// DefaultVersion is the nodes default protocol version
DefaultVersion Version = 0
// UserAgent is the nodes user agent or human-readable name
UserAgent = "/NEO-GO/"
)
// ServiceFlag indicates the services provided by the node. 1 = P2P Full Node
type ServiceFlag uint64
// List of Services offered by the node
const (
NodePeerService ServiceFlag = 1
// BloomFilerService ServiceFlag = 2 // Not implemented
// PrunedNode ServiceFlag = 3 // Not implemented
// LightNode ServiceFlag = 4 // Not implemented
)
// Magic is the network that NEO is running on
type Magic uint32
// List of possible networks
const (
MainNet Magic = 7630401
TestNet Magic = 0x74746e41
)
// String implements the stringer interface
func (m Magic) String() string {
switch m {
case MainNet:
return "Mainnet"
case TestNet:
return "Testnet"
default:
return "UnknownNet"
}
}

View file

@ -0,0 +1,33 @@
package checksum
import (
"bytes"
"encoding/binary"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
)
// Compare calculates the checksum of b
// then compares it with the `have` checksum passed as a parameter
func Compare(have uint32, b []byte) bool {
want := FromBytes(b)
return have == want
}
// FromBuf calculates the checksum of a buffer
func FromBuf(buf *bytes.Buffer) uint32 {
return FromBytes(buf.Bytes())
}
// FromBytes calculates the checksum of a byte slice
func FromBytes(buf []byte) uint32 {
b, err := hash.DoubleSha256(buf)
if err != nil {
return 0
}
// checksum := SumSHA256(SumSHA256(buf.Bytes()))
return binary.LittleEndian.Uint32(b.Bytes()[:4])
}

View file

@ -0,0 +1,43 @@
package address
import (
"github.com/CityOfZion/neo-go/pkg/crypto/base58"
"github.com/CityOfZion/neo-go/pkg/wire/util"
)
// ToScriptHash converts an address to a script hash
func ToScriptHash(address string) string {
a, err := Uint160Decode(address)
if err != nil {
return ""
}
return a.String()
}
// ToReverseScriptHash converts an address to a reverse script hash
func ToReverseScriptHash(address string) string {
a, err := Uint160Decode(address)
if err != nil {
return ""
}
return a.ReverseString()
}
// FromUint160 returns the "NEO address" from the given
// Uint160.
func FromUint160(u util.Uint160) (string, error) {
// Dont forget to prepend the Address version 0x17 (23) A
b := append([]byte{0x17}, u.Bytes()...)
return base58.CheckEncode(b)
}
// Uint160Decode attempts to decode the given NEO address string
// into an Uint160.
func Uint160Decode(s string) (u util.Uint160, err error) {
b, err := base58.CheckDecode(s)
if err != nil {
return u, err
}
return util.Uint160DecodeBytes(b[1:21])
}

View file

@ -0,0 +1,17 @@
package address
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestScriptHash(t *testing.T) {
address := "AJeAEsmeD6t279Dx4n2HWdUvUmmXQ4iJvP"
hash := ToScriptHash(address)
reverseHash := ToReverseScriptHash(address)
assert.Equal(t, "b28427088a3729b2536d10122960394e8be6721f", reverseHash)
assert.Equal(t, "1f72e68b4e39602912106d53b229378a082784b2", hash)
}

View file

@ -0,0 +1,71 @@
package util
import (
"encoding/binary"
"io"
)
//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
Err error
}
// Read reads from the underlying io.Reader
// into the interface v in LE
func (r *BinReader) Read(v interface{}) {
if r.Err != nil {
return
}
r.Err = binary.Read(r.R, binary.LittleEndian, v)
}
// ReadBigEnd reads from the underlying io.Reader
// into the interface v in BE
func (r *BinReader) ReadBigEnd(v interface{}) {
if r.Err != nil {
return
}
r.Err = binary.Read(r.R, binary.BigEndian, v)
}
//VarUint reads a variable integer from the
// underlying reader
func (r *BinReader) VarUint() uint64 {
var b uint8
r.Err = binary.Read(r.R, binary.LittleEndian, &b)
if b == 0xfd {
var v uint16
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)
return uint64(v)
}
if b == 0xff {
var v uint64
r.Err = binary.Read(r.R, binary.LittleEndian, &v)
return v
}
return uint64(b)
}
// VarBytes reads the next set of bytes from the underlying reader.
// VarUInt is used to determine how large that slice is
func (r *BinReader) VarBytes() []byte {
n := r.VarUint()
b := make([]byte, n)
r.Read(b)
return b
}
// VarString calls VarBytes and casts the results as a string
func (r *BinReader) VarString() string {
b := r.VarBytes()
return string(b)
}

View file

@ -0,0 +1,75 @@
package util
import (
"encoding/binary"
"errors"
"io"
)
//BinWriter is a convenient wrapper around a io.Writer and err object
// Used to simplify error handling when writing into a io.Writer
// from a struct with many fields
type BinWriter struct {
W io.Writer
Err error
}
// Write writes into the underlying io.Writer from an object v in LE format
func (w *BinWriter) Write(v interface{}) {
if w.Err != nil {
return
}
w.Err = binary.Write(w.W, binary.LittleEndian, v)
}
// WriteBigEnd writes into the underlying io.Writer from an object v in BE format
// Only used for IP and PORT. Additional method makes the default LittleEndian case clean
func (w *BinWriter) WriteBigEnd(v interface{}) {
if w.Err != nil {
return
}
w.Err = binary.Write(w.W, binary.BigEndian, v)
}
// VarUint writes a uint64 into the underlying writer
func (w *BinWriter) VarUint(val uint64) {
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))
return
}
if val < 0xFFFF {
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))
return
}
w.Err = binary.Write(w.W, binary.LittleEndian, byte(0xff))
w.Err = binary.Write(w.W, binary.LittleEndian, val)
}
// VarBytes writes a variable length byte array into the underlying io.Writer
func (w *BinWriter) VarBytes(b []byte) {
w.VarUint(uint64(len(b)))
w.Write(b)
}
//VarString casts the string as a byte slice and calls VarBytes
func (w *BinWriter) VarString(s string) {
w.VarBytes([]byte(s))
}

View file

@ -0,0 +1,62 @@
package fixed8
import (
"errors"
"fmt"
"strconv"
)
const (
decimals = 100000000
)
var errInvalidString = errors.New("Fixed8 must satisfy following regex \\d+(\\.\\d{1,8})?")
// Fixed8 represents a fixed-point number with precision 10^-8.
type Fixed8 int64
// String implements the Stringer interface.
func (f Fixed8) String() string {
val := f.Value()
return strconv.FormatFloat(val, 'f', -1, 64)
}
// Value returns the original value representing the Fixed8.
func (f Fixed8) Value() float64 {
return float64(f) / float64(decimals)
}
// Add adds two Fixed8 values together
func (f Fixed8) Add(val Fixed8) Fixed8 {
a := int64(f.Value())
b := int64(val.Value())
c := a + b
return FromInt(c)
}
//Sub subtracts two fixed values from each other
func (f Fixed8) Sub(val Fixed8) Fixed8 {
a := int64(f.Value())
b := int64(val.Value())
c := a - b
return FromInt(c)
}
//FromInt returns a Fixed8 objects from an int64
func FromInt(val int64) Fixed8 {
return Fixed8(val * decimals)
}
// FromFloat returns a Fixed8 object from a float64
func FromFloat(val float64) Fixed8 {
return Fixed8(val * decimals)
}
// FromString returns a Fixed8 object from a string
func FromString(val string) (Fixed8, error) {
res, err := strconv.ParseFloat(val, 64)
if err != nil {
return 0, fmt.Errorf("failed at parsing string %s", val)
}
return FromFloat(res), nil
}

View file

@ -0,0 +1,85 @@
package fixed8
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFixed8Value(t *testing.T) {
input := int64(12)
assert.Equal(t, float64(input), FromInt(input).Value())
}
func TestFixed8Add(t *testing.T) {
a := FromInt(1)
b := FromInt(2)
c := a.Add(b)
expected := float64(3)
assert.Equal(t, expected, c.Value())
}
func TestFixed8AddRecursive(t *testing.T) {
a := FromInt(1)
sum := int64(1)
for i := int64(2); i <= 10; i++ {
sum += i
b := FromInt(i)
c := a.Add(b)
a = c // 1 + 2 + 3 ... + 10
}
assert.Equal(t, float64(sum), a.Value())
}
func TestFromInt(t *testing.T) {
inputs := []int64{12, 23, 100, 456789}
for _, val := range inputs {
assert.Equal(t, Fixed8(val*decimals), FromInt(val))
assert.Equal(t, float64(val), FromInt(val).Value())
}
for _, val := range inputs {
valString := strconv.FormatInt(val, 10)
assert.Equal(t, valString, FromInt(val).String())
}
}
func TestFromFloat(t *testing.T) {
inputs := []float64{12.98, 23.87654333, 100.654322, 456789.12345665}
for _, val := range inputs {
assert.Equal(t, Fixed8(val*decimals), FromFloat(val))
assert.Equal(t, float64(val), FromFloat(val).Value())
}
}
func TestFromString(t *testing.T) {
inputs := []string{"9000", "100000000", "5", "10945", "20.45", "0.00000001"}
for _, val := range inputs {
n, err := FromString(val)
assert.Nil(t, err)
assert.Equal(t, val, n.String())
}
val := "123456789.12345678"
n, err := FromString(val)
assert.Nil(t, err)
assert.Equal(t, Fixed8(12345678912345678), n)
val = "901.2341"
n, err = FromString(val)
assert.Nil(t, err)
assert.Equal(t, Fixed8(90123410000), n)
}

View file

@ -0,0 +1,17 @@
package fileutils
import (
"os"
)
// UpdateFile appends a byte slice to a file
func UpdateFile(filename string, data []byte) error {
f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
dataWNewline := append(data, []byte("\n")...)
_, err = f.Write(dataWNewline)
err = f.Close()
return err
}

View file

@ -0,0 +1,20 @@
package iputils
import (
"log"
"net"
)
//GetLocalIP returns the ip address of the current node
// https://stackoverflow.com/a/37382208
func GetLocalIP() net.IP {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP
}

View file

@ -0,0 +1,15 @@
package slice
// Reverse return a reversed version of the given byte slice.
func Reverse(b []byte) []byte {
// Protect from big.Ints that have 1 len bytes.
if len(b) < 2 {
return b
}
dest := make([]byte, len(b))
for i, j := 0, len(b)-1; i < j+1; i, j = i+1, j-1 {
dest[i], dest[j] = b[j], b[i]
}
return dest
}

View file

@ -0,0 +1,33 @@
package slice
import (
"bytes"
"testing"
)
func TestSliceReverse(t *testing.T) {
arr := []byte{0x01, 0x02, 0x03, 0x04}
have := Reverse(arr)
want := []byte{0x04, 0x03, 0x02, 0x01}
if bytes.Compare(have, want) != 0 {
t.Fatalf("expected %v got %v", want, have)
}
}
func TestSliceReverseOddNumberOfElements(t *testing.T) {
arr := []byte{0x01, 0x02, 0x03, 0x04, 0x05}
have := Reverse(arr)
want := []byte{0x05, 0x04, 0x03, 0x02, 0x01}
if bytes.Compare(have, want) != 0 {
t.Fatalf("expected %v got %v", want, have)
}
}
// This tests a bug that occured with arrays of size 1
func TestSliceReverseLen2(t *testing.T) {
arr := []byte{0x01}
have := Reverse(arr)
want := []byte{0x01}
if bytes.Compare(have, want) != 0 {
t.Fatalf("expected %v got %v", want, have)
}
}

View file

@ -0,0 +1,91 @@
package util
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/CityOfZion/neo-go/pkg/wire/util/slice"
"golang.org/x/crypto/ripemd160"
)
const uint160Size = 20
// Uint160 is a 20 byte long unsigned integer.
type Uint160 [uint160Size]uint8
// Uint160DecodeString attempts to decode the given string into an Uint160.
func Uint160DecodeString(s string) (u Uint160, err error) {
if len(s) != uint160Size*2 {
return u, fmt.Errorf("expected string size of %d got %d", uint160Size*2, len(s))
}
b, err := hex.DecodeString(s)
if err != nil {
return u, err
}
return Uint160DecodeBytes(b)
}
// Uint160DecodeBytes attempts to decode the given bytes into an Uint160.
func Uint160DecodeBytes(b []byte) (u Uint160, err error) {
if len(b) != uint160Size {
return u, fmt.Errorf("expected byte size of %d got %d", uint160Size, len(b))
}
for i := 0; i < uint160Size; i++ {
u[i] = b[i]
}
return
}
// Uint160FromScript returns a Uint160 type from a raw script.
func Uint160FromScript(script []byte) (u Uint160, err error) {
sha := sha256.New()
sha.Write(script)
b := sha.Sum(nil)
ripemd := ripemd160.New()
ripemd.Write(b)
b = ripemd.Sum(nil)
return Uint160DecodeBytes(b)
}
// Bytes returns the byte slice representation of u.
func (u Uint160) Bytes() []byte {
b := make([]byte, uint160Size)
for i := 0; i < uint160Size; i++ {
b[i] = byte(u[i])
}
return b
}
// BytesReverse return a reversed byte representation of u.
func (u Uint160) BytesReverse() []byte {
return slice.Reverse(u.Bytes())
}
// String implements the stringer interface.
func (u Uint160) String() string {
return hex.EncodeToString(u.Bytes())
}
// ReverseString implements the stringer interface.
func (u Uint160) ReverseString() string {
return hex.EncodeToString(u.BytesReverse())
}
// Equals returns true if both Uint256 values are the same.
func (u Uint160) Equals(other Uint160) bool {
for i := 0; i < uint160Size; i++ {
if u[i] != other[i] {
return false
}
}
return true
}
// MarshalJSON implements the json marshaller interface.
func (u Uint160) MarshalJSON() ([]byte, error) {
return json.Marshal(
fmt.Sprintf("0x%s", hex.EncodeToString(slice.Reverse(u.Bytes()))),
)
}

View file

@ -0,0 +1,62 @@
package util
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUInt160DecodeString(t *testing.T) {
hexStr := "2d3b96ae1bcc5a585e075e3b81920210dec16302"
val, err := Uint160DecodeString(hexStr)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, hexStr, val.String())
}
func TestUint160DecodeBytes(t *testing.T) {
hexStr := "2d3b96ae1bcc5a585e075e3b81920210dec16302"
b, err := hex.DecodeString(hexStr)
if err != nil {
t.Fatal(err)
}
val, err := Uint160DecodeBytes(b)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, hexStr, val.String())
}
func TestUInt160Equals(t *testing.T) {
a := "2d3b96ae1bcc5a585e075e3b81920210dec16302"
b := "4d3b96ae1bcc5a585e075e3b81920210dec16302"
ua, err := Uint160DecodeString(a)
if err != nil {
t.Fatal(err)
}
ub, err := Uint160DecodeString(b)
if err != nil {
t.Fatal(err)
}
if ua.Equals(ub) {
t.Fatalf("%s and %s cannot be equal", ua, ub)
}
if !ua.Equals(ua) {
t.Fatalf("%s and %s must be equal", ua, ua)
}
}
func TestUInt160String(t *testing.T) {
hexStr := "b28427088a3729b2536d10122960394e8be6721f"
hexRevStr := "1f72e68b4e39602912106d53b229378a082784b2"
val, err := Uint160DecodeString(hexStr)
assert.Nil(t, err)
assert.Equal(t, hexStr, val.String())
assert.Equal(t, hexRevStr, val.ReverseString())
}

View file

@ -0,0 +1,77 @@
package util
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/CityOfZion/neo-go/pkg/wire/util/slice"
)
const uint256Size = 32
// Uint256 is a 32 byte long unsigned integer.
type Uint256 [uint256Size]uint8
// Uint256DecodeString attempts to decode the given string into an Uint256.
func Uint256DecodeString(s string) (u Uint256, err error) {
if len(s) != uint256Size*2 {
return u, fmt.Errorf("expected string size of %d got %d", uint256Size*2, len(s))
}
b, err := hex.DecodeString(s)
if err != nil {
return u, err
}
return Uint256DecodeBytes(b)
}
// Uint256DecodeBytes attempts to decode the given string into an Uint256.
func Uint256DecodeBytes(b []byte) (u Uint256, err error) {
if len(b) != uint256Size {
return u, fmt.Errorf("expected []byte of size %d got %d", uint256Size, len(b))
}
for i := 0; i < uint256Size; i++ {
u[i] = b[i]
}
return u, nil
}
// Bytes returns a byte slice representation of u.
func (u Uint256) Bytes() []byte {
b := make([]byte, uint256Size)
for i := 0; i < uint256Size; i++ {
b[i] = byte(u[i])
}
return b
}
// Reverse reverses the Uint256 object
func (u Uint256) Reverse() Uint256 {
res, _ := Uint256DecodeBytes(u.BytesReverse())
return res
}
// BytesReverse return a reversed byte representation of u.
func (u Uint256) BytesReverse() []byte {
return slice.Reverse(u.Bytes())
}
// Equals returns true if both Uint256 values are the same.
func (u Uint256) Equals(other Uint256) bool {
return u.String() == other.String()
}
// String implements the stringer interface.
func (u Uint256) String() string {
return hex.EncodeToString(u.Bytes())
}
// ReverseString displays a reverse string representation of Uint256.
func (u Uint256) ReverseString() string {
return hex.EncodeToString(slice.Reverse(u.Bytes()))
}
// MarshalJSON implements the json marshaller interface.
func (u Uint256) MarshalJSON() ([]byte, error) {
return json.Marshal(fmt.Sprintf("0x%s", u.String()))
}

View file

@ -0,0 +1,50 @@
package util
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUint256DecodeString(t *testing.T) {
hexStr := "f037308fa0ab18155bccfc08485468c112409ea5064595699e98c545f245f32d"
val, err := Uint256DecodeString(hexStr)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, hexStr, val.String())
}
func TestUint256DecodeBytes(t *testing.T) {
hexStr := "f037308fa0ab18155bccfc08485468c112409ea5064595699e98c545f245f32d"
b, err := hex.DecodeString(hexStr)
if err != nil {
t.Fatal(err)
}
val, err := Uint256DecodeBytes(b)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, hexStr, val.String())
}
func TestUInt256Equals(t *testing.T) {
a := "f037308fa0ab18155bccfc08485468c112409ea5064595699e98c545f245f32d"
b := "e287c5b29a1b66092be6803c59c765308ac20287e1b4977fd399da5fc8f66ab5"
ua, err := Uint256DecodeString(a)
if err != nil {
t.Fatal(err)
}
ub, err := Uint256DecodeString(b)
if err != nil {
t.Fatal(err)
}
if ua.Equals(ub) {
t.Fatalf("%s and %s cannot be equal", ua, ub)
}
if !ua.Equals(ua) {
t.Fatalf("%s and %s must be equal", ua, ua)
}
}

View file

@ -0,0 +1,49 @@
package util
import (
"bytes"
"crypto/sha256"
"io"
"io/ioutil"
)
// Convenience function
// BufferLength returns the length of a buffer as uint32
func BufferLength(buf *bytes.Buffer) uint32 {
return uint32(buf.Len())
}
// SumSHA256 returns the sha256 sum of the data
func SumSHA256(b []byte) []byte {
h := sha256.New()
h.Write(b)
return h.Sum(nil)
}
// CalculateHash takes a function with a binary writer and returns
// the double hash of the io.Writer
func CalculateHash(f func(bw *BinWriter)) (Uint256, error) {
buf := new(bytes.Buffer)
bw := &BinWriter{W: buf}
f(bw)
var hash Uint256
hash = sha256.Sum256(buf.Bytes())
hash = sha256.Sum256(hash.Bytes())
return hash, bw.Err
}
//ReaderToBuffer converts a io.Reader into a bytes.Buffer
func ReaderToBuffer(r io.Reader) (*bytes.Buffer, error) {
byt, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(byt)
return buf, nil
}