rpc: fix gettxout result for already spent outputs

As per C# documentation [1]:
  If the transaction output is already spent, the result value will be null .

[1]: https://docs.neo.org/docs/en-us/reference/rpc/latest-version/api/gettxout.html
This commit is contained in:
Roman Khimov 2020-12-31 13:54:42 +03:00
parent 124c674b17
commit 9e35758653
4 changed files with 25 additions and 8 deletions

View file

@ -43,6 +43,11 @@ func (s *UnspentCoin) EncodeBinary(bw *io.BinWriter) {
func (s *UnspentCoin) DecodeBinary(br *io.BinReader) {
s.Height = br.ReadU32LE()
br.ReadArray(&s.States)
if br.Err == nil {
for i := range s.States {
s.States[i].Output.Position = i
}
}
}
// EncodeBinary implements Serializable interface.

View file

@ -27,6 +27,7 @@ func TestDecodeEncodeUnspentCoin(t *testing.T) {
AssetID: random.Uint256(),
Amount: util.Fixed8(420),
ScriptHash: random.Uint160(),
Position: 1,
},
SpendHeight: 0,
State: CoinConfirmed,
@ -36,6 +37,7 @@ func TestDecodeEncodeUnspentCoin(t *testing.T) {
AssetID: random.Uint256(),
Amount: util.Fixed8(4200),
ScriptHash: random.Uint160(),
Position: 2,
},
SpendHeight: 111000,
State: CoinSpent & CoinClaimed,

View file

@ -1207,17 +1207,19 @@ func (s *Server) getTxOut(ps request.Params) (interface{}, *response.Error) {
return nil, response.ErrInvalidParams
}
tx, _, err := s.chain.GetTransaction(h)
if err != nil {
return nil, response.NewInvalidParamsError(err.Error(), err)
ucs := s.chain.GetUnspentCoinState(h)
if ucs == nil {
return nil, response.NewInvalidParamsError("invalid tx hash", errors.New("unknown"))
}
if num >= len(tx.Outputs) {
if num >= len(ucs.States) {
return nil, response.NewInvalidParamsError("invalid index", errors.New("too big index"))
}
out := tx.Outputs[num]
return result.NewTxOutput(&out), nil
if ucs.States[num].State&state.CoinSpent != 0 {
return nil, nil
}
return result.NewTxOutput(&ucs.States[num].Output), nil
}
// getContractState returns contract state (contract information, according to the contract script hash).

View file

@ -1204,13 +1204,21 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
`"`+tx.Hash().StringLE()+`"`, 0)
body := doRPCCall(rpc, httpSrv.URL, t)
res := checkErrGetResult(t, body, false)
assert.Equal(t, "null", string(res)) // already spent
block, _ = chain.GetBlock(e.chain.GetHeaderHash(1))
tx = block.Transactions[1]
rpc = fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "gettxout", "params": [%s, %d]}"`,
`"`+tx.Hash().StringLE()+`"`, 1) // Neo remainder in txMoveNeo
body = doRPCCall(rpc, httpSrv.URL, t)
res = checkErrGetResult(t, body, false)
var txOut result.TransactionOutput
err := json.Unmarshal(res, &txOut)
require.NoErrorf(t, err, "could not parse response: %s", res)
assert.Equal(t, 0, txOut.N)
assert.Equal(t, 1, txOut.N)
assert.Equal(t, "0x9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5", txOut.Asset)
assert.Equal(t, util.Fixed8FromInt64(100000000), txOut.Value)
assert.Equal(t, util.Fixed8FromInt64(1000), txOut.Value)
assert.Equal(t, "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", txOut.Address)
})