mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-12 01:10:36 +00:00
Merge pull request #1760 from nspcc-dev/rpc-compatibility-fixes-and-docs
[2.x] RPC compatibility (and other) fixes and docs
This commit is contained in:
commit
3f50f90dc5
17 changed files with 293 additions and 101 deletions
73
docs/rpc.md
73
docs/rpc.md
|
@ -46,11 +46,15 @@ which would yield the response:
|
|||
| `getclaimable` |
|
||||
| `getconnectioncount` |
|
||||
| `getcontractstate` |
|
||||
| `getminimumnetworkfee` |
|
||||
| `getnep5balances` |
|
||||
| `getnep5transfers` |
|
||||
| `getpeers` |
|
||||
| `getproof` |
|
||||
| `getrawmempool` |
|
||||
| `getrawtransaction` |
|
||||
| `getstateheight` |
|
||||
| `getstateroot` |
|
||||
| `getstorage` |
|
||||
| `gettransactionheight` |
|
||||
| `gettxout` |
|
||||
|
@ -65,9 +69,78 @@ which would yield the response:
|
|||
| `sendrawtransaction` |
|
||||
| `submitblock` |
|
||||
| `validateaddress` |
|
||||
| `verifyproof` |
|
||||
|
||||
#### Implementation notices
|
||||
|
||||
##### `getaccountstate`
|
||||
|
||||
The order of assets in `balances` section may differ from the one returned by
|
||||
C# implementation. Assets can still be identified by their hashes so it
|
||||
shouldn't be an issue.
|
||||
|
||||
##### `getapplicationlog`
|
||||
|
||||
Error handling for incorrect stack items differs with C# implementation. C#
|
||||
implementation substitutes `stack` and `state` arrays with "error: recursive
|
||||
reference" string if there are any invalid items. NeoGo never does this, for
|
||||
bad `state` items it uses byte array susbstitute with message "bad
|
||||
notification: ..." (may vary depending on the problem), for incorrect `stack`
|
||||
items it just omits them (still returning valid ones).
|
||||
|
||||
##### `getassetstate`
|
||||
|
||||
It returns "NEO" for NEO and "NEOGas" for GAS in the `name` field instead of
|
||||
language-aware JSON structures.
|
||||
|
||||
##### `getblock` and `getrawtransaction`
|
||||
|
||||
In their verbose outputs neo-go can omit some fields with default values for
|
||||
transactions, this includes:
|
||||
* zero "nonce" for Miner transactions (usually nonce is not zero)
|
||||
* zero "gas" for Invocation transactions (most of the time it is zero).
|
||||
|
||||
##### `getclaimable`
|
||||
|
||||
`claimable` array ordering differs, neo-go orders entries there by the
|
||||
`end_height` field, while C# implementation orders by `txid`.
|
||||
|
||||
##### `getcontractstate`
|
||||
|
||||
C# implementation doesn't return `Payable` flag in its output, neo-go has
|
||||
`is_payable` field in `properties` for that.
|
||||
|
||||
##### `getnep5transfers`
|
||||
|
||||
`received` and `sent` entries are sorted differently, C# node uses
|
||||
chronological order and neo-go uses reverse chronological order (which is
|
||||
important for paging support, see Extensions section down below).
|
||||
|
||||
##### `getrawmempool`
|
||||
|
||||
neo-go doesn't support boolean parameter to `getrawmempool` for unverified
|
||||
transactions request because neo-go actually never stores unverified
|
||||
transactions in the mempool.
|
||||
|
||||
##### `getunclaimed`
|
||||
|
||||
Numeric results are wrapped into strings in neo-go (the same way fees are
|
||||
encoded) to prevent floating point rounding errors.
|
||||
|
||||
##### `getunspents`
|
||||
|
||||
neo-go uses standard "0xhash" syntax for `txid` and `asset_hash` fields
|
||||
whereas C# module doesn't add "0x" prefix. The order of `balance` or `unspent`
|
||||
entries can differ. neo-go returns all UTXO assets while C# module only tracks
|
||||
and returns NEO and GAS.
|
||||
|
||||
##### `getutxotransfers`
|
||||
|
||||
`transactions` are sorted differently, C# node uses chronological order and
|
||||
neo-go uses reverse chronological order (which is important for paging
|
||||
support, see Extensions section down below).
|
||||
|
||||
|
||||
##### `invokefunction` and `invoke`
|
||||
|
||||
neo-go's implementation of `invokefunction` and `invoke` does not return `tx`
|
||||
|
|
|
@ -3,6 +3,7 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -31,7 +32,7 @@ import (
|
|||
// Tuning parameters.
|
||||
const (
|
||||
headerBatchCount = 2000
|
||||
version = "0.0.9"
|
||||
version = "0.0.10"
|
||||
|
||||
// This one comes from C# code and it's different from the constant used
|
||||
// when creating an asset with Neo.Asset.Create interop call. It looks
|
||||
|
@ -551,9 +552,9 @@ func (bc *Blockchain) processHeader(h *block.Header, batch storage.Batch, header
|
|||
return nil
|
||||
}
|
||||
|
||||
// bc.GetHeaderHash(int(endHeight)) returns sum of all system fees for blocks up to h.
|
||||
// GetSystemFeeAmount returns sum of all system fees for blocks up to h.
|
||||
// and 0 if no such block exists.
|
||||
func (bc *Blockchain) getSystemFeeAmount(h util.Uint256) uint32 {
|
||||
func (bc *Blockchain) GetSystemFeeAmount(h util.Uint256) uint32 {
|
||||
_, sf, _ := bc.dao.GetBlock(h)
|
||||
return sf
|
||||
}
|
||||
|
@ -582,7 +583,7 @@ func (bc *Blockchain) GetStateRoot(height uint32) (*state.MPTRootState, error) {
|
|||
func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||
cache := dao.NewCached(bc.dao)
|
||||
appExecResults := make([]*state.AppExecResult, 0, len(block.Transactions))
|
||||
fee := bc.getSystemFeeAmount(block.PrevHash)
|
||||
fee := bc.GetSystemFeeAmount(block.PrevHash)
|
||||
for _, tx := range block.Transactions {
|
||||
fee += uint32(bc.SystemFee(tx).IntegralValue())
|
||||
}
|
||||
|
@ -688,6 +689,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
|||
Precision: t.Precision,
|
||||
Owner: t.Owner,
|
||||
Admin: t.Admin,
|
||||
Issuer: t.Admin,
|
||||
Expiration: bc.BlockHeight() + registeredAssetLifetime,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -943,16 +945,23 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, transfer *state.NEP
|
|||
return
|
||||
}
|
||||
bs := balances.Trackers[transfer.Asset]
|
||||
bs.Balance -= transfer.Amount
|
||||
if bs.Balance == nil {
|
||||
return
|
||||
}
|
||||
bs.Balance.Sub(bs.Balance, transfer.Amount)
|
||||
if bs.Balance.Sign() > 0 {
|
||||
bs.LastUpdatedBlock = transfer.Block
|
||||
balances.Trackers[transfer.Asset] = bs
|
||||
} else {
|
||||
delete(balances.Trackers, transfer.Asset)
|
||||
}
|
||||
|
||||
transfer.Amount = -transfer.Amount
|
||||
transfer.Amount.Neg(transfer.Amount)
|
||||
isBig, err := cache.AppendNEP5Transfer(transfer.From, balances.NextTransferBatch, transfer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
transfer.Amount = -transfer.Amount
|
||||
transfer.Amount.Neg(transfer.Amount)
|
||||
if isBig {
|
||||
balances.NextTransferBatch++
|
||||
}
|
||||
|
@ -966,7 +975,10 @@ func (bc *Blockchain) processNEP5Transfer(cache *dao.Cached, transfer *state.NEP
|
|||
return
|
||||
}
|
||||
bs := balances.Trackers[transfer.Asset]
|
||||
bs.Balance += transfer.Amount
|
||||
if bs.Balance == nil {
|
||||
bs.Balance = new(big.Int)
|
||||
}
|
||||
bs.Balance.Add(bs.Balance, transfer.Amount)
|
||||
bs.LastUpdatedBlock = transfer.Block
|
||||
balances.Trackers[transfer.Asset] = bs
|
||||
|
||||
|
@ -1502,9 +1514,9 @@ func (bc *Blockchain) CalculateClaimable(value util.Fixed8, startHeight, endHeig
|
|||
startHeight++
|
||||
}
|
||||
h := bc.GetHeaderHash(int(startHeight - 1))
|
||||
feeStart := bc.getSystemFeeAmount(h)
|
||||
feeStart := bc.GetSystemFeeAmount(h)
|
||||
h = bc.GetHeaderHash(int(endHeight - 1))
|
||||
feeEnd := bc.getSystemFeeAmount(h)
|
||||
feeEnd := bc.GetSystemFeeAmount(h)
|
||||
|
||||
sysFeeTotal := util.Fixed8(feeEnd - feeStart)
|
||||
ratio := value / 100000000
|
||||
|
@ -1576,11 +1588,44 @@ func (bc *Blockchain) NetworkFee(t *transaction.Transaction) util.Fixed8 {
|
|||
|
||||
// SystemFee returns system fee.
|
||||
func (bc *Blockchain) SystemFee(t *transaction.Transaction) util.Fixed8 {
|
||||
if t.Type == transaction.InvocationType {
|
||||
switch t.Type {
|
||||
case transaction.InvocationType:
|
||||
inv := t.Data.(*transaction.InvocationTX)
|
||||
if inv.Version >= 1 {
|
||||
return inv.Gas
|
||||
case transaction.IssueType:
|
||||
if t.Version >= 1 {
|
||||
return util.Fixed8(0)
|
||||
}
|
||||
var iszero = true
|
||||
for i := range t.Outputs {
|
||||
asset := t.Outputs[i].AssetID
|
||||
if asset != UtilityTokenID() && asset != GoverningTokenID() {
|
||||
iszero = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if iszero {
|
||||
return util.Fixed8(0)
|
||||
}
|
||||
case transaction.RegisterType:
|
||||
reg := t.Data.(*transaction.RegisterTX)
|
||||
if reg.AssetType == transaction.GoverningToken || reg.AssetType == transaction.UtilityToken {
|
||||
return util.Fixed8(0)
|
||||
}
|
||||
case transaction.StateType:
|
||||
res := util.Fixed8(0)
|
||||
st := t.Data.(*transaction.StateTX)
|
||||
for _, desc := range st.Descriptors {
|
||||
if desc.Type == transaction.Validator && desc.Field == "Registered" {
|
||||
for i := range desc.Value {
|
||||
if desc.Value[i] != 0 {
|
||||
res += util.Fixed8FromInt64(1000)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
return bc.GetConfig().SystemFee.TryGetValue(t.Type)
|
||||
}
|
||||
|
@ -2232,23 +2277,14 @@ func (bc *Blockchain) GetEnrollments() ([]*state.Validator, error) {
|
|||
for _, validator := range validators {
|
||||
if validator.Registered {
|
||||
result = append(result, validator)
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, sBValidator := range uniqueSBValidators {
|
||||
isAdded := false
|
||||
for _, v := range result {
|
||||
if v.PublicKey == sBValidator {
|
||||
isAdded = true
|
||||
for _, sbValidator := range uniqueSBValidators {
|
||||
if validator.PublicKey.Equal(sbValidator) {
|
||||
result = append(result, validator)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isAdded {
|
||||
result = append(result, &state.Validator{
|
||||
PublicKey: sBValidator,
|
||||
Registered: false,
|
||||
Votes: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ type Blockchainer interface {
|
|||
GetStateRoot(height uint32) (*state.MPTRootState, error)
|
||||
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
||||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetSystemFeeAmount(h util.Uint256) uint32
|
||||
GetTestVM(tx *transaction.Transaction) *vm.VM
|
||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
GetUnspentCoinState(util.Uint256) *state.UnspentCoin
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
// NEP5Tracker contains info about a single account in a NEP5 contract.
|
||||
type NEP5Tracker struct {
|
||||
// Balance is the current balance of the account.
|
||||
Balance int64
|
||||
Balance *big.Int
|
||||
// LastUpdatedBlock is a number of block when last `transfer` to or from the
|
||||
// account occured.
|
||||
LastUpdatedBlock uint32
|
||||
|
@ -25,7 +25,7 @@ type TransferLog struct {
|
|||
}
|
||||
|
||||
// NEP5TransferSize is a size of a marshaled NEP5Transfer struct in bytes.
|
||||
const NEP5TransferSize = util.Uint160Size*3 + 8 + 4 + 4 + util.Uint256Size + 4
|
||||
const NEP5TransferSize = util.Uint160Size*3 + amountSize + 4 + 4 + util.Uint256Size + 4
|
||||
|
||||
// NEP5Transfer represents a single NEP5 Transfer event.
|
||||
type NEP5Transfer struct {
|
||||
|
@ -37,7 +37,7 @@ type NEP5Transfer struct {
|
|||
To util.Uint160
|
||||
// Amount is the amount of tokens transferred.
|
||||
// It is negative when tokens are sent and positive if they are received.
|
||||
Amount int64
|
||||
Amount *big.Int
|
||||
// Block is a number of block when the event occured.
|
||||
Block uint32
|
||||
// Timestamp is the timestamp of the block where transfer occured.
|
||||
|
@ -48,6 +48,8 @@ type NEP5Transfer struct {
|
|||
Index uint32
|
||||
}
|
||||
|
||||
const amountSize = 32
|
||||
|
||||
// NEP5Balances is a map of the NEP5 contract hashes
|
||||
// to the corresponding structures.
|
||||
type NEP5Balances struct {
|
||||
|
@ -143,13 +145,13 @@ func (lg *TransferLog) Size() int {
|
|||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (t *NEP5Tracker) EncodeBinary(w *io.BinWriter) {
|
||||
w.WriteU64LE(uint64(t.Balance))
|
||||
w.WriteVarBytes(emit.IntToBytes(t.Balance))
|
||||
w.WriteU32LE(t.LastUpdatedBlock)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (t *NEP5Tracker) DecodeBinary(r *io.BinReader) {
|
||||
t.Balance = int64(r.ReadU64LE())
|
||||
t.Balance = emit.BytesToInt(r.ReadVarBytes(amountSize))
|
||||
t.LastUpdatedBlock = r.ReadU32LE()
|
||||
}
|
||||
|
||||
|
@ -194,7 +196,7 @@ func NEP5TransferFromNotification(ne NotificationEvent, txHash util.Uint256, hei
|
|||
Asset: ne.ScriptHash,
|
||||
From: fromAddr,
|
||||
To: toAddr,
|
||||
Amount: amount.Int64(),
|
||||
Amount: amount,
|
||||
Block: height,
|
||||
Timestamp: time,
|
||||
Tx: txHash,
|
||||
|
@ -212,7 +214,19 @@ func (t *NEP5Transfer) EncodeBinary(w *io.BinWriter) {
|
|||
w.WriteBytes(t.To[:])
|
||||
w.WriteU32LE(t.Block)
|
||||
w.WriteU32LE(t.Timestamp)
|
||||
w.WriteU64LE(uint64(t.Amount))
|
||||
am := emit.IntToBytes(t.Amount)
|
||||
if len(am) > amountSize {
|
||||
panic("bad integer length")
|
||||
}
|
||||
fillerLen := amountSize - len(am)
|
||||
w.WriteBytes(am)
|
||||
var filler byte
|
||||
if t.Amount.Sign() < 0 {
|
||||
filler = 0xff
|
||||
}
|
||||
for i := 0; i < fillerLen; i++ {
|
||||
w.WriteB(filler)
|
||||
}
|
||||
w.WriteU32LE(t.Index)
|
||||
}
|
||||
|
||||
|
@ -224,6 +238,8 @@ func (t *NEP5Transfer) DecodeBinary(r *io.BinReader) {
|
|||
r.ReadBytes(t.To[:])
|
||||
t.Block = r.ReadU32LE()
|
||||
t.Timestamp = r.ReadU32LE()
|
||||
t.Amount = int64(r.ReadU64LE())
|
||||
amount := make([]byte, amountSize)
|
||||
r.ReadBytes(amount)
|
||||
t.Amount = emit.BytesToInt(amount)
|
||||
t.Index = r.ReadU32LE()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -41,7 +42,7 @@ func TestNEP5TransferLog_Append(t *testing.T) {
|
|||
|
||||
func TestNEP5Tracker_EncodeBinary(t *testing.T) {
|
||||
expected := &NEP5Tracker{
|
||||
Balance: int64(rand.Uint64()),
|
||||
Balance: big.NewInt(int64(rand.Uint64())),
|
||||
LastUpdatedBlock: rand.Uint32(),
|
||||
}
|
||||
|
||||
|
@ -53,7 +54,7 @@ func TestNEP5Transfer_DecodeBinary(t *testing.T) {
|
|||
Asset: util.Uint160{1, 2, 3},
|
||||
From: util.Uint160{5, 6, 7},
|
||||
To: util.Uint160{8, 9, 10},
|
||||
Amount: 42,
|
||||
Amount: big.NewInt(42),
|
||||
Block: 12345,
|
||||
Timestamp: 54321,
|
||||
Tx: util.Uint256{8, 5, 3},
|
||||
|
@ -70,7 +71,7 @@ func TestNEP5TransferSize(t *testing.T) {
|
|||
|
||||
func randomTransfer(r *rand.Rand) *NEP5Transfer {
|
||||
return &NEP5Transfer{
|
||||
Amount: int64(r.Uint64()),
|
||||
Amount: big.NewInt(int64(r.Uint64())),
|
||||
Block: r.Uint32(),
|
||||
Asset: random.Uint160(),
|
||||
From: random.Uint160(),
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AssetType represents a NEO asset type.
|
||||
type AssetType uint8
|
||||
|
||||
|
@ -14,3 +19,68 @@ const (
|
|||
Invoice AssetType = DutyFlag | 0x18
|
||||
Token AssetType = CreditFlag | 0x20
|
||||
)
|
||||
|
||||
// String implements Stringer interface.
|
||||
func (a AssetType) String() string {
|
||||
switch a {
|
||||
case CreditFlag:
|
||||
return "CreditFlag"
|
||||
case DutyFlag:
|
||||
return "DutyFlag"
|
||||
case GoverningToken:
|
||||
return "GoverningToken"
|
||||
case UtilityToken:
|
||||
return "UtilityToken"
|
||||
case Currency:
|
||||
return "Currency"
|
||||
case Share:
|
||||
return "Share"
|
||||
case Invoice:
|
||||
return "Invoice"
|
||||
case Token:
|
||||
return "Token"
|
||||
default:
|
||||
return fmt.Sprintf("Unknonwn (%d)", a)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json marshaller interface.
|
||||
func (a AssetType) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + a.String() + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (a *AssetType) UnmarshalJSON(data []byte) error {
|
||||
l := len(data)
|
||||
if l < 2 || data[0] != '"' || data[l-1] != '"' {
|
||||
return errors.New("wrong format")
|
||||
}
|
||||
var err error
|
||||
*a, err = AssetTypeFromString(string(data[1 : l-1]))
|
||||
return err
|
||||
}
|
||||
|
||||
// AssetTypeFromString converts string into AssetType.
|
||||
func AssetTypeFromString(s string) (AssetType, error) {
|
||||
switch s {
|
||||
case "CreditFlag":
|
||||
return CreditFlag, nil
|
||||
case "DutyFlag":
|
||||
return DutyFlag, nil
|
||||
case "GoverningToken":
|
||||
return GoverningToken, nil
|
||||
case "UtilityToken":
|
||||
return UtilityToken, nil
|
||||
case "Currency":
|
||||
return Currency, nil
|
||||
case "Share":
|
||||
return Share, nil
|
||||
case "Invoice":
|
||||
return Invoice, nil
|
||||
case "Token":
|
||||
return Token, nil
|
||||
default:
|
||||
return 0, errors.New("bad asset type")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,18 +67,18 @@ func (tx *PublishTX) EncodeBinary(bw *io.BinWriter) {
|
|||
// 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"`
|
||||
NeedStorage bool `json:"needstorage"`
|
||||
Name string `json:"name"`
|
||||
CodeVersion string `json:"version"`
|
||||
Author string `json:"author"`
|
||||
Email string `json:"email"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
Hash util.Uint160 `json:"hash"`
|
||||
Script string `json:"script"`
|
||||
ParamList []smartcontract.ParamType `json:"parameters"`
|
||||
ReturnType smartcontract.ParamType `json:"returntype"`
|
||||
}
|
||||
|
|
|
@ -56,10 +56,10 @@ func (tx *RegisterTX) EncodeBinary(bw *io.BinWriter) {
|
|||
|
||||
// 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"`
|
||||
AssetType AssetType `json:"type"`
|
||||
Name json.RawMessage `json:"name"`
|
||||
Amount util.Fixed8 `json:"amount"`
|
||||
Precision uint8 `json:"precision"`
|
||||
Owner keys.PublicKey `json:"owner"`
|
||||
Admin string `json:"admin"`
|
||||
}
|
||||
|
|
|
@ -123,6 +123,9 @@ func (chain testChain) GetStateRoot(height uint32) (*state.MPTRootState, error)
|
|||
func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetSystemFeeAmount(h util.Uint256) uint32 {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetTestVM(tx *transaction.Transaction) *vm.VM {
|
||||
panic("TODO")
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetAssetState(util.Uint256{})
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","type":0,"name":"NEO","amount":"100000000","available":"100000000","precision":0,"owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":4000000,"is_frozen":false}}`,
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","type":"GoverningToken","name":"NEO","amount":"100000000","available":"100000000","precision":0,"owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":4000000,"frozen":false}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
return &result.AssetState{
|
||||
ID: core.GoverningTokenID(),
|
||||
|
@ -517,25 +517,25 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetPeers()
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"unconnected":[{"address":"172.200.0.1","port":"20333"}],"connected":[{"address":"127.0.0.1","port":"20335"}],"bad":[{"address":"172.200.0.254","port":"20332"}]}}`,
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"unconnected":[{"address":"172.200.0.1","port":20333}],"connected":[{"address":"127.0.0.1","port":20335}],"bad":[{"address":"172.200.0.254","port":20332}]}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
return &result.GetPeers{
|
||||
Unconnected: result.Peers{
|
||||
{
|
||||
Address: "172.200.0.1",
|
||||
Port: "20333",
|
||||
Port: 20333,
|
||||
},
|
||||
},
|
||||
Connected: result.Peers{
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "20335",
|
||||
Port: 20335,
|
||||
},
|
||||
},
|
||||
Bad: result.Peers{
|
||||
{
|
||||
Address: "172.200.0.254",
|
||||
Port: "20332",
|
||||
Port: 20332,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ type AssetState struct {
|
|||
Admin string `json:"admin"`
|
||||
Issuer string `json:"issuer"`
|
||||
Expiration uint32 `json:"expiration"`
|
||||
IsFrozen bool `json:"is_frozen"`
|
||||
IsFrozen bool `json:"frozen"`
|
||||
}
|
||||
|
||||
// NewAssetState creates a new Asset wrapper.
|
||||
|
|
|
@ -48,7 +48,7 @@ func NewBlock(b *block.Block, chain core.Blockchainer) Block {
|
|||
Base: &b.Base,
|
||||
BlockMetadataAndTx: BlockMetadataAndTx{
|
||||
Size: io.GetVarSize(b),
|
||||
Confirmations: chain.BlockHeight() - b.Index - 1,
|
||||
Confirmations: chain.BlockHeight() - b.Index + 1,
|
||||
Tx: make([]Tx, 0, len(b.Transactions)),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ type (
|
|||
// Peer represents the peer.
|
||||
Peer struct {
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
Port uint16 `json:"port"`
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -50,9 +51,10 @@ func (g *GetPeers) AddBad(addrs []string) {
|
|||
func (p *Peers) addPeers(addrs []string) {
|
||||
for i := range addrs {
|
||||
addressParts := strings.Split(addrs[i], ":")
|
||||
port, _ := strconv.Atoi(addressParts[1]) // We know it's a good port number.
|
||||
peer := Peer{
|
||||
Address: addressParts[0],
|
||||
Port: addressParts[1],
|
||||
Port: uint16(port),
|
||||
}
|
||||
|
||||
*p = append(*p, peer)
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestGetPeers(t *testing.T) {
|
|||
require.Equal(t, 1, len(gp.Connected))
|
||||
require.Equal(t, 1, len(gp.Bad))
|
||||
require.Equal(t, "192.168.0.1", gp.Connected[0].Address)
|
||||
require.Equal(t, "10333", gp.Connected[0].Port)
|
||||
require.Equal(t, uint16(10333), gp.Connected[0].Port)
|
||||
require.Equal(t, "127.0.0.1", gp.Bad[0].Address)
|
||||
require.Equal(t, "20333", gp.Bad[0].Port)
|
||||
require.Equal(t, uint16(20333), gp.Bad[0].Port)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ func NewTxOutput(out *transaction.Output) *TransactionOutput {
|
|||
|
||||
return &TransactionOutput{
|
||||
N: out.Position,
|
||||
Asset: "0x" + out.AssetID.String(),
|
||||
Asset: "0x" + out.AssetID.StringLE(),
|
||||
Value: out.Amount,
|
||||
Address: addr,
|
||||
}
|
||||
|
|
|
@ -461,7 +461,7 @@ func (s *Server) getBlockHash(reqParams request.Params) (interface{}, *response.
|
|||
}
|
||||
num, err := s.blockHeightFromParam(param)
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.chain.GetHeaderHash(num), nil
|
||||
|
@ -771,7 +771,7 @@ func (s *Server) getNEP5Balances(ps request.Params) (interface{}, *response.Erro
|
|||
}
|
||||
if as != nil {
|
||||
for h, bal := range as.Trackers {
|
||||
amount := strconv.FormatInt(bal.Balance, 10)
|
||||
amount := bal.Balance.String()
|
||||
bs.Balances = append(bs.Balances, result.NEP5Balance{
|
||||
Asset: h,
|
||||
Amount: amount,
|
||||
|
@ -824,14 +824,15 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, *response.Err
|
|||
|
||||
NotifyIndex: tr.Index,
|
||||
}
|
||||
if tr.Amount > 0 { // token was received
|
||||
transfer.Amount = strconv.FormatInt(tr.Amount, 10)
|
||||
if tr.Amount.Sign() > 0 { // token was received
|
||||
transfer.Amount = tr.Amount.String()
|
||||
if !tr.From.Equals(util.Uint160{}) {
|
||||
transfer.Address = address.Uint160ToString(tr.From)
|
||||
}
|
||||
bs.Received = append(bs.Received, transfer)
|
||||
} else {
|
||||
transfer.Amount = strconv.FormatInt(-tr.Amount, 10)
|
||||
tr.Amount.Neg(tr.Amount)
|
||||
transfer.Amount = tr.Amount.String()
|
||||
if !tr.To.Equals(util.Uint160{}) {
|
||||
transfer.Address = address.Uint160ToString(tr.To)
|
||||
}
|
||||
|
@ -888,12 +889,14 @@ func uint160ToString(u util.Uint160) string {
|
|||
func appendNEP5ToTransferTx(transfer *result.TransferTx, nepTr *state.NEP5Transfer) {
|
||||
var event result.TransferTxEvent
|
||||
event.Asset = nepTr.Asset.StringLE()
|
||||
if nepTr.Amount > 0 { // token was received
|
||||
event.Value = strconv.FormatInt(nepTr.Amount, 10)
|
||||
if nepTr.Amount.Sign() > 0 { // token was received
|
||||
event.Value = nepTr.Amount.String()
|
||||
event.Type = "receive"
|
||||
event.Address = uint160ToString(nepTr.From)
|
||||
} else {
|
||||
event.Value = strconv.FormatInt(-nepTr.Amount, 10)
|
||||
nepTr.Amount.Neg(nepTr.Amount)
|
||||
event.Value = nepTr.Amount.String()
|
||||
nepTr.Amount.Neg(nepTr.Amount)
|
||||
event.Type = "send"
|
||||
event.Address = uint160ToString(nepTr.To)
|
||||
}
|
||||
|
@ -1327,7 +1330,7 @@ func (s *Server) getBlockTransferTx(ps request.Params) (interface{}, *response.E
|
|||
Asset: nepTr.Asset.StringLE(),
|
||||
From: uint160ToString(nepTr.From),
|
||||
To: uint160ToString(nepTr.To),
|
||||
Value: strconv.FormatInt(nepTr.Amount, 10),
|
||||
Value: nepTr.Amount.String(),
|
||||
})
|
||||
index++
|
||||
}
|
||||
|
@ -1356,24 +1359,15 @@ func (s *Server) getBlockSysFee(reqParams request.Params) (interface{}, *respons
|
|||
}
|
||||
|
||||
headerHash := s.chain.GetHeaderHash(num)
|
||||
block, errBlock := s.chain.GetBlock(headerHash)
|
||||
if errBlock != nil {
|
||||
return 0, response.NewRPCError(errBlock.Error(), "", nil)
|
||||
}
|
||||
|
||||
var blockSysFee util.Fixed8
|
||||
for _, tx := range block.Transactions {
|
||||
blockSysFee += s.chain.SystemFee(tx)
|
||||
}
|
||||
|
||||
return blockSysFee, nil
|
||||
return util.Fixed8FromInt64(int64(s.chain.GetSystemFeeAmount(headerHash))), nil
|
||||
}
|
||||
|
||||
// getBlockHeader returns the corresponding block header information according to the specified script hash.
|
||||
func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, *response.Error) {
|
||||
hash, err := reqParams.ValueWithType(0, request.StringT).GetUint256()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
hash, respErr := s.getBlockHashFromParam(reqParams.Value(0))
|
||||
if respErr != nil {
|
||||
return nil, respErr
|
||||
}
|
||||
|
||||
verbose := reqParams.Value(1).GetBoolean()
|
||||
|
|
|
@ -548,13 +548,9 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
name: "positive",
|
||||
params: "[1]",
|
||||
result: func(e *executor) interface{} {
|
||||
block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||
|
||||
var expectedBlockSysFee util.Fixed8
|
||||
for _, tx := range block.Transactions {
|
||||
expectedBlockSysFee += e.chain.SystemFee(tx)
|
||||
}
|
||||
return &expectedBlockSysFee
|
||||
sf := e.chain.GetSystemFeeAmount(e.chain.GetHeaderHash(1))
|
||||
r := util.Fixed8FromInt64(int64(sf))
|
||||
return &r
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -727,12 +723,12 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
},
|
||||
check: func(t *testing.T, e *executor, validators interface{}) {
|
||||
var expected []result.Validator
|
||||
sBValidators, err := e.chain.GetStandByValidators()
|
||||
enrolls, err := e.chain.GetEnrollments()
|
||||
require.NoError(t, err)
|
||||
for _, sbValidator := range sBValidators {
|
||||
for _, validator := range enrolls {
|
||||
expected = append(expected, result.Validator{
|
||||
PublicKey: *sbValidator,
|
||||
Votes: 0,
|
||||
PublicKey: *validator.PublicKey,
|
||||
Votes: validator.Votes,
|
||||
Active: true,
|
||||
})
|
||||
}
|
||||
|
@ -1217,7 +1213,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
|||
err := json.Unmarshal(res, &txOut)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", res)
|
||||
assert.Equal(t, 1, txOut.N)
|
||||
assert.Equal(t, "0x9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5", txOut.Asset)
|
||||
assert.Equal(t, "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", txOut.Asset)
|
||||
assert.Equal(t, util.Fixed8FromInt64(1000), txOut.Value)
|
||||
assert.Equal(t, "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", txOut.Address)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue