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:
parent
bb2568cc53
commit
ddd1d92ff1
183 changed files with 0 additions and 0 deletions
62
_pkg.dev/wire/Readme.md
Normal file
62
_pkg.dev/wire/Readme.md
Normal 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
42
_pkg.dev/wire/base.go
Normal 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)
|
||||
}
|
29
_pkg.dev/wire/command/command.go
Normal file
29
_pkg.dev/wire/command/command.go
Normal 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
156
_pkg.dev/wire/message.go
Normal 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
|
||||
}
|
55
_pkg.dev/wire/message_test.go
Normal file
55
_pkg.dev/wire/message_test.go
Normal 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
|
||||
}
|
67
_pkg.dev/wire/payload/block.go
Normal file
67
_pkg.dev/wire/payload/block.go
Normal 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
|
||||
}
|
172
_pkg.dev/wire/payload/block_test.go
Normal file
172
_pkg.dev/wire/payload/block_test.go
Normal file
File diff suppressed because one or more lines are too long
132
_pkg.dev/wire/payload/blockbase.go
Normal file
132
_pkg.dev/wire/payload/blockbase.go
Normal 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
|
||||
}
|
8
_pkg.dev/wire/payload/blockbase_test.go
Normal file
8
_pkg.dev/wire/payload/blockbase_test.go
Normal 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
|
||||
|
||||
}
|
64
_pkg.dev/wire/payload/maddr.go
Normal file
64
_pkg.dev/wire/payload/maddr.go
Normal 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
|
||||
}
|
40
_pkg.dev/wire/payload/maddr_test.go
Normal file
40
_pkg.dev/wire/payload/maddr_test.go
Normal 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)
|
||||
}
|
38
_pkg.dev/wire/payload/mblock.go
Normal file
38
_pkg.dev/wire/payload/mblock.go
Normal 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
|
||||
}
|
30
_pkg.dev/wire/payload/mgetaddr.go
Normal file
30
_pkg.dev/wire/payload/mgetaddr.go
Normal 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
|
||||
}
|
24
_pkg.dev/wire/payload/mgetaddr_test.go
Normal file
24
_pkg.dev/wire/payload/mgetaddr_test.go
Normal 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()))
|
||||
}
|
24
_pkg.dev/wire/payload/mgetblocks.go
Normal file
24
_pkg.dev/wire/payload/mgetblocks.go
Normal 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
|
||||
|
||||
}
|
27
_pkg.dev/wire/payload/mgetblocks_test.go
Normal file
27
_pkg.dev/wire/payload/mgetblocks_test.go
Normal 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())
|
||||
}
|
18
_pkg.dev/wire/payload/mgetdata.go
Normal file
18
_pkg.dev/wire/payload/mgetdata.go
Normal 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
|
||||
}
|
19
_pkg.dev/wire/payload/mgetdata_test.go
Normal file
19
_pkg.dev/wire/payload/mgetdata_test.go
Normal 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
|
||||
}
|
61
_pkg.dev/wire/payload/mgetheaders.go
Normal file
61
_pkg.dev/wire/payload/mgetheaders.go
Normal 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
|
||||
}
|
47
_pkg.dev/wire/payload/mgetheaders_test.go
Normal file
47
_pkg.dev/wire/payload/mgetheaders_test.go
Normal 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)
|
||||
}
|
82
_pkg.dev/wire/payload/mheaders.go
Normal file
82
_pkg.dev/wire/payload/mheaders.go
Normal 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
|
||||
}
|
82
_pkg.dev/wire/payload/mheaders_test.go
Normal file
82
_pkg.dev/wire/payload/mheaders_test.go
Normal 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()))
|
||||
}
|
114
_pkg.dev/wire/payload/minventory.go
Normal file
114
_pkg.dev/wire/payload/minventory.go
Normal 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
|
||||
}
|
78
_pkg.dev/wire/payload/minventory_test.go
Normal file
78
_pkg.dev/wire/payload/minventory_test.go
Normal 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))
|
||||
}
|
30
_pkg.dev/wire/payload/mmempool.go
Normal file
30
_pkg.dev/wire/payload/mmempool.go
Normal 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
|
||||
}
|
35
_pkg.dev/wire/payload/mtx.go
Normal file
35
_pkg.dev/wire/payload/mtx.go
Normal 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
|
||||
}
|
30
_pkg.dev/wire/payload/mverack.go
Normal file
30
_pkg.dev/wire/payload/mverack.go
Normal 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
|
||||
}
|
17
_pkg.dev/wire/payload/mverack_test.go
Normal file
17
_pkg.dev/wire/payload/mverack_test.go
Normal 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())
|
||||
}
|
93
_pkg.dev/wire/payload/mversion.go
Normal file
93
_pkg.dev/wire/payload/mversion.go
Normal 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
|
||||
}
|
59
_pkg.dev/wire/payload/mversion_test.go
Normal file
59
_pkg.dev/wire/payload/mversion_test.go
Normal 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)
|
||||
}
|
59
_pkg.dev/wire/payload/net_addr.go
Normal file
59
_pkg.dev/wire/payload/net_addr.go
Normal 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
|
||||
}
|
61
_pkg.dev/wire/payload/transaction/Attribute.go
Normal file
61
_pkg.dev/wire/payload/transaction/Attribute.go
Normal 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)
|
||||
}
|
||||
}
|
32
_pkg.dev/wire/payload/transaction/Input.go
Normal file
32
_pkg.dev/wire/payload/transaction/Input.go
Normal 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)
|
||||
}
|
38
_pkg.dev/wire/payload/transaction/Output.go
Normal file
38
_pkg.dev/wire/payload/transaction/Output.go
Normal 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)
|
||||
}
|
37
_pkg.dev/wire/payload/transaction/Witness.go
Normal file
37
_pkg.dev/wire/payload/transaction/Witness.go
Normal 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
|
||||
}
|
16
_pkg.dev/wire/payload/transaction/assettype.go
Normal file
16
_pkg.dev/wire/payload/transaction/assettype.go
Normal 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
|
||||
)
|
49
_pkg.dev/wire/payload/transaction/attr_usage.go
Normal file
49
_pkg.dev/wire/payload/transaction/attr_usage.go
Normal 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
|
||||
)
|
202
_pkg.dev/wire/payload/transaction/base.go
Normal file
202
_pkg.dev/wire/payload/transaction/base.go
Normal 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
|
||||
}
|
43
_pkg.dev/wire/payload/transaction/claim.go
Normal file
43
_pkg.dev/wire/payload/transaction/claim.go
Normal 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)
|
||||
}
|
||||
|
||||
}
|
38
_pkg.dev/wire/payload/transaction/claim_test.go
Normal file
38
_pkg.dev/wire/payload/transaction/claim_test.go
Normal 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()))
|
||||
}
|
28
_pkg.dev/wire/payload/transaction/contract.go
Normal file
28
_pkg.dev/wire/payload/transaction/contract.go
Normal 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) {}
|
45
_pkg.dev/wire/payload/transaction/contract_test.go
Normal file
45
_pkg.dev/wire/payload/transaction/contract_test.go
Normal 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())
|
||||
|
||||
}
|
33
_pkg.dev/wire/payload/transaction/enrollment.go
Normal file
33
_pkg.dev/wire/payload/transaction/enrollment.go
Normal 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)
|
||||
}
|
30
_pkg.dev/wire/payload/transaction/enrollment_test.go
Normal file
30
_pkg.dev/wire/payload/transaction/enrollment_test.go
Normal 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())
|
||||
}
|
61
_pkg.dev/wire/payload/transaction/invocation.go
Normal file
61
_pkg.dev/wire/payload/transaction/invocation.go
Normal 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
|
||||
}
|
78
_pkg.dev/wire/payload/transaction/invocation_test.go
Normal file
78
_pkg.dev/wire/payload/transaction/invocation_test.go
Normal 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())
|
||||
|
||||
}
|
34
_pkg.dev/wire/payload/transaction/issue.go
Normal file
34
_pkg.dev/wire/payload/transaction/issue.go
Normal 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) {}
|
8
_pkg.dev/wire/payload/transaction/issue_test.go
Normal file
8
_pkg.dev/wire/payload/transaction/issue_test.go
Normal 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.
|
||||
}
|
32
_pkg.dev/wire/payload/transaction/miner.go
Normal file
32
_pkg.dev/wire/payload/transaction/miner.go
Normal 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)
|
||||
}
|
37
_pkg.dev/wire/payload/transaction/miner_test.go
Normal file
37
_pkg.dev/wire/payload/transaction/miner_test.go
Normal 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())
|
||||
|
||||
}
|
17
_pkg.dev/wire/payload/transaction/paramtype.go
Normal file
17
_pkg.dev/wire/payload/transaction/paramtype.go
Normal 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
|
||||
)
|
38
_pkg.dev/wire/payload/transaction/publickey.go
Normal file
38
_pkg.dev/wire/payload/transaction/publickey.go
Normal 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...)
|
||||
}
|
90
_pkg.dev/wire/payload/transaction/publish.go
Normal file
90
_pkg.dev/wire/payload/transaction/publish.go
Normal 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()
|
||||
}
|
33
_pkg.dev/wire/payload/transaction/publish_test.go
Normal file
33
_pkg.dev/wire/payload/transaction/publish_test.go
Normal 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())
|
||||
|
||||
}
|
58
_pkg.dev/wire/payload/transaction/register.go
Normal file
58
_pkg.dev/wire/payload/transaction/register.go
Normal 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)
|
||||
}
|
54
_pkg.dev/wire/payload/transaction/register_test.go
Normal file
54
_pkg.dev/wire/payload/transaction/register_test.go
Normal 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())
|
||||
}
|
43
_pkg.dev/wire/payload/transaction/state.go
Normal file
43
_pkg.dev/wire/payload/transaction/state.go
Normal 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)
|
||||
}
|
||||
}
|
47
_pkg.dev/wire/payload/transaction/state_test.go
Normal file
47
_pkg.dev/wire/payload/transaction/state_test.go
Normal 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())
|
||||
}
|
55
_pkg.dev/wire/payload/transaction/statedescriptor.go
Normal file
55
_pkg.dev/wire/payload/transaction/statedescriptor.go
Normal 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)
|
||||
}
|
63
_pkg.dev/wire/payload/transaction/types/types.go
Normal file
63
_pkg.dev/wire/payload/transaction/types/types.go
Normal 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"
|
||||
}
|
||||
}
|
62
_pkg.dev/wire/payload/transaction/util.go
Normal file
62
_pkg.dev/wire/payload/transaction/util.go
Normal 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
|
||||
}
|
24
_pkg.dev/wire/payload/transaction/version/version.go
Normal file
24
_pkg.dev/wire/payload/transaction/version/version.go
Normal 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)
|
||||
}
|
44
_pkg.dev/wire/protocol/protocol.go
Normal file
44
_pkg.dev/wire/protocol/protocol.go
Normal 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"
|
||||
}
|
||||
}
|
33
_pkg.dev/wire/util/Checksum/checksum.go
Normal file
33
_pkg.dev/wire/util/Checksum/checksum.go
Normal 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])
|
||||
}
|
43
_pkg.dev/wire/util/address/address.go
Normal file
43
_pkg.dev/wire/util/address/address.go
Normal 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])
|
||||
}
|
17
_pkg.dev/wire/util/address/address_test.go
Normal file
17
_pkg.dev/wire/util/address/address_test.go
Normal 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)
|
||||
}
|
71
_pkg.dev/wire/util/binaryReader.go
Normal file
71
_pkg.dev/wire/util/binaryReader.go
Normal 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)
|
||||
}
|
75
_pkg.dev/wire/util/binaryWriter.go
Normal file
75
_pkg.dev/wire/util/binaryWriter.go
Normal 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))
|
||||
}
|
62
_pkg.dev/wire/util/fixed8/fixed8.go
Normal file
62
_pkg.dev/wire/util/fixed8/fixed8.go
Normal 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
|
||||
}
|
85
_pkg.dev/wire/util/fixed8/fixed8_test.go
Normal file
85
_pkg.dev/wire/util/fixed8/fixed8_test.go
Normal 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)
|
||||
}
|
17
_pkg.dev/wire/util/io/io.go
Normal file
17
_pkg.dev/wire/util/io/io.go
Normal 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
|
||||
}
|
20
_pkg.dev/wire/util/ip/ip.go
Normal file
20
_pkg.dev/wire/util/ip/ip.go
Normal 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
|
||||
}
|
15
_pkg.dev/wire/util/slice/slice.go
Normal file
15
_pkg.dev/wire/util/slice/slice.go
Normal 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
|
||||
}
|
33
_pkg.dev/wire/util/slice/slice_test.go
Normal file
33
_pkg.dev/wire/util/slice/slice_test.go
Normal 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)
|
||||
}
|
||||
}
|
91
_pkg.dev/wire/util/uint160.go
Normal file
91
_pkg.dev/wire/util/uint160.go
Normal 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()))),
|
||||
)
|
||||
}
|
62
_pkg.dev/wire/util/uint160_test.go
Normal file
62
_pkg.dev/wire/util/uint160_test.go
Normal 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())
|
||||
|
||||
}
|
77
_pkg.dev/wire/util/uint256.go
Normal file
77
_pkg.dev/wire/util/uint256.go
Normal 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()))
|
||||
}
|
50
_pkg.dev/wire/util/uint256_test.go
Normal file
50
_pkg.dev/wire/util/uint256_test.go
Normal 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)
|
||||
}
|
||||
}
|
49
_pkg.dev/wire/util/util.go
Normal file
49
_pkg.dev/wire/util/util.go
Normal 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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue