Smartcontract (#39)
* deleted transfer_output added asset type and transaction result to core * removed writing 0x00 when buffer length is 0 * Refactored emit into VM package + moved tx to own package. * implemented transaction along with claimTransaction. * refactored naming of transaction + added decode address for uint160 types * removed unnecessary folder and files. * transaction/smartcontract logic * bumped version 0.24.0
This commit is contained in:
parent
42195b1af4
commit
1a1a19da7d
36 changed files with 1066 additions and 170 deletions
26
Gopkg.lock
generated
26
Gopkg.lock
generated
|
@ -1,18 +1,42 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/anthdm/neo-go"
|
||||
packages = ["pkg/util"]
|
||||
revision = "da01cdae5c15dcf7d6a046b27d2710c95e2cc66a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/anthdm/rfc6979"
|
||||
packages = ["."]
|
||||
revision = "6a90f24967ebb1aa57b22f74a13dbb3faad8cf3d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/snappy"
|
||||
packages = ["."]
|
||||
revision = "553a641470496b2327abcac10b36396bd98e45c9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/syndtr/goleveldb"
|
||||
|
@ -74,6 +98,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "74c6b0a11057b8c0fd68342f85e010e323cda6407bec2a889f161b5890928aaf"
|
||||
inputs-digest = "069a738aa1487766b26f9efb8103d2ce0526d43c83049cb5b792f0edf91568de"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -47,3 +47,7 @@
|
|||
[[constraint]]
|
||||
name = "golang.org/x/text"
|
||||
version = "0.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.23.0
|
||||
0.24.0
|
||||
|
|
18
pkg/core/asset_type.go
Normal file
18
pkg/core/asset_type.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package core
|
||||
|
||||
// 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 = DutyFlag | 0x10
|
||||
Invoice = DutyFlag | 0x18
|
||||
Token = CreditFlag | 0x20
|
||||
)
|
|
@ -8,6 +8,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
|
@ -26,12 +27,12 @@ type BlockBase struct {
|
|||
Index uint32
|
||||
// Random number also called nonce
|
||||
ConsensusData uint64
|
||||
// contract addresss of the next miner
|
||||
// Contract addresss of the next miner
|
||||
NextConsensus util.Uint160
|
||||
// fixed to 1
|
||||
_ uint8 // padding
|
||||
// Script used to validate the block
|
||||
Script *Witness
|
||||
Script *transaction.Witness
|
||||
}
|
||||
|
||||
// DecodeBinary implements the payload interface.
|
||||
|
@ -66,11 +67,11 @@ func (b *BlockBase) DecodeBinary(r io.Reader) error {
|
|||
return fmt.Errorf("format error: padding must equal 1 got %d", padding)
|
||||
}
|
||||
|
||||
b.Script = &Witness{}
|
||||
b.Script = &transaction.Witness{}
|
||||
return b.Script.DecodeBinary(r)
|
||||
}
|
||||
|
||||
// Hash return the hash of the block.
|
||||
// Hash returns the hash of the block.
|
||||
// When calculating the hash value of the block, instead of calculating the entire block,
|
||||
// only first seven fields in the block head will be calculated, which are
|
||||
// version, PrevBlock, MerkleRoot, timestamp, and height, the nonce, NextMiner.
|
||||
|
@ -158,7 +159,7 @@ func (h *Header) EncodeBinary(w io.Writer) error {
|
|||
type Block struct {
|
||||
BlockBase
|
||||
// transaction list
|
||||
Transactions []*Transaction
|
||||
Transactions []*transaction.Transaction
|
||||
}
|
||||
|
||||
// Header returns a pointer to the head of the block (BlockHead).
|
||||
|
@ -171,13 +172,13 @@ func (b *Block) Header() *Header {
|
|||
// Verify the integrity of the block.
|
||||
func (b *Block) Verify(full bool) bool {
|
||||
// The first TX has to be a miner transaction.
|
||||
if b.Transactions[0].Type != MinerTX {
|
||||
if b.Transactions[0].Type != transaction.MinerType {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the first TX is a minerTX then all others cant.
|
||||
for _, tx := range b.Transactions[1:] {
|
||||
if tx.Type == MinerTX {
|
||||
if tx.Type == transaction.MinerType {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -202,9 +203,9 @@ func (b *Block) DecodeBinary(r io.Reader) error {
|
|||
}
|
||||
|
||||
lentx := util.ReadVarUint(r)
|
||||
b.Transactions = make([]*Transaction, lentx)
|
||||
b.Transactions = make([]*transaction.Transaction, lentx)
|
||||
for i := 0; i < int(lentx); i++ {
|
||||
tx := &Transaction{}
|
||||
tx := &transaction.Transaction{}
|
||||
if err := tx.DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,33 +6,10 @@ import (
|
|||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
func TestGenisis(t *testing.T) {
|
||||
var (
|
||||
rawBlock = "000000000000000000000000000000000000000000000000000000000000000000000000845c34e7c1aed302b1718e914da0c42bf47c476ac4d89671f278d8ab6d27aa3d65fc8857000000001dac2b7c00000000be48d3a3f5d10013ab9ffee489706078714f1ea2010001510400001dac2b7c00000000400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000400001445b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e5b881227d2c7b226c616e67223a22656e222c226e616d65223a22416e74436f696e227d5d0000c16ff286230008009f7fd096d37ed2c0e3f7f0cfc924beef4ffceb680000000001000000019b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50000c16ff2862300be48d3a3f5d10013ab9ffee489706078714f1ea201000151"
|
||||
//rawBlockHash = "996e37358dc369912041f966f8c5d8d3a8255ba5dcbd3447f8a82b55db869099"
|
||||
)
|
||||
|
||||
rawBlockBytes, err := hex.DecodeString(rawBlock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
block := &Block{}
|
||||
if err := block.DecodeBinary(bytes.NewReader(rawBlockBytes)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hash, err := block.Hash()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(hash)
|
||||
}
|
||||
|
||||
func TestDecodeBlock(t *testing.T) {
|
||||
var (
|
||||
rawBlock = "00000000b7def681f0080262aa293071c53b41fc3146b196067243700b68acd059734fd19543108bf9ddc738cbee2ed1160f153aa0d057f062de0aa3cbb64ba88735c23d43667e59543f050095df82b02e324c5ff3812db982f3b0089a21a278988efeec6a027b2501fd450140113ac66657c2f544e8ad13905fcb2ebaadfef9502cbefb07960fbe56df098814c223dcdd3d0efa0b43a9459e654d948516dcbd8b370f50fbecfb8b411d48051a408500ce85591e516525db24065411f6a88f43de90fa9c167c2e6f5af43bc84e65e5a4bb174bc83a19b6965ff10f476b1b151ae15439a985f33916abc6822b0bb140f4aae522ffaea229987a10d01beec826c3b9a189fe02aa82680581b78f3df0ea4d3f93ca8ea35ffc90f15f7db9017f92fafd9380d9ba3237973cf4313cf626fc40e30e50e3588bd047b39f478b59323868cd50c7ab54355d8245bf0f1988d37528f9bbfc68110cf917debbdbf1f4bdd02cdcccdc3269fdf18a6c727ee54b6934d840e43918dd1ec6123550ec37a513e72b34b2c2a3baa510dec3037cbef2fa9f6ed1e7ccd1f3f6e19d4ce2c0919af55249a970c2685217f75a5589cf9e54dff8449af155210209e7fd41dfb5c2f8dc72eb30358ac100ea8c72da18847befe06eade68cebfcb9210327da12b5c40200e9f65569476bbff2218da4f32548ff43b6387ec1416a231ee821034ff5ceeac41acf22cd5ed2da17a6df4dd8358fcb2bfb1a43208ad0feaab2746b21026ce35b29147ad09e4afe4ec4a7319095f08198fa8babbe3c56e970b143528d2221038dddc06ce687677a53d54f096d2591ba2302068cf123c1f2d75c2dddc542557921039dafd8571a641058ccc832c5e2111ea39b09c0bde36050914384f7a48bce9bf92102d02b1873a0863cd042cc717da31cea0d7cf9db32b74d4c72c01b0011503e2e2257ae01000095df82b000000000"
|
||||
|
@ -82,7 +59,7 @@ func newBlockBase() BlockBase {
|
|||
Index: 1,
|
||||
ConsensusData: 1111,
|
||||
NextConsensus: util.Uint160{},
|
||||
Script: &Witness{
|
||||
Script: &transaction.Witness{
|
||||
VerificationScript: []byte{0x0},
|
||||
InvocationScript: []byte{0x1},
|
||||
},
|
||||
|
@ -104,9 +81,9 @@ func TestHashBlockEqualsHashHeader(t *testing.T) {
|
|||
func TestBlockVerify(t *testing.T) {
|
||||
block := &Block{
|
||||
BlockBase: newBlockBase(),
|
||||
Transactions: []*Transaction{
|
||||
{Type: MinerTX},
|
||||
{Type: IssueTX},
|
||||
Transactions: []*transaction.Transaction{
|
||||
{Type: transaction.MinerType},
|
||||
{Type: transaction.IssueType},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -114,18 +91,18 @@ func TestBlockVerify(t *testing.T) {
|
|||
t.Fatal("block should be verified")
|
||||
}
|
||||
|
||||
block.Transactions = []*Transaction{
|
||||
{Type: IssueTX},
|
||||
{Type: MinerTX},
|
||||
block.Transactions = []*transaction.Transaction{
|
||||
{Type: transaction.IssueType},
|
||||
{Type: transaction.MinerType},
|
||||
}
|
||||
|
||||
if block.Verify(false) {
|
||||
t.Fatal("block should not by verified")
|
||||
}
|
||||
|
||||
block.Transactions = []*Transaction{
|
||||
{Type: MinerTX},
|
||||
{Type: MinerTX},
|
||||
block.Transactions = []*transaction.Transaction{
|
||||
{Type: transaction.MinerType},
|
||||
{Type: transaction.MinerType},
|
||||
}
|
||||
|
||||
if block.Verify(false) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
|
@ -31,9 +32,9 @@ func TestAddHeaders(t *testing.T) {
|
|||
startHash, _ := util.Uint256DecodeString("996e37358dc369912041f966f8c5d8d3a8255ba5dcbd3447f8a82b55db869099")
|
||||
bc := NewBlockchain(NewMemoryStore(), log.New(os.Stdout, "", 0), startHash)
|
||||
|
||||
h1 := &Header{BlockBase: BlockBase{Version: 0, Index: 1, Script: &Witness{}}}
|
||||
h2 := &Header{BlockBase: BlockBase{Version: 0, Index: 2, Script: &Witness{}}}
|
||||
h3 := &Header{BlockBase: BlockBase{Version: 0, Index: 3, Script: &Witness{}}}
|
||||
h1 := &Header{BlockBase: BlockBase{Version: 0, Index: 1, Script: &transaction.Witness{}}}
|
||||
h2 := &Header{BlockBase: BlockBase{Version: 0, Index: 2, Script: &transaction.Witness{}}}
|
||||
h3 := &Header{BlockBase: BlockBase{Version: 0, Index: 3, Script: &transaction.Witness{}}}
|
||||
|
||||
if err := bc.AddHeaders(h1, h2, h3); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ func TestHeaderEncodeDecode(t *testing.T) {
|
|||
Index: 3445,
|
||||
ConsensusData: 394949,
|
||||
NextConsensus: util.Uint160{},
|
||||
Script: &Witness{
|
||||
Script: &transaction.Witness{
|
||||
InvocationScript: []byte{0x10},
|
||||
VerificationScript: []byte{0x11},
|
||||
},
|
||||
|
@ -51,7 +52,6 @@ func TestHeaderEncodeDecode(t *testing.T) {
|
|||
if !header.NextConsensus.Equals(headerDecode.NextConsensus) {
|
||||
t.Fatalf("expected both next consensus fields to be equal")
|
||||
}
|
||||
|
||||
if bytes.Compare(header.Script.InvocationScript, headerDecode.Script.InvocationScript) != 0 {
|
||||
t.Fatalf("expected equal invocation scripts %v and %v", header.Script.InvocationScript, headerDecode.Script.InvocationScript)
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Transaction is a process recorded in the NEO system.
|
||||
type Transaction struct {
|
||||
Type TransactionType
|
||||
}
|
||||
|
||||
// All processes in NEO system are recorded in transactions.
|
||||
// There are several types of transactions.
|
||||
const (
|
||||
MinerTX TransactionType = 0x00
|
||||
IssueTX = 0x01
|
||||
ClaimTX = 0x02
|
||||
EnrollmentTX = 0x20
|
||||
VotingTX = 0x24
|
||||
RegisterTX = 0x40
|
||||
ContractTX = 0x80
|
||||
AgencyTX = 0xb0
|
||||
)
|
||||
|
||||
// DecodeBinary implements the payload interface.
|
||||
func (t Transaction) DecodeBinary(r io.Reader) error {
|
||||
err := binary.Read(r, binary.LittleEndian, &t.Type)
|
||||
return err
|
||||
}
|
||||
|
||||
// EncodeBinary implements the payload interface.
|
||||
func (t Transaction) EncodeBinary(w io.Writer) error {
|
||||
return nil
|
||||
}
|
49
pkg/core/transaction/attr_usage.go
Normal file
49
pkg/core/transaction/attr_usage.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package transaction
|
||||
|
||||
// AttrUsage represents the purpose of the attribute.
|
||||
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 = 0xf
|
||||
)
|
79
pkg/core/transaction/attribute.go
Normal file
79
pkg/core/transaction/attribute.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Attribute represents a Transaction attribute.
|
||||
type Attribute struct {
|
||||
Usage AttrUsage
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payloader interface.
|
||||
func (attr *Attribute) DecodeBinary(r io.Reader) error {
|
||||
if err := binary.Read(r, binary.LittleEndian, &attr.Usage); err != nil {
|
||||
return err
|
||||
}
|
||||
if attr.Usage == ContractHash ||
|
||||
attr.Usage == Vote ||
|
||||
(attr.Usage >= Hash1 && attr.Usage <= Hash15) {
|
||||
attr.Data = make([]byte, 32)
|
||||
return binary.Read(r, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
if attr.Usage == ECDH02 || attr.Usage == ECDH03 {
|
||||
attr.Data = make([]byte, 33)
|
||||
attr.Data[0] = byte(attr.Usage)
|
||||
return binary.Read(r, binary.LittleEndian, attr.Data[1:])
|
||||
}
|
||||
if attr.Usage == Script {
|
||||
attr.Data = make([]byte, 20)
|
||||
return binary.Read(r, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
if attr.Usage == DescriptionUrl {
|
||||
attr.Data = make([]byte, 1)
|
||||
return binary.Read(r, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
if attr.Usage == Description || attr.Usage >= Remark {
|
||||
lenData := util.ReadVarUint(r)
|
||||
attr.Data = make([]byte, lenData)
|
||||
return binary.Read(r, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
return errors.New("format error in decoding transaction attribute")
|
||||
}
|
||||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (attr *Attribute) EncodeBinary(w io.Writer) error {
|
||||
if err := binary.Write(w, binary.LittleEndian, &attr.Usage); err != nil {
|
||||
return err
|
||||
}
|
||||
if attr.Usage == ContractHash ||
|
||||
attr.Usage == Vote ||
|
||||
(attr.Usage >= Hash1 && attr.Usage <= Hash15) {
|
||||
return binary.Write(w, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
if attr.Usage == ECDH02 || attr.Usage == ECDH03 {
|
||||
attr.Data[0] = byte(attr.Usage)
|
||||
return binary.Write(w, binary.LittleEndian, attr.Data[1:33])
|
||||
}
|
||||
if attr.Usage == Script {
|
||||
return binary.Write(w, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
if attr.Usage == DescriptionUrl {
|
||||
if err := util.WriteVarUint(w, uint64(len(attr.Data))); err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Write(w, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
if attr.Usage == Description || attr.Usage >= Remark {
|
||||
if err := util.WriteVarUint(w, uint64(len(attr.Data))); err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Write(w, binary.LittleEndian, attr.Data)
|
||||
}
|
||||
return errors.New("format error in encoding transaction attribute")
|
||||
}
|
38
pkg/core/transaction/claim.go
Normal file
38
pkg/core/transaction/claim.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// ClaimTX represents a claim transaction.
|
||||
type ClaimTX struct {
|
||||
Claims []*Input
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (tx *ClaimTX) DecodeBinary(r io.Reader) error {
|
||||
lenClaims := util.ReadVarUint(r)
|
||||
tx.Claims = make([]*Input, lenClaims)
|
||||
for i := 0; i < int(lenClaims); i++ {
|
||||
tx.Claims[i] = &Input{}
|
||||
if err := tx.Claims[i].DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (tx *ClaimTX) EncodeBinary(w io.Writer) error {
|
||||
if err := util.WriteVarUint(w, uint64(len(tx.Claims))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, claim := range tx.Claims {
|
||||
if err := claim.EncodeBinary(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
36
pkg/core/transaction/input.go
Normal file
36
pkg/core/transaction/input.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/anthdm/neo-go/pkg/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
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (in *Input) DecodeBinary(r io.Reader) error {
|
||||
if err := binary.Read(r, binary.LittleEndian, &in.PrevHash); err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Read(r, binary.LittleEndian, &in.PrevIndex)
|
||||
}
|
||||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (in *Input) EncodeBinary(w io.Writer) error {
|
||||
if err := binary.Write(w, binary.LittleEndian, in.PrevHash); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(w, binary.LittleEndian, in.PrevIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
54
pkg/core/transaction/invocation.go
Normal file
54
pkg/core/transaction/invocation.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// InvocationTX represents a invocation transaction and is used to
|
||||
// deploy smart contract to the NEO blockchain.
|
||||
type InvocationTX struct {
|
||||
// Script output of the smart contract.
|
||||
Script []byte
|
||||
|
||||
// Gas cost of the smart contract.
|
||||
Gas util.Fixed8
|
||||
}
|
||||
|
||||
// NewInvocationTX returns a new invocation transaction.
|
||||
func NewInvocationTX(script []byte) *Transaction {
|
||||
return &Transaction{
|
||||
Type: InvocationType,
|
||||
Version: 1,
|
||||
Data: &InvocationTX{
|
||||
Script: script,
|
||||
},
|
||||
Attributes: []*Attribute{},
|
||||
Inputs: []*Input{},
|
||||
Outputs: []*Output{},
|
||||
Scripts: []*Witness{},
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (tx *InvocationTX) DecodeBinary(r io.Reader) error {
|
||||
lenScript := util.ReadVarUint(r)
|
||||
tx.Script = make([]byte, lenScript)
|
||||
if err := binary.Read(r, binary.LittleEndian, tx.Script); err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Read(r, binary.LittleEndian, &tx.Gas)
|
||||
}
|
||||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (tx *InvocationTX) EncodeBinary(w io.Writer) error {
|
||||
if err := util.WriteVarUint(w, uint64(len(tx.Script))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(w, binary.LittleEndian, tx.Script); err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Write(w, binary.LittleEndian, tx.Gas)
|
||||
}
|
22
pkg/core/transaction/miner.go
Normal file
22
pkg/core/transaction/miner.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// MinerTX represents a miner transaction.
|
||||
type MinerTX struct {
|
||||
// Random number/identifier
|
||||
Nonce uint32
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (tx *MinerTX) DecodeBinary(r io.Reader) error {
|
||||
return binary.Read(r, binary.LittleEndian, &tx.Nonce)
|
||||
}
|
||||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (tx *MinerTX) EncodeBinary(w io.Writer) error {
|
||||
return binary.Write(w, binary.LittleEndian, tx.Nonce)
|
||||
}
|
51
pkg/core/transaction/output.go
Normal file
51
pkg/core/transaction/output.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Output represents a Transaction output.
|
||||
type Output struct {
|
||||
// The NEO asset id used in the transaction.
|
||||
AssetID util.Uint256
|
||||
|
||||
// Amount of AssetType send or received.
|
||||
Amount util.Fixed8
|
||||
|
||||
// The address of the remittee.
|
||||
ScriptHash util.Uint160
|
||||
}
|
||||
|
||||
// NewOutput returns a new transaction output.
|
||||
func NewOutput(assetID util.Uint256, amount util.Fixed8, scriptHash util.Uint160) *Output {
|
||||
return &Output{
|
||||
AssetID: assetID,
|
||||
Amount: amount,
|
||||
ScriptHash: scriptHash,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
func (out *Output) DecodeBinary(r io.Reader) error {
|
||||
if err := binary.Read(r, binary.LittleEndian, &out.AssetID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Read(r, binary.LittleEndian, &out.Amount); err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Read(r, binary.LittleEndian, &out.ScriptHash)
|
||||
}
|
||||
|
||||
// EncodeBinary implements the Payload interface.
|
||||
func (out *Output) EncodeBinary(w io.Writer) error {
|
||||
if err := binary.Write(w, binary.LittleEndian, out.AssetID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(w, binary.LittleEndian, out.Amount); err != nil {
|
||||
return err
|
||||
}
|
||||
return binary.Write(w, binary.LittleEndian, out.ScriptHash)
|
||||
}
|
178
pkg/core/transaction/transaction.go
Normal file
178
pkg/core/transaction/transaction.go
Normal file
|
@ -0,0 +1,178 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Transaction is a process recorded in the NEO blockchain.
|
||||
type Transaction struct {
|
||||
// The type of the transaction.
|
||||
Type TransactionType
|
||||
|
||||
// The trading version which is currently 0.
|
||||
Version uint8
|
||||
|
||||
// Data specific to the type of the transaction.
|
||||
// This is always a pointer to a <Type>Transaction.
|
||||
Data interface{}
|
||||
|
||||
// Transaction attributes.
|
||||
Attributes []*Attribute
|
||||
|
||||
// The inputs of the transaction.
|
||||
Inputs []*Input
|
||||
|
||||
// The outputs of the transaction.
|
||||
Outputs []*Output
|
||||
|
||||
// The scripts that comes with this transaction.
|
||||
// Scripts exist out of the verification script
|
||||
// and invocation script.
|
||||
Scripts []*Witness
|
||||
}
|
||||
|
||||
// AddOutput adds the given output to the transaction outputs.
|
||||
func (t *Transaction) AddOutput(out *Output) {
|
||||
t.Outputs = append(t.Outputs, out)
|
||||
}
|
||||
|
||||
// AddInput adds the given input to the transaction inputs.
|
||||
func (t *Transaction) AddInput(in *Input) {
|
||||
t.Inputs = append(t.Inputs, in)
|
||||
}
|
||||
|
||||
// DecodeBinary implements the payload interface.
|
||||
func (t *Transaction) DecodeBinary(r io.Reader) error {
|
||||
if err := binary.Read(r, binary.LittleEndian, &t.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Read(r, binary.LittleEndian, &t.Version); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.decodeData(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lenAttrs := util.ReadVarUint(r)
|
||||
t.Attributes = make([]*Attribute, lenAttrs)
|
||||
for i := 0; i < int(lenAttrs); i++ {
|
||||
t.Attributes[i] = &Attribute{}
|
||||
if err := t.Attributes[i].DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
lenInputs := util.ReadVarUint(r)
|
||||
t.Inputs = make([]*Input, lenInputs)
|
||||
for i := 0; i < int(lenInputs); i++ {
|
||||
t.Inputs[i] = &Input{}
|
||||
if err := t.Inputs[i].DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
lenOutputs := util.ReadVarUint(r)
|
||||
t.Outputs = make([]*Output, lenOutputs)
|
||||
for i := 0; i < int(lenOutputs); i++ {
|
||||
t.Outputs[i] = &Output{}
|
||||
if err := t.Outputs[i].DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
lenScripts := util.ReadVarUint(r)
|
||||
t.Scripts = make([]*Witness, lenScripts)
|
||||
for i := 0; i < int(lenScripts); i++ {
|
||||
t.Scripts[i] = &Witness{}
|
||||
if err := t.Scripts[i].DecodeBinary(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transaction) decodeData(r io.Reader) error {
|
||||
switch t.Type {
|
||||
case InvocationType:
|
||||
t.Data = &InvocationTX{}
|
||||
return t.Data.(*InvocationTX).DecodeBinary(r)
|
||||
case MinerType:
|
||||
t.Data = &MinerTX{}
|
||||
return t.Data.(*MinerTX).DecodeBinary(r)
|
||||
case ClaimType:
|
||||
t.Data = &ClaimTX{}
|
||||
return t.Data.(*ClaimTX).DecodeBinary(r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeBinary implements the payload interface.
|
||||
func (t *Transaction) EncodeBinary(w io.Writer) error {
|
||||
if err := binary.Write(w, binary.LittleEndian, t.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(w, binary.LittleEndian, t.Version); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.encodeData(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attributes
|
||||
if err := util.WriteVarUint(w, uint64(len(t.Attributes))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, attr := range t.Attributes {
|
||||
if err := attr.EncodeBinary(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Inputs
|
||||
if err := util.WriteVarUint(w, uint64(len(t.Inputs))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, in := range t.Inputs {
|
||||
if err := in.EncodeBinary(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs
|
||||
if err := util.WriteVarUint(w, uint64(len(t.Outputs))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, out := range t.Outputs {
|
||||
if err := out.EncodeBinary(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Scripts
|
||||
if err := util.WriteVarUint(w, uint64(len(t.Scripts))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range t.Scripts {
|
||||
if err := s.EncodeBinary(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transaction) encodeData(w io.Writer) error {
|
||||
switch t.Type {
|
||||
case InvocationType:
|
||||
return t.Data.(*InvocationTX).EncodeBinary(w)
|
||||
case MinerType:
|
||||
return t.Data.(*MinerTX).EncodeBinary(w)
|
||||
case ClaimType:
|
||||
return t.Data.(*ClaimTX).EncodeBinary(w)
|
||||
}
|
||||
return nil
|
||||
}
|
81
pkg/core/transaction/transaction_test.go
Normal file
81
pkg/core/transaction/transaction_test.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Source of this TX: https://neotracker.io/tx/2c6a45547b3898318e400e541628990a07acb00f3b9a15a8e966ae49525304da
|
||||
var rawTXClaim = "020004bc67ba325d6412ff4c55b10f7e9afb54bbb2228d201b37363c3d697ac7c198f70300591cd454d7318d2087c0196abfbbd1573230380672f0f0cd004dcb4857e58cbd010031bcfbed573f5318437e95edd603922a4455ff3326a979fdd1c149a84c4cb0290000b51eb6159c58cac4fe23d90e292ad2bcb7002b0da2c474e81e1889c0649d2c490000000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c603b555f00000000005d9de59d99c0d1f6ed1496444473f4a0b538302f014140456349cec43053009accdb7781b0799c6b591c812768804ab0a0b56b5eae7a97694227fcd33e70899c075848b2cee8fae733faac6865b484d3f7df8949e2aadb232103945fae1ed3c31d778f149192b76734fcc951b400ba3598faa81ff92ebe477eacac"
|
||||
|
||||
func TestDecodeEncodeClaimTX(t *testing.T) {
|
||||
b, err := hex.DecodeString(rawTXClaim)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx := &Transaction{}
|
||||
if err := tx.DecodeBinary(bytes.NewReader(b)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, tx.Type, ClaimType)
|
||||
assert.IsType(t, tx.Data, &ClaimTX{})
|
||||
claimTX := tx.Data.(*ClaimTX)
|
||||
assert.Equal(t, 4, len(claimTX.Claims))
|
||||
assert.Equal(t, 0, len(tx.Attributes))
|
||||
assert.Equal(t, 0, len(tx.Inputs))
|
||||
assert.Equal(t, 1, len(tx.Outputs))
|
||||
assert.Equal(t, "AQJseD8iBmCD4sgfHRhMahmoi9zvopG6yz", tx.Outputs[0].ScriptHash.Address())
|
||||
assert.Equal(t, "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7", tx.Outputs[0].AssetID.String())
|
||||
assert.Equal(t, tx.Outputs[0].Amount.String(), "0.06247739")
|
||||
invoc := "40456349cec43053009accdb7781b0799c6b591c812768804ab0a0b56b5eae7a97694227fcd33e70899c075848b2cee8fae733faac6865b484d3f7df8949e2aadb"
|
||||
verif := "2103945fae1ed3c31d778f149192b76734fcc951b400ba3598faa81ff92ebe477eacac"
|
||||
assert.Equal(t, 1, len(tx.Scripts))
|
||||
assert.Equal(t, invoc, hex.EncodeToString(tx.Scripts[0].InvocationScript))
|
||||
assert.Equal(t, verif, hex.EncodeToString(tx.Scripts[0].VerificationScript))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := tx.EncodeBinary(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, rawTXClaim, hex.EncodeToString(buf.Bytes()))
|
||||
}
|
||||
|
||||
// Source of this TX: https://neotracker.io/tx/fe4b3af60677204c57e573a57bdc97bc5059b05ad85b1474f84431f88d910f64
|
||||
var rawTXInvocation = "d101590400b33f7114839c33710da24cf8e7d536b8d244f3991cf565c8146063795d3b9b3cd55aef026eae992b91063db0db53c1087472616e7366657267c5cc1cb5392019e2cc4e6d6b5ea54c8d4b6d11acf166cb072961424c54f6000000000000000001206063795d3b9b3cd55aef026eae992b91063db0db0000014140c6a131c55ca38995402dff8e92ac55d89cbed4b98dfebbcb01acbc01bd78fa2ce2061be921b8999a9ab79c2958875bccfafe7ce1bbbaf1f56580815ea3a4feed232102d41ddce2c97be4c9aa571b8a32cbc305aa29afffbcae71b0ef568db0e93929aaac"
|
||||
|
||||
func TestDecodeEncodeInvocationTX(t *testing.T) {
|
||||
b, err := hex.DecodeString(rawTXInvocation)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx := &Transaction{}
|
||||
if err := tx.DecodeBinary(bytes.NewReader(b)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, tx.Type, InvocationType)
|
||||
assert.IsType(t, tx.Data, &InvocationTX{})
|
||||
|
||||
invocTX := tx.Data.(*InvocationTX)
|
||||
script := "0400b33f7114839c33710da24cf8e7d536b8d244f3991cf565c8146063795d3b9b3cd55aef026eae992b91063db0db53c1087472616e7366657267c5cc1cb5392019e2cc4e6d6b5ea54c8d4b6d11acf166cb072961424c54f6"
|
||||
assert.Equal(t, script, hex.EncodeToString(invocTX.Script))
|
||||
assert.Equal(t, util.Fixed8(0), invocTX.Gas)
|
||||
|
||||
assert.Equal(t, 1, len(tx.Attributes))
|
||||
assert.Equal(t, 0, len(tx.Inputs))
|
||||
assert.Equal(t, 0, len(tx.Outputs))
|
||||
invoc := "40c6a131c55ca38995402dff8e92ac55d89cbed4b98dfebbcb01acbc01bd78fa2ce2061be921b8999a9ab79c2958875bccfafe7ce1bbbaf1f56580815ea3a4feed"
|
||||
verif := "2102d41ddce2c97be4c9aa571b8a32cbc305aa29afffbcae71b0ef568db0e93929aaac"
|
||||
assert.Equal(t, 1, len(tx.Scripts))
|
||||
assert.Equal(t, invoc, hex.EncodeToString(tx.Scripts[0].InvocationScript))
|
||||
assert.Equal(t, verif, hex.EncodeToString(tx.Scripts[0].VerificationScript))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := tx.EncodeBinary(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, rawTXInvocation, hex.EncodeToString(buf.Bytes()))
|
||||
}
|
50
pkg/core/transaction/type.go
Normal file
50
pkg/core/transaction/type.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package transaction
|
||||
|
||||
// TransactionType is the type of a transaction.
|
||||
type TransactionType uint8
|
||||
|
||||
// All processes in NEO system are recorded in transactions.
|
||||
// There are several types of transactions.
|
||||
const (
|
||||
MinerType TransactionType = 0x00
|
||||
IssueType TransactionType = 0x01
|
||||
ClaimType TransactionType = 0x02
|
||||
EnrollmentType TransactionType = 0x20
|
||||
VotingType TransactionType = 0x24
|
||||
RegisterType TransactionType = 0x40
|
||||
ContractType TransactionType = 0x80
|
||||
StateType TransactionType = 0x90
|
||||
AgencyType TransactionType = 0xb0
|
||||
PublishType TransactionType = 0xd0
|
||||
InvocationType TransactionType = 0xd1
|
||||
)
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (t TransactionType) String() string {
|
||||
switch t {
|
||||
case MinerType:
|
||||
return "miner transaction"
|
||||
case IssueType:
|
||||
return "issue transaction"
|
||||
case ClaimType:
|
||||
return "claim transaction"
|
||||
case EnrollmentType:
|
||||
return "enrollment transaction"
|
||||
case VotingType:
|
||||
return "voting transaction"
|
||||
case RegisterType:
|
||||
return "register transaction"
|
||||
case ContractType:
|
||||
return "contract transaction"
|
||||
case StateType:
|
||||
return "state transaction"
|
||||
case AgencyType:
|
||||
return "agency transaction"
|
||||
case PublishType:
|
||||
return "publish transaction"
|
||||
case InvocationType:
|
||||
return "invocation transaction"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Witness ...
|
||||
// Witness contains 2 scripts.
|
||||
type Witness struct {
|
||||
InvocationScript []byte
|
||||
VerificationScript []byte
|
||||
|
@ -17,17 +17,12 @@ type Witness struct {
|
|||
func (wit *Witness) DecodeBinary(r io.Reader) error {
|
||||
lenb := util.ReadVarUint(r)
|
||||
wit.InvocationScript = make([]byte, lenb)
|
||||
if err := binary.Read(r, binary.LittleEndian, &wit.InvocationScript); err != nil {
|
||||
panic(err)
|
||||
if err := binary.Read(r, binary.LittleEndian, wit.InvocationScript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lenb = util.ReadVarUint(r)
|
||||
wit.VerificationScript = make([]byte, lenb)
|
||||
if err := binary.Read(r, binary.LittleEndian, &wit.VerificationScript); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return binary.Read(r, binary.LittleEndian, wit.VerificationScript)
|
||||
}
|
||||
|
||||
// EncodeBinary implements the payload interface.
|
|
@ -1,28 +0,0 @@
|
|||
package core
|
||||
|
||||
// TransactionType is the type of a transaction.
|
||||
type TransactionType uint8
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (t TransactionType) String() string {
|
||||
switch t {
|
||||
case MinerTX:
|
||||
return "miner transaction"
|
||||
case IssueTX:
|
||||
return "issue transaction"
|
||||
case ClaimTX:
|
||||
return "claim transaction"
|
||||
case EnrollmentTX:
|
||||
return "enrollment transaction"
|
||||
case VotingTX:
|
||||
return "voting transaction"
|
||||
case RegisterTX:
|
||||
return "register transaction"
|
||||
case ContractTX:
|
||||
return "contract transaction"
|
||||
case AgencyTX:
|
||||
return "agency transaction"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
)
|
||||
|
||||
func TestHeadersEncodeDecode(t *testing.T) {
|
||||
|
@ -14,7 +15,7 @@ func TestHeadersEncodeDecode(t *testing.T) {
|
|||
BlockBase: core.BlockBase{
|
||||
Version: 0,
|
||||
Index: 1,
|
||||
Script: &core.Witness{
|
||||
Script: &transaction.Witness{
|
||||
InvocationScript: []byte{0x0},
|
||||
VerificationScript: []byte{0x1},
|
||||
},
|
||||
|
@ -23,7 +24,7 @@ func TestHeadersEncodeDecode(t *testing.T) {
|
|||
BlockBase: core.BlockBase{
|
||||
Version: 0,
|
||||
Index: 2,
|
||||
Script: &core.Witness{
|
||||
Script: &transaction.Witness{
|
||||
InvocationScript: []byte{0x0},
|
||||
VerificationScript: []byte{0x1},
|
||||
},
|
||||
|
@ -32,7 +33,7 @@ func TestHeadersEncodeDecode(t *testing.T) {
|
|||
BlockBase: core.BlockBase{
|
||||
Version: 0,
|
||||
Index: 3,
|
||||
Script: &core.Witness{
|
||||
Script: &transaction.Witness{
|
||||
InvocationScript: []byte{0x0},
|
||||
VerificationScript: []byte{0x1},
|
||||
},
|
||||
|
|
|
@ -4,6 +4,6 @@ import "io"
|
|||
|
||||
// Payload is anything that can be binary encoded/decoded.
|
||||
type Payload interface {
|
||||
EncodeBinary(io.Writer) error
|
||||
DecodeBinary(io.Reader) error
|
||||
EncodeBinary(io.Writer) error
|
||||
DecodeBinary(io.Reader) error
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
package smartcontract
|
||||
|
||||
// Contract represents a NEO smartcontract.
|
||||
type Contract struct {
|
||||
}
|
||||
type Contract struct{}
|
||||
|
|
52
pkg/smartcontract/param_context.go
Normal file
52
pkg/smartcontract/param_context.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
package smartcontract
|
||||
|
||||
import "github.com/anthdm/neo-go/pkg/util"
|
||||
|
||||
// ParamType represent the Type of the contract parameter
|
||||
type ParamType int
|
||||
|
||||
// A list of supported smart contract parameter types.
|
||||
const (
|
||||
SignatureType ParamType = iota
|
||||
BoolType
|
||||
IntegerType
|
||||
Hash160Type
|
||||
Hash256Type
|
||||
ByteArrayType
|
||||
PublicKeyType
|
||||
StringType
|
||||
ArrayType
|
||||
)
|
||||
|
||||
// Parameter represents a smart contract parameter.
|
||||
type Parameter struct {
|
||||
// Type of the parameter
|
||||
Type ParamType
|
||||
// The actual value of the parameter.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// NewParameter returns a Parameter with proper initialized Value
|
||||
// of the given ParamType.
|
||||
func NewParameter(t ParamType) Parameter {
|
||||
return Parameter{
|
||||
Type: t,
|
||||
Value: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// ContextItem represents a transaction context item.
|
||||
type ContextItem struct {
|
||||
Script util.Uint160
|
||||
Parameters []Parameter
|
||||
Signatures []Signature
|
||||
}
|
||||
|
||||
// Signature represents a transaction signature.
|
||||
type Signature struct {
|
||||
Data []byte
|
||||
PublicKey []byte
|
||||
}
|
||||
|
||||
// ParameterContext holds the parameter context.
|
||||
type ParameterContext struct{}
|
|
@ -1,9 +0,0 @@
|
|||
package types
|
||||
|
||||
// Block represents a block in the blockchain.
|
||||
type Block struct{}
|
||||
|
||||
// Index returns the height of the block.
|
||||
func (b Block) Index() int {
|
||||
return 0
|
||||
}
|
31
pkg/util/fixed8.go
Normal file
31
pkg/util/fixed8.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Fixed8 represents a fixed-point number with precision 10^-8.
|
||||
type Fixed8 int64
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (f Fixed8) String() string {
|
||||
buf := new(bytes.Buffer)
|
||||
val := int64(f)
|
||||
if val < 0 {
|
||||
buf.WriteRune('-')
|
||||
val = -val
|
||||
}
|
||||
str := strconv.FormatInt(val/100000000, 10)
|
||||
buf.WriteString(str)
|
||||
val %= 100000000
|
||||
if val > 0 {
|
||||
buf.WriteRune('.')
|
||||
str = strconv.FormatInt(val, 10)
|
||||
for i := len(str); i < 8; i++ {
|
||||
buf.WriteRune('0')
|
||||
}
|
||||
buf.WriteString(str)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
|
@ -3,6 +3,8 @@ package util
|
|||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto"
|
||||
)
|
||||
|
||||
const uint160Size = 20
|
||||
|
@ -22,6 +24,16 @@ func Uint160DecodeString(s string) (u Uint160, err error) {
|
|||
return Uint160DecodeBytes(b)
|
||||
}
|
||||
|
||||
// Uint160DecodeAddress attempts to decode the given NEO address string
|
||||
// into an Uint160.
|
||||
func Uint160DecodeAddress(s string) (u Uint160, err error) {
|
||||
b, err := crypto.Base58CheckDecode(s)
|
||||
if err != nil {
|
||||
return u, err
|
||||
}
|
||||
return Uint160DecodeBytes(b[1:21])
|
||||
}
|
||||
|
||||
// Uint160DecodeBytes attempts to decode the given bytes into an Uint160.
|
||||
func Uint160DecodeBytes(b []byte) (u Uint160, err error) {
|
||||
if len(b) != uint160Size {
|
||||
|
@ -42,6 +54,13 @@ func (u Uint160) Bytes() []byte {
|
|||
return b
|
||||
}
|
||||
|
||||
// Address returns the NEO address representation of u.
|
||||
func (u Uint160) Address() string {
|
||||
// Dont forget to prepend the Address version 0x17 (23) A
|
||||
b := append([]byte{0x17}, u.Bytes()...)
|
||||
return crypto.Base58CheckEncode(b)
|
||||
}
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (u Uint160) String() string {
|
||||
return hex.EncodeToString(u.Bytes())
|
||||
|
|
|
@ -1 +1,22 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUint160FromToAddress(t *testing.T) {
|
||||
addrs := []string{
|
||||
"AMLr1CpPQtbEdiJdriX1HpRNMZUwbU2Huj",
|
||||
"AKtwd3DRXj3nL5kHMUoNsdnsCEVjnuuTFF",
|
||||
"AMxkaxFVG8Q1BhnB4fjTA5ZmUTEnnTMJMa",
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
val, err := Uint160DecodeAddress(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, addr, val.Address())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package util
|
|||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUint256DecodeString(t *testing.T) {
|
||||
|
@ -11,7 +13,7 @@ func TestUint256DecodeString(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(val)
|
||||
assert.Equal(t, hexStr, val.String())
|
||||
}
|
||||
|
||||
func TestUint256DecodeBytes(t *testing.T) {
|
||||
|
@ -24,9 +26,7 @@ func TestUint256DecodeBytes(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if val.String() != hexStr {
|
||||
t.Fatalf("expected %s and %s to be equal", val, hexStr)
|
||||
}
|
||||
assert.Equal(t, hexStr, val.String())
|
||||
}
|
||||
|
||||
func TestUInt256Equals(t *testing.T) {
|
||||
|
|
|
@ -57,11 +57,6 @@ func emitBytes(w *bytes.Buffer, b []byte) error {
|
|||
n = len(b)
|
||||
)
|
||||
|
||||
if n == 0 {
|
||||
// The VM expects a pushf (0x00).
|
||||
// Empty strings on the stack for example.
|
||||
return emitOpcode(w, vm.Opushf)
|
||||
}
|
||||
if n <= int(vm.Opushbytes75) {
|
||||
return emit(w, vm.Opcode(n), b)
|
||||
} else if n < 0x100 {
|
||||
|
|
|
@ -39,7 +39,7 @@ const (
|
|||
Ojmpifnot Opcode = 0x64
|
||||
Ocall Opcode = 0x65
|
||||
Oret Opcode = 0x66
|
||||
Opcall Opcode = 0x67
|
||||
Oappcall Opcode = 0x67
|
||||
Osyscall Opcode = 0x68
|
||||
Otailcall Opcode = 0x69
|
||||
|
||||
|
|
149
pkg/vm/script_builder.go
Normal file
149
pkg/vm/script_builder.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Emit a VM Opcode with data to the given buffer.
|
||||
func Emit(w *bytes.Buffer, op Opcode, b []byte) error {
|
||||
if err := w.WriteByte(byte(op)); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// EmitOpcode emits a single VM Opcode the given buffer.
|
||||
func EmitOpcode(w *bytes.Buffer, op Opcode) error {
|
||||
return w.WriteByte(byte(op))
|
||||
}
|
||||
|
||||
// EmitBool emits a bool type the given buffer.
|
||||
func EmitBool(w *bytes.Buffer, ok bool) error {
|
||||
if ok {
|
||||
return EmitOpcode(w, Opusht)
|
||||
}
|
||||
return EmitOpcode(w, Opushf)
|
||||
}
|
||||
|
||||
// EmitInt emits a int type to the given buffer.
|
||||
func EmitInt(w *bytes.Buffer, i int64) error {
|
||||
if i == -1 {
|
||||
return EmitOpcode(w, Opushm1)
|
||||
}
|
||||
if i == 0 {
|
||||
return EmitOpcode(w, Opushf)
|
||||
}
|
||||
if i > 0 && i < 16 {
|
||||
val := Opcode((int(Opush1) - 1 + int(i)))
|
||||
return EmitOpcode(w, val)
|
||||
}
|
||||
|
||||
bInt := big.NewInt(i)
|
||||
val := util.ArrayReverse(bInt.Bytes())
|
||||
return EmitBytes(w, val)
|
||||
}
|
||||
|
||||
// EmitString emits a string to the given buffer.
|
||||
func EmitString(w *bytes.Buffer, s string) error {
|
||||
return EmitBytes(w, []byte(s))
|
||||
}
|
||||
|
||||
// EmitBytes emits a byte array the given buffer.
|
||||
func EmitBytes(w *bytes.Buffer, b []byte) error {
|
||||
var (
|
||||
err error
|
||||
n = len(b)
|
||||
)
|
||||
|
||||
if n <= int(Opushbytes75) {
|
||||
return Emit(w, Opcode(n), b)
|
||||
} else if n < 0x100 {
|
||||
err = Emit(w, Opushdata1, []byte{byte(n)})
|
||||
} else if n < 0x10000 {
|
||||
buf := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
||||
err = Emit(w, Opushdata2, buf)
|
||||
} else {
|
||||
buf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
||||
err = Emit(w, Opushdata4, buf)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
// EmitSyscall emits the syscall API to the given buffer.
|
||||
// Syscall API string cannot be 0.
|
||||
func EmitSyscall(w *bytes.Buffer, api string) error {
|
||||
if len(api) == 0 {
|
||||
return errors.New("syscall api cannot be of length 0")
|
||||
}
|
||||
buf := make([]byte, len(api)+1)
|
||||
buf[0] = byte(len(api))
|
||||
copy(buf[1:len(buf)], []byte(api))
|
||||
return Emit(w, Osyscall, buf)
|
||||
}
|
||||
|
||||
// EmitCall emits a call Opcode with label to the given buffer.
|
||||
func EmitCall(w *bytes.Buffer, op Opcode, label int16) error {
|
||||
return EmitJmp(w, op, label)
|
||||
}
|
||||
|
||||
// EmitJmp emits a jump Opcode along with label to the given buffer.
|
||||
func EmitJmp(w *bytes.Buffer, op Opcode, label int16) error {
|
||||
if !isOpcodeJmp(op) {
|
||||
return fmt.Errorf("opcode %s is not a jump or call type", op)
|
||||
}
|
||||
buf := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(buf, uint16(label))
|
||||
return Emit(w, op, buf)
|
||||
}
|
||||
|
||||
// EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
||||
// emitted instead.
|
||||
func EmitAppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error {
|
||||
op := Oappcall
|
||||
if tailCall {
|
||||
op = Otailcall
|
||||
}
|
||||
return Emit(w, op, scriptHash.Bytes())
|
||||
}
|
||||
|
||||
// EmitAppCallWithOperationAndData emits an appcall with the given operation and data.
|
||||
func EmitAppCallWithOperationAndData(w *bytes.Buffer, scriptHash util.Uint160, operation string, data []byte) error {
|
||||
if err := EmitBytes(w, data); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := EmitString(w, operation); err != nil {
|
||||
return err
|
||||
}
|
||||
return EmitAppCall(w, scriptHash, false)
|
||||
}
|
||||
|
||||
// EmitAppCallWithOperation emits an appcall with the given operation.
|
||||
func EmitAppCallWithOperation(w *bytes.Buffer, scriptHash util.Uint160, operation string) error {
|
||||
if err := EmitBool(w, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := EmitString(w, operation); err != nil {
|
||||
return err
|
||||
}
|
||||
return EmitAppCall(w, scriptHash, false)
|
||||
}
|
||||
|
||||
func isOpcodeJmp(op Opcode) bool {
|
||||
if op == Ojmp || op == Ojmpifnot || op == Ojmpif || op == Ocall {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
64
pkg/vm/script_builder_test.go
Normal file
64
pkg/vm/script_builder_test.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEmitInt(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
EmitInt(buf, 10)
|
||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Opush10)
|
||||
buf.Reset()
|
||||
EmitInt(buf, 100)
|
||||
assert.Equal(t, buf.Bytes()[0], uint8(1))
|
||||
assert.Equal(t, buf.Bytes()[1], uint8(100))
|
||||
buf.Reset()
|
||||
EmitInt(buf, 1000)
|
||||
assert.Equal(t, buf.Bytes()[0], uint8(2))
|
||||
assert.Equal(t, buf.Bytes()[1:3], []byte{0xe8, 0x03})
|
||||
}
|
||||
|
||||
func TestEmitBool(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
EmitBool(buf, true)
|
||||
EmitBool(buf, false)
|
||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Opush1)
|
||||
assert.Equal(t, Opcode(buf.Bytes()[1]), Opush0)
|
||||
}
|
||||
|
||||
func TestEmitString(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
str := "City Of Zion"
|
||||
EmitString(buf, str)
|
||||
assert.Equal(t, buf.Len(), len(str)+1)
|
||||
assert.Equal(t, buf.Bytes()[1:], []byte(str))
|
||||
}
|
||||
|
||||
func TestEmitSyscall(t *testing.T) {
|
||||
syscalls := []string{
|
||||
"Neo.Runtime.Log",
|
||||
"Neo.Runtime.Notify",
|
||||
"Neo.Runtime.Whatever",
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
for _, syscall := range syscalls {
|
||||
EmitSyscall(buf, syscall)
|
||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Osyscall)
|
||||
assert.Equal(t, buf.Bytes()[1], uint8(len(syscall)))
|
||||
assert.Equal(t, buf.Bytes()[2:], []byte(syscall))
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmitCall(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
EmitCall(buf, Ojmp, 100)
|
||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Ojmp)
|
||||
label := binary.LittleEndian.Uint16(buf.Bytes()[1:3])
|
||||
assert.Equal(t, label, uint16(100))
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package wallet
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// TransferOutput respresents the output of a transaction.
|
||||
type TransferOutput struct {
|
||||
// The asset identifier. This should be of type Uint256.
|
||||
// NEO governing token: c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b
|
||||
// NEO gas: 602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7
|
||||
AssetID util.Uint256
|
||||
|
||||
// Value of the transfer
|
||||
Value *big.Int
|
||||
|
||||
// ScriptHash of the transfer.
|
||||
ScriptHash util.Uint160
|
||||
}
|
Loading…
Reference in a new issue