mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 19:42:23 +00:00
parent
0be237beb6
commit
9c09ad9c89
10 changed files with 648 additions and 109 deletions
|
@ -3,6 +3,7 @@ package transaction
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublishTX represents a publish transaction.
|
// PublishTX represents a publish transaction.
|
||||||
|
@ -62,3 +63,22 @@ func (tx *PublishTX) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteString(tx.Email)
|
bw.WriteString(tx.Email)
|
||||||
bw.WriteString(tx.Description)
|
bw.WriteString(tx.Description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// publishedContract is a JSON wrapper for PublishTransaction
|
||||||
|
type publishedContract struct {
|
||||||
|
Code publishedCode `json:"code"`
|
||||||
|
NeedStorage bool `json:"needstorage,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
CodeVersion string `json:"version,omitempty"`
|
||||||
|
Author string `json:"author,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// publishedCode is a JSON wrapper for PublishTransaction Code
|
||||||
|
type publishedCode struct {
|
||||||
|
Hash util.Uint160 `json:"hash,omitempty"`
|
||||||
|
Script string `json:"script,omitempty"`
|
||||||
|
ParamList []smartcontract.ParamType `json:"parameters,omitempty"`
|
||||||
|
ReturnType smartcontract.ParamType `json:"returntype,omitempty"`
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -51,3 +53,13 @@ func (tx *RegisterTX) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteBytes(tx.Owner.Bytes())
|
bw.WriteBytes(tx.Owner.Bytes())
|
||||||
bw.WriteBytes(tx.Admin[:])
|
bw.WriteBytes(tx.Admin[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// registeredAsset is a wrapper for RegisterTransaction
|
||||||
|
type registeredAsset struct {
|
||||||
|
AssetType AssetType `json:"type,omitempty"`
|
||||||
|
Name json.RawMessage `json:"name,omitempty"`
|
||||||
|
Amount util.Fixed8 `json:"amount,omitempty"`
|
||||||
|
Precision uint8 `json:"precision,omitempty"`
|
||||||
|
Owner keys.PublicKey `json:"owner,omitempty"`
|
||||||
|
Admin string `json:"admin,omitempty"`
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,3 +40,42 @@ func (s *StateDescriptor) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteString(s.Field)
|
w.WriteString(s.Field)
|
||||||
w.WriteVarBytes(s.Value)
|
w.WriteVarBytes(s.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stateDescriptor is a wrapper for StateDescriptor
|
||||||
|
type stateDescriptor struct {
|
||||||
|
Type DescStateType `json:"type"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Field string `json:"field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (s *StateDescriptor) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(&stateDescriptor{
|
||||||
|
Type: s.Type,
|
||||||
|
Key: hex.EncodeToString(s.Key),
|
||||||
|
Value: hex.EncodeToString(s.Value),
|
||||||
|
Field: s.Field,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||||
|
func (s *StateDescriptor) UnmarshalJSON(data []byte) error {
|
||||||
|
t := new(stateDescriptor)
|
||||||
|
if err := json.Unmarshal(data, t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key, err := hex.DecodeString(t.Key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value, err := hex.DecodeString(t.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.Key = key
|
||||||
|
s.Value = value
|
||||||
|
s.Field = t.Field
|
||||||
|
s.Type = t.Type
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -18,28 +22,28 @@ const (
|
||||||
// Transaction is a process recorded in the NEO blockchain.
|
// Transaction is a process recorded in the NEO blockchain.
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
// The type of the transaction.
|
// The type of the transaction.
|
||||||
Type TXType `json:"type"`
|
Type TXType
|
||||||
|
|
||||||
// The trading version which is currently 0.
|
// The trading version which is currently 0.
|
||||||
Version uint8 `json:"version"`
|
Version uint8
|
||||||
|
|
||||||
// Data specific to the type of the transaction.
|
// Data specific to the type of the transaction.
|
||||||
// This is always a pointer to a <Type>Transaction.
|
// This is always a pointer to a <Type>Transaction.
|
||||||
Data TXer `json:"-"`
|
Data TXer
|
||||||
|
|
||||||
// Transaction attributes.
|
// Transaction attributes.
|
||||||
Attributes []Attribute `json:"attributes"`
|
Attributes []Attribute
|
||||||
|
|
||||||
// The inputs of the transaction.
|
// The inputs of the transaction.
|
||||||
Inputs []Input `json:"vin"`
|
Inputs []Input
|
||||||
|
|
||||||
// The outputs of the transaction.
|
// The outputs of the transaction.
|
||||||
Outputs []Output `json:"vout"`
|
Outputs []Output
|
||||||
|
|
||||||
// The scripts that comes with this transaction.
|
// The scripts that comes with this transaction.
|
||||||
// Scripts exist out of the verification script
|
// Scripts exist out of the verification script
|
||||||
// and invocation script.
|
// and invocation script.
|
||||||
Scripts []Witness `json:"scripts"`
|
Scripts []Witness
|
||||||
|
|
||||||
// Hash of the transaction (double SHA256).
|
// Hash of the transaction (double SHA256).
|
||||||
hash util.Uint256
|
hash util.Uint256
|
||||||
|
@ -49,7 +53,7 @@ type Transaction struct {
|
||||||
|
|
||||||
// Trimmed indicates this is a transaction from trimmed
|
// Trimmed indicates this is a transaction from trimmed
|
||||||
// data.
|
// data.
|
||||||
Trimmed bool `json:"-"`
|
Trimmed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTrimmedTX returns a trimmed transaction with only its hash
|
// NewTrimmedTX returns a trimmed transaction with only its hash
|
||||||
|
@ -233,3 +237,160 @@ func (t *Transaction) Bytes() []byte {
|
||||||
}
|
}
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transactionJSON is a wrapper for Transaction and
|
||||||
|
// used for correct marhalling of transaction.Data
|
||||||
|
type transactionJSON struct {
|
||||||
|
TxID util.Uint256 `json:"txid"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Type TXType `json:"type"`
|
||||||
|
Version uint8 `json:"version"`
|
||||||
|
Attributes []Attribute `json:"attributes"`
|
||||||
|
Inputs []Input `json:"vin"`
|
||||||
|
Outputs []Output `json:"vout"`
|
||||||
|
Scripts []Witness `json:"scripts"`
|
||||||
|
|
||||||
|
Claims []Input `json:"claims,omitempty"`
|
||||||
|
PublicKey *keys.PublicKey `json:"pubkey,omitempty"`
|
||||||
|
Script string `json:"script,omitempty"`
|
||||||
|
Gas util.Fixed8 `json:"gas,omitempty"`
|
||||||
|
Nonce uint32 `json:"nonce,omitempty"`
|
||||||
|
Contract *publishedContract `json:"contract,omitempty"`
|
||||||
|
Asset *registeredAsset `json:"asset,omitempty"`
|
||||||
|
Descriptors []*StateDescriptor `json:"descriptors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||||
|
tx := transactionJSON{
|
||||||
|
TxID: t.Hash(),
|
||||||
|
Size: io.GetVarSize(t),
|
||||||
|
Type: t.Type,
|
||||||
|
Version: t.Version,
|
||||||
|
Attributes: t.Attributes,
|
||||||
|
Inputs: t.Inputs,
|
||||||
|
Outputs: t.Outputs,
|
||||||
|
Scripts: t.Scripts,
|
||||||
|
}
|
||||||
|
switch t.Type {
|
||||||
|
case MinerType:
|
||||||
|
tx.Nonce = t.Data.(*MinerTX).Nonce
|
||||||
|
case ClaimType:
|
||||||
|
tx.Claims = t.Data.(*ClaimTX).Claims
|
||||||
|
case EnrollmentType:
|
||||||
|
tx.PublicKey = &t.Data.(*EnrollmentTX).PublicKey
|
||||||
|
case InvocationType:
|
||||||
|
tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script)
|
||||||
|
tx.Gas = t.Data.(*InvocationTX).Gas
|
||||||
|
case PublishType:
|
||||||
|
transaction := t.Data.(*PublishTX)
|
||||||
|
tx.Contract = &publishedContract{
|
||||||
|
Code: publishedCode{
|
||||||
|
Hash: hash.Hash160(transaction.Script),
|
||||||
|
Script: hex.EncodeToString(transaction.Script),
|
||||||
|
ParamList: transaction.ParamList,
|
||||||
|
ReturnType: transaction.ReturnType,
|
||||||
|
},
|
||||||
|
NeedStorage: transaction.NeedStorage,
|
||||||
|
Name: transaction.Name,
|
||||||
|
CodeVersion: transaction.CodeVersion,
|
||||||
|
Author: transaction.Author,
|
||||||
|
Email: transaction.Email,
|
||||||
|
Description: transaction.Description,
|
||||||
|
}
|
||||||
|
case RegisterType:
|
||||||
|
transaction := *t.Data.(*RegisterTX)
|
||||||
|
tx.Asset = ®isteredAsset{
|
||||||
|
AssetType: transaction.AssetType,
|
||||||
|
Name: json.RawMessage(transaction.Name),
|
||||||
|
Amount: transaction.Amount,
|
||||||
|
Precision: transaction.Precision,
|
||||||
|
Owner: transaction.Owner,
|
||||||
|
Admin: address.Uint160ToString(transaction.Admin),
|
||||||
|
}
|
||||||
|
case StateType:
|
||||||
|
tx.Descriptors = t.Data.(*StateTX).Descriptors
|
||||||
|
}
|
||||||
|
return json.Marshal(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||||
|
func (t *Transaction) UnmarshalJSON(data []byte) error {
|
||||||
|
tx := new(transactionJSON)
|
||||||
|
if err := json.Unmarshal(data, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Type = tx.Type
|
||||||
|
t.Version = tx.Version
|
||||||
|
t.Attributes = tx.Attributes
|
||||||
|
t.Inputs = tx.Inputs
|
||||||
|
t.Outputs = tx.Outputs
|
||||||
|
t.Scripts = tx.Scripts
|
||||||
|
switch tx.Type {
|
||||||
|
case MinerType:
|
||||||
|
t.Data = &MinerTX{
|
||||||
|
Nonce: tx.Nonce,
|
||||||
|
}
|
||||||
|
case ClaimType:
|
||||||
|
t.Data = &ClaimTX{
|
||||||
|
Claims: tx.Claims,
|
||||||
|
}
|
||||||
|
case EnrollmentType:
|
||||||
|
t.Data = &EnrollmentTX{
|
||||||
|
PublicKey: *tx.PublicKey,
|
||||||
|
}
|
||||||
|
case InvocationType:
|
||||||
|
bytes, err := hex.DecodeString(tx.Script)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Data = &InvocationTX{
|
||||||
|
Script: bytes,
|
||||||
|
Gas: tx.Gas,
|
||||||
|
Version: tx.Version,
|
||||||
|
}
|
||||||
|
case PublishType:
|
||||||
|
bytes, err := hex.DecodeString(tx.Contract.Code.Script)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Data = &PublishTX{
|
||||||
|
Script: bytes,
|
||||||
|
ParamList: tx.Contract.Code.ParamList,
|
||||||
|
ReturnType: tx.Contract.Code.ReturnType,
|
||||||
|
NeedStorage: tx.Contract.NeedStorage,
|
||||||
|
Name: tx.Contract.Name,
|
||||||
|
CodeVersion: tx.Contract.CodeVersion,
|
||||||
|
Author: tx.Contract.Author,
|
||||||
|
Email: tx.Contract.Email,
|
||||||
|
Description: tx.Contract.Description,
|
||||||
|
Version: tx.Version,
|
||||||
|
}
|
||||||
|
case RegisterType:
|
||||||
|
admin, err := address.StringToUint160(tx.Asset.Admin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Data = &RegisterTX{
|
||||||
|
AssetType: tx.Asset.AssetType,
|
||||||
|
Name: string(tx.Asset.Name),
|
||||||
|
Amount: tx.Asset.Amount,
|
||||||
|
Precision: tx.Asset.Precision,
|
||||||
|
Owner: tx.Asset.Owner,
|
||||||
|
Admin: admin,
|
||||||
|
}
|
||||||
|
case StateType:
|
||||||
|
t.Data = &StateTX{
|
||||||
|
Descriptors: tx.Descriptors,
|
||||||
|
}
|
||||||
|
case ContractType:
|
||||||
|
t.Data = &ContractTX{}
|
||||||
|
case IssueType:
|
||||||
|
t.Data = &IssueTX{}
|
||||||
|
}
|
||||||
|
if t.Hash() != tx.TxID {
|
||||||
|
return errors.New("txid doesn't match transaction hash")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
@ -155,7 +156,7 @@ func TestEncodingTXWithNoData(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalUnmarshalJSON(t *testing.T) {
|
func TestMarshalUnmarshalJSONContractTX(t *testing.T) {
|
||||||
tx := NewContractTX()
|
tx := NewContractTX()
|
||||||
tx.Outputs = []Output{{
|
tx.Outputs = []Output{{
|
||||||
AssetID: util.Uint256{1, 2, 3, 4},
|
AssetID: util.Uint256{1, 2, 3, 4},
|
||||||
|
@ -167,5 +168,196 @@ func TestMarshalUnmarshalJSON(t *testing.T) {
|
||||||
InvocationScript: []byte{5, 3, 1},
|
InvocationScript: []byte{5, 3, 1},
|
||||||
VerificationScript: []byte{2, 4, 6},
|
VerificationScript: []byte{2, 4, 6},
|
||||||
}}
|
}}
|
||||||
|
tx.Data = &ContractTX{}
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJSONMinerTX(t *testing.T) {
|
||||||
|
tx := &Transaction{
|
||||||
|
Type: MinerType,
|
||||||
|
Version: 0,
|
||||||
|
Data: &MinerTX{Nonce: 12345},
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
Inputs: []Input{},
|
||||||
|
Outputs: []Output{},
|
||||||
|
Scripts: []Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJSONClaimTX(t *testing.T) {
|
||||||
|
tx := &Transaction{
|
||||||
|
Type: ClaimType,
|
||||||
|
Version: 0,
|
||||||
|
Data: &ClaimTX{Claims: []Input{
|
||||||
|
{
|
||||||
|
PrevHash: util.Uint256{1, 2, 3, 4},
|
||||||
|
PrevIndex: uint16(56),
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
Inputs: []Input{{
|
||||||
|
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||||
|
PrevIndex: uint16(12),
|
||||||
|
}},
|
||||||
|
Outputs: []Output{{
|
||||||
|
AssetID: util.Uint256{1, 2, 3},
|
||||||
|
Amount: util.Fixed8FromInt64(1),
|
||||||
|
ScriptHash: util.Uint160{1, 2, 3},
|
||||||
|
Position: 0,
|
||||||
|
}},
|
||||||
|
Scripts: []Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJSONEnrollmentTX(t *testing.T) {
|
||||||
|
str := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
|
||||||
|
pubKey, err := keys.NewPublicKeyFromString(str)
|
||||||
|
require.NoError(t, err)
|
||||||
|
tx := &Transaction{
|
||||||
|
Type: EnrollmentType,
|
||||||
|
Version: 5,
|
||||||
|
Data: &EnrollmentTX{PublicKey: *pubKey},
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
Inputs: []Input{{
|
||||||
|
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||||
|
PrevIndex: uint16(12),
|
||||||
|
}},
|
||||||
|
Outputs: []Output{{
|
||||||
|
AssetID: util.Uint256{1, 2, 3},
|
||||||
|
Amount: util.Fixed8FromInt64(1),
|
||||||
|
ScriptHash: util.Uint160{1, 2, 3},
|
||||||
|
Position: 0,
|
||||||
|
}},
|
||||||
|
Scripts: []Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
|
||||||
|
tx := &Transaction{
|
||||||
|
Type: InvocationType,
|
||||||
|
Version: 3,
|
||||||
|
Data: &InvocationTX{
|
||||||
|
Script: []byte{1, 2, 3, 4},
|
||||||
|
Gas: util.Fixed8FromFloat(100),
|
||||||
|
Version: 3,
|
||||||
|
},
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
Inputs: []Input{{
|
||||||
|
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||||
|
PrevIndex: uint16(12),
|
||||||
|
}},
|
||||||
|
Outputs: []Output{{
|
||||||
|
AssetID: util.Uint256{1, 2, 3},
|
||||||
|
Amount: util.Fixed8FromInt64(1),
|
||||||
|
ScriptHash: util.Uint160{1, 2, 3},
|
||||||
|
Position: 0,
|
||||||
|
}},
|
||||||
|
Scripts: []Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJSONPublishTX(t *testing.T) {
|
||||||
|
tx := &Transaction{
|
||||||
|
Type: PublishType,
|
||||||
|
Version: 5,
|
||||||
|
Data: &PublishTX{
|
||||||
|
Script: []byte{1, 2, 3, 4},
|
||||||
|
ParamList: []smartcontract.ParamType{smartcontract.IntegerType, smartcontract.Hash160Type},
|
||||||
|
ReturnType: smartcontract.BoolType,
|
||||||
|
NeedStorage: true,
|
||||||
|
Name: "Name",
|
||||||
|
CodeVersion: "1.0",
|
||||||
|
Author: "Author",
|
||||||
|
Email: "Email",
|
||||||
|
Description: "Description",
|
||||||
|
Version: 5,
|
||||||
|
},
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
Inputs: []Input{{
|
||||||
|
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||||
|
PrevIndex: uint16(12),
|
||||||
|
}},
|
||||||
|
Outputs: []Output{{
|
||||||
|
AssetID: util.Uint256{1, 2, 3},
|
||||||
|
Amount: util.Fixed8FromInt64(1),
|
||||||
|
ScriptHash: util.Uint160{1, 2, 3},
|
||||||
|
Position: 0,
|
||||||
|
}},
|
||||||
|
Scripts: []Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJSONRegisterTX(t *testing.T) {
|
||||||
|
tx := &Transaction{
|
||||||
|
Type: RegisterType,
|
||||||
|
Version: 5,
|
||||||
|
Data: &RegisterTX{
|
||||||
|
AssetType: 0,
|
||||||
|
Name: `[{"lang":"zh-CN","name":"小蚁股"},{"lang":"en","name":"AntShare"}]`,
|
||||||
|
Amount: 1000000,
|
||||||
|
Precision: 0,
|
||||||
|
Owner: keys.PublicKey{},
|
||||||
|
Admin: util.Uint160{},
|
||||||
|
},
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
Inputs: []Input{{
|
||||||
|
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||||
|
PrevIndex: uint16(12),
|
||||||
|
}},
|
||||||
|
Outputs: []Output{{
|
||||||
|
AssetID: util.Uint256{1, 2, 3},
|
||||||
|
Amount: util.Fixed8FromInt64(1),
|
||||||
|
ScriptHash: util.Uint160{1, 2, 3},
|
||||||
|
Position: 0,
|
||||||
|
}},
|
||||||
|
Scripts: []Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJSONStateTX(t *testing.T) {
|
||||||
|
tx := &Transaction{
|
||||||
|
Type: StateType,
|
||||||
|
Version: 5,
|
||||||
|
Data: &StateTX{
|
||||||
|
Descriptors: []*StateDescriptor{&StateDescriptor{
|
||||||
|
Type: Validator,
|
||||||
|
Key: []byte{1, 2, 3},
|
||||||
|
Value: []byte{4, 5, 6},
|
||||||
|
Field: "Field",
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Attributes: []Attribute{},
|
||||||
|
Inputs: []Input{{
|
||||||
|
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||||
|
PrevIndex: uint16(12),
|
||||||
|
}},
|
||||||
|
Outputs: []Output{{
|
||||||
|
AssetID: util.Uint256{1, 2, 3},
|
||||||
|
Amount: util.Fixed8FromInt64(1),
|
||||||
|
ScriptHash: util.Uint160{1, 2, 3},
|
||||||
|
Position: 0,
|
||||||
|
}},
|
||||||
|
Scripts: []Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
|
||||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ func (c *Client) getBlock(params request.RawParams) (*block.Block, error) {
|
||||||
|
|
||||||
// GetBlockByIndexVerbose returns a block wrapper with additional metadata by
|
// GetBlockByIndexVerbose returns a block wrapper with additional metadata by
|
||||||
// its height.
|
// its height.
|
||||||
|
// NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
|
||||||
func (c *Client) GetBlockByIndexVerbose(index uint32) (*result.Block, error) {
|
func (c *Client) GetBlockByIndexVerbose(index uint32) (*result.Block, error) {
|
||||||
return c.getBlockVerbose(request.NewRawParams(index, 1))
|
return c.getBlockVerbose(request.NewRawParams(index, 1))
|
||||||
}
|
}
|
||||||
|
@ -289,6 +290,7 @@ func (c *Client) GetRawTransaction(hash util.Uint256) (*transaction.Transaction,
|
||||||
|
|
||||||
// GetRawTransactionVerbose returns a transaction wrapper with additional
|
// GetRawTransactionVerbose returns a transaction wrapper with additional
|
||||||
// metadata by transaction's hash.
|
// metadata by transaction's hash.
|
||||||
|
// NOTE: to get transaction.ID and transaction.Size, use t.Hash() and io.GetVarSize(t) respectively.
|
||||||
func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) {
|
func (c *Client) GetRawTransactionVerbose(hash util.Uint256) (*result.TransactionOutputRaw, error) {
|
||||||
var (
|
var (
|
||||||
params = request.NewRawParams(hash.StringLE(), 1)
|
params = request.NewRawParams(hash.StringLE(), 1)
|
||||||
|
|
|
@ -172,10 +172,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
txID, err := util.Uint256DecodeStringLE("cb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
invScript, err := hex.DecodeString("40356a91d94e398170e47447d6a0f60aa5470e209782a5452403115a49166db3e1c4a3898122db19f779c30f8ccd0b7d401acdf71eda340655e4ae5237a64961bf4034dd47955e5a71627dafc39dd92999140e9eaeec6b11dbb2b313efa3f1093ed915b4455e199c69ec53778f94ffc236b92f8b97fff97a1f6bbb3770c0c0b3844a40fbe743bd5c90b2f5255e0b073281d7aeb2fb516572f36bec8446bcc37ac755cbf10d08b16c95644db1b2dddc2df5daa377880b20198fc7b967ac6e76474b22df")
|
invScript, err := hex.DecodeString("40356a91d94e398170e47447d6a0f60aa5470e209782a5452403115a49166db3e1c4a3898122db19f779c30f8ccd0b7d401acdf71eda340655e4ae5237a64961bf4034dd47955e5a71627dafc39dd92999140e9eaeec6b11dbb2b313efa3f1093ed915b4455e199c69ec53778f94ffc236b92f8b97fff97a1f6bbb3770c0c0b3844a40fbe743bd5c90b2f5255e0b073281d7aeb2fb516572f36bec8446bcc37ac755cbf10d08b16c95644db1b2dddc2df5daa377880b20198fc7b967ac6e76474b22df")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -184,6 +180,18 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
tx := &transaction.Transaction{
|
||||||
|
Type: transaction.MinerType,
|
||||||
|
Version: 0,
|
||||||
|
Data: &transaction.MinerTX{Nonce: 4266257741},
|
||||||
|
Attributes: []transaction.Attribute{},
|
||||||
|
Inputs: []transaction.Input{},
|
||||||
|
Outputs: []transaction.Output{},
|
||||||
|
Scripts: []transaction.Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
// Update hashes for correct result comparison.
|
||||||
|
_ = tx.Hash()
|
||||||
return &result.Block{
|
return &result.Block{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Size: 452,
|
Size: 452,
|
||||||
|
@ -201,17 +209,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
VerificationScript: verifScript,
|
VerificationScript: verifScript,
|
||||||
},
|
},
|
||||||
Tx: []result.Tx{{
|
Tx: []result.Tx{{
|
||||||
TxID: txID,
|
Transaction: tx,
|
||||||
Size: 10,
|
Fees: result.Fees{
|
||||||
Type: transaction.MinerType,
|
SysFee: 0,
|
||||||
Version: 0,
|
NetFee: 0,
|
||||||
Attributes: []transaction.Attribute{},
|
},
|
||||||
VIn: []transaction.Input{},
|
|
||||||
VOut: []transaction.Output{},
|
|
||||||
Scripts: []transaction.Witness{},
|
|
||||||
SysFee: 0,
|
|
||||||
NetFee: 0,
|
|
||||||
Nonce: 4266257741,
|
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -265,10 +267,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
txID, err := util.Uint256DecodeStringLE("cb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
invScript, err := hex.DecodeString("40356a91d94e398170e47447d6a0f60aa5470e209782a5452403115a49166db3e1c4a3898122db19f779c30f8ccd0b7d401acdf71eda340655e4ae5237a64961bf4034dd47955e5a71627dafc39dd92999140e9eaeec6b11dbb2b313efa3f1093ed915b4455e199c69ec53778f94ffc236b92f8b97fff97a1f6bbb3770c0c0b3844a40fbe743bd5c90b2f5255e0b073281d7aeb2fb516572f36bec8446bcc37ac755cbf10d08b16c95644db1b2dddc2df5daa377880b20198fc7b967ac6e76474b22df")
|
invScript, err := hex.DecodeString("40356a91d94e398170e47447d6a0f60aa5470e209782a5452403115a49166db3e1c4a3898122db19f779c30f8ccd0b7d401acdf71eda340655e4ae5237a64961bf4034dd47955e5a71627dafc39dd92999140e9eaeec6b11dbb2b313efa3f1093ed915b4455e199c69ec53778f94ffc236b92f8b97fff97a1f6bbb3770c0c0b3844a40fbe743bd5c90b2f5255e0b073281d7aeb2fb516572f36bec8446bcc37ac755cbf10d08b16c95644db1b2dddc2df5daa377880b20198fc7b967ac6e76474b22df")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -277,6 +275,18 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
tx := &transaction.Transaction{
|
||||||
|
Type: transaction.MinerType,
|
||||||
|
Version: 0,
|
||||||
|
Data: &transaction.MinerTX{Nonce: 4266257741},
|
||||||
|
Attributes: []transaction.Attribute{},
|
||||||
|
Inputs: []transaction.Input{},
|
||||||
|
Outputs: []transaction.Output{},
|
||||||
|
Scripts: []transaction.Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
// Update hashes for correct result comparison.
|
||||||
|
_ = tx.Hash()
|
||||||
return &result.Block{
|
return &result.Block{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Size: 452,
|
Size: 452,
|
||||||
|
@ -294,17 +304,11 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
VerificationScript: verifScript,
|
VerificationScript: verifScript,
|
||||||
},
|
},
|
||||||
Tx: []result.Tx{{
|
Tx: []result.Tx{{
|
||||||
TxID: txID,
|
Transaction: tx,
|
||||||
Size: 10,
|
Fees: result.Fees{
|
||||||
Type: transaction.MinerType,
|
SysFee: 0,
|
||||||
Version: 0,
|
NetFee: 0,
|
||||||
Attributes: []transaction.Attribute{},
|
},
|
||||||
VIn: []transaction.Input{},
|
|
||||||
VOut: []transaction.Output{},
|
|
||||||
Scripts: []transaction.Witness{},
|
|
||||||
SysFee: 0,
|
|
||||||
NetFee: 0,
|
|
||||||
Nonce: 4266257741,
|
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -646,34 +650,34 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
}
|
}
|
||||||
return c.GetRawTransactionVerbose(hash)
|
return c.GetRawTransactionVerbose(hash)
|
||||||
},
|
},
|
||||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"type":"MinerTransaction","version":0,"attributes":[],"vin":[],"vout":[],"scripts":[],"txid":"0xcb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2","size":10,"sys_fee":"0","net_fee":"0","blockhash":"0xe93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c","confirmations":20875,"blocktime":1541215200}}`,
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"nonce":4266257741,"type":"MinerTransaction","version":0,"attributes":[],"vin":[],"vout":[],"scripts":[],"txid":"0xcb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2","size":10,"sys_fee":"0","net_fee":"0","blockhash":"0xe93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c","confirmations":20875,"blocktime":1541215200}}`,
|
||||||
result: func(c *Client) interface{} {
|
result: func(c *Client) interface{} {
|
||||||
txHash, err := util.Uint256DecodeStringLE("cb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
blockHash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
|
blockHash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
tx := &transaction.Transaction{
|
||||||
|
Type: transaction.MinerType,
|
||||||
|
Version: 0,
|
||||||
|
Data: &transaction.MinerTX{Nonce: 4266257741},
|
||||||
|
Attributes: []transaction.Attribute{},
|
||||||
|
Inputs: []transaction.Input{},
|
||||||
|
Outputs: []transaction.Output{},
|
||||||
|
Scripts: []transaction.Witness{},
|
||||||
|
Trimmed: false,
|
||||||
|
}
|
||||||
|
// Update hashes for correct result comparison.
|
||||||
|
_ = tx.Hash()
|
||||||
|
|
||||||
return &result.TransactionOutputRaw{
|
return &result.TransactionOutputRaw{
|
||||||
Transaction: &transaction.Transaction{
|
Transaction: tx,
|
||||||
Type: transaction.MinerType,
|
TransactionMetadata: result.TransactionMetadata{
|
||||||
Version: 0,
|
SysFee: 0,
|
||||||
Data: nil,
|
NetFee: 0,
|
||||||
Attributes: []transaction.Attribute{},
|
Blockhash: blockHash,
|
||||||
Inputs: []transaction.Input{},
|
Confirmations: 20875,
|
||||||
Outputs: []transaction.Output{},
|
Timestamp: uint32(1541215200),
|
||||||
Scripts: []transaction.Witness{},
|
|
||||||
Trimmed: false,
|
|
||||||
},
|
},
|
||||||
TxHash: txHash,
|
|
||||||
Size: 10,
|
|
||||||
SysFee: 0,
|
|
||||||
NetFee: 0,
|
|
||||||
Blockhash: blockHash,
|
|
||||||
Confirmations: 20875,
|
|
||||||
Timestamp: 1541215200,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package result
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
@ -15,19 +17,14 @@ type (
|
||||||
// Tx wrapper used for the representation of
|
// Tx wrapper used for the representation of
|
||||||
// transaction on the RPC Server.
|
// transaction on the RPC Server.
|
||||||
Tx struct {
|
Tx struct {
|
||||||
TxID util.Uint256 `json:"txid"`
|
*transaction.Transaction
|
||||||
Size int `json:"size"`
|
Fees
|
||||||
Type transaction.TXType `json:"type"`
|
}
|
||||||
Version uint8 `json:"version"`
|
|
||||||
Attributes []transaction.Attribute `json:"attributes"`
|
|
||||||
VIn []transaction.Input `json:"vin"`
|
|
||||||
VOut []transaction.Output `json:"vout"`
|
|
||||||
Scripts []transaction.Witness `json:"scripts"`
|
|
||||||
|
|
||||||
|
// Fees is an auxilliary struct for proper Tx marshaling.
|
||||||
|
Fees struct {
|
||||||
SysFee util.Fixed8 `json:"sys_fee"`
|
SysFee util.Fixed8 `json:"sys_fee"`
|
||||||
NetFee util.Fixed8 `json:"net_fee"`
|
NetFee util.Fixed8 `json:"net_fee"`
|
||||||
|
|
||||||
Nonce uint32 `json:"nonce,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block wrapper used for the representation of
|
// Block wrapper used for the representation of
|
||||||
|
@ -77,32 +74,59 @@ func NewBlock(b *block.Block, chain core.Blockchainer) Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range b.Transactions {
|
for i := range b.Transactions {
|
||||||
tx := Tx{
|
res.Tx = append(res.Tx, Tx{
|
||||||
TxID: b.Transactions[i].Hash(),
|
Transaction: b.Transactions[i],
|
||||||
Size: io.GetVarSize(b.Transactions[i]),
|
Fees: Fees{
|
||||||
Type: b.Transactions[i].Type,
|
SysFee: chain.SystemFee(b.Transactions[i]),
|
||||||
Version: b.Transactions[i].Version,
|
NetFee: chain.NetworkFee(b.Transactions[i]),
|
||||||
Attributes: make([]transaction.Attribute, 0, len(b.Transactions[i].Attributes)),
|
},
|
||||||
VIn: make([]transaction.Input, 0, len(b.Transactions[i].Inputs)),
|
})
|
||||||
VOut: make([]transaction.Output, 0, len(b.Transactions[i].Outputs)),
|
|
||||||
Scripts: make([]transaction.Witness, 0, len(b.Transactions[i].Scripts)),
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(tx.Attributes, b.Transactions[i].Attributes)
|
|
||||||
copy(tx.VIn, b.Transactions[i].Inputs)
|
|
||||||
copy(tx.VOut, b.Transactions[i].Outputs)
|
|
||||||
copy(tx.Scripts, b.Transactions[i].Scripts)
|
|
||||||
|
|
||||||
tx.SysFee = chain.SystemFee(b.Transactions[i])
|
|
||||||
tx.NetFee = chain.NetworkFee(b.Transactions[i])
|
|
||||||
|
|
||||||
// set nonce only for MinerTransaction
|
|
||||||
if miner, ok := b.Transactions[i].Data.(*transaction.MinerTX); ok {
|
|
||||||
tx.Nonce = miner.Nonce
|
|
||||||
}
|
|
||||||
|
|
||||||
res.Tx = append(res.Tx, tx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (t Tx) MarshalJSON() ([]byte, error) {
|
||||||
|
output, err := json.Marshal(&Fees{
|
||||||
|
SysFee: t.SysFee,
|
||||||
|
NetFee: t.NetFee,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
txBytes, err := json.Marshal(t.Transaction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have to keep both transaction.Transaction and tx at the same level in json in order to match C# API,
|
||||||
|
// so there's no way to marshall Tx correctly with standard json.Marshaller tool.
|
||||||
|
if output[len(output)-1] != '}' || txBytes[0] != '{' {
|
||||||
|
return nil, errors.New("can't merge internal jsons")
|
||||||
|
}
|
||||||
|
output[len(output)-1] = ','
|
||||||
|
output = append(output, txBytes[1:]...)
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Marshaler interface.
|
||||||
|
func (t *Tx) UnmarshalJSON(data []byte) error {
|
||||||
|
// As transaction.Transaction and tx are at the same level in json, do unmarshalling
|
||||||
|
// separately for both structs.
|
||||||
|
output := new(Fees)
|
||||||
|
err := json.Unmarshal(data, output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.SysFee = output.SysFee
|
||||||
|
t.NetFee = output.NetFee
|
||||||
|
|
||||||
|
transaction := new(transaction.Transaction)
|
||||||
|
err = json.Unmarshal(data, transaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Transaction = transaction
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package result
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,8 +14,11 @@ import (
|
||||||
// a Transaction.
|
// a Transaction.
|
||||||
type TransactionOutputRaw struct {
|
type TransactionOutputRaw struct {
|
||||||
*transaction.Transaction
|
*transaction.Transaction
|
||||||
TxHash util.Uint256 `json:"txid"`
|
TransactionMetadata
|
||||||
Size int `json:"size"`
|
}
|
||||||
|
|
||||||
|
// TransactionMetadata is an auxilliary struct for proper TransactionOutputRaw marshaling.
|
||||||
|
type TransactionMetadata struct {
|
||||||
SysFee util.Fixed8 `json:"sys_fee"`
|
SysFee util.Fixed8 `json:"sys_fee"`
|
||||||
NetFee util.Fixed8 `json:"net_fee"`
|
NetFee util.Fixed8 `json:"net_fee"`
|
||||||
Blockhash util.Uint256 `json:"blockhash,omitempty"`
|
Blockhash util.Uint256 `json:"blockhash,omitempty"`
|
||||||
|
@ -30,13 +35,64 @@ func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header,
|
||||||
o.Position = i
|
o.Position = i
|
||||||
}
|
}
|
||||||
return TransactionOutputRaw{
|
return TransactionOutputRaw{
|
||||||
Transaction: tx,
|
Transaction: tx,
|
||||||
TxHash: tx.Hash(),
|
TransactionMetadata: TransactionMetadata{
|
||||||
Size: io.GetVarSize(tx),
|
SysFee: chain.SystemFee(tx),
|
||||||
SysFee: chain.SystemFee(tx),
|
NetFee: chain.NetworkFee(tx),
|
||||||
NetFee: chain.NetworkFee(tx),
|
Blockhash: header.Hash(),
|
||||||
Blockhash: header.Hash(),
|
Confirmations: confirmations,
|
||||||
Confirmations: confirmations,
|
Timestamp: header.Timestamp,
|
||||||
Timestamp: header.Timestamp,
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (t TransactionOutputRaw) MarshalJSON() ([]byte, error) {
|
||||||
|
output, err := json.Marshal(TransactionMetadata{
|
||||||
|
SysFee: t.SysFee,
|
||||||
|
NetFee: t.NetFee,
|
||||||
|
Blockhash: t.Blockhash,
|
||||||
|
Confirmations: t.Confirmations,
|
||||||
|
Timestamp: t.Timestamp,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
txBytes, err := json.Marshal(t.Transaction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have to keep both transaction.Transaction and tranactionOutputRaw at the same level in json
|
||||||
|
// in order to match C# API, so there's no way to marshall Tx correctly with standard json.Marshaller tool.
|
||||||
|
if output[len(output)-1] != '}' || txBytes[0] != '{' {
|
||||||
|
return nil, errors.New("can't merge internal jsons")
|
||||||
|
}
|
||||||
|
output[len(output)-1] = ','
|
||||||
|
output = append(output, txBytes[1:]...)
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Marshaler interface.
|
||||||
|
func (t *TransactionOutputRaw) UnmarshalJSON(data []byte) error {
|
||||||
|
// As transaction.Transaction and tranactionOutputRaw are at the same level in json,
|
||||||
|
// do unmarshalling separately for both structs.
|
||||||
|
output := new(TransactionMetadata)
|
||||||
|
err := json.Unmarshal(data, output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.SysFee = output.SysFee
|
||||||
|
t.NetFee = output.NetFee
|
||||||
|
t.Blockhash = output.Blockhash
|
||||||
|
t.Confirmations = output.Confirmations
|
||||||
|
t.Timestamp = output.Timestamp
|
||||||
|
|
||||||
|
transaction := new(transaction.Transaction)
|
||||||
|
err = json.Unmarshal(data, transaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Transaction = transaction
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
@ -336,12 +337,12 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
assert.Equal(t, block.Hash(), res.Hash)
|
assert.Equal(t, block.Hash(), res.Hash)
|
||||||
for i := range res.Tx {
|
for i := range res.Tx {
|
||||||
tx := res.Tx[i]
|
tx := res.Tx[i]
|
||||||
require.Equal(t, transaction.MinerType, tx.Type)
|
require.Equal(t, transaction.MinerType, tx.Transaction.Type)
|
||||||
|
|
||||||
miner, ok := block.Transactions[i].Data.(*transaction.MinerTX)
|
miner, ok := block.Transactions[i].Data.(*transaction.MinerTX)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, miner.Nonce, tx.Nonce)
|
require.Equal(t, miner.Nonce, tx.Transaction.Data.(*transaction.MinerTX).Nonce)
|
||||||
require.Equal(t, block.Transactions[i].Hash(), tx.TxID)
|
require.Equal(t, block.Transactions[i].Hash(), tx.Transaction.Hash())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -935,6 +936,31 @@ func TestRPC(t *testing.T) {
|
||||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res)
|
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) {
|
||||||
|
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
||||||
|
TXHash := block.Transactions[1].Hash()
|
||||||
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 1]}"`, TXHash.StringLE())
|
||||||
|
body := doRPCCall(rpc, handler, t)
|
||||||
|
txOut := checkErrGetResult(t, body, false)
|
||||||
|
actual := result.TransactionOutputRaw{}
|
||||||
|
err := json.Unmarshal(txOut, &actual)
|
||||||
|
require.NoErrorf(t, err, "could not parse response: %s", txOut)
|
||||||
|
admin, err := util.Uint160DecodeStringBE("da1745e9b549bd0bfa1a569971c77eba30cd5a4b")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, transaction.RegisterType, actual.Transaction.Type)
|
||||||
|
assert.Equal(t, &transaction.RegisterTX{
|
||||||
|
AssetType: 0,
|
||||||
|
Name: `[{"lang":"zh-CN","name":"小蚁股"},{"lang":"en","name":"AntShare"}]`,
|
||||||
|
Amount: util.Fixed8FromInt64(100000000),
|
||||||
|
Precision: 0,
|
||||||
|
Owner: keys.PublicKey{},
|
||||||
|
Admin: admin,
|
||||||
|
}, actual.Transaction.Data.(*transaction.RegisterTX))
|
||||||
|
assert.Equal(t, 210, actual.Confirmations)
|
||||||
|
assert.Equal(t, TXHash, actual.Transaction.Hash())
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("gettxout", func(t *testing.T) {
|
t.Run("gettxout", func(t *testing.T) {
|
||||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
||||||
tx := block.Transactions[3]
|
tx := block.Transactions[3]
|
||||||
|
|
Loading…
Reference in a new issue