Implemented rpc server method GetRawTransaction (#135)
* Added utility function GetVarSize * 1) Added Size method: this implied that Fixed8 implements now the serializable interface. 2) Added few arithmetic operation (Add, Sub, div): this will be used to calculated networkfeeand feePerByte. Changed return value of the Value() method to int instead of int64. Modified fixed8_test accordingly. * Implemented Size or MarshalJSON method. - Structs accepting the Size method implement the serializable interface. - Structs accepting the MarshalJSON method implements the customized json marshaller interface. * Added fee calculation * Implemented rcp server method GetRawTransaction * Updated Tests * Fixed: 1) NewFixed8 will accept as input int64 2) race condition affecting configDeafault, blockchainDefault * Simplified Size calculation * 1) Removed global variable blockchainDefault, configDefault 2) Extended Blockchainer interface to include the methods: References, FeePerByte, SystemFee, NetworkFee 3) Deleted fee_test.go, fee.go. Moved corresponding methods to blockchain_test.go and blockchain.go respectively 4) Amended tx_raw_output.go * Simplified GetVarSize Method * Replaced ValueAtAndType with ValueWithType * Cosmetic changes + Added test case getrawtransaction_7 * Clean up Print statement * Filled up keys * Aligned verbose logic with the C#-neo implementation * Implemented @Kim requests Refactor server_test.go * Small fixes * Fixed verbose logic Added more tests Cosmetic changes * Replaced assert.NoError with require.NoError * Fixed tests by adding context.Background() as argument * Fixed tests
This commit is contained in:
parent
20bb05b335
commit
19201dcf52
23 changed files with 859 additions and 151 deletions
|
@ -6,6 +6,8 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -118,3 +120,19 @@ func Load(path string, netMode NetMode) (Config, error) {
|
|||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// TryGetValue returns the system fee base on transaction type.
|
||||
func (s SystemFee) TryGetValue(txType transaction.TXType) util.Fixed8 {
|
||||
switch txType {
|
||||
case transaction.EnrollmentType:
|
||||
return util.NewFixed8(s.EnrollmentTransaction)
|
||||
case transaction.IssueType:
|
||||
return util.NewFixed8(s.IssueTransaction)
|
||||
case transaction.PublishType:
|
||||
return util.NewFixed8(s.PublishTransaction)
|
||||
case transaction.RegisterType:
|
||||
return util.NewFixed8(s.RegisterTransaction)
|
||||
default:
|
||||
return util.NewFixed8(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,9 @@ func NewBlockchain(ctx context.Context, s storage.Store, cfg config.ProtocolConf
|
|||
blockCache: NewCache(),
|
||||
verifyBlocks: false,
|
||||
}
|
||||
go bc.run(ctx)
|
||||
|
||||
if err := bc.init(); err != nil {
|
||||
go bc.run(ctx)
|
||||
if err := bc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ func (bc *Blockchain) init() error {
|
|||
headers := make([]*Header, 0)
|
||||
|
||||
for hash != targetHash {
|
||||
header, err := bc.getHeader(hash)
|
||||
header, err := bc.GetHeader(hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get header %s: %s", hash, err)
|
||||
}
|
||||
|
@ -494,7 +494,7 @@ func (bc *Blockchain) GetBlock(hash util.Uint256) (*Block, error) {
|
|||
return block, nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) getHeader(hash util.Uint256) (*Header, error) {
|
||||
func (bc *Blockchain) GetHeader(hash util.Uint256) (*Header, error) {
|
||||
b, err := bc.Get(storage.AppendPrefix(storage.DataBlock, hash.BytesReverse()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -515,7 +515,7 @@ func (bc *Blockchain) HasTransaction(hash util.Uint256) bool {
|
|||
// HasBlock return true if the blockchain contains the given
|
||||
// block hash.
|
||||
func (bc *Blockchain) HasBlock(hash util.Uint256) bool {
|
||||
if header, err := bc.getHeader(hash); err == nil {
|
||||
if header, err := bc.GetHeader(hash); err == nil {
|
||||
return header.Index <= bc.BlockHeight()
|
||||
}
|
||||
return false
|
||||
|
@ -585,6 +585,62 @@ func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *AccountState {
|
|||
return as
|
||||
}
|
||||
|
||||
// GetConfig returns the config stored in the blockchain
|
||||
func (bc *Blockchain) GetConfig() config.ProtocolConfiguration {
|
||||
return bc.config
|
||||
}
|
||||
|
||||
// References returns a map with input prevHash as key (util.Uint256)
|
||||
// and transaction output as value from a transaction t.
|
||||
// @TODO: unfortunately we couldn't attach this method to the Transaction struct in the
|
||||
// transaction package because of a import cycle problem. Perhaps we should think to re-design
|
||||
// the code base to avoid this situation.
|
||||
func (bc *Blockchain) References(t *transaction.Transaction) map[util.Uint256]*transaction.Output {
|
||||
references := make(map[util.Uint256]*transaction.Output)
|
||||
|
||||
for prevHash, inputs := range t.GroupInputsByPrevHash() {
|
||||
if tx, _, err := bc.GetTransaction(prevHash); err != nil {
|
||||
tx = nil
|
||||
} else if tx != nil {
|
||||
for _, in := range inputs {
|
||||
references[in.PrevHash] = tx.Outputs[in.PrevIndex]
|
||||
}
|
||||
} else {
|
||||
references = nil
|
||||
}
|
||||
}
|
||||
return references
|
||||
}
|
||||
|
||||
// FeePerByte returns network fee divided by the size of the transaction
|
||||
func (bc *Blockchain) FeePerByte(t *transaction.Transaction) util.Fixed8 {
|
||||
return bc.NetworkFee(t).Div(int64(t.Size()))
|
||||
}
|
||||
|
||||
// NetworkFee returns network fee
|
||||
func (bc *Blockchain) NetworkFee(t *transaction.Transaction) util.Fixed8 {
|
||||
inputAmount := util.NewFixed8(0)
|
||||
for _, txOutput := range bc.References(t) {
|
||||
if txOutput.AssetID == utilityTokenTX().Hash() {
|
||||
inputAmount.Add(txOutput.Amount)
|
||||
}
|
||||
}
|
||||
|
||||
outputAmount := util.NewFixed8(0)
|
||||
for _, txOutput := range t.Outputs {
|
||||
if txOutput.AssetID == utilityTokenTX().Hash() {
|
||||
outputAmount.Add(txOutput.Amount)
|
||||
}
|
||||
}
|
||||
|
||||
return inputAmount.Sub(outputAmount).Sub(bc.SystemFee(t))
|
||||
}
|
||||
|
||||
// SystemFee returns system fee
|
||||
func (bc *Blockchain) SystemFee(t *transaction.Transaction) util.Fixed8 {
|
||||
return bc.GetConfig().SystemFee.TryGetValue(t.Type)
|
||||
}
|
||||
|
||||
func hashAndIndexToBytes(h util.Uint256, index uint32) []byte {
|
||||
buf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(buf, index)
|
||||
|
|
|
@ -6,7 +6,10 @@ import (
|
|||
|
||||
"github.com/CityOfZion/neo-go/config"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddHeaders(t *testing.T) {
|
||||
|
@ -78,16 +81,14 @@ func TestGetHeader(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
|
||||
hash := block.Hash()
|
||||
header, err := bc.getHeader(hash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
header, err := bc.GetHeader(hash)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, block.Header(), header)
|
||||
|
||||
block = newBlock(2)
|
||||
hash = block.Hash()
|
||||
_, err = bc.getHeader(hash)
|
||||
assert.NotNil(t, err)
|
||||
_, err = bc.GetHeader(block.Hash())
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetBlock(t *testing.T) {
|
||||
|
@ -155,3 +156,40 @@ func newTestChain(t *testing.T) *Blockchain {
|
|||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
txID := "f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a"
|
||||
tx := getTestTransaction(txID, t)
|
||||
|
||||
assert.Equal(t, 283, tx.Size())
|
||||
assert.Equal(t, 22, util.GetVarSize(tx.Attributes))
|
||||
assert.Equal(t, 35, util.GetVarSize(tx.Inputs))
|
||||
assert.Equal(t, 121, util.GetVarSize(tx.Outputs))
|
||||
assert.Equal(t, 103, util.GetVarSize(tx.Scripts))
|
||||
}
|
||||
|
||||
func getTestBlockchain(t *testing.T) *Blockchain {
|
||||
net := config.ModeUnitTestNet
|
||||
configPath := "../../config"
|
||||
cfg, err := config.Load(configPath, net)
|
||||
require.NoError(t, err, "could not create levelDB chain")
|
||||
|
||||
// adjust datadirectory to point to the correct folder
|
||||
cfg.ApplicationConfiguration.DataDirectoryPath = "../rpc/chains/unit_testnet"
|
||||
chain, err := NewBlockchainLevelDB(context.Background(), cfg)
|
||||
require.NoErrorf(t, err, "could not create levelDB chain")
|
||||
|
||||
return chain
|
||||
}
|
||||
|
||||
func getTestTransaction(txID string, t *testing.T) *transaction.Transaction {
|
||||
chain := getTestBlockchain(t)
|
||||
|
||||
txHash, err := util.Uint256DecodeString(txID)
|
||||
require.NoErrorf(t, err, "could not decode string %s to Uint256", txID)
|
||||
|
||||
tx, _, err := chain.GetTransaction(txHash)
|
||||
require.NoErrorf(t, err, "could not get transaction with hash=%s", txHash)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
|
|
@ -1,22 +1,31 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/config"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Blockchainer is an interface that abstract the implementation
|
||||
// of the blockchain.
|
||||
type Blockchainer interface {
|
||||
GetConfig() config.ProtocolConfiguration
|
||||
AddHeaders(...*Header) error
|
||||
AddBlock(*Block) error
|
||||
BlockHeight() uint32
|
||||
HeaderHeight() uint32
|
||||
GetBlock(hash util.Uint256) (*Block, error)
|
||||
GetHeaderHash(int) util.Uint256
|
||||
GetHeader(hash util.Uint256) (*Header, error)
|
||||
CurrentHeaderHash() util.Uint256
|
||||
CurrentBlockHash() util.Uint256
|
||||
HasBlock(util.Uint256) bool
|
||||
HasTransaction(util.Uint256) bool
|
||||
GetAssetState(util.Uint256) *AssetState
|
||||
GetAccountState(util.Uint160) *AccountState
|
||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
References(t *transaction.Transaction) map[util.Uint256]*transaction.Output
|
||||
FeePerByte(t *transaction.Transaction) util.Fixed8
|
||||
SystemFee(t *transaction.Transaction) util.Fixed8
|
||||
NetworkFee(t *transaction.Transaction) util.Fixed8
|
||||
}
|
||||
|
|
|
@ -1,5 +1,51 @@
|
|||
package transaction
|
||||
|
||||
var (
|
||||
attrLookup = map[AttrUsage]string{
|
||||
ContractHash: "ContractHash",
|
||||
ECDH02: "ECDH02",
|
||||
ECDH03: "ECDH03",
|
||||
Script: "Script",
|
||||
Vote: "Vote",
|
||||
CertURL: "CertURL",
|
||||
DescriptionURL: "DescriptionURL",
|
||||
Description: "Description",
|
||||
|
||||
Hash1: "Hash1",
|
||||
Hash2: "Hash2",
|
||||
Hash3: "Hash3",
|
||||
Hash4: "Hash4",
|
||||
Hash5: "Hash5",
|
||||
Hash6: "Hash6",
|
||||
Hash7: "Hash7",
|
||||
Hash8: "Hash8",
|
||||
Hash9: "Hash9",
|
||||
Hash10: "Hash10",
|
||||
Hash11: "Hash11",
|
||||
Hash12: "Hash12",
|
||||
Hash13: "Hash13",
|
||||
Hash14: "Hash14",
|
||||
Hash15: "Hash15",
|
||||
|
||||
Remark: "Remark",
|
||||
Remark1: "Remark1",
|
||||
Remark2: "Remark2",
|
||||
Remark3: "Remark3",
|
||||
Remark4: "Remark4",
|
||||
Remark5: "Remark5",
|
||||
Remark6: "Remark6",
|
||||
Remark7: "Remark7",
|
||||
Remark8: "Remark8",
|
||||
Remark9: "Remark9",
|
||||
Remark10: "Remark10",
|
||||
Remark11: "Remark11",
|
||||
Remark12: "Remark12",
|
||||
Remark13: "Remark13",
|
||||
Remark14: "Remark14",
|
||||
Remark15: "Remark15",
|
||||
}
|
||||
)
|
||||
|
||||
// AttrUsage represents the purpose of the attribute.
|
||||
type AttrUsage uint8
|
||||
|
||||
|
@ -47,3 +93,11 @@ const (
|
|||
Remark14 AttrUsage = 0xfe
|
||||
Remark15 AttrUsage = 0xff
|
||||
)
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (attr AttrUsage) String() string {
|
||||
if v, ok := attrLookup[attr]; ok {
|
||||
return v
|
||||
}
|
||||
return "Unkown Attribute"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package transaction
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
|
@ -77,3 +79,26 @@ func (attr *Attribute) EncodeBinary(w io.Writer) error {
|
|||
}
|
||||
return fmt.Errorf("failed encoding TX attribute usage: 0x%2x", attr.Usage)
|
||||
}
|
||||
|
||||
// Size returns the size in number bytes of the Attribute
|
||||
func (attr *Attribute) Size() int {
|
||||
switch attr.Usage {
|
||||
case ContractHash, ECDH02, ECDH03, Vote,
|
||||
Hash1, Hash2, Hash3, Hash4, Hash5, Hash6, Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15:
|
||||
return 33 // uint8 + 32 = size(attrUsage) + 32
|
||||
case Script:
|
||||
return 21 // uint8 + 20 = size(attrUsage) + 20
|
||||
case Description:
|
||||
return 2 + len(attr.Data) // uint8 + uint8+ len of data = size(attrUsage) + size(byte) + len of data
|
||||
default:
|
||||
return 1 + len(attr.Data) // uint8 + len of data = size(attrUsage) + len of data
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json Marschaller interface
|
||||
func (attr *Attribute) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]string{
|
||||
"usage": attr.Usage.String(),
|
||||
"data": hex.EncodeToString(attr.Data),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Input represents a Transaction input.
|
||||
// Input represents a Transaction input (CoinReference).
|
||||
type Input struct {
|
||||
// The hash of the previous transaction.
|
||||
PrevHash util.Uint256
|
||||
PrevHash util.Uint256 `json:"txid"`
|
||||
|
||||
// The index of the previous transaction.
|
||||
PrevIndex uint16
|
||||
PrevIndex uint16 `json:"vout"`
|
||||
}
|
||||
|
||||
// DecodeBinary implements the Payload interface.
|
||||
|
@ -34,3 +34,8 @@ func (in *Input) EncodeBinary(w io.Writer) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size returns the size in bytes of the Input
|
||||
func (in *Input) Size() int {
|
||||
return in.PrevHash.Size() + 2 // 2 = sizeOf uint16
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ package transaction
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
|
@ -17,6 +19,10 @@ type Output struct {
|
|||
|
||||
// The address of the recipient.
|
||||
ScriptHash util.Uint160
|
||||
|
||||
// The position of the Output in slice []Output. This is actually set in NewTransactionOutputRaw
|
||||
// and used for diplaying purposes.
|
||||
Position int
|
||||
}
|
||||
|
||||
// NewOutput returns a new transaction output.
|
||||
|
@ -49,3 +55,18 @@ func (out *Output) EncodeBinary(w io.Writer) error {
|
|||
}
|
||||
return binary.Write(w, binary.LittleEndian, out.ScriptHash)
|
||||
}
|
||||
|
||||
// Size returns the size in bytes of the Output
|
||||
func (out *Output) Size() int {
|
||||
return out.AssetID.Size() + out.Amount.Size() + out.ScriptHash.Size()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaler interface
|
||||
func (out *Output) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"asset": out.AssetID,
|
||||
"value": out.Amount,
|
||||
"address": crypto.AddressFromUint160(out.ScriptHash),
|
||||
"n": out.Position,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -250,3 +250,24 @@ func (t *Transaction) GroupInputsByPrevHash() map[util.Uint256][]*Input {
|
|||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Size returns the size of the transaction in term of bytes
|
||||
func (t *Transaction) Size() int {
|
||||
attrSize := util.GetVarSize(t.Attributes)
|
||||
inputSize := util.GetVarSize(t.Inputs)
|
||||
outputSize := util.GetVarSize(t.Outputs)
|
||||
witnesSize := util.GetVarSize(t.Scripts)
|
||||
// uint8 + uint8 + attrSize + inputSize + outputSize + witnesSize
|
||||
return 2 + attrSize + inputSize + outputSize + witnesSize
|
||||
|
||||
}
|
||||
|
||||
// Bytes convert the transaction to []byte
|
||||
func (t *Transaction) Bytes() []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := t.EncodeBinary(buf); err != nil {
|
||||
return nil
|
||||
}
|
||||
return buf.Bytes()
|
||||
|
||||
}
|
||||
|
|
|
@ -46,3 +46,8 @@ func (t TXType) String() string {
|
|||
return "UnknownTransaction"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json marshaller interface.
|
||||
func (t TXType) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + t.String() + `"`), nil
|
||||
}
|
||||
|
|
|
@ -50,3 +50,8 @@ func (w *Witness) MarshalJSON() ([]byte, error) {
|
|||
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
// Size returns the size in bytes of the Witness.
|
||||
func (w *Witness) Size() int {
|
||||
return util.GetVarSize(w.InvocationScript) + util.GetVarSize(w.VerificationScript)
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ func calculateUtilityAmount() util.Fixed8 {
|
|||
for i := 0; i < len(genAmount); i++ {
|
||||
sum += genAmount[i]
|
||||
}
|
||||
return util.NewFixed8(sum * decrementInterval)
|
||||
return util.NewFixed8(int64(sum * decrementInterval))
|
||||
}
|
||||
|
||||
// headerSliceReverse reverses the given slice of *Header.
|
||||
|
|
10
pkg/io/serializable.go
Normal file
10
pkg/io/serializable.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package io
|
||||
|
||||
import "io"
|
||||
|
||||
// Serializable defines the binary encoding/decoding interface.
|
||||
type Serializable interface {
|
||||
Size() int
|
||||
DecodeBinary(io.Reader) error
|
||||
EncodeBinary(io.Writer) error
|
||||
}
|
|
@ -4,18 +4,40 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/CityOfZion/neo-go/config"
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
type testChain struct{}
|
||||
|
||||
func (chain testChain) GetConfig() config.ProtocolConfiguration {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) References(t *transaction.Transaction) map[util.Uint256]*transaction.Output {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) FeePerByte(t *transaction.Transaction) util.Fixed8 {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) SystemFee(t *transaction.Transaction) util.Fixed8 {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) NetworkFee(t *transaction.Transaction) util.Fixed8 {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) AddHeaders(...*core.Header) error {
|
||||
return nil
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) AddBlock(*core.Block) error {
|
||||
return nil
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) BlockHeight() uint32 {
|
||||
return 0
|
||||
|
@ -24,16 +46,20 @@ func (chain testChain) HeaderHeight() uint32 {
|
|||
return 0
|
||||
}
|
||||
func (chain testChain) GetBlock(hash util.Uint256) (*core.Block, error) {
|
||||
return nil, nil
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetHeaderHash(int) util.Uint256 {
|
||||
return util.Uint256{}
|
||||
}
|
||||
func (chain testChain) GetHeader(hash util.Uint256) (*core.Header, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetAssetState(util.Uint256) *core.AssetState {
|
||||
return nil
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetAccountState(util.Uint160) *core.AccountState {
|
||||
return nil
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) CurrentHeaderHash() util.Uint256 {
|
||||
return util.Uint256{}
|
||||
|
@ -47,6 +73,9 @@ func (chain testChain) HasBlock(util.Uint256) bool {
|
|||
func (chain testChain) HasTransaction(util.Uint256) bool {
|
||||
return false
|
||||
}
|
||||
func (chain testChain) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
type testDiscovery struct{}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package rpc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -182,7 +183,8 @@ Methods:
|
|||
|
||||
results = peers
|
||||
|
||||
case "getblocksysfee", "getcontractstate", "getrawmempool", "getrawtransaction", "getstorage", "submitblock", "gettxout", "invoke", "invokefunction", "invokescript", "sendrawtransaction":
|
||||
case "getblocksysfee", "getcontractstate", "getrawmempool", "getstorage", "submitblock", "gettxout", "invoke", "invokefunction", "invokescript", "sendrawtransaction":
|
||||
|
||||
results = "TODO"
|
||||
|
||||
case "validateaddress":
|
||||
|
@ -224,6 +226,38 @@ Methods:
|
|||
} else {
|
||||
results = "Invalid public account address"
|
||||
}
|
||||
case "getrawtransaction":
|
||||
param0, err := reqParams.ValueWithType(0, "string")
|
||||
if err != nil {
|
||||
resultsErr = err
|
||||
} else if txHash, err := util.Uint256DecodeString(param0.StringVal); err != nil {
|
||||
err = errors.Wrapf(err, "param at index 0, (%s), could not be decode to Uint256", param0.StringVal)
|
||||
resultsErr = NewInvalidParamsError(err.Error(), err)
|
||||
} else if tx, height, err := s.chain.GetTransaction(txHash); err != nil {
|
||||
err = errors.Wrapf(err, "Invalid transaction hash: %s", txHash)
|
||||
resultsErr = NewInvalidParamsError(err.Error(), err)
|
||||
} else if len(reqParams) >= 2 {
|
||||
_header := s.chain.GetHeaderHash(int(height))
|
||||
header, err := s.chain.GetHeader(_header)
|
||||
if err != nil {
|
||||
resultsErr = NewInvalidParamsError(err.Error(), err)
|
||||
}
|
||||
|
||||
param1, _ := reqParams.ValueAt(1)
|
||||
switch v := param1.RawValue.(type) {
|
||||
|
||||
case int, float64, bool, string:
|
||||
if v == 0 || v == "0" || v == 0.0 || v == false || v == "false" {
|
||||
results = hex.EncodeToString(tx.Bytes())
|
||||
} else {
|
||||
results = wrappers.NewTransactionOutputRaw(tx, header, s.chain)
|
||||
}
|
||||
default:
|
||||
results = wrappers.NewTransactionOutputRaw(tx, header, s.chain)
|
||||
}
|
||||
} else {
|
||||
results = hex.EncodeToString(tx.Bytes())
|
||||
}
|
||||
|
||||
default:
|
||||
resultsErr = NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil)
|
||||
|
|
|
@ -15,21 +15,203 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/network"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type tc struct {
|
||||
rpcCall string
|
||||
method string
|
||||
expectedResult string
|
||||
}
|
||||
|
||||
var testRpcCases = []tc{
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`,
|
||||
"getassetstate_1",
|
||||
`{"jsonrpc":"2.0","result":{"assetId":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","assetType":1,"name":"NEOGas","amount":"100000000","available":"0","precision":8,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`},
|
||||
|
||||
{`{ "jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"] }`,
|
||||
"getassetstate_2",
|
||||
`{"jsonrpc":"2.0","result":{"assetId":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","assetType":0,"name":"NEO","amount":"100000000","available":"0","precision":0,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`},
|
||||
|
||||
{
|
||||
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["62c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`,
|
||||
method: "getassetstate_3",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
{
|
||||
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": [123] }`,
|
||||
method: "getassetstate_4",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [10] }`,
|
||||
"getblockhash_1",
|
||||
`{"jsonrpc":"2.0","result":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e","id":1}`},
|
||||
|
||||
{
|
||||
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [-2] }`,
|
||||
method: "getblockhash_2",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":"Internal server error"},"id":1}`,
|
||||
},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [10] }`,
|
||||
"getblock",
|
||||
`{"jsonrpc":"2.0","result":{"version":0,"previousblockhash":"0x7c5b4c8a70336bf68e8679be7c9a2a15f85c0f6d0e14389019dcc3edfab2bb4b","merkleroot":"0xc027979ad29226b7d34523b1439a64a6cf57fe3f4e823e9d3e90d43934783d26","time":1529926220,"height":10,"nonce":8313828522725096825,"next_consensus":"0xbe48d3a3f5d10013ab9ffee489706078714f1ea2","script":{"invocation":"40ac828e1c2a214e4d356fd2eccc7c7be9ef426f8e4ea67a50464e90ca4367e611c4c5247082b85a7d5ed985cfb90b9af2f1195531038f49c63fb6894b517071ea40b22b83d9457ca5c4c5bb2d8d7e95333820611d447bb171ce7b8af3b999d0a5a61c2301cdd645a33a47defd09c0f237a0afc86e9a84c2fe675d701e4015c0302240a6899296660c612736edc22f8d630927649d4ef1301868079032d80aae6cc1e21622f256497a84a71d7afeeef4c124135f611db24a0f7ab3d2a6886f15db7865","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"type":"MinerTransaction","version":0,"attributes":null,"vin":null,"vout":null,"scripts":null}],"confirmations":12338,"nextblockhash":"0x2b1c78633dae7ab81f64362e0828153079a17b018d779d0406491f84c27b086f","hash":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e"},"id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getblockcount", "params": [] }`,
|
||||
"getblockcount",
|
||||
`{"jsonrpc":"2.0","result":12349,"id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getconnectioncount", "params": [] }`,
|
||||
"getconnectioncount",
|
||||
`{"jsonrpc":"2.0","result":0,"id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": [] }`,
|
||||
"getbestblockhash",
|
||||
`{"jsonrpc":"2.0","result":"877f5f2084181b85ce4726ab0a86bea6cc82cdbcb6f2eb59e6b04d27fd10929c","id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getpeers", "params": [] }`,
|
||||
"getpeers",
|
||||
`{"jsonrpc":"2.0","result":{"unconnected":[],"connected":[],"bad":[]},"id":1}`},
|
||||
|
||||
// Good case, valid transaction ((param[1]=1 -> verbose = 1))
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 1] }`,
|
||||
method: "getrawtransaction_1",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"type":"ContractTransaction","version":0,"attributes":[{"data":"23ba2703c53263e8d6e522dc32203339dcd8eee9","usage":"Script"}],"vin":[{"txid":"0x539084697cc220916cb5b16d2805945ec9f267aa004b6688fbf15e116c846aff","vout":0}],"vout":[{"address":"AXpNr3SDfLXbPHNdqxYeHK5cYpKMHZxMZ9","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":0,"value":"10000"},{"address":"AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":1,"value":"99990000"}],"scripts":[{"invocation":"40a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c6","verification":"21031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac"}],"txid":"0xf999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a","size":283,"sys_fee":"0","net_fee":"0","blockhash":"0x6088bf9d3b55c67184f60b00d2e380228f713b4028b24c1719796dcd2006e417","confirmations":2902,"blocktime":1533756500},"id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid transaction (param[1]=3 -> verbose = 1. Following the C# any number different from 0 is interpreted as verbose = 1)
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 3] }`,
|
||||
method: "getrawtransaction_2",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"type":"ContractTransaction","version":0,"attributes":[{"data":"23ba2703c53263e8d6e522dc32203339dcd8eee9","usage":"Script"}],"vin":[{"txid":"0x539084697cc220916cb5b16d2805945ec9f267aa004b6688fbf15e116c846aff","vout":0}],"vout":[{"address":"AXpNr3SDfLXbPHNdqxYeHK5cYpKMHZxMZ9","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":0,"value":"10000"},{"address":"AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":1,"value":"99990000"}],"scripts":[{"invocation":"40a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c6","verification":"21031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac"}],"txid":"0xf999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a","size":283,"sys_fee":"0","net_fee":"0","blockhash":"0x6088bf9d3b55c67184f60b00d2e380228f713b4028b24c1719796dcd2006e417","confirmations":2902,"blocktime":1533756500},"id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid transaction (param[1]="dads" -> verbose = 1. Following the C# any string different from "0", "false" is interpreted as verbose = 1)
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", "dads"] }`,
|
||||
method: "getrawtransaction_3",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"type":"ContractTransaction","version":0,"attributes":[{"data":"23ba2703c53263e8d6e522dc32203339dcd8eee9","usage":"Script"}],"vin":[{"txid":"0x539084697cc220916cb5b16d2805945ec9f267aa004b6688fbf15e116c846aff","vout":0}],"vout":[{"address":"AXpNr3SDfLXbPHNdqxYeHK5cYpKMHZxMZ9","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":0,"value":"10000"},{"address":"AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":1,"value":"99990000"}],"scripts":[{"invocation":"40a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c6","verification":"21031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac"}],"txid":"0xf999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a","size":283,"sys_fee":"0","net_fee":"0","blockhash":"0x6088bf9d3b55c67184f60b00d2e380228f713b4028b24c1719796dcd2006e417","confirmations":2902,"blocktime":1533756500},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, invalid transaction
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["45a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 1] }`,
|
||||
method: "getrawtransaction_4",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params","data":"param at index 0, (45a41306c846ea80290416143e8e856559818065be3f4e143c60e43a), could not be decode to Uint256: expected string size of 64 got 56"},"id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid transaction
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a"] }`,
|
||||
method: "getrawtransaction_5",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid transaction (param[1]= 0 -> verbose = 0)
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 0] }`,
|
||||
method: "getrawtransaction_6",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid transaction (param[1]="false" -> verbose = 0)
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", "false"] }`,
|
||||
method: "getrawtransaction_6_a",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid transaction (param[1]=false -> verbose = 0)
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", false] }`,
|
||||
method: "getrawtransaction_6_b",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid transaction (param[1]="0" -> verbose = 0)
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", "0"] }`,
|
||||
method: "getrawtransaction_6_c",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, param at index 0 not a string
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": [123, 0] }`,
|
||||
method: "getrawtransaction_7",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"] }`,
|
||||
method: "getaccountstate_1",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"version":0,"script_hash":"0xe9eed8dc39332032dc22e5d6e86332c50327ba23","frozen":false,"votes":[],"balances":[{"asset":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","value":"72099.99960000"},{"asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","value":"99989900"}]},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, invalid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zR"] }`,
|
||||
method: "getaccountstate_2",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, not string
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": [123] }`,
|
||||
method: "getaccountstate_3",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, empty params
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": [] }`,
|
||||
method: "getaccountstate_4",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"] }`,
|
||||
method: "validateaddress_1",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"address":"AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i","isvalid":true},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, invalid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["152f1muMCNa7goXYhYAQC61hxEgGacmncB"] }`,
|
||||
method: "validateaddress_2",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"address":"152f1muMCNa7goXYhYAQC61hxEgGacmncB","isvalid":false},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, not string
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [1] }`,
|
||||
method: "validateaddress_3",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"address":1,"isvalid":false},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, empty params
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [] }`,
|
||||
method: "validateaddress_4",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
// setup rpcServer server
|
||||
net := config.ModeUnitTestNet
|
||||
configPath := "../../config"
|
||||
cfg, err := config.Load(configPath, net)
|
||||
if err != nil {
|
||||
t.Fatal("could not create levelDB chain", err)
|
||||
}
|
||||
require.NoError(t, err, "could not load config")
|
||||
|
||||
chain, err := core.NewBlockchainLevelDB(context.Background(), cfg)
|
||||
if err != nil {
|
||||
t.Fatal("could not create levelDB chain", err)
|
||||
}
|
||||
require.NoError(t, err, "could not create levelDB chain")
|
||||
|
||||
serverConfig := network.NewServerConfig(cfg)
|
||||
server := network.NewServer(serverConfig, chain)
|
||||
|
@ -38,122 +220,14 @@ func TestHandler(t *testing.T) {
|
|||
// setup handler
|
||||
handler := http.HandlerFunc(rpcServer.requestHandler)
|
||||
|
||||
var testCases = []struct {
|
||||
rpcCall string
|
||||
method string
|
||||
expectedResult string
|
||||
}{
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`,
|
||||
"getassetstate_1",
|
||||
`{"jsonrpc":"2.0","result":{"assetId":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","assetType":1,"name":"NEOGas","amount":"100000000","available":"0","precision":8,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`},
|
||||
testRpcCases = append(testRpcCases, tc{
|
||||
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getversion", "params": [] }`,
|
||||
method: "getversion",
|
||||
expectedResult: fmt.Sprintf(`{"jsonrpc":"2.0","result":{"port":20333,"nonce":%s,"useragent":"/NEO-GO:/"},"id":1}`, strconv.FormatUint(uint64(server.ID()), 10)),
|
||||
},
|
||||
)
|
||||
|
||||
{`{ "jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"] }`,
|
||||
"getassetstate_2",
|
||||
`{"jsonrpc":"2.0","result":{"assetId":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","assetType":0,"name":"NEO","amount":"100000000","available":"0","precision":0,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`},
|
||||
|
||||
{
|
||||
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["62c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`,
|
||||
method: "getassetstate_3",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
{
|
||||
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": [123] }`,
|
||||
method: "getassetstate_4",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [10] }`,
|
||||
"getblockhash_1",
|
||||
`{"jsonrpc":"2.0","result":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e","id":1}`},
|
||||
|
||||
{
|
||||
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [-2] }`,
|
||||
method: "getblockhash_2",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":"Internal server error"},"id":1}`,
|
||||
},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [10] }`,
|
||||
"getblock",
|
||||
`{"jsonrpc":"2.0","result":{"version":0,"previousblockhash":"0x7c5b4c8a70336bf68e8679be7c9a2a15f85c0f6d0e14389019dcc3edfab2bb4b","merkleroot":"0xc027979ad29226b7d34523b1439a64a6cf57fe3f4e823e9d3e90d43934783d26","time":1529926220,"height":10,"nonce":8313828522725096825,"next_consensus":"0xbe48d3a3f5d10013ab9ffee489706078714f1ea2","script":{"invocation":"40ac828e1c2a214e4d356fd2eccc7c7be9ef426f8e4ea67a50464e90ca4367e611c4c5247082b85a7d5ed985cfb90b9af2f1195531038f49c63fb6894b517071ea40b22b83d9457ca5c4c5bb2d8d7e95333820611d447bb171ce7b8af3b999d0a5a61c2301cdd645a33a47defd09c0f237a0afc86e9a84c2fe675d701e4015c0302240a6899296660c612736edc22f8d630927649d4ef1301868079032d80aae6cc1e21622f256497a84a71d7afeeef4c124135f611db24a0f7ab3d2a6886f15db7865","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"type":0,"version":0,"attributes":null,"vin":null,"vout":null,"scripts":null}],"confirmations":12338,"nextblockhash":"0x2b1c78633dae7ab81f64362e0828153079a17b018d779d0406491f84c27b086f","hash":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e"},"id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getblockcount", "params": [] }`,
|
||||
"getblockcount",
|
||||
`{"jsonrpc":"2.0","result":12349,"id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getconnectioncount", "params": [] }`,
|
||||
"getconnectioncount",
|
||||
`{"jsonrpc":"2.0","result":0,"id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getversion", "params": [] }`,
|
||||
"getversion",
|
||||
fmt.Sprintf(`{"jsonrpc":"2.0","result":{"port":20333,"nonce":%s,"useragent":"/NEO-GO:/"},"id":1}`, strconv.FormatUint(uint64(server.ID()), 10))},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": [] }`,
|
||||
"getbestblockhash",
|
||||
`{"jsonrpc":"2.0","result":"877f5f2084181b85ce4726ab0a86bea6cc82cdbcb6f2eb59e6b04d27fd10929c","id":1}`},
|
||||
|
||||
{`{"jsonrpc": "2.0", "id": 1, "method": "getpeers", "params": [] }`,
|
||||
"getpeers",
|
||||
`{"jsonrpc":"2.0","result":{"unconnected":[],"connected":[],"bad":[]},"id":1}`},
|
||||
|
||||
// Good case, valid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"] }`,
|
||||
method: "getaccountstate_1",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"version":0,"script_hash":"0xe9eed8dc39332032dc22e5d6e86332c50327ba23","frozen":false,"votes":[],"balances":[{"asset":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","value":"72099.99960000"},{"asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","value":"99989900"}]},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, invalid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zR"] }`,
|
||||
method: "getaccountstate_2",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, not string
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": [123] }`,
|
||||
method: "getaccountstate_3",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, empty params
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": [] }`,
|
||||
method: "getaccountstate_4",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
|
||||
// Good case, valid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"] }`,
|
||||
method: "validateaddress_1",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"address":"AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i","isvalid":true},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, invalid address
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["152f1muMCNa7goXYhYAQC61hxEgGacmncB"] }`,
|
||||
method: "validateaddress_2",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"address":"152f1muMCNa7goXYhYAQC61hxEgGacmncB","isvalid":false},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, not string
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [1] }`,
|
||||
method: "validateaddress_3",
|
||||
expectedResult: `{"jsonrpc":"2.0","result":{"address":1,"isvalid":false},"id":1}`,
|
||||
},
|
||||
|
||||
// Bad case, empty params
|
||||
{
|
||||
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [] }`,
|
||||
method: "validateaddress_4",
|
||||
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
for _, tc := range testRpcCases {
|
||||
t.Run(fmt.Sprintf("method: %s, rpc call: %s", tc.method, tc.rpcCall), func(t *testing.T) {
|
||||
|
||||
req := httptest.NewRequest("POST", "http://0.0.0.0:20333/", strings.NewReader(tc.rpcCall))
|
||||
|
@ -163,10 +237,7 @@ func TestHandler(t *testing.T) {
|
|||
handler(w, req)
|
||||
resp := w.Result()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("could not read response from the request: %s", tc.rpcCall)
|
||||
}
|
||||
|
||||
assert.NoErrorf(t, err, "could not read response from the request: %s", tc.rpcCall)
|
||||
assert.Equal(t, tc.expectedResult, string(bytes.TrimSpace(body)))
|
||||
})
|
||||
|
||||
|
|
40
pkg/rpc/wrappers/tx_raw_output.go
Normal file
40
pkg/rpc/wrappers/tx_raw_output.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package wrappers
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// TransactionOutputRaw is used as a wrapper to represents
|
||||
// a Transaction.
|
||||
type TransactionOutputRaw struct {
|
||||
*transaction.Transaction
|
||||
TxHash util.Uint256 `json:"txid"`
|
||||
Size int `json:"size"`
|
||||
SysFee util.Fixed8 `json:"sys_fee"`
|
||||
NetFee util.Fixed8 `json:"net_fee"`
|
||||
Blockhash util.Uint256 `json:"blockhash"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
Timestamp uint32 `json:"blocktime"`
|
||||
}
|
||||
|
||||
// NewTransactionOutputRaw returns a new ransactionOutputRaw object.
|
||||
func NewTransactionOutputRaw(tx *transaction.Transaction, header *core.Header, chain core.Blockchainer) TransactionOutputRaw {
|
||||
// confirmations formula
|
||||
confirmations := int(chain.BlockHeight() - header.BlockBase.Index + 1)
|
||||
// set index position
|
||||
for i, o := range tx.Outputs {
|
||||
o.Position = i
|
||||
}
|
||||
return TransactionOutputRaw{
|
||||
Transaction: tx,
|
||||
TxHash: tx.Hash(),
|
||||
Size: tx.Size(),
|
||||
SysFee: chain.SystemFee(tx),
|
||||
NetFee: chain.NetworkFee(tx),
|
||||
Blockhash: header.Hash(),
|
||||
Confirmations: confirmations,
|
||||
Timestamp: header.Timestamp,
|
||||
}
|
||||
}
|
|
@ -41,14 +41,19 @@ func (f Fixed8) String() string {
|
|||
|
||||
// Value returns the original value representing the Fixed8.
|
||||
func (f Fixed8) Value() int64 {
|
||||
return int64(f) / int64(decimals)
|
||||
return int64(f) / decimals
|
||||
}
|
||||
|
||||
// NewFixed8 return a new Fixed8 type multiplied by decimals.
|
||||
func NewFixed8(val int) Fixed8 {
|
||||
// NewFixed8 returns a new Fixed8 type multiplied by decimals.
|
||||
func NewFixed8(val int64) Fixed8 {
|
||||
return Fixed8(decimals * val)
|
||||
}
|
||||
|
||||
// NewFixed8FromFloat returns a new Fixed8 type multiplied by decimals.
|
||||
func NewFixed8FromFloat(val float64) Fixed8 {
|
||||
return Fixed8(int64(decimals * val))
|
||||
}
|
||||
|
||||
// Fixed8DecodeString parses s which must be a fixed point number
|
||||
// with precision up to 10^-8
|
||||
func Fixed8DecodeString(s string) (Fixed8, error) {
|
||||
|
@ -94,7 +99,32 @@ func (f *Fixed8) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Size returns the size in number of bytes of Fixed8.
|
||||
func (f *Fixed8) Size() int {
|
||||
return 8
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json marshaller interface.
|
||||
func (f Fixed8) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + f.String() + `"`), nil
|
||||
}
|
||||
|
||||
// Satoshi defines the value of a 'Satoshi'.
|
||||
func Satoshi() Fixed8 {
|
||||
return NewFixed8(1)
|
||||
}
|
||||
|
||||
// Div implements Fixd8 division operator.
|
||||
func (f Fixed8) Div(i int64) Fixed8 {
|
||||
return NewFixed8(f.Value() / i)
|
||||
}
|
||||
|
||||
// Add implements Fixd8 addition operator.
|
||||
func (f Fixed8) Add(g Fixed8) Fixed8 {
|
||||
return NewFixed8(f.Value() + g.Value())
|
||||
}
|
||||
|
||||
// Sub implements Fixd8 subtraction operator.
|
||||
func (f Fixed8) Sub(g Fixed8) Fixed8 {
|
||||
return NewFixed8(f.Value() - g.Value())
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ import (
|
|||
)
|
||||
|
||||
func TestNewFixed8(t *testing.T) {
|
||||
values := []int{9000, 100000000, 5, 10945}
|
||||
values := []int64{9000, 100000000, 5, 10945}
|
||||
|
||||
for _, val := range values {
|
||||
assert.Equal(t, Fixed8(val*decimals), NewFixed8(val))
|
||||
assert.Equal(t, int64(val), NewFixed8(val).Value())
|
||||
assert.Equal(t, val, NewFixed8(val).Value())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
88
pkg/util/size.go
Normal file
88
pkg/util/size.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
var (
|
||||
bit8 byte
|
||||
ui8 uint8
|
||||
ui16 uint16
|
||||
ui32 uint32
|
||||
ui64 uint64
|
||||
i8 int8
|
||||
i16 int16
|
||||
i32 int32
|
||||
i64 int64
|
||||
)
|
||||
|
||||
// GetVarIntSize returns the size in number of bytes of a variable integer
|
||||
// (reference: GetVarSize(int value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs)
|
||||
func GetVarIntSize(value int) int {
|
||||
var size uintptr
|
||||
|
||||
if value < 0xFD {
|
||||
size = 1 // unit8
|
||||
} else if value <= 0xFFFF {
|
||||
size = 3 // byte + uint16
|
||||
} else {
|
||||
size = 5 // byte + uint32
|
||||
}
|
||||
return int(size)
|
||||
}
|
||||
|
||||
// GetVarStringSize returns the size of a variable string
|
||||
// (reference: GetVarSize(this string value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs)
|
||||
func GetVarStringSize(value string) int {
|
||||
valueSize := len([]byte(value))
|
||||
return GetVarIntSize(valueSize) + valueSize
|
||||
}
|
||||
|
||||
// GetVarSize return the size om bytes of a variable. This implementation is not exactly like the C#
|
||||
// (reference: GetVarSize<T>(this T[] value), https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs#L53) as in the C# variable
|
||||
// like Uint160, Uint256 are not supported. @TODO: make sure to have full unit tests coverage.
|
||||
func GetVarSize(value interface{}) int {
|
||||
v := reflect.ValueOf(value)
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
return GetVarStringSize(v.String())
|
||||
case reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Int64:
|
||||
return GetVarIntSize(int(v.Int()))
|
||||
case reflect.Uint,
|
||||
reflect.Uint8,
|
||||
reflect.Uint16,
|
||||
reflect.Uint32,
|
||||
reflect.Uint64:
|
||||
return GetVarIntSize(int(v.Uint()))
|
||||
case reflect.Slice, reflect.Array:
|
||||
valueLength := v.Len()
|
||||
valueSize := 0
|
||||
|
||||
switch reflect.ValueOf(value).Index(0).Interface().(type) {
|
||||
case io.Serializable:
|
||||
for i := 0; i < valueLength; i++ {
|
||||
elem := v.Index(i).Interface().(io.Serializable)
|
||||
valueSize += elem.Size()
|
||||
}
|
||||
case uint8, int8:
|
||||
valueSize = valueLength
|
||||
case uint16, int16:
|
||||
valueSize = valueLength * 2
|
||||
case uint32, int32:
|
||||
valueSize = valueLength * 4
|
||||
case uint64, int64:
|
||||
valueSize = valueLength * 8
|
||||
}
|
||||
|
||||
return GetVarIntSize(valueLength) + valueSize
|
||||
default:
|
||||
panic(fmt.Sprintf("unable to calculate GetVarSize, %s", reflect.TypeOf(value)))
|
||||
}
|
||||
}
|
139
pkg/util/size_test.go
Normal file
139
pkg/util/size_test.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestVarSize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
variable interface{}
|
||||
name string
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
252,
|
||||
"test_int_1",
|
||||
1,
|
||||
},
|
||||
{
|
||||
253,
|
||||
"test_int_2",
|
||||
3,
|
||||
},
|
||||
{
|
||||
65535,
|
||||
"test_int_3",
|
||||
3,
|
||||
},
|
||||
{
|
||||
65536,
|
||||
"test_int_4",
|
||||
5,
|
||||
},
|
||||
{
|
||||
4294967295,
|
||||
"test_int_5",
|
||||
5,
|
||||
},
|
||||
{
|
||||
[]byte{1, 2, 4, 5, 6},
|
||||
"test_[]byte_1",
|
||||
6,
|
||||
},
|
||||
{
|
||||
// The neo C# implementation doe not allowed this!
|
||||
Uint160{1, 2, 4, 5, 6},
|
||||
"test_Uint160_1",
|
||||
21,
|
||||
},
|
||||
|
||||
{[20]uint8{1, 2, 3, 4, 5, 6},
|
||||
"test_uint8_1",
|
||||
21,
|
||||
},
|
||||
{[20]uint8{1, 2, 3, 4, 5, 6, 8, 9},
|
||||
"test_uint8_2",
|
||||
21,
|
||||
},
|
||||
|
||||
{[32]uint8{1, 2, 3, 4, 5, 6},
|
||||
"test_uint8_3",
|
||||
33,
|
||||
},
|
||||
{[10]uint16{1, 2, 3, 4, 5, 6},
|
||||
"test_uint16_1",
|
||||
21,
|
||||
},
|
||||
|
||||
{[10]uint16{1, 2, 3, 4, 5, 6, 10, 21},
|
||||
"test_uint16_2",
|
||||
21,
|
||||
},
|
||||
{[30]uint32{1, 2, 3, 4, 5, 6, 10, 21},
|
||||
"test_uint32_2",
|
||||
121,
|
||||
},
|
||||
{[30]uint64{1, 2, 3, 4, 5, 6, 10, 21},
|
||||
"test_uint64_2",
|
||||
241,
|
||||
},
|
||||
{[20]int8{1, 2, 3, 4, 5, 6},
|
||||
"test_int8_1",
|
||||
21,
|
||||
},
|
||||
{[20]int8{-1, 2, 3, 4, 5, 6, 8, 9},
|
||||
"test_int8_2",
|
||||
21,
|
||||
},
|
||||
|
||||
{[32]int8{-1, 2, 3, 4, 5, 6},
|
||||
"test_int8_3",
|
||||
33,
|
||||
},
|
||||
{[10]int16{-1, 2, 3, 4, 5, 6},
|
||||
"test_int16_1",
|
||||
21,
|
||||
},
|
||||
|
||||
{[10]int16{-1, 2, 3, 4, 5, 6, 10, 21},
|
||||
"test_int16_2",
|
||||
21,
|
||||
},
|
||||
{[30]int32{-1, 2, 3, 4, 5, 6, 10, 21},
|
||||
"test_int32_2",
|
||||
121,
|
||||
},
|
||||
{[30]int64{-1, 2, 3, 4, 5, 6, 10, 21},
|
||||
"test_int64_2",
|
||||
241,
|
||||
},
|
||||
// The neo C# implementation doe not allowed this!
|
||||
{Uint256{1, 2, 3, 4, 5, 6},
|
||||
"test_Uint256_1",
|
||||
33,
|
||||
},
|
||||
|
||||
{"abc",
|
||||
"test_string_1",
|
||||
4,
|
||||
},
|
||||
{"abcà",
|
||||
"test_string_2",
|
||||
6,
|
||||
},
|
||||
{"2d3b96ae1bcc5a585e075e3b81920210dec16302",
|
||||
"test_string_3",
|
||||
41,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("run: %s", tc.name), func(t *testing.T) {
|
||||
result := GetVarSize(tc.variable)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -79,6 +79,11 @@ func (u *Uint160) UnmarshalJSON(data []byte) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// Size returns the lenght of the bytes representation of Uint160
|
||||
func (u Uint160) Size() int {
|
||||
return uint160Size
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json marshaller interface.
|
||||
func (u Uint160) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"0x` + u.String() + `"`), nil
|
||||
|
|
|
@ -65,6 +65,11 @@ func (u *Uint256) UnmarshalJSON(data []byte) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// Size returns the lenght of the bytes representation of Uint256
|
||||
func (u Uint256) Size() int {
|
||||
return uint256Size
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json marshaller interface.
|
||||
func (u Uint256) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"0x` + u.String() + `"`), nil
|
||||
|
|
Loading…
Reference in a new issue