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

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