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'.
|
# 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]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/anthdm/rfc6979"
|
name = "github.com/anthdm/rfc6979"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "6a90f24967ebb1aa57b22f74a13dbb3faad8cf3d"
|
revision = "6a90f24967ebb1aa57b22f74a13dbb3faad8cf3d"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/davecgh/go-spew"
|
||||||
|
packages = ["spew"]
|
||||||
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/golang/snappy"
|
name = "github.com/golang/snappy"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "553a641470496b2327abcac10b36396bd98e45c9"
|
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]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/syndtr/goleveldb"
|
name = "github.com/syndtr/goleveldb"
|
||||||
|
@ -74,6 +98,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "74c6b0a11057b8c0fd68342f85e010e323cda6407bec2a889f161b5890928aaf"
|
inputs-digest = "069a738aa1487766b26f9efb8103d2ce0526d43c83049cb5b792f0edf91568de"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -47,3 +47,7 @@
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "golang.org/x/text"
|
name = "golang.org/x/text"
|
||||||
version = "0.3.0"
|
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"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,12 +27,12 @@ type BlockBase struct {
|
||||||
Index uint32
|
Index uint32
|
||||||
// Random number also called nonce
|
// Random number also called nonce
|
||||||
ConsensusData uint64
|
ConsensusData uint64
|
||||||
// contract addresss of the next miner
|
// Contract addresss of the next miner
|
||||||
NextConsensus util.Uint160
|
NextConsensus util.Uint160
|
||||||
// fixed to 1
|
// fixed to 1
|
||||||
_ uint8 // padding
|
_ uint8 // padding
|
||||||
// Script used to validate the block
|
// Script used to validate the block
|
||||||
Script *Witness
|
Script *transaction.Witness
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements the payload interface.
|
// 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)
|
return fmt.Errorf("format error: padding must equal 1 got %d", padding)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Script = &Witness{}
|
b.Script = &transaction.Witness{}
|
||||||
return b.Script.DecodeBinary(r)
|
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,
|
// 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
|
// only first seven fields in the block head will be calculated, which are
|
||||||
// version, PrevBlock, MerkleRoot, timestamp, and height, the nonce, NextMiner.
|
// version, PrevBlock, MerkleRoot, timestamp, and height, the nonce, NextMiner.
|
||||||
|
@ -158,7 +159,7 @@ func (h *Header) EncodeBinary(w io.Writer) error {
|
||||||
type Block struct {
|
type Block struct {
|
||||||
BlockBase
|
BlockBase
|
||||||
// transaction list
|
// transaction list
|
||||||
Transactions []*Transaction
|
Transactions []*transaction.Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header returns a pointer to the head of the block (BlockHead).
|
// 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.
|
// Verify the integrity of the block.
|
||||||
func (b *Block) Verify(full bool) bool {
|
func (b *Block) Verify(full bool) bool {
|
||||||
// The first TX has to be a miner transaction.
|
// The first TX has to be a miner transaction.
|
||||||
if b.Transactions[0].Type != MinerTX {
|
if b.Transactions[0].Type != transaction.MinerType {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the first TX is a minerTX then all others cant.
|
// If the first TX is a minerTX then all others cant.
|
||||||
for _, tx := range b.Transactions[1:] {
|
for _, tx := range b.Transactions[1:] {
|
||||||
if tx.Type == MinerTX {
|
if tx.Type == transaction.MinerType {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,9 +203,9 @@ func (b *Block) DecodeBinary(r io.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
lentx := util.ReadVarUint(r)
|
lentx := util.ReadVarUint(r)
|
||||||
b.Transactions = make([]*Transaction, lentx)
|
b.Transactions = make([]*transaction.Transaction, lentx)
|
||||||
for i := 0; i < int(lentx); i++ {
|
for i := 0; i < int(lentx); i++ {
|
||||||
tx := &Transaction{}
|
tx := &transaction.Transaction{}
|
||||||
if err := tx.DecodeBinary(r); err != nil {
|
if err := tx.DecodeBinary(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,33 +6,10 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"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) {
|
func TestDecodeBlock(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
rawBlock = "00000000b7def681f0080262aa293071c53b41fc3146b196067243700b68acd059734fd19543108bf9ddc738cbee2ed1160f153aa0d057f062de0aa3cbb64ba88735c23d43667e59543f050095df82b02e324c5ff3812db982f3b0089a21a278988efeec6a027b2501fd450140113ac66657c2f544e8ad13905fcb2ebaadfef9502cbefb07960fbe56df098814c223dcdd3d0efa0b43a9459e654d948516dcbd8b370f50fbecfb8b411d48051a408500ce85591e516525db24065411f6a88f43de90fa9c167c2e6f5af43bc84e65e5a4bb174bc83a19b6965ff10f476b1b151ae15439a985f33916abc6822b0bb140f4aae522ffaea229987a10d01beec826c3b9a189fe02aa82680581b78f3df0ea4d3f93ca8ea35ffc90f15f7db9017f92fafd9380d9ba3237973cf4313cf626fc40e30e50e3588bd047b39f478b59323868cd50c7ab54355d8245bf0f1988d37528f9bbfc68110cf917debbdbf1f4bdd02cdcccdc3269fdf18a6c727ee54b6934d840e43918dd1ec6123550ec37a513e72b34b2c2a3baa510dec3037cbef2fa9f6ed1e7ccd1f3f6e19d4ce2c0919af55249a970c2685217f75a5589cf9e54dff8449af155210209e7fd41dfb5c2f8dc72eb30358ac100ea8c72da18847befe06eade68cebfcb9210327da12b5c40200e9f65569476bbff2218da4f32548ff43b6387ec1416a231ee821034ff5ceeac41acf22cd5ed2da17a6df4dd8358fcb2bfb1a43208ad0feaab2746b21026ce35b29147ad09e4afe4ec4a7319095f08198fa8babbe3c56e970b143528d2221038dddc06ce687677a53d54f096d2591ba2302068cf123c1f2d75c2dddc542557921039dafd8571a641058ccc832c5e2111ea39b09c0bde36050914384f7a48bce9bf92102d02b1873a0863cd042cc717da31cea0d7cf9db32b74d4c72c01b0011503e2e2257ae01000095df82b000000000"
|
rawBlock = "00000000b7def681f0080262aa293071c53b41fc3146b196067243700b68acd059734fd19543108bf9ddc738cbee2ed1160f153aa0d057f062de0aa3cbb64ba88735c23d43667e59543f050095df82b02e324c5ff3812db982f3b0089a21a278988efeec6a027b2501fd450140113ac66657c2f544e8ad13905fcb2ebaadfef9502cbefb07960fbe56df098814c223dcdd3d0efa0b43a9459e654d948516dcbd8b370f50fbecfb8b411d48051a408500ce85591e516525db24065411f6a88f43de90fa9c167c2e6f5af43bc84e65e5a4bb174bc83a19b6965ff10f476b1b151ae15439a985f33916abc6822b0bb140f4aae522ffaea229987a10d01beec826c3b9a189fe02aa82680581b78f3df0ea4d3f93ca8ea35ffc90f15f7db9017f92fafd9380d9ba3237973cf4313cf626fc40e30e50e3588bd047b39f478b59323868cd50c7ab54355d8245bf0f1988d37528f9bbfc68110cf917debbdbf1f4bdd02cdcccdc3269fdf18a6c727ee54b6934d840e43918dd1ec6123550ec37a513e72b34b2c2a3baa510dec3037cbef2fa9f6ed1e7ccd1f3f6e19d4ce2c0919af55249a970c2685217f75a5589cf9e54dff8449af155210209e7fd41dfb5c2f8dc72eb30358ac100ea8c72da18847befe06eade68cebfcb9210327da12b5c40200e9f65569476bbff2218da4f32548ff43b6387ec1416a231ee821034ff5ceeac41acf22cd5ed2da17a6df4dd8358fcb2bfb1a43208ad0feaab2746b21026ce35b29147ad09e4afe4ec4a7319095f08198fa8babbe3c56e970b143528d2221038dddc06ce687677a53d54f096d2591ba2302068cf123c1f2d75c2dddc542557921039dafd8571a641058ccc832c5e2111ea39b09c0bde36050914384f7a48bce9bf92102d02b1873a0863cd042cc717da31cea0d7cf9db32b74d4c72c01b0011503e2e2257ae01000095df82b000000000"
|
||||||
|
@ -82,7 +59,7 @@ func newBlockBase() BlockBase {
|
||||||
Index: 1,
|
Index: 1,
|
||||||
ConsensusData: 1111,
|
ConsensusData: 1111,
|
||||||
NextConsensus: util.Uint160{},
|
NextConsensus: util.Uint160{},
|
||||||
Script: &Witness{
|
Script: &transaction.Witness{
|
||||||
VerificationScript: []byte{0x0},
|
VerificationScript: []byte{0x0},
|
||||||
InvocationScript: []byte{0x1},
|
InvocationScript: []byte{0x1},
|
||||||
},
|
},
|
||||||
|
@ -104,9 +81,9 @@ func TestHashBlockEqualsHashHeader(t *testing.T) {
|
||||||
func TestBlockVerify(t *testing.T) {
|
func TestBlockVerify(t *testing.T) {
|
||||||
block := &Block{
|
block := &Block{
|
||||||
BlockBase: newBlockBase(),
|
BlockBase: newBlockBase(),
|
||||||
Transactions: []*Transaction{
|
Transactions: []*transaction.Transaction{
|
||||||
{Type: MinerTX},
|
{Type: transaction.MinerType},
|
||||||
{Type: IssueTX},
|
{Type: transaction.IssueType},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,18 +91,18 @@ func TestBlockVerify(t *testing.T) {
|
||||||
t.Fatal("block should be verified")
|
t.Fatal("block should be verified")
|
||||||
}
|
}
|
||||||
|
|
||||||
block.Transactions = []*Transaction{
|
block.Transactions = []*transaction.Transaction{
|
||||||
{Type: IssueTX},
|
{Type: transaction.IssueType},
|
||||||
{Type: MinerTX},
|
{Type: transaction.MinerType},
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.Verify(false) {
|
if block.Verify(false) {
|
||||||
t.Fatal("block should not by verified")
|
t.Fatal("block should not by verified")
|
||||||
}
|
}
|
||||||
|
|
||||||
block.Transactions = []*Transaction{
|
block.Transactions = []*transaction.Transaction{
|
||||||
{Type: MinerTX},
|
{Type: transaction.MinerType},
|
||||||
{Type: MinerTX},
|
{Type: transaction.MinerType},
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.Verify(false) {
|
if block.Verify(false) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,9 +32,9 @@ func TestAddHeaders(t *testing.T) {
|
||||||
startHash, _ := util.Uint256DecodeString("996e37358dc369912041f966f8c5d8d3a8255ba5dcbd3447f8a82b55db869099")
|
startHash, _ := util.Uint256DecodeString("996e37358dc369912041f966f8c5d8d3a8255ba5dcbd3447f8a82b55db869099")
|
||||||
bc := NewBlockchain(NewMemoryStore(), log.New(os.Stdout, "", 0), startHash)
|
bc := NewBlockchain(NewMemoryStore(), log.New(os.Stdout, "", 0), startHash)
|
||||||
|
|
||||||
h1 := &Header{BlockBase: BlockBase{Version: 0, Index: 1, Script: &Witness{}}}
|
h1 := &Header{BlockBase: BlockBase{Version: 0, Index: 1, Script: &transaction.Witness{}}}
|
||||||
h2 := &Header{BlockBase: BlockBase{Version: 0, Index: 2, Script: &Witness{}}}
|
h2 := &Header{BlockBase: BlockBase{Version: 0, Index: 2, Script: &transaction.Witness{}}}
|
||||||
h3 := &Header{BlockBase: BlockBase{Version: 0, Index: 3, Script: &Witness{}}}
|
h3 := &Header{BlockBase: BlockBase{Version: 0, Index: 3, Script: &transaction.Witness{}}}
|
||||||
|
|
||||||
if err := bc.AddHeaders(h1, h2, h3); err != nil {
|
if err := bc.AddHeaders(h1, h2, h3); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ func TestHeaderEncodeDecode(t *testing.T) {
|
||||||
Index: 3445,
|
Index: 3445,
|
||||||
ConsensusData: 394949,
|
ConsensusData: 394949,
|
||||||
NextConsensus: util.Uint160{},
|
NextConsensus: util.Uint160{},
|
||||||
Script: &Witness{
|
Script: &transaction.Witness{
|
||||||
InvocationScript: []byte{0x10},
|
InvocationScript: []byte{0x10},
|
||||||
VerificationScript: []byte{0x11},
|
VerificationScript: []byte{0x11},
|
||||||
},
|
},
|
||||||
|
@ -51,7 +52,6 @@ func TestHeaderEncodeDecode(t *testing.T) {
|
||||||
if !header.NextConsensus.Equals(headerDecode.NextConsensus) {
|
if !header.NextConsensus.Equals(headerDecode.NextConsensus) {
|
||||||
t.Fatalf("expected both next consensus fields to be equal")
|
t.Fatalf("expected both next consensus fields to be equal")
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Compare(header.Script.InvocationScript, headerDecode.Script.InvocationScript) != 0 {
|
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)
|
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 (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Witness ...
|
// Witness contains 2 scripts.
|
||||||
type Witness struct {
|
type Witness struct {
|
||||||
InvocationScript []byte
|
InvocationScript []byte
|
||||||
VerificationScript []byte
|
VerificationScript []byte
|
||||||
|
@ -17,17 +17,12 @@ type Witness struct {
|
||||||
func (wit *Witness) DecodeBinary(r io.Reader) error {
|
func (wit *Witness) DecodeBinary(r io.Reader) error {
|
||||||
lenb := util.ReadVarUint(r)
|
lenb := util.ReadVarUint(r)
|
||||||
wit.InvocationScript = make([]byte, lenb)
|
wit.InvocationScript = make([]byte, lenb)
|
||||||
if err := binary.Read(r, binary.LittleEndian, &wit.InvocationScript); err != nil {
|
if err := binary.Read(r, binary.LittleEndian, wit.InvocationScript); err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
lenb = util.ReadVarUint(r)
|
lenb = util.ReadVarUint(r)
|
||||||
wit.VerificationScript = make([]byte, lenb)
|
wit.VerificationScript = make([]byte, lenb)
|
||||||
if err := binary.Read(r, binary.LittleEndian, &wit.VerificationScript); err != nil {
|
return binary.Read(r, binary.LittleEndian, wit.VerificationScript)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements the payload interface.
|
// 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"
|
"testing"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHeadersEncodeDecode(t *testing.T) {
|
func TestHeadersEncodeDecode(t *testing.T) {
|
||||||
|
@ -14,7 +15,7 @@ func TestHeadersEncodeDecode(t *testing.T) {
|
||||||
BlockBase: core.BlockBase{
|
BlockBase: core.BlockBase{
|
||||||
Version: 0,
|
Version: 0,
|
||||||
Index: 1,
|
Index: 1,
|
||||||
Script: &core.Witness{
|
Script: &transaction.Witness{
|
||||||
InvocationScript: []byte{0x0},
|
InvocationScript: []byte{0x0},
|
||||||
VerificationScript: []byte{0x1},
|
VerificationScript: []byte{0x1},
|
||||||
},
|
},
|
||||||
|
@ -23,7 +24,7 @@ func TestHeadersEncodeDecode(t *testing.T) {
|
||||||
BlockBase: core.BlockBase{
|
BlockBase: core.BlockBase{
|
||||||
Version: 0,
|
Version: 0,
|
||||||
Index: 2,
|
Index: 2,
|
||||||
Script: &core.Witness{
|
Script: &transaction.Witness{
|
||||||
InvocationScript: []byte{0x0},
|
InvocationScript: []byte{0x0},
|
||||||
VerificationScript: []byte{0x1},
|
VerificationScript: []byte{0x1},
|
||||||
},
|
},
|
||||||
|
@ -32,7 +33,7 @@ func TestHeadersEncodeDecode(t *testing.T) {
|
||||||
BlockBase: core.BlockBase{
|
BlockBase: core.BlockBase{
|
||||||
Version: 0,
|
Version: 0,
|
||||||
Index: 3,
|
Index: 3,
|
||||||
Script: &core.Witness{
|
Script: &transaction.Witness{
|
||||||
InvocationScript: []byte{0x0},
|
InvocationScript: []byte{0x0},
|
||||||
VerificationScript: []byte{0x1},
|
VerificationScript: []byte{0x1},
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,6 @@ import "io"
|
||||||
|
|
||||||
// Payload is anything that can be binary encoded/decoded.
|
// Payload is anything that can be binary encoded/decoded.
|
||||||
type Payload interface {
|
type Payload interface {
|
||||||
EncodeBinary(io.Writer) error
|
EncodeBinary(io.Writer) error
|
||||||
DecodeBinary(io.Reader) error
|
DecodeBinary(io.Reader) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
package smartcontract
|
package smartcontract
|
||||||
|
|
||||||
// Contract represents a NEO 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 (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const uint160Size = 20
|
const uint160Size = 20
|
||||||
|
@ -22,6 +24,16 @@ func Uint160DecodeString(s string) (u Uint160, err error) {
|
||||||
return Uint160DecodeBytes(b)
|
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.
|
// Uint160DecodeBytes attempts to decode the given bytes into an Uint160.
|
||||||
func Uint160DecodeBytes(b []byte) (u Uint160, err error) {
|
func Uint160DecodeBytes(b []byte) (u Uint160, err error) {
|
||||||
if len(b) != uint160Size {
|
if len(b) != uint160Size {
|
||||||
|
@ -42,6 +54,13 @@ func (u Uint160) Bytes() []byte {
|
||||||
return b
|
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.
|
// String implements the stringer interface.
|
||||||
func (u Uint160) String() string {
|
func (u Uint160) String() string {
|
||||||
return hex.EncodeToString(u.Bytes())
|
return hex.EncodeToString(u.Bytes())
|
||||||
|
|
|
@ -1 +1,22 @@
|
||||||
package util
|
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 (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUint256DecodeString(t *testing.T) {
|
func TestUint256DecodeString(t *testing.T) {
|
||||||
|
@ -11,7 +13,7 @@ func TestUint256DecodeString(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Log(val)
|
assert.Equal(t, hexStr, val.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUint256DecodeBytes(t *testing.T) {
|
func TestUint256DecodeBytes(t *testing.T) {
|
||||||
|
@ -24,9 +26,7 @@ func TestUint256DecodeBytes(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if val.String() != hexStr {
|
assert.Equal(t, hexStr, val.String())
|
||||||
t.Fatalf("expected %s and %s to be equal", val, hexStr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUInt256Equals(t *testing.T) {
|
func TestUInt256Equals(t *testing.T) {
|
||||||
|
|
|
@ -57,11 +57,6 @@ func emitBytes(w *bytes.Buffer, b []byte) error {
|
||||||
n = len(b)
|
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) {
|
if n <= int(vm.Opushbytes75) {
|
||||||
return emit(w, vm.Opcode(n), b)
|
return emit(w, vm.Opcode(n), b)
|
||||||
} else if n < 0x100 {
|
} else if n < 0x100 {
|
||||||
|
|
|
@ -39,7 +39,7 @@ const (
|
||||||
Ojmpifnot Opcode = 0x64
|
Ojmpifnot Opcode = 0x64
|
||||||
Ocall Opcode = 0x65
|
Ocall Opcode = 0x65
|
||||||
Oret Opcode = 0x66
|
Oret Opcode = 0x66
|
||||||
Opcall Opcode = 0x67
|
Oappcall Opcode = 0x67
|
||||||
Osyscall Opcode = 0x68
|
Osyscall Opcode = 0x68
|
||||||
Otailcall Opcode = 0x69
|
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