diff --git a/docs/rpc.md b/docs/rpc.md index 57c5513d7..0aacd9147 100644 --- a/docs/rpc.md +++ b/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` diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index ec9d14f57..46d448668 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -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 - bs.LastUpdatedBlock = transfer.Block - balances.Trackers[transfer.Asset] = bs + 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 + 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 } diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index 76c032c61..5d72d8543 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -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 diff --git a/pkg/core/state/nep5.go b/pkg/core/state/nep5.go index d289ec00e..a87685293 100644 --- a/pkg/core/state/nep5.go +++ b/pkg/core/state/nep5.go @@ -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() } diff --git a/pkg/core/state/nep5_test.go b/pkg/core/state/nep5_test.go index 19f055d1c..a2c8ea764 100644 --- a/pkg/core/state/nep5_test.go +++ b/pkg/core/state/nep5_test.go @@ -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(), diff --git a/pkg/core/transaction/asset_type.go b/pkg/core/transaction/asset_type.go index 9d54eb28f..2a3caba05 100644 --- a/pkg/core/transaction/asset_type.go +++ b/pkg/core/transaction/asset_type.go @@ -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") + } +} diff --git a/pkg/core/transaction/publish.go b/pkg/core/transaction/publish.go index 1cd89fa96..f9811e759 100644 --- a/pkg/core/transaction/publish.go +++ b/pkg/core/transaction/publish.go @@ -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"` } diff --git a/pkg/core/transaction/register.go b/pkg/core/transaction/register.go index 3ce5be61f..014864ab7 100644 --- a/pkg/core/transaction/register.go +++ b/pkg/core/transaction/register.go @@ -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"` } diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 5f078fddc..8922733aa 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -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") } diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 8369f2fb2..856c72216 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -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, }, }, } diff --git a/pkg/rpc/response/result/asset_state.go b/pkg/rpc/response/result/asset_state.go index 3c3d5f38d..930bdc1dd 100644 --- a/pkg/rpc/response/result/asset_state.go +++ b/pkg/rpc/response/result/asset_state.go @@ -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. diff --git a/pkg/rpc/response/result/block.go b/pkg/rpc/response/result/block.go index b1cadb605..a074021fb 100644 --- a/pkg/rpc/response/result/block.go +++ b/pkg/rpc/response/result/block.go @@ -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)), }, } diff --git a/pkg/rpc/response/result/peers.go b/pkg/rpc/response/result/peers.go index b18acc0e8..67c637c36 100644 --- a/pkg/rpc/response/result/peers.go +++ b/pkg/rpc/response/result/peers.go @@ -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) diff --git a/pkg/rpc/response/result/peers_test.go b/pkg/rpc/response/result/peers_test.go index 6e5a9339e..8187f4154 100644 --- a/pkg/rpc/response/result/peers_test.go +++ b/pkg/rpc/response/result/peers_test.go @@ -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) } diff --git a/pkg/rpc/response/result/tx_output.go b/pkg/rpc/response/result/tx_output.go index 4078de755..61baa6973 100644 --- a/pkg/rpc/response/result/tx_output.go +++ b/pkg/rpc/response/result/tx_output.go @@ -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, } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 68db88eb1..b1ba0636c 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -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() diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 4b544610c..4902396ba 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -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) })