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
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 ""
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue