mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 23:33:37 +00:00
commit
cd307c3cd0
113 changed files with 645 additions and 5682 deletions
Binary file not shown.
Binary file not shown.
|
@ -81,7 +81,6 @@ services:
|
|||
- ../config/protocol.privnet.docker.single.yml:/config/protocol.privnet.yml
|
||||
- ./wallets/wallet1.json:/wallet1.json
|
||||
- volume_chain:/chains
|
||||
- ./1600-privnet-blocks-single.acc.gz:/privnet-blocks.acc.gz
|
||||
networks:
|
||||
neo_go_network:
|
||||
ipv4_address: 172.200.0.1
|
||||
|
|
|
@ -30,8 +30,6 @@ LABEL version=$VERSION
|
|||
WORKDIR /
|
||||
|
||||
COPY --from=builder /neo-go/config /config
|
||||
COPY --from=builder /neo-go/.docker/6000-privnet-blocks.acc.gz /6000-privnet-blocks.acc.gz
|
||||
COPY --from=builder /neo-go/.docker/1600-privnet-blocks-single.acc.gz /1600-privnet-blocks-single.acc.gz
|
||||
COPY --from=builder /neo-go/.docker/privnet-entrypoint.sh /usr/bin/privnet-entrypoint.sh
|
||||
COPY --from=builder /go/bin/neo-go /usr/bin/neo-go
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
|
|
@ -121,15 +121,4 @@ func writeParameterContext(c *context.ParameterContext, filename string) error {
|
|||
|
||||
func printTxInfo(t *transaction.Transaction) {
|
||||
fmt.Printf("Hash: %s\n", t.Hash().StringLE())
|
||||
for i := range t.Inputs {
|
||||
fmt.Printf("Input%02d: [%2d] %s\n", i, t.Inputs[i].PrevIndex, t.Inputs[i].PrevHash.StringLE())
|
||||
}
|
||||
for i := range t.Outputs {
|
||||
fmt.Printf("Output%02d:\n", i)
|
||||
fmt.Printf("\tAssetID : %s\n", t.Outputs[i].AssetID.StringLE())
|
||||
fmt.Printf("\tAmount : %s\n", t.Outputs[i].Amount.String())
|
||||
h := t.Outputs[i].ScriptHash
|
||||
fmt.Printf("\tScriptHash: %s\n", t.Outputs[i].ScriptHash.StringLE())
|
||||
fmt.Printf("\tToAddr : %s\n", address.Uint160ToString(h))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package wallet
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -82,6 +85,7 @@ func newNEP5Commands() []cli.Command {
|
|||
Flags: []cli.Flag{
|
||||
walletPathFlag,
|
||||
rpcFlag,
|
||||
outFlag,
|
||||
timeoutFlag,
|
||||
fromAddrFlag,
|
||||
toAddrFlag,
|
||||
|
@ -341,11 +345,30 @@ func transferNEP5(ctx *cli.Context) error {
|
|||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
hash, err := c.TransferNEP5(acc, to, token, amount, gas)
|
||||
tx, err := c.CreateNEP5TransferTx(acc, to, token.Hash, amount, gas)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
fmt.Println(hash.StringLE())
|
||||
if outFile := ctx.String("out"); outFile != "" {
|
||||
priv := acc.PrivateKey()
|
||||
pub := priv.PublicKey()
|
||||
sign := priv.Sign(tx.GetSignedPart())
|
||||
scCtx := context.NewParameterContext("Neo.Core.ContractTransaction", tx)
|
||||
if err := scCtx.AddSignature(acc.Contract, pub, sign); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't add signature: %v", err), 1)
|
||||
} else if data, err := json.Marshal(scCtx); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't marshal tx to JSON: %v", err), 1)
|
||||
} else if err := ioutil.WriteFile(outFile, data, 0644); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't write tx to file: %v", err), 1)
|
||||
}
|
||||
} else {
|
||||
_ = acc.SignTx(tx)
|
||||
if err := c.SendRawTransaction(tx); err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(tx.Hash().StringLE())
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -4,22 +4,16 @@ import (
|
|||
"bufio"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||
context2 "github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -179,29 +173,6 @@ func NewCommands() []cli.Command {
|
|||
forceFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transfer",
|
||||
Usage: "transfer NEO/GAS",
|
||||
UsageText: "transfer --path <path> --from <addr> --to <addr>" +
|
||||
" --amount <amount> --asset [NEO|GAS|<hex-id>] [--out <path>]",
|
||||
Action: transferAsset,
|
||||
Flags: []cli.Flag{
|
||||
walletPathFlag,
|
||||
rpcFlag,
|
||||
timeoutFlag,
|
||||
outFlag,
|
||||
fromAddrFlag,
|
||||
toAddrFlag,
|
||||
cli.StringFlag{
|
||||
Name: "amount",
|
||||
Usage: "Amount of asset to send",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "asset",
|
||||
Usage: "Asset ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "multisig",
|
||||
Usage: "work with multisig address",
|
||||
|
@ -247,46 +218,18 @@ func claimGas(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
info, err := c.GetClaimable(addrFlag.String())
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
} else if info.Unclaimed == 0 || len(info.Spents) == 0 {
|
||||
fmt.Println("Nothing to claim")
|
||||
return nil
|
||||
}
|
||||
|
||||
var claim transaction.ClaimTX
|
||||
for i := range info.Spents {
|
||||
claim.Claims = append(claim.Claims, transaction.Input{
|
||||
PrevHash: info.Spents[i].Tx,
|
||||
PrevIndex: uint16(info.Spents[i].N),
|
||||
})
|
||||
}
|
||||
|
||||
tx := transaction.NewClaimTX(&claim)
|
||||
validUntilBlock, err := c.CalculateValidUntilBlock()
|
||||
// Temporary.
|
||||
neoHash, err := util.Uint160DecodeStringLE("3b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c")
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
tx.ValidUntilBlock = validUntilBlock
|
||||
tx.Sender = scriptHash
|
||||
|
||||
tx.AddOutput(&transaction.Output{
|
||||
AssetID: core.UtilityTokenID(),
|
||||
Amount: info.Unclaimed,
|
||||
ScriptHash: scriptHash,
|
||||
})
|
||||
|
||||
err = c.AddNetworkFee(tx, acc)
|
||||
hash, err := c.TransferNEP5(acc, scriptHash, neoHash, 0, 0)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
_ = acc.SignTx(tx)
|
||||
if err := c.SendRawTransaction(tx); err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
fmt.Println(tx.Hash().StringLE())
|
||||
fmt.Println(hash.StringLE())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -477,99 +420,6 @@ func askForConsent() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func transferAsset(ctx *cli.Context) error {
|
||||
wall, err := openWallet(ctx.String("path"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
defer wall.Close()
|
||||
|
||||
fromFlag := ctx.Generic("from").(*flags.Address)
|
||||
if !fromFlag.IsSet {
|
||||
return cli.NewExitError("'from' address was not provided", 1)
|
||||
}
|
||||
from := fromFlag.Uint160()
|
||||
acc := wall.GetAccount(from)
|
||||
if acc == nil {
|
||||
return cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", from), 1)
|
||||
}
|
||||
|
||||
asset, err := getAssetID(ctx.String("asset"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("invalid asset id: %v", err), 1)
|
||||
}
|
||||
|
||||
amount, err := util.Fixed8FromString(ctx.String("amount"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("invalid amount: %v", err), 1)
|
||||
}
|
||||
|
||||
pass, err := readPassword("Enter wallet password > ")
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
} else if err := acc.Decrypt(pass); err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
gctx, cancel := getGoContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
c, err := client.New(gctx, ctx.String("rpc"), client.Options{})
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
tx := transaction.NewContractTX()
|
||||
validUntilBlock, err := c.CalculateValidUntilBlock()
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
tx.ValidUntilBlock = validUntilBlock
|
||||
if err := request.AddInputsAndUnspentsToTx(tx, fromFlag.String(), asset, amount, c); err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
tx.Sender = from
|
||||
|
||||
toFlag := ctx.Generic("to").(*flags.Address)
|
||||
if !toFlag.IsSet {
|
||||
return cli.NewExitError("'to' address was not provided", 1)
|
||||
}
|
||||
toAddr := toFlag.Uint160()
|
||||
tx.AddOutput(&transaction.Output{
|
||||
AssetID: asset,
|
||||
Amount: amount,
|
||||
ScriptHash: toAddr,
|
||||
Position: 1,
|
||||
})
|
||||
|
||||
err = c.AddNetworkFee(tx, acc)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
if outFile := ctx.String("out"); outFile != "" {
|
||||
priv := acc.PrivateKey()
|
||||
pub := priv.PublicKey()
|
||||
sign := priv.Sign(tx.GetSignedPart())
|
||||
c := context2.NewParameterContext("Neo.Core.ContractTransaction", tx)
|
||||
if err := c.AddSignature(acc.Contract, pub, sign); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't add signature: %v", err), 1)
|
||||
} else if data, err := json.Marshal(c); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't marshal tx to JSON: %v", err), 1)
|
||||
} else if err := ioutil.WriteFile(outFile, data, 0644); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't write tx to file: %v", err), 1)
|
||||
}
|
||||
} else {
|
||||
_ = acc.SignTx(tx)
|
||||
if err := c.SendRawTransaction(tx); err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(tx.Hash().StringLE())
|
||||
return nil
|
||||
}
|
||||
|
||||
func getGoContext(ctx *cli.Context) (context.Context, func()) {
|
||||
if dur := ctx.Duration("timeout"); dur != 0 {
|
||||
return context.WithTimeout(context.Background(), dur)
|
||||
|
@ -659,18 +509,6 @@ func openWallet(path string) (*wallet.Wallet, error) {
|
|||
return wallet.NewWalletFromFile(path)
|
||||
}
|
||||
|
||||
func getAssetID(s string) (util.Uint256, error) {
|
||||
s = strings.ToLower(s)
|
||||
switch {
|
||||
case s == "neo":
|
||||
return core.GoverningTokenID(), nil
|
||||
case s == "gas":
|
||||
return core.UtilityTokenID(), nil
|
||||
default:
|
||||
return util.Uint256DecodeStringLE(s)
|
||||
}
|
||||
}
|
||||
|
||||
func newAccountFromWIF(wif string) (*wallet.Account, error) {
|
||||
// note: NEP2 strings always have length of 58 even though
|
||||
// base58 strings can have different lengths even if slice lengths are equal
|
||||
|
|
|
@ -18,9 +18,6 @@ ProtocolConfiguration:
|
|||
- seed3.neo.org:10333
|
||||
- seed4.neo.org:10333
|
||||
- seed5.neo.org:10333
|
||||
SystemFee:
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: false
|
||||
FreeGasLimit: 10.0
|
||||
|
|
|
@ -14,9 +14,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.2:20334
|
||||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: true
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.2:20334
|
||||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: true
|
||||
|
||||
|
|
|
@ -8,9 +8,6 @@ ProtocolConfiguration:
|
|||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||
SeedList:
|
||||
- 172.200.0.1:20333
|
||||
SystemFee:
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: true
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.2:20334
|
||||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: true
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.2:20334
|
||||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: true
|
||||
|
||||
|
|
|
@ -14,10 +14,6 @@ ProtocolConfiguration:
|
|||
- 127.0.0.1:20334
|
||||
- 127.0.0.1:20335
|
||||
- 127.0.0.1:20336
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: true
|
||||
|
||||
|
|
|
@ -18,9 +18,6 @@ ProtocolConfiguration:
|
|||
- seed3t.neo.org:20333
|
||||
- seed4t.neo.org:20333
|
||||
- seed5t.neo.org:20333
|
||||
SystemFee:
|
||||
IssueTransaction: 5
|
||||
RegisterTransaction: 100
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: false
|
||||
FreeGasLimit: 10.0
|
||||
|
|
|
@ -13,9 +13,6 @@ ProtocolConfiguration:
|
|||
- 127.0.0.1:20334
|
||||
- 127.0.0.1:20335
|
||||
- 127.0.0.1:20336
|
||||
SystemFee:
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
VerifyTransactions: true
|
||||
|
||||
|
|
|
@ -59,10 +59,6 @@ ProtocolConfiguration:
|
|||
- 127.0.0.1:20334
|
||||
- 127.0.0.1:20335
|
||||
- 127.0.0.1:20336
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
|
||||
ApplicationConfiguration:
|
||||
DataDirectoryPath: "./chains/privnet"
|
||||
|
|
15
docs/rpc.md
15
docs/rpc.md
|
@ -34,16 +34,13 @@ which would yield the response:
|
|||
|
||||
| Method |
|
||||
| ------- |
|
||||
| `getaccountstate` |
|
||||
| `getapplicationlog` |
|
||||
| `getassetstate` |
|
||||
| `getbestblockhash` |
|
||||
| `getblock` |
|
||||
| `getblockcount` |
|
||||
| `getblockhash` |
|
||||
| `getblockheader` |
|
||||
| `getblocksysfee` |
|
||||
| `getclaimable` |
|
||||
| `getconnectioncount` |
|
||||
| `getcontractstate` |
|
||||
| `getnep5balances` |
|
||||
|
@ -53,9 +50,7 @@ which would yield the response:
|
|||
| `getrawtransaction` |
|
||||
| `getstorage` |
|
||||
| `gettransactionheight` |
|
||||
| `gettxout` |
|
||||
| `getunclaimed` |
|
||||
| `getunspents` |
|
||||
| `getunclaimedgas` |
|
||||
| `getvalidators` |
|
||||
| `getversion` |
|
||||
| `invoke` |
|
||||
|
@ -77,6 +72,11 @@ in returning it.
|
|||
|
||||
Both methods also don't currently support arrays in function parameters.
|
||||
|
||||
##### `getunclaimedgas`
|
||||
|
||||
It's possible to call this method for any address with neo-go, unlike with C#
|
||||
node where it only works for addresses from opened wallet.
|
||||
|
||||
### Unsupported methods
|
||||
|
||||
Methods listed down below are not going to be supported for various reasons
|
||||
|
@ -86,10 +86,9 @@ and we're not accepting issues related to them.
|
|||
| ------- | ------------|
|
||||
| `claimgas` | Doesn't fit neo-go wallet model, use CLI to do that |
|
||||
| `dumpprivkey` | Shouldn't exist for security reasons, see `claimgas` comment also |
|
||||
| `getbalance` | Use `getaccountstate` instead, see `claimgas` comment also |
|
||||
| `getbalance` | To be implemented |
|
||||
| `getmetricblocktimestamp` | Not really useful, use other means for node monitoring |
|
||||
| `getnewaddress` | See `claimgas` comment |
|
||||
| `getunclaimedgas` | Use `getunclaimed` instead, see `claimgas` comment also |
|
||||
| `getwalletheight` | Not applicable to neo-go, see `claimgas` comment |
|
||||
| `importprivkey` | Not applicable to neo-go, see `claimgas` comment |
|
||||
| `listaddress` | Not applicable to neo-go, see `claimgas` comment |
|
||||
|
|
|
@ -77,7 +77,7 @@ func getTX(t *testing.B, wif *keys.WIF) *transaction.Transaction {
|
|||
fromAddressHash, err := address.StringToUint160(fromAddress)
|
||||
require.NoError(t, err)
|
||||
|
||||
tx := transaction.NewInvocationTX([]byte{0x51}, 1)
|
||||
tx := transaction.New([]byte{0x51}, 1)
|
||||
tx.Version = 0
|
||||
tx.Sender = fromAddressHash
|
||||
tx.Attributes = append(tx.Attributes,
|
||||
|
|
|
@ -40,7 +40,6 @@ var syscalls = map[string]map[string]string{
|
|||
},
|
||||
"blockchain": {
|
||||
"GetAccount": "Neo.Blockchain.GetAccount",
|
||||
"GetAsset": "Neo.Blockchain.GetAsset",
|
||||
"GetBlock": "Neo.Blockchain.GetBlock",
|
||||
"GetContract": "Neo.Blockchain.GetContract",
|
||||
"GetHeader": "Neo.Blockchain.GetHeader",
|
||||
|
@ -65,27 +64,9 @@ var syscalls = map[string]map[string]string{
|
|||
"GetTransaction": "Neo.Block.GetTransaction",
|
||||
},
|
||||
"transaction": {
|
||||
"GetAttributes": "Neo.Transaction.GetAttributes",
|
||||
"GetHash": "Neo.Transaction.GetHash",
|
||||
"GetInputs": "Neo.Transaction.GetInputs",
|
||||
"GetOutputs": "Neo.Transaction.GetOutputs",
|
||||
"GetReferences": "Neo.Transaction.GetReferences",
|
||||
"GetScript": "Neo.InvocationTransaction.GetScript",
|
||||
"GetType": "Neo.Transaction.GetType",
|
||||
"GetUnspentCoins": "Neo.Transaction.GetUnspentCoins",
|
||||
"GetWitnesses": "Neo.Transaction.GetWitnesses",
|
||||
},
|
||||
"asset": {
|
||||
"Create": "Neo.Asset.Create",
|
||||
"GetAdmin": "Neo.Asset.GetAdmin",
|
||||
"GetAmount": "Neo.Asset.GetAmount",
|
||||
"GetAssetID": "Neo.Asset.GetAssetID",
|
||||
"GetAssetType": "Neo.Asset.GetAssetType",
|
||||
"GetAvailable": "Neo.Asset.GetAvailable",
|
||||
"GetIssuer": "Neo.Asset.GetIssuer",
|
||||
"GetOwner": "Neo.Asset.GetOwner",
|
||||
"GetPrecision": "Neo.Asset.GetPrecision",
|
||||
"Renew": "Neo.Asset.Renew",
|
||||
"GetAttributes": "Neo.Transaction.GetAttributes",
|
||||
"GetHash": "Neo.Transaction.GetHash",
|
||||
"GetWitnesses": "Neo.Transaction.GetWitnesses",
|
||||
},
|
||||
"contract": {
|
||||
"GetScript": "Neo.Contract.GetScript",
|
||||
|
@ -95,15 +76,6 @@ var syscalls = map[string]map[string]string{
|
|||
"Migrate": "Neo.Contract.Migrate",
|
||||
"GetStorageContext": "Neo.Contract.GetStorageContext",
|
||||
},
|
||||
"input": {
|
||||
"GetHash": "Neo.Input.GetHash",
|
||||
"GetIndex": "Neo.Input.GetIndex",
|
||||
},
|
||||
"output": {
|
||||
"GetAssetID": "Neo.Output.GetAssetID",
|
||||
"GetValue": "Neo.Output.GetValue",
|
||||
"GetScriptHash": "Neo.Output.GetScriptHash",
|
||||
},
|
||||
"engine": {
|
||||
"GetScriptContainer": "System.ExecutionEngine.GetScriptContainer",
|
||||
"GetCallingScriptHash": "System.ExecutionEngine.GetCallingScriptHash",
|
||||
|
|
|
@ -40,9 +40,6 @@ func Load(path string, netMode NetMode) (Config, error) {
|
|||
}
|
||||
|
||||
config := Config{
|
||||
ProtocolConfiguration: ProtocolConfiguration{
|
||||
SystemFee: SystemFee{},
|
||||
},
|
||||
ApplicationConfiguration: ApplicationConfiguration{
|
||||
PingInterval: 30,
|
||||
PingTimeout: 90,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
|
@ -34,23 +33,16 @@ type (
|
|||
MaxFreeTransactionsPerBlock int `yaml:"MaxFreeTransactionsPerBlock"`
|
||||
MemPoolSize int `yaml:"MemPoolSize"`
|
||||
// SaveStorageBatch enables storage batch saving before every persist.
|
||||
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
||||
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
||||
SeedList []string `yaml:"SeedList"`
|
||||
StandbyValidators []string `yaml:"StandbyValidators"`
|
||||
SystemFee SystemFee `yaml:"SystemFee"`
|
||||
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
||||
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
||||
SeedList []string `yaml:"SeedList"`
|
||||
StandbyValidators []string `yaml:"StandbyValidators"`
|
||||
// Whether to verify received blocks.
|
||||
VerifyBlocks bool `yaml:"VerifyBlocks"`
|
||||
// Whether to verify transactions in received blocks.
|
||||
VerifyTransactions bool `yaml:"VerifyTransactions"`
|
||||
}
|
||||
|
||||
// SystemFee fees related to system.
|
||||
SystemFee struct {
|
||||
IssueTransaction int64 `yaml:"IssueTransaction"`
|
||||
RegisterTransaction int64 `yaml:"RegisterTransaction"`
|
||||
}
|
||||
|
||||
// NetMode describes the mode the blockchain will operate on.
|
||||
NetMode uint32
|
||||
)
|
||||
|
@ -70,15 +62,3 @@ func (n NetMode) String() string {
|
|||
return "net unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// TryGetValue returns the system fee base on transaction type.
|
||||
func (s SystemFee) TryGetValue(txType transaction.TXType) util.Fixed8 {
|
||||
switch txType {
|
||||
case transaction.IssueType:
|
||||
return util.Fixed8FromInt64(s.IssueTransaction)
|
||||
case transaction.RegisterType:
|
||||
return util.Fixed8FromInt64(s.RegisterTransaction)
|
||||
default:
|
||||
return util.Fixed8FromInt64(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/nspcc-dev/dbft/crypto"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -44,7 +45,7 @@ func TestNeoBlock_Setters(t *testing.T) {
|
|||
b.Block.PrevHash = util.Uint256{9, 8, 7}
|
||||
require.Equal(t, util.Uint256{9, 8, 7}, b.PrevHash())
|
||||
|
||||
txx := []block.Transaction{transaction.NewIssueTX()}
|
||||
txx := []block.Transaction{transaction.New([]byte{byte(opcode.PUSH1)}, 1)}
|
||||
b.SetTransactions(txx)
|
||||
require.Equal(t, txx, b.Transactions())
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/nspcc-dev/dbft/payload"
|
||||
coreb "github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
|
@ -394,13 +393,13 @@ func (s *service) getBlock(h util.Uint256) block.Block {
|
|||
func (s *service) getVerifiedTx(count int) []block.Transaction {
|
||||
pool := s.Config.Chain.GetMemPool()
|
||||
|
||||
var txx []mempool.TxWithFee
|
||||
var txx []*transaction.Transaction
|
||||
|
||||
if s.dbft.ViewNumber > 0 {
|
||||
txx = make([]mempool.TxWithFee, 0, len(s.lastProposal))
|
||||
txx = make([]*transaction.Transaction, 0, len(s.lastProposal))
|
||||
for i := range s.lastProposal {
|
||||
if tx, fee, ok := pool.TryGetValue(s.lastProposal[i]); ok {
|
||||
txx = append(txx, mempool.TxWithFee{Tx: tx, Fee: fee})
|
||||
if tx, ok := pool.TryGetValue(s.lastProposal[i]); ok {
|
||||
txx = append(txx, tx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,7 +416,7 @@ func (s *service) getVerifiedTx(count int) []block.Transaction {
|
|||
|
||||
res := make([]block.Transaction, len(txx))
|
||||
for i := range txx {
|
||||
res[i] = txx[i].Tx
|
||||
res[i] = txx[i]
|
||||
}
|
||||
|
||||
return res
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
@ -22,7 +23,7 @@ import (
|
|||
|
||||
func TestNewService(t *testing.T) {
|
||||
srv := newTestService(t)
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.ValidUntilBlock = 1
|
||||
addSender(t, tx)
|
||||
signTx(t, srv.Chain.FeePerByte(), tx)
|
||||
|
@ -39,7 +40,7 @@ func TestService_GetVerified(t *testing.T) {
|
|||
srv := newTestService(t)
|
||||
var txs []*transaction.Transaction
|
||||
for i := 0; i < 4; i++ {
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = 123 + uint32(i)
|
||||
tx.ValidUntilBlock = 1
|
||||
txs = append(txs, tx)
|
||||
|
@ -53,7 +54,7 @@ func TestService_GetVerified(t *testing.T) {
|
|||
p := new(Payload)
|
||||
p.message = &message{}
|
||||
p.SetType(payload.PrepareRequestType)
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = 999
|
||||
p.SetPayload(&prepareRequest{transactionHashes: hashes})
|
||||
p.SetValidatorIndex(1)
|
||||
|
@ -120,7 +121,7 @@ func TestService_getTx(t *testing.T) {
|
|||
srv := newTestService(t)
|
||||
|
||||
t.Run("transaction in mempool", func(t *testing.T) {
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = 1234
|
||||
tx.ValidUntilBlock = 1
|
||||
addSender(t, tx)
|
||||
|
@ -137,7 +138,7 @@ func TestService_getTx(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("transaction in local cache", func(t *testing.T) {
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = 4321
|
||||
tx.ValidUntilBlock = 1
|
||||
h := tx.Hash()
|
||||
|
|
|
@ -160,6 +160,9 @@ func (b *Block) DecodeBinary(br *io.BinReader) {
|
|||
txes[i] = tx
|
||||
}
|
||||
b.Transactions = txes
|
||||
if br.Err != nil {
|
||||
return
|
||||
}
|
||||
br.Err = b.Verify()
|
||||
}
|
||||
|
||||
|
@ -227,5 +230,8 @@ func (b *Block) UnmarshalJSON(data []byte) error {
|
|||
b.Base = *base
|
||||
b.Transactions = auxb.Transactions
|
||||
b.ConsensusData = auxb.ConsensusData
|
||||
// Some tests rely on hash presence and we're usually precomputing
|
||||
// other hashes upon deserialization.
|
||||
_ = b.ConsensusData.Hash()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -84,7 +85,7 @@ func newDumbBlock() *Block {
|
|||
Nonce: 1111,
|
||||
},
|
||||
Transactions: []*transaction.Transaction{
|
||||
transaction.NewIssueTX(),
|
||||
transaction.New([]byte{byte(opcode.PUSH1)}, 0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package core
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
|
@ -33,13 +32,6 @@ const (
|
|||
headerBatchCount = 2000
|
||||
version = "0.1.0"
|
||||
|
||||
// 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
|
||||
// like 2000000 is coming from the decrementInterval, but C# code doesn't
|
||||
// contain any relationship between the two, so we should follow this
|
||||
// behavior.
|
||||
registeredAssetLifetime = 2 * 2000000
|
||||
|
||||
defaultMemPoolSize = 50000
|
||||
)
|
||||
|
||||
|
@ -370,20 +362,18 @@ func (bc *Blockchain) notificationDispatcher() {
|
|||
if len(txFeed) != 0 || len(notificationFeed) != 0 || len(executionFeed) != 0 {
|
||||
var aerIdx int
|
||||
for _, tx := range event.block.Transactions {
|
||||
if tx.Type == transaction.InvocationType {
|
||||
aer := event.appExecResults[aerIdx]
|
||||
if !aer.TxHash.Equals(tx.Hash()) {
|
||||
panic("inconsistent application execution results")
|
||||
}
|
||||
aerIdx++
|
||||
for ch := range executionFeed {
|
||||
ch <- aer
|
||||
}
|
||||
if aer.VMState == "HALT" {
|
||||
for i := range aer.Events {
|
||||
for ch := range notificationFeed {
|
||||
ch <- &aer.Events[i]
|
||||
}
|
||||
aer := event.appExecResults[aerIdx]
|
||||
if !aer.TxHash.Equals(tx.Hash()) {
|
||||
panic("inconsistent application execution results")
|
||||
}
|
||||
aerIdx++
|
||||
for ch := range executionFeed {
|
||||
ch <- aer
|
||||
}
|
||||
if aer.VMState == "HALT" {
|
||||
for i := range aer.Events {
|
||||
for ch := range notificationFeed {
|
||||
ch <- &aer.Events[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -536,7 +526,6 @@ func (bc *Blockchain) processHeader(h *block.Header, batch storage.Batch, header
|
|||
}
|
||||
|
||||
buf.Reset()
|
||||
buf.BinWriter.WriteU32LE(0) // sys fee is yet to be calculated
|
||||
h.EncodeBinary(buf.BinWriter)
|
||||
if buf.Err != nil {
|
||||
return buf.Err
|
||||
|
@ -549,13 +538,6 @@ func (bc *Blockchain) processHeader(h *block.Header, batch storage.Batch, header
|
|||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
_, sf, _ := bc.dao.GetBlock(h)
|
||||
return sf
|
||||
}
|
||||
|
||||
// TODO: storeBlock needs some more love, its implemented as in the original
|
||||
// project. This for the sake of development speed and understanding of what
|
||||
// is happening here, quite allot as you can see :). If things are wired together
|
||||
|
@ -563,11 +545,7 @@ func (bc *Blockchain) getSystemFeeAmount(h util.Uint256) uint32 {
|
|||
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)
|
||||
for _, tx := range block.Transactions {
|
||||
fee += uint32(tx.SystemFee.IntegralValue())
|
||||
}
|
||||
if err := cache.StoreAsBlock(block, fee); err != nil {
|
||||
if err := cache.StoreAsBlock(block); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -580,226 +558,75 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := cache.PutUnspentCoinState(tx.Hash(), state.NewUnspentCoin(block.Index, tx)); err != nil {
|
||||
return err
|
||||
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
|
||||
v := SpawnVM(systemInterop)
|
||||
v.LoadScript(tx.Script)
|
||||
v.SetPriceGetter(getPrice)
|
||||
if bc.config.FreeGasLimit > 0 {
|
||||
v.SetGasLimit(bc.config.FreeGasLimit + tx.SystemFee)
|
||||
}
|
||||
|
||||
// Process TX outputs.
|
||||
if err := processOutputs(tx, cache); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process TX inputs that are grouped by previous hash.
|
||||
for _, inputs := range transaction.GroupInputsByPrevHash(tx.Inputs) {
|
||||
prevHash := inputs[0].PrevHash
|
||||
unspent, err := cache.GetUnspentCoinState(prevHash)
|
||||
err := v.Run()
|
||||
if !v.HasFailed() {
|
||||
_, err := systemInterop.DAO.Persist()
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to persist invocation results")
|
||||
}
|
||||
for _, input := range inputs {
|
||||
if len(unspent.States) <= int(input.PrevIndex) {
|
||||
return fmt.Errorf("bad input: %s/%d", input.PrevHash.StringLE(), input.PrevIndex)
|
||||
for _, note := range systemInterop.Notifications {
|
||||
arr, ok := note.Item.Value().([]vm.StackItem)
|
||||
if !ok || len(arr) != 4 {
|
||||
continue
|
||||
}
|
||||
if unspent.States[input.PrevIndex].State&state.CoinSpent != 0 {
|
||||
return fmt.Errorf("double spend: %s/%d", input.PrevHash.StringLE(), input.PrevIndex)
|
||||
op, ok := arr[0].Value().([]byte)
|
||||
if !ok || (string(op) != "transfer" && string(op) != "Transfer") {
|
||||
continue
|
||||
}
|
||||
unspent.States[input.PrevIndex].State |= state.CoinSpent
|
||||
unspent.States[input.PrevIndex].SpendHeight = block.Index
|
||||
prevTXOutput := &unspent.States[input.PrevIndex].Output
|
||||
account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if prevTXOutput.AssetID.Equals(GoverningTokenID()) {
|
||||
err = account.Unclaimed.Put(&state.UnclaimedBalance{
|
||||
Tx: input.PrevHash,
|
||||
Index: input.PrevIndex,
|
||||
Start: unspent.Height,
|
||||
End: block.Index,
|
||||
Value: prevTXOutput.Amount,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
balancesLen := len(account.Balances[prevTXOutput.AssetID])
|
||||
if balancesLen <= 1 {
|
||||
delete(account.Balances, prevTXOutput.AssetID)
|
||||
} else {
|
||||
var index = -1
|
||||
for i, balance := range account.Balances[prevTXOutput.AssetID] {
|
||||
if balance.Tx.Equals(input.PrevHash) && balance.Index == input.PrevIndex {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index >= 0 {
|
||||
last := balancesLen - 1
|
||||
if last > index {
|
||||
account.Balances[prevTXOutput.AssetID][index] = account.Balances[prevTXOutput.AssetID][last]
|
||||
}
|
||||
account.Balances[prevTXOutput.AssetID] = account.Balances[prevTXOutput.AssetID][:last]
|
||||
}
|
||||
}
|
||||
if err = cache.PutAccountState(account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = cache.PutUnspentCoinState(prevHash, unspent); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Process the underlying type of the TX.
|
||||
switch t := tx.Data.(type) {
|
||||
case *transaction.RegisterTX:
|
||||
err := cache.PutAssetState(&state.Asset{
|
||||
ID: tx.Hash(),
|
||||
AssetType: t.AssetType,
|
||||
Name: t.Name,
|
||||
Amount: t.Amount,
|
||||
Precision: t.Precision,
|
||||
Owner: t.Owner,
|
||||
Admin: t.Admin,
|
||||
Expiration: bc.BlockHeight() + registeredAssetLifetime,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case *transaction.IssueTX:
|
||||
for _, res := range bc.GetTransactionResults(tx) {
|
||||
if res.Amount < 0 {
|
||||
asset, err := cache.GetAssetState(res.AssetID)
|
||||
if asset == nil || err != nil {
|
||||
return fmt.Errorf("issue failed: no asset %s or error %s", res.AssetID, err)
|
||||
}
|
||||
asset.Available -= res.Amount
|
||||
if err := cache.PutAssetState(asset); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case *transaction.ClaimTX:
|
||||
// Remove claimed NEO from spent coins making it unavalaible for
|
||||
// additional claims.
|
||||
for _, input := range t.Claims {
|
||||
scs, err := cache.GetUnspentCoinState(input.PrevHash)
|
||||
if err == nil {
|
||||
if len(scs.States) <= int(input.PrevIndex) {
|
||||
err = errors.New("invalid claim index")
|
||||
} else if scs.States[input.PrevIndex].State&state.CoinClaimed != 0 {
|
||||
err = errors.New("double claim")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// We can't really do anything about it
|
||||
// as it's a transaction in a signed block.
|
||||
bc.log.Warn("FALSE OR DOUBLE CLAIM",
|
||||
zap.String("PrevHash", input.PrevHash.StringLE()),
|
||||
zap.Uint16("PrevIndex", input.PrevIndex),
|
||||
zap.String("tx", tx.Hash().StringLE()),
|
||||
zap.Uint32("block", block.Index),
|
||||
)
|
||||
// "Strict" mode.
|
||||
if bc.config.VerifyTransactions {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
acc, err := cache.GetAccountState(scs.States[input.PrevIndex].ScriptHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scs.States[input.PrevIndex].State |= state.CoinClaimed
|
||||
if err = cache.PutUnspentCoinState(input.PrevHash, scs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changed := acc.Unclaimed.Remove(input.PrevHash, input.PrevIndex)
|
||||
if !changed {
|
||||
bc.log.Warn("no spent coin in the account",
|
||||
zap.String("tx", tx.Hash().StringLE()),
|
||||
zap.String("input", input.PrevHash.StringLE()),
|
||||
zap.String("account", acc.ScriptHash.String()))
|
||||
} else if err := cache.PutAccountState(acc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *transaction.InvocationTX:
|
||||
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
|
||||
v := SpawnVM(systemInterop)
|
||||
v.LoadScript(t.Script)
|
||||
v.SetPriceGetter(getPrice)
|
||||
if bc.config.FreeGasLimit > 0 {
|
||||
v.SetGasLimit(bc.config.FreeGasLimit + t.Gas)
|
||||
}
|
||||
|
||||
err := v.Run()
|
||||
if !v.HasFailed() {
|
||||
_, err := systemInterop.DAO.Persist()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to persist invocation results")
|
||||
}
|
||||
for _, note := range systemInterop.Notifications {
|
||||
arr, ok := note.Item.Value().([]vm.StackItem)
|
||||
if !ok || len(arr) != 4 {
|
||||
continue
|
||||
}
|
||||
op, ok := arr[0].Value().([]byte)
|
||||
if !ok || (string(op) != "transfer" && string(op) != "Transfer") {
|
||||
continue
|
||||
}
|
||||
var from []byte
|
||||
fromValue := arr[1].Value()
|
||||
// we don't have `from` set when we are minting tokens
|
||||
if fromValue != nil {
|
||||
from, ok = fromValue.([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
var to []byte
|
||||
toValue := arr[2].Value()
|
||||
// we don't have `to` set when we are burning tokens
|
||||
if toValue != nil {
|
||||
to, ok = toValue.([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
amount, ok := arr[3].Value().(*big.Int)
|
||||
var from []byte
|
||||
fromValue := arr[1].Value()
|
||||
// we don't have `from` set when we are minting tokens
|
||||
if fromValue != nil {
|
||||
from, ok = fromValue.([]byte)
|
||||
if !ok {
|
||||
bs, ok := arr[3].Value().([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
amount = emit.BytesToInt(bs)
|
||||
continue
|
||||
}
|
||||
bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64())
|
||||
}
|
||||
} else {
|
||||
bc.log.Warn("contract invocation failed",
|
||||
zap.String("tx", tx.Hash().StringLE()),
|
||||
zap.Uint32("block", block.Index),
|
||||
zap.Error(err))
|
||||
}
|
||||
aer := &state.AppExecResult{
|
||||
TxHash: tx.Hash(),
|
||||
Trigger: trigger.Application,
|
||||
VMState: v.State(),
|
||||
GasConsumed: v.GasConsumed(),
|
||||
Stack: v.Estack().ToContractParameters(),
|
||||
Events: systemInterop.Notifications,
|
||||
}
|
||||
appExecResults = append(appExecResults, aer)
|
||||
err = cache.PutAppExecResult(aer)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to Store notifications")
|
||||
var to []byte
|
||||
toValue := arr[2].Value()
|
||||
// we don't have `to` set when we are burning tokens
|
||||
if toValue != nil {
|
||||
to, ok = toValue.([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
amount, ok := arr[3].Value().(*big.Int)
|
||||
if !ok {
|
||||
bs, ok := arr[3].Value().([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
amount = emit.BytesToInt(bs)
|
||||
}
|
||||
bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64())
|
||||
}
|
||||
} else {
|
||||
bc.log.Warn("contract invocation failed",
|
||||
zap.String("tx", tx.Hash().StringLE()),
|
||||
zap.Uint32("block", block.Index),
|
||||
zap.Error(err))
|
||||
}
|
||||
aer := &state.AppExecResult{
|
||||
TxHash: tx.Hash(),
|
||||
Trigger: trigger.Application,
|
||||
VMState: v.State(),
|
||||
GasConsumed: v.GasConsumed(),
|
||||
Stack: v.Estack().ToContractParameters(),
|
||||
Events: systemInterop.Notifications,
|
||||
}
|
||||
appExecResults = append(appExecResults, aer)
|
||||
err = cache.PutAppExecResult(aer)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to Store notifications")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -927,7 +754,22 @@ func (bc *Blockchain) GetNEP5Balances(acc util.Uint160) *state.NEP5Balances {
|
|||
|
||||
// GetUtilityTokenBalance returns utility token (GAS) balance for the acc.
|
||||
func (bc *Blockchain) GetUtilityTokenBalance(acc util.Uint160) util.Fixed8 {
|
||||
return util.Fixed8FromInt64(bc.GetNEP5Balances(acc).Trackers[bc.contracts.GAS.Hash].Balance)
|
||||
bs, err := bc.dao.GetNEP5Balances(acc)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return util.Fixed8(bs.Trackers[bc.contracts.GAS.Hash].Balance)
|
||||
}
|
||||
|
||||
// GetGoverningTokenBalance returns governing token (NEO) balance and the height
|
||||
// of the last balance change for the account.
|
||||
func (bc *Blockchain) GetGoverningTokenBalance(acc util.Uint160) (util.Fixed8, uint32) {
|
||||
bs, err := bc.dao.GetNEP5Balances(acc)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
neo := bs.Trackers[bc.contracts.NEO.Hash]
|
||||
return util.Fixed8(neo.Balance), neo.LastUpdatedBlock
|
||||
}
|
||||
|
||||
// LastBatch returns last persisted storage batch.
|
||||
|
@ -935,25 +777,6 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
|
|||
return bc.lastBatch
|
||||
}
|
||||
|
||||
// processOutputs processes transaction outputs.
|
||||
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
||||
for index, output := range tx.Outputs {
|
||||
account, err := dao.GetAccountStateOrNew(output.ScriptHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account.Balances[output.AssetID] = append(account.Balances[output.AssetID], state.UnspentBalance{
|
||||
Tx: tx.Hash(),
|
||||
Index: uint16(index),
|
||||
Value: output.Amount,
|
||||
})
|
||||
if err = dao.PutAccountState(account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// persist flushes current in-memory Store contents to the persistent storage.
|
||||
func (bc *Blockchain) persist() error {
|
||||
var (
|
||||
|
@ -1002,7 +825,7 @@ func (bc *Blockchain) headerListLen() (n int) {
|
|||
|
||||
// GetTransaction returns a TX and its height by the given hash.
|
||||
func (bc *Blockchain) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) {
|
||||
if tx, _, ok := bc.memPool.TryGetValue(hash); ok {
|
||||
if tx, ok := bc.memPool.TryGetValue(hash); ok {
|
||||
return tx, 0, nil // the height is not actually defined for memPool transaction. Not sure if zero is a good number in this case.
|
||||
}
|
||||
return bc.dao.GetTransaction(hash)
|
||||
|
@ -1033,7 +856,7 @@ func (bc *Blockchain) GetBlock(hash util.Uint256) (*block.Block, error) {
|
|||
}
|
||||
}
|
||||
|
||||
block, _, err := bc.dao.GetBlock(hash)
|
||||
block, err := bc.dao.GetBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1055,7 +878,7 @@ func (bc *Blockchain) GetHeader(hash util.Uint256) (*block.Header, error) {
|
|||
return tb.Header(), nil
|
||||
}
|
||||
}
|
||||
block, _, err := bc.dao.GetBlock(hash)
|
||||
block, err := bc.dao.GetBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1115,17 +938,6 @@ func (bc *Blockchain) HeaderHeight() uint32 {
|
|||
return uint32(bc.headerListLen() - 1)
|
||||
}
|
||||
|
||||
// GetAssetState returns asset state from its assetID.
|
||||
func (bc *Blockchain) GetAssetState(assetID util.Uint256) *state.Asset {
|
||||
asset, err := bc.dao.GetAssetState(assetID)
|
||||
if asset == nil && err != storage.ErrKeyNotFound {
|
||||
bc.log.Warn("failed to get asset state",
|
||||
zap.Stringer("asset", assetID),
|
||||
zap.Error(err))
|
||||
}
|
||||
return asset
|
||||
}
|
||||
|
||||
// GetContractState returns contract by its script hash.
|
||||
func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
||||
contract, err := bc.dao.GetContractState(hash)
|
||||
|
@ -1144,15 +956,6 @@ func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account {
|
|||
return as
|
||||
}
|
||||
|
||||
// GetUnspentCoinState returns unspent coin state for given tx hash.
|
||||
func (bc *Blockchain) GetUnspentCoinState(hash util.Uint256) *state.UnspentCoin {
|
||||
ucs, err := bc.dao.GetUnspentCoinState(hash)
|
||||
if ucs == nil && err != storage.ErrKeyNotFound {
|
||||
bc.log.Warn("failed to get unspent coin state", zap.Error(err))
|
||||
}
|
||||
return ucs
|
||||
}
|
||||
|
||||
// GetConfig returns the config stored in the blockchain.
|
||||
func (bc *Blockchain) GetConfig() config.ProtocolConfiguration {
|
||||
return bc.config
|
||||
|
@ -1220,10 +1023,11 @@ func (bc *Blockchain) UnsubscribeFromExecutions(ch chan<- *state.AppExecResult)
|
|||
bc.unsubCh <- ch
|
||||
}
|
||||
|
||||
// CalculateClaimable calculates the amount of GAS which can be claimed for a transaction with value.
|
||||
// First return value is GAS generated between startHeight and endHeight.
|
||||
// Second return value is GAS returned from accumulated SystemFees between startHeight and endHeight.
|
||||
func (bc *Blockchain) CalculateClaimable(value util.Fixed8, startHeight, endHeight uint32) (util.Fixed8, util.Fixed8, error) {
|
||||
// CalculateClaimable calculates the amount of GAS generated by owning specified
|
||||
// amount of NEO between specified blocks. The amount of NEO being passed is in
|
||||
// its natural non-divisible form (1 NEO as 1, 2 NEO as 2, no multiplication by
|
||||
// 10⁸ is neeeded as for Fixed8).
|
||||
func (bc *Blockchain) CalculateClaimable(value int64, startHeight, endHeight uint32) util.Fixed8 {
|
||||
var amount util.Fixed8
|
||||
di := uint32(bc.decrementInterval)
|
||||
|
||||
|
@ -1249,47 +1053,7 @@ func (bc *Blockchain) CalculateClaimable(value util.Fixed8, startHeight, endHeig
|
|||
amount += util.Fixed8(iend-istart) * util.Fixed8(bc.generationAmount[ustart])
|
||||
}
|
||||
|
||||
if startHeight == 0 {
|
||||
startHeight++
|
||||
}
|
||||
h := bc.GetHeaderHash(int(startHeight - 1))
|
||||
feeStart := bc.getSystemFeeAmount(h)
|
||||
h = bc.GetHeaderHash(int(endHeight - 1))
|
||||
feeEnd := bc.getSystemFeeAmount(h)
|
||||
|
||||
sysFeeTotal := util.Fixed8(feeEnd - feeStart)
|
||||
ratio := value / 100000000
|
||||
return amount * ratio, sysFeeTotal * ratio, nil
|
||||
}
|
||||
|
||||
// References maps transaction's inputs into a slice of InOuts, effectively
|
||||
// joining each Input with the corresponding Output.
|
||||
// @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) ([]transaction.InOut, error) {
|
||||
return bc.references(t.Inputs)
|
||||
}
|
||||
|
||||
// references is an internal implementation of References that operates directly
|
||||
// on a slice of Input.
|
||||
func (bc *Blockchain) references(ins []transaction.Input) ([]transaction.InOut, error) {
|
||||
references := make([]transaction.InOut, 0, len(ins))
|
||||
|
||||
for _, inputs := range transaction.GroupInputsByPrevHash(ins) {
|
||||
prevHash := inputs[0].PrevHash
|
||||
unspent, err := bc.dao.GetUnspentCoinState(prevHash)
|
||||
if err != nil {
|
||||
return nil, errors.New("bad input reference")
|
||||
}
|
||||
for _, in := range inputs {
|
||||
if int(in.PrevIndex) > len(unspent.States)-1 {
|
||||
return nil, errors.New("bad input reference")
|
||||
}
|
||||
references = append(references, transaction.InOut{In: *in, Out: unspent.States[in.PrevIndex].Output})
|
||||
}
|
||||
}
|
||||
return references, nil
|
||||
return amount * util.Fixed8(value)
|
||||
}
|
||||
|
||||
// FeePerByte returns transaction network fee per byte.
|
||||
|
@ -1311,14 +1075,14 @@ func (bc *Blockchain) GetMemPool() *mempool.Pool {
|
|||
|
||||
// ApplyPolicyToTxSet applies configured policies to given transaction set. It
|
||||
// expects slice to be ordered by fee and returns a subslice of it.
|
||||
func (bc *Blockchain) ApplyPolicyToTxSet(txes []mempool.TxWithFee) []mempool.TxWithFee {
|
||||
func (bc *Blockchain) ApplyPolicyToTxSet(txes []*transaction.Transaction) []*transaction.Transaction {
|
||||
if bc.config.MaxTransactionsPerBlock != 0 && len(txes) > bc.config.MaxTransactionsPerBlock {
|
||||
txes = txes[:bc.config.MaxTransactionsPerBlock]
|
||||
}
|
||||
maxFree := bc.config.MaxFreeTransactionsPerBlock
|
||||
if maxFree != 0 {
|
||||
lowStart := sort.Search(len(txes), func(i int) bool {
|
||||
return bc.IsLowPriority(txes[i].Fee)
|
||||
return bc.IsLowPriority(txes[i].NetworkFee)
|
||||
})
|
||||
if lowStart+maxFree < len(txes) {
|
||||
txes = txes[:lowStart+maxFree]
|
||||
|
@ -1360,28 +1124,11 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
|||
if netFee < 0 {
|
||||
return errors.Errorf("insufficient funds: net fee is %v, need %v", t.NetworkFee, needNetworkFee)
|
||||
}
|
||||
if transaction.HaveDuplicateInputs(t.Inputs) {
|
||||
return errors.New("invalid transaction's inputs")
|
||||
}
|
||||
if block == nil {
|
||||
if ok := bc.memPool.Verify(t, bc); !ok {
|
||||
return errors.New("invalid transaction due to conflicts with the memory pool")
|
||||
}
|
||||
}
|
||||
if bc.dao.IsDoubleSpend(t) {
|
||||
return errors.New("invalid transaction caused by double spending")
|
||||
}
|
||||
if err := bc.verifyOutputs(t); err != nil {
|
||||
return errors.Wrap(err, "wrong outputs")
|
||||
}
|
||||
refs, err := bc.References(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results := refsAndOutsToResults(refs, t.Outputs)
|
||||
if err := bc.verifyResults(t, results); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, a := range t.Attributes {
|
||||
if a.Usage == transaction.ECDH02 || a.Usage == transaction.ECDH03 {
|
||||
|
@ -1389,96 +1136,9 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
|||
}
|
||||
}
|
||||
|
||||
switch t.Type {
|
||||
case transaction.ClaimType:
|
||||
claim := t.Data.(*transaction.ClaimTX)
|
||||
if transaction.HaveDuplicateInputs(claim.Claims) {
|
||||
return errors.New("duplicate claims")
|
||||
}
|
||||
if bc.dao.IsDoubleClaim(claim) {
|
||||
return errors.New("double claim")
|
||||
}
|
||||
if err := bc.verifyClaims(t, results); err != nil {
|
||||
return err
|
||||
}
|
||||
case transaction.InvocationType:
|
||||
inv := t.Data.(*transaction.InvocationTX)
|
||||
if inv.Gas.FractionalValue() != 0 {
|
||||
return errors.New("invocation gas can only be integer")
|
||||
}
|
||||
}
|
||||
|
||||
return bc.verifyTxWitnesses(t, block)
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyClaims(tx *transaction.Transaction, results []*transaction.Result) (err error) {
|
||||
t := tx.Data.(*transaction.ClaimTX)
|
||||
var result *transaction.Result
|
||||
for i := range results {
|
||||
if results[i].AssetID == UtilityTokenID() {
|
||||
result = results[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if result == nil || result.Amount.GreaterThan(0) {
|
||||
return errors.New("invalid output in claim tx")
|
||||
}
|
||||
|
||||
bonus, err := bc.calculateBonus(t.Claims)
|
||||
if err == nil && bonus != -result.Amount {
|
||||
return fmt.Errorf("wrong bonus calculated in claim tx: %s != %s",
|
||||
bonus.String(), (-result.Amount).String())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (bc *Blockchain) calculateBonus(claims []transaction.Input) (util.Fixed8, error) {
|
||||
unclaimed := []*spentCoin{}
|
||||
inputs := transaction.GroupInputsByPrevHash(claims)
|
||||
|
||||
for _, group := range inputs {
|
||||
h := group[0].PrevHash
|
||||
unspent, err := bc.dao.GetUnspentCoinState(h)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, c := range group {
|
||||
if len(unspent.States) <= int(c.PrevIndex) {
|
||||
return 0, fmt.Errorf("can't find spent coins for %s (%d)", c.PrevHash.StringLE(), c.PrevIndex)
|
||||
}
|
||||
if unspent.States[c.PrevIndex].State&state.CoinSpent == 0 {
|
||||
return 0, fmt.Errorf("not spent yet: %s/%d", c.PrevHash.StringLE(), c.PrevIndex)
|
||||
}
|
||||
if unspent.States[c.PrevIndex].State&state.CoinClaimed != 0 {
|
||||
return 0, fmt.Errorf("already claimed: %s/%d", c.PrevHash.StringLE(), c.PrevIndex)
|
||||
}
|
||||
unclaimed = append(unclaimed, &spentCoin{
|
||||
Output: &unspent.States[c.PrevIndex].Output,
|
||||
StartHeight: unspent.Height,
|
||||
EndHeight: unspent.States[c.PrevIndex].SpendHeight,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return bc.calculateBonusInternal(unclaimed)
|
||||
}
|
||||
|
||||
func (bc *Blockchain) calculateBonusInternal(scs []*spentCoin) (util.Fixed8, error) {
|
||||
var claimed util.Fixed8
|
||||
for _, sc := range scs {
|
||||
gen, sys, err := bc.CalculateClaimable(sc.Output.Amount, sc.StartHeight, sc.EndHeight)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
claimed += gen + sys
|
||||
}
|
||||
|
||||
return claimed, nil
|
||||
}
|
||||
|
||||
// isTxStillRelevant is a callback for mempool transaction filtering after the
|
||||
// new block addition. It returns false for transactions already present in the
|
||||
// chain (added by the new block), transactions using some inputs that are
|
||||
|
@ -1492,15 +1152,6 @@ func (bc *Blockchain) isTxStillRelevant(t *transaction.Transaction) bool {
|
|||
if bc.dao.HasTransaction(t.Hash()) {
|
||||
return false
|
||||
}
|
||||
if bc.dao.IsDoubleSpend(t) {
|
||||
return false
|
||||
}
|
||||
if t.Type == transaction.ClaimType {
|
||||
claim := t.Data.(*transaction.ClaimTX)
|
||||
if bc.dao.IsDoubleClaim(claim) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := range t.Scripts {
|
||||
if !vm.IsStandardContract(t.Scripts[i].VerificationScript) {
|
||||
recheckWitness = true
|
||||
|
@ -1536,14 +1187,12 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
|
|||
return err
|
||||
}
|
||||
// Policying.
|
||||
if t.Type != transaction.ClaimType {
|
||||
txSize := io.GetVarSize(t)
|
||||
maxFree := bc.config.MaxFreeTransactionSize
|
||||
if maxFree != 0 && txSize > maxFree {
|
||||
if bc.IsLowPriority(t.NetworkFee) ||
|
||||
t.NetworkFee < util.Fixed8FromFloat(bc.config.FeePerExtraByte)*util.Fixed8(txSize-maxFree) {
|
||||
return ErrPolicy
|
||||
}
|
||||
txSize := io.GetVarSize(t)
|
||||
maxFree := bc.config.MaxFreeTransactionSize
|
||||
if maxFree != 0 && txSize > maxFree {
|
||||
if bc.IsLowPriority(t.NetworkFee) ||
|
||||
t.NetworkFee < util.Fixed8FromFloat(bc.config.FeePerExtraByte)*util.Fixed8(txSize-maxFree) {
|
||||
return ErrPolicy
|
||||
}
|
||||
}
|
||||
if err := bc.memPool.Add(t, bc); err != nil {
|
||||
|
@ -1559,125 +1208,6 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyOutputs(t *transaction.Transaction) error {
|
||||
for assetID, outputs := range t.GroupOutputByAssetID() {
|
||||
assetState := bc.GetAssetState(assetID)
|
||||
if assetState == nil {
|
||||
return fmt.Errorf("no asset state for %s", assetID.StringLE())
|
||||
}
|
||||
|
||||
if assetState.Expiration < bc.blockHeight+1 && assetState.AssetType != transaction.GoverningToken && assetState.AssetType != transaction.UtilityToken {
|
||||
return fmt.Errorf("asset %s expired", assetID.StringLE())
|
||||
}
|
||||
|
||||
for _, out := range outputs {
|
||||
if int64(out.Amount)%int64(math.Pow10(8-int(assetState.Precision))) != 0 {
|
||||
return fmt.Errorf("output is not compliant with %s asset precision", assetID.StringLE())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *Blockchain) verifyResults(t *transaction.Transaction, results []*transaction.Result) error {
|
||||
var resultsDestroy []*transaction.Result
|
||||
var resultsIssue []*transaction.Result
|
||||
for _, re := range results {
|
||||
if re.Amount.GreaterThan(util.Fixed8(0)) {
|
||||
resultsDestroy = append(resultsDestroy, re)
|
||||
}
|
||||
|
||||
if re.Amount.LessThan(util.Fixed8(0)) {
|
||||
resultsIssue = append(resultsIssue, re)
|
||||
}
|
||||
}
|
||||
if len(resultsDestroy) > 1 {
|
||||
return errors.New("tx has more than 1 destroy output")
|
||||
}
|
||||
if len(resultsDestroy) == 1 && resultsDestroy[0].AssetID != UtilityTokenID() {
|
||||
return errors.New("tx destroys non-utility token")
|
||||
}
|
||||
sysfee := t.SystemFee
|
||||
if sysfee.GreaterThan(util.Fixed8(0)) {
|
||||
if len(resultsDestroy) == 0 {
|
||||
return fmt.Errorf("system requires to pay %s fee, but tx pays nothing", sysfee.String())
|
||||
}
|
||||
if resultsDestroy[0].Amount.LessThan(sysfee) {
|
||||
return fmt.Errorf("system requires to pay %s fee, but tx pays %s only", sysfee.String(), resultsDestroy[0].Amount.String())
|
||||
}
|
||||
}
|
||||
|
||||
switch t.Type {
|
||||
case transaction.ClaimType:
|
||||
for _, r := range resultsIssue {
|
||||
if r.AssetID != UtilityTokenID() {
|
||||
return errors.New("miner or claim tx issues non-utility tokens")
|
||||
}
|
||||
}
|
||||
break
|
||||
case transaction.IssueType:
|
||||
for _, r := range resultsIssue {
|
||||
if r.AssetID == UtilityTokenID() {
|
||||
return errors.New("issue tx issues utility tokens")
|
||||
}
|
||||
asset, err := bc.dao.GetAssetState(r.AssetID)
|
||||
if asset == nil || err != nil {
|
||||
return errors.New("invalid asset in issue tx")
|
||||
}
|
||||
if asset.Available < r.Amount {
|
||||
return errors.New("trying to issue more than available")
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
if len(resultsIssue) > 0 {
|
||||
return errors.New("non issue/miner/claim tx issues tokens")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTransactionResults returns the transaction results aggregate by assetID.
|
||||
// Golang of GetTransationResults method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L207)
|
||||
func (bc *Blockchain) GetTransactionResults(t *transaction.Transaction) []*transaction.Result {
|
||||
references, err := bc.References(t)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return refsAndOutsToResults(references, t.Outputs)
|
||||
}
|
||||
|
||||
// mapReferencesToResults returns cumulative results of transaction based in its
|
||||
// references and outputs.
|
||||
func refsAndOutsToResults(references []transaction.InOut, outputs []transaction.Output) []*transaction.Result {
|
||||
var results []*transaction.Result
|
||||
tempResult := make(map[util.Uint256]util.Fixed8)
|
||||
|
||||
for _, inout := range references {
|
||||
c := tempResult[inout.Out.AssetID]
|
||||
tempResult[inout.Out.AssetID] = c.Add(inout.Out.Amount)
|
||||
}
|
||||
for _, output := range outputs {
|
||||
c := tempResult[output.AssetID]
|
||||
tempResult[output.AssetID] = c.Sub(output.Amount)
|
||||
}
|
||||
|
||||
results = []*transaction.Result{} // this assignment is necessary. (Most of the time amount == 0 and results is the empty slice.)
|
||||
for assetID, amount := range tempResult {
|
||||
if amount != util.Fixed8(0) {
|
||||
results = append(results, &transaction.Result{
|
||||
AssetID: assetID,
|
||||
Amount: amount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
//GetStandByValidators returns validators from the configuration.
|
||||
func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) {
|
||||
return getValidators(bc.config)
|
||||
|
@ -1697,57 +1227,11 @@ func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
|
|||
// to verify whether the transaction is bonafide or not.
|
||||
// Golang implementation of GetScriptHashesForVerifying method in C# (https://github.com/neo-project/neo/blob/master/neo/Network/P2P/Payloads/Transaction.cs#L190)
|
||||
func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([]util.Uint160, error) {
|
||||
references, err := bc.References(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashes := make(map[util.Uint160]bool)
|
||||
for i := range references {
|
||||
hashes[references[i].Out.ScriptHash] = true
|
||||
}
|
||||
|
||||
for a, outputs := range t.GroupOutputByAssetID() {
|
||||
as := bc.GetAssetState(a)
|
||||
if as == nil {
|
||||
return nil, errors.New("Invalid operation")
|
||||
}
|
||||
if as.AssetType&transaction.DutyFlag != 0 {
|
||||
for _, o := range outputs {
|
||||
h := o.ScriptHash
|
||||
if _, ok := hashes[h]; !ok {
|
||||
hashes[h] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hashes[t.Sender] = true
|
||||
for _, c := range t.Cosigners {
|
||||
hashes[c.Account] = true
|
||||
}
|
||||
switch t.Type {
|
||||
case transaction.ClaimType:
|
||||
claim := t.Data.(*transaction.ClaimTX)
|
||||
refs, err := bc.references(claim.Claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range refs {
|
||||
hashes[refs[i].Out.ScriptHash] = true
|
||||
}
|
||||
case transaction.IssueType:
|
||||
for _, res := range refsAndOutsToResults(references, t.Outputs) {
|
||||
if res.Amount < 0 {
|
||||
asset, err := bc.dao.GetAssetState(res.AssetID)
|
||||
if asset == nil || err != nil {
|
||||
return nil, errors.New("invalid asset in issue tx")
|
||||
}
|
||||
hashes[asset.Issuer] = true
|
||||
}
|
||||
}
|
||||
case transaction.RegisterType:
|
||||
reg := t.Data.(*transaction.RegisterTX)
|
||||
hashes[reg.Owner.GetScriptHash()] = true
|
||||
}
|
||||
// convert hashes to []util.Uint160
|
||||
hashesResult := make([]util.Uint160, 0, len(hashes))
|
||||
for h := range hashes {
|
||||
|
@ -1830,7 +1314,6 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa
|
|||
// is used for easy interop access and can be omitted for transactions that are
|
||||
// not yet added into any block.
|
||||
// Golang implementation of VerifyWitnesses method in C# (https://github.com/neo-project/neo/blob/master/neo/SmartContract/Helper.cs#L87).
|
||||
// Unfortunately the IVerifiable interface could not be implemented because we can't move the References method in blockchain.go to the transaction.go file.
|
||||
func (bc *Blockchain) verifyTxWitnesses(t *transaction.Transaction, block *block.Block) error {
|
||||
hashes, err := bc.GetScriptHashesForVerifying(t)
|
||||
if err != nil {
|
||||
|
|
|
@ -102,7 +102,7 @@ func TestScriptFromWitness(t *testing.T) {
|
|||
|
||||
func TestGetHeader(t *testing.T) {
|
||||
bc := newTestChain(t)
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||
assert.Nil(t, addSender(tx))
|
||||
assert.Nil(t, signTx(bc, tx))
|
||||
|
@ -194,31 +194,23 @@ func TestGetClaimable(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
t.Run("first generation period", func(t *testing.T) {
|
||||
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 0, 2)
|
||||
require.NoError(t, err)
|
||||
amount := bc.CalculateClaimable(1, 0, 2)
|
||||
require.EqualValues(t, 8, amount)
|
||||
require.EqualValues(t, 0, sysfee)
|
||||
})
|
||||
|
||||
t.Run("a number of full periods", func(t *testing.T) {
|
||||
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 0, 6)
|
||||
require.NoError(t, err)
|
||||
amount := bc.CalculateClaimable(1, 0, 6)
|
||||
require.EqualValues(t, 4+4+3+3+2+2, amount)
|
||||
require.EqualValues(t, 0, sysfee)
|
||||
})
|
||||
|
||||
t.Run("start from the 2-nd block", func(t *testing.T) {
|
||||
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 1, 7)
|
||||
require.NoError(t, err)
|
||||
amount := bc.CalculateClaimable(1, 1, 7)
|
||||
require.EqualValues(t, 4+3+3+2+2+1, amount)
|
||||
require.EqualValues(t, 0, sysfee)
|
||||
})
|
||||
|
||||
t.Run("end height after generation has ended", func(t *testing.T) {
|
||||
amount, sysfee, err := bc.CalculateClaimable(util.Fixed8FromInt64(1), 1, 10)
|
||||
require.NoError(t, err)
|
||||
amount := bc.CalculateClaimable(1, 1, 10)
|
||||
require.EqualValues(t, 4+3+3+2+2+1+1, amount)
|
||||
require.EqualValues(t, 0, sysfee)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -275,7 +267,7 @@ func TestSubscriptions(t *testing.T) {
|
|||
emit.Bytes(script.BinWriter, []byte("yay!"))
|
||||
emit.Syscall(script.BinWriter, "Neo.Runtime.Notify")
|
||||
require.NoError(t, script.Err)
|
||||
txGood1 := transaction.NewInvocationTX(script.Bytes(), 0)
|
||||
txGood1 := transaction.New(script.Bytes(), 0)
|
||||
txGood1.Sender = neoOwner
|
||||
txGood1.Nonce = 1
|
||||
txGood1.ValidUntilBlock = 100500
|
||||
|
@ -287,7 +279,7 @@ func TestSubscriptions(t *testing.T) {
|
|||
emit.Syscall(script.BinWriter, "Neo.Runtime.Notify")
|
||||
emit.Opcode(script.BinWriter, opcode.THROW)
|
||||
require.NoError(t, script.Err)
|
||||
txBad := transaction.NewInvocationTX(script.Bytes(), 0)
|
||||
txBad := transaction.New(script.Bytes(), 0)
|
||||
txBad.Sender = neoOwner
|
||||
txBad.Nonce = 2
|
||||
txBad.ValidUntilBlock = 100500
|
||||
|
@ -297,7 +289,7 @@ func TestSubscriptions(t *testing.T) {
|
|||
emit.Bytes(script.BinWriter, []byte("yay! yay! yay!"))
|
||||
emit.Syscall(script.BinWriter, "Neo.Runtime.Notify")
|
||||
require.NoError(t, script.Err)
|
||||
txGood2 := transaction.NewInvocationTX(script.Bytes(), 0)
|
||||
txGood2 := transaction.New(script.Bytes(), 0)
|
||||
txGood2.Sender = neoOwner
|
||||
txGood2.Nonce = 3
|
||||
txGood2.ValidUntilBlock = 100500
|
||||
|
@ -319,14 +311,11 @@ func TestSubscriptions(t *testing.T) {
|
|||
for _, txExpected := range invBlock.Transactions {
|
||||
tx := <-txCh
|
||||
require.Equal(t, txExpected, tx)
|
||||
if txExpected.Type == transaction.InvocationType {
|
||||
exec := <-executionCh
|
||||
require.Equal(t, tx.Hash(), exec.TxHash)
|
||||
if exec.VMState == "HALT" {
|
||||
notif := <-notificationCh
|
||||
inv := tx.Data.(*transaction.InvocationTX)
|
||||
require.Equal(t, hash.Hash160(inv.Script), notif.ScriptHash)
|
||||
}
|
||||
exec := <-executionCh
|
||||
require.Equal(t, tx.Hash(), exec.TxHash)
|
||||
if exec.VMState == "HALT" {
|
||||
notif := <-notificationCh
|
||||
require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash)
|
||||
}
|
||||
}
|
||||
assert.Empty(t, txCh)
|
||||
|
|
|
@ -14,24 +14,24 @@ import (
|
|||
// Blockchainer is an interface that abstract the implementation
|
||||
// of the blockchain.
|
||||
type Blockchainer interface {
|
||||
ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithFee
|
||||
ApplyPolicyToTxSet([]*transaction.Transaction) []*transaction.Transaction
|
||||
GetConfig() config.ProtocolConfiguration
|
||||
AddHeaders(...*block.Header) error
|
||||
AddBlock(*block.Block) error
|
||||
BlockHeight() uint32
|
||||
CalculateClaimable(value util.Fixed8, startHeight, endHeight uint32) (util.Fixed8, util.Fixed8, error)
|
||||
CalculateClaimable(value int64, startHeight, endHeight uint32) util.Fixed8
|
||||
Close()
|
||||
HeaderHeight() uint32
|
||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||
GetContractState(hash util.Uint160) *state.Contract
|
||||
GetEnrollments() ([]state.Validator, error)
|
||||
GetGoverningTokenBalance(acc util.Uint160) (util.Fixed8, uint32)
|
||||
GetHeaderHash(int) util.Uint256
|
||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||
CurrentHeaderHash() util.Uint256
|
||||
CurrentBlockHash() util.Uint256
|
||||
HasBlock(util.Uint256) bool
|
||||
HasTransaction(util.Uint256) bool
|
||||
GetAssetState(util.Uint256) *state.Asset
|
||||
GetAccountState(util.Uint160) *state.Account
|
||||
GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
|
||||
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
|
||||
|
@ -43,8 +43,6 @@ type Blockchainer interface {
|
|||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetTestVM() *vm.VM
|
||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
GetUnspentCoinState(util.Uint256) *state.UnspentCoin
|
||||
References(t *transaction.Transaction) ([]transaction.InOut, error)
|
||||
mempool.Feer // fee interface
|
||||
PoolTx(*transaction.Transaction) error
|
||||
SubscribeForBlocks(ch chan<- *block.Block)
|
||||
|
|
|
@ -15,7 +15,6 @@ type Cached struct {
|
|||
DAO
|
||||
accounts map[util.Uint160]*state.Account
|
||||
contracts map[util.Uint160]*state.Contract
|
||||
unspents map[util.Uint256]*state.UnspentCoin
|
||||
balances map[util.Uint160]*state.NEP5Balances
|
||||
transfers map[util.Uint160]map[uint32]*state.NEP5TransferLog
|
||||
}
|
||||
|
@ -24,10 +23,9 @@ type Cached struct {
|
|||
func NewCached(d DAO) *Cached {
|
||||
accs := make(map[util.Uint160]*state.Account)
|
||||
ctrs := make(map[util.Uint160]*state.Contract)
|
||||
unspents := make(map[util.Uint256]*state.UnspentCoin)
|
||||
balances := make(map[util.Uint160]*state.NEP5Balances)
|
||||
transfers := make(map[util.Uint160]map[uint32]*state.NEP5TransferLog)
|
||||
return &Cached{d.GetWrapped(), accs, ctrs, unspents, balances, transfers}
|
||||
return &Cached{d.GetWrapped(), accs, ctrs, balances, transfers}
|
||||
}
|
||||
|
||||
// GetAccountStateOrNew retrieves Account from cache or underlying store
|
||||
|
@ -77,20 +75,6 @@ func (cd *Cached) DeleteContractState(hash util.Uint160) error {
|
|||
return cd.DAO.DeleteContractState(hash)
|
||||
}
|
||||
|
||||
// GetUnspentCoinState retrieves UnspentCoin from cache or underlying store.
|
||||
func (cd *Cached) GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error) {
|
||||
if cd.unspents[hash] != nil {
|
||||
return cd.unspents[hash], nil
|
||||
}
|
||||
return cd.DAO.GetUnspentCoinState(hash)
|
||||
}
|
||||
|
||||
// PutUnspentCoinState saves given UnspentCoin in the cache.
|
||||
func (cd *Cached) PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error {
|
||||
cd.unspents[hash] = ucs
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNEP5Balances retrieves NEP5Balances for the acc.
|
||||
func (cd *Cached) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) {
|
||||
if bs := cd.balances[acc]; bs != nil {
|
||||
|
@ -167,13 +151,6 @@ func (cd *Cached) Persist() (int, error) {
|
|||
}
|
||||
buf.Reset()
|
||||
}
|
||||
for hash := range cd.unspents {
|
||||
err := cd.DAO.putUnspentCoinState(hash, cd.unspents[hash], buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf.Reset()
|
||||
}
|
||||
for acc, bs := range cd.balances {
|
||||
err := cd.DAO.putNEP5Balances(acc, bs, buf)
|
||||
if err != nil {
|
||||
|
@ -197,7 +174,6 @@ func (cd *Cached) GetWrapped() DAO {
|
|||
return &Cached{cd.DAO.GetWrapped(),
|
||||
cd.accounts,
|
||||
cd.contracts,
|
||||
cd.unspents,
|
||||
cd.balances,
|
||||
cd.transfers,
|
||||
}
|
||||
|
|
|
@ -23,9 +23,8 @@ type DAO interface {
|
|||
GetAccountStateOrNew(hash util.Uint160) (*state.Account, error)
|
||||
GetAndDecode(entity io.Serializable, key []byte) error
|
||||
GetAppExecResult(hash util.Uint256) (*state.AppExecResult, error)
|
||||
GetAssetState(assetID util.Uint256) (*state.Asset, error)
|
||||
GetBatch() *storage.MemBatch
|
||||
GetBlock(hash util.Uint256) (*block.Block, uint32, error)
|
||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||
GetContractState(hash util.Uint160) (*state.Contract, error)
|
||||
GetCurrentBlockHeight() (uint32, error)
|
||||
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
||||
|
@ -36,29 +35,23 @@ type DAO interface {
|
|||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error)
|
||||
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error)
|
||||
GetVersion() (string, error)
|
||||
GetWrapped() DAO
|
||||
HasTransaction(hash util.Uint256) bool
|
||||
IsDoubleClaim(claim *transaction.ClaimTX) bool
|
||||
IsDoubleSpend(tx *transaction.Transaction) bool
|
||||
Persist() (int, error)
|
||||
PutAccountState(as *state.Account) error
|
||||
PutAppExecResult(aer *state.AppExecResult) error
|
||||
PutAssetState(as *state.Asset) error
|
||||
PutContractState(cs *state.Contract) error
|
||||
PutCurrentHeader(hashAndIndex []byte) error
|
||||
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
|
||||
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
|
||||
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
|
||||
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
|
||||
PutVersion(v string) error
|
||||
StoreAsBlock(block *block.Block, sysFee uint32) error
|
||||
StoreAsBlock(block *block.Block) error
|
||||
StoreAsCurrentBlock(block *block.Block) error
|
||||
StoreAsTransaction(tx *transaction.Transaction, index uint32) error
|
||||
putAccountState(as *state.Account, buf *io.BufBinWriter) error
|
||||
putNEP5Balances(acc util.Uint160, bs *state.NEP5Balances, buf *io.BufBinWriter) error
|
||||
putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin, buf *io.BufBinWriter) error
|
||||
}
|
||||
|
||||
// Simple is memCached wrapper around DB, simple DAO implementation.
|
||||
|
@ -146,30 +139,6 @@ func (dao *Simple) putAccountState(as *state.Account, buf *io.BufBinWriter) erro
|
|||
|
||||
// -- end accounts.
|
||||
|
||||
// -- start assets.
|
||||
|
||||
// GetAssetState returns given asset state as recorded in the given store.
|
||||
func (dao *Simple) GetAssetState(assetID util.Uint256) (*state.Asset, error) {
|
||||
asset := &state.Asset{}
|
||||
key := storage.AppendPrefix(storage.STAsset, assetID.BytesBE())
|
||||
err := dao.GetAndDecode(asset, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if asset.ID != assetID {
|
||||
return nil, fmt.Errorf("found asset id is not equal to expected")
|
||||
}
|
||||
return asset, nil
|
||||
}
|
||||
|
||||
// PutAssetState puts given asset state into the given store.
|
||||
func (dao *Simple) PutAssetState(as *state.Asset) error {
|
||||
key := storage.AppendPrefix(storage.STAsset, as.ID.BytesBE())
|
||||
return dao.Put(as, key)
|
||||
}
|
||||
|
||||
// -- end assets.
|
||||
|
||||
// -- start contracts.
|
||||
|
||||
// GetContractState returns contract state as recorded in the given
|
||||
|
@ -276,31 +245,6 @@ func (dao *Simple) AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.
|
|||
|
||||
// -- end transfer log.
|
||||
|
||||
// -- start unspent coins.
|
||||
|
||||
// GetUnspentCoinState retrieves UnspentCoinState from the given store.
|
||||
func (dao *Simple) GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error) {
|
||||
unspent := &state.UnspentCoin{}
|
||||
key := storage.AppendPrefix(storage.STCoin, hash.BytesLE())
|
||||
err := dao.GetAndDecode(unspent, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return unspent, nil
|
||||
}
|
||||
|
||||
// PutUnspentCoinState puts given UnspentCoinState into the given store.
|
||||
func (dao *Simple) PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error {
|
||||
return dao.putUnspentCoinState(hash, ucs, io.NewBufBinWriter())
|
||||
}
|
||||
|
||||
func (dao *Simple) putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin, buf *io.BufBinWriter) error {
|
||||
key := storage.AppendPrefix(storage.STCoin, hash.BytesLE())
|
||||
return dao.putWithBuffer(ucs, key, buf)
|
||||
}
|
||||
|
||||
// -- end unspent coins.
|
||||
|
||||
// -- start notification event.
|
||||
|
||||
// GetAppExecResult gets application execution result from the
|
||||
|
@ -402,18 +346,18 @@ func makeStorageItemKey(scripthash util.Uint160, key []byte) []byte {
|
|||
// -- other.
|
||||
|
||||
// GetBlock returns Block by the given hash if it exists in the store.
|
||||
func (dao *Simple) GetBlock(hash util.Uint256) (*block.Block, uint32, error) {
|
||||
func (dao *Simple) GetBlock(hash util.Uint256) (*block.Block, error) {
|
||||
key := storage.AppendPrefix(storage.DataBlock, hash.BytesLE())
|
||||
b, err := dao.Store.Get(key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := block.NewBlockFromTrimmedBytes(b[4:])
|
||||
block, err := block.NewBlockFromTrimmedBytes(b)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
return block, binary.LittleEndian.Uint32(b[:4]), nil
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// GetVersion attempts to get the current version stored in the
|
||||
|
@ -531,12 +475,11 @@ func (dao *Simple) HasTransaction(hash util.Uint256) bool {
|
|||
}
|
||||
|
||||
// StoreAsBlock stores the given block as DataBlock.
|
||||
func (dao *Simple) StoreAsBlock(block *block.Block, sysFee uint32) error {
|
||||
func (dao *Simple) StoreAsBlock(block *block.Block) error {
|
||||
var (
|
||||
key = storage.AppendPrefix(storage.DataBlock, block.Hash().BytesLE())
|
||||
buf = io.NewBufBinWriter()
|
||||
)
|
||||
buf.WriteU32LE(sysFee)
|
||||
b, err := block.Trim()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -569,35 +512,6 @@ func (dao *Simple) StoreAsTransaction(tx *transaction.Transaction, index uint32)
|
|||
return dao.Store.Put(key, buf.Bytes())
|
||||
}
|
||||
|
||||
// IsDoubleSpend verifies that the input transactions are not double spent.
|
||||
func (dao *Simple) IsDoubleSpend(tx *transaction.Transaction) bool {
|
||||
return dao.checkUsedInputs(tx.Inputs, state.CoinSpent)
|
||||
}
|
||||
|
||||
// IsDoubleClaim verifies that given claim inputs are not already claimed by another tx.
|
||||
func (dao *Simple) IsDoubleClaim(claim *transaction.ClaimTX) bool {
|
||||
return dao.checkUsedInputs(claim.Claims, state.CoinClaimed)
|
||||
}
|
||||
|
||||
func (dao *Simple) checkUsedInputs(inputs []transaction.Input, coin state.Coin) bool {
|
||||
if len(inputs) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, inputs := range transaction.GroupInputsByPrevHash(inputs) {
|
||||
prevHash := inputs[0].PrevHash
|
||||
unspent, err := dao.GetUnspentCoinState(prevHash)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
for _, input := range inputs {
|
||||
if int(input.PrevIndex) >= len(unspent.States) || (unspent.States[input.PrevIndex].State&coin) != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Persist flushes all the changes made into the (supposedly) persistent
|
||||
// underlying store.
|
||||
func (dao *Simple) Persist() (int, error) {
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
|
@ -59,17 +58,6 @@ func TestPutAndGetAccountStateOrNew(t *testing.T) {
|
|||
require.Equal(t, accountState.ScriptHash, gotAccount.ScriptHash)
|
||||
}
|
||||
|
||||
func TestPutAndGetAssetState(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
id := random.Uint256()
|
||||
assetState := &state.Asset{ID: id, Owner: keys.PublicKey{}}
|
||||
err := dao.PutAssetState(assetState)
|
||||
require.NoError(t, err)
|
||||
gotAssetState, err := dao.GetAssetState(id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, assetState, gotAssetState)
|
||||
}
|
||||
|
||||
func TestPutAndGetContractState(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
contractState := &state.Contract{Script: []byte{}, ParamList: []smartcontract.ParamType{}}
|
||||
|
@ -94,25 +82,6 @@ func TestDeleteContractState(t *testing.T) {
|
|||
require.Nil(t, gotContractState)
|
||||
}
|
||||
|
||||
func TestGetUnspentCoinState_Err(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
hash := random.Uint256()
|
||||
gotUnspentCoinState, err := dao.GetUnspentCoinState(hash)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, gotUnspentCoinState)
|
||||
}
|
||||
|
||||
func TestPutGetUnspentCoinState(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
hash := random.Uint256()
|
||||
unspentCoinState := &state.UnspentCoin{Height: 42, States: []state.OutputState{}}
|
||||
err := dao.PutUnspentCoinState(hash, unspentCoinState)
|
||||
require.NoError(t, err)
|
||||
gotUnspentCoinState, err := dao.GetUnspentCoinState(hash)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, unspentCoinState, gotUnspentCoinState)
|
||||
}
|
||||
|
||||
func TestPutGetAppExecResult(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
hash := random.Uint256()
|
||||
|
@ -155,7 +124,7 @@ func TestDeleteStorageItem(t *testing.T) {
|
|||
func TestGetBlock_NotExists(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
hash := random.Uint256()
|
||||
block, _, err := dao.GetBlock(hash)
|
||||
block, err := dao.GetBlock(hash)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, block)
|
||||
}
|
||||
|
@ -171,12 +140,11 @@ func TestPutGetBlock(t *testing.T) {
|
|||
},
|
||||
}
|
||||
hash := b.Hash()
|
||||
err := dao.StoreAsBlock(b, 42)
|
||||
err := dao.StoreAsBlock(b)
|
||||
require.NoError(t, err)
|
||||
gotBlock, sysfee, err := dao.GetBlock(hash)
|
||||
gotBlock, err := dao.GetBlock(hash)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, gotBlock)
|
||||
require.EqualValues(t, 42, sysfee)
|
||||
}
|
||||
|
||||
func TestGetVersion_NoVersion(t *testing.T) {
|
||||
|
@ -221,7 +189,7 @@ func TestGetCurrentHeaderHeight_Store(t *testing.T) {
|
|||
|
||||
func TestStoreAsTransaction(t *testing.T) {
|
||||
dao := NewSimple(storage.NewMemoryStore())
|
||||
tx := transaction.NewIssueTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 1)
|
||||
hash := tx.Hash()
|
||||
err := dao.StoreAsTransaction(tx, 0)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -41,10 +41,6 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 {
|
|||
}
|
||||
|
||||
const (
|
||||
neoAssetCreate = 0x1fc6c583 // Neo.Asset.Create
|
||||
antSharesAssetCreate = 0x99025068 // AntShares.Asset.Create
|
||||
neoAssetRenew = 0x71908478 // Neo.Asset.Renew
|
||||
antSharesAssetRenew = 0xaf22447b // AntShares.Asset.Renew
|
||||
neoContractCreate = 0x6ea56cf6 // Neo.Contract.Create
|
||||
neoContractMigrate = 0x90621b47 // Neo.Contract.Migrate
|
||||
antSharesContractCreate = 0x2a28d29b // AntShares.Contract.Create
|
||||
|
@ -58,11 +54,6 @@ func getSyscallPrice(v *vm.VM, id uint32) util.Fixed8 {
|
|||
estack := v.Estack()
|
||||
|
||||
switch id {
|
||||
case neoAssetCreate, antSharesAssetCreate:
|
||||
return util.Fixed8FromInt64(5000)
|
||||
case neoAssetRenew, antSharesAssetRenew:
|
||||
arg := estack.Peek(1).BigInt().Int64()
|
||||
return util.Fixed8FromInt64(arg * 5000)
|
||||
case neoContractCreate, neoContractMigrate, antSharesContractCreate, antSharesContractMigrate:
|
||||
return smartcontract.GetDeploymentPrice(smartcontract.PropertyState(estack.Peek(3).BigInt().Int64()))
|
||||
case systemStoragePut, systemStoragePutEx, neoStoragePut, antSharesStoragePut:
|
||||
|
|
|
@ -23,21 +23,6 @@ func TestGetPrice(t *testing.T) {
|
|||
v := SpawnVM(systemInterop)
|
||||
v.SetPriceGetter(getPrice)
|
||||
|
||||
t.Run("Neo.Asset.Create", func(t *testing.T) {
|
||||
// Neo.Asset.Create: 83c5c61f
|
||||
v.Load([]byte{byte(opcode.SYSCALL), 0x83, 0xc5, 0xc6, 0x1f})
|
||||
checkGas(t, util.Fixed8FromInt64(5000), v)
|
||||
})
|
||||
|
||||
t.Run("Neo.Asset.Renew", func(t *testing.T) {
|
||||
// Neo.Asset.Renew: 78849071 (requires push 09 push 09 before)
|
||||
v.Load([]byte{byte(opcode.PUSH9), byte(opcode.PUSH9), byte(opcode.SYSCALL), 0x78, 0x84, 0x90, 0x71})
|
||||
require.NoError(t, v.StepInto()) // push 9
|
||||
require.NoError(t, v.StepInto()) // push 9
|
||||
|
||||
checkGas(t, util.Fixed8FromInt64(9*5000), v)
|
||||
})
|
||||
|
||||
t.Run("Neo.Contract.Create (no props)", func(t *testing.T) {
|
||||
// Neo.Contract.Create: f66ca56e (requires push properties on fourth position)
|
||||
v.Load([]byte{byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0), byte(opcode.PUSH0),
|
||||
|
|
|
@ -60,7 +60,7 @@ func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256,
|
|||
Base: block.Base{
|
||||
Version: 0,
|
||||
PrevHash: prev,
|
||||
Timestamp: uint64(time.Now().UTC().Unix()) + uint64(index),
|
||||
Timestamp: uint64(time.Now().UTC().Unix())*1000 + uint64(index),
|
||||
Index: index,
|
||||
NextConsensus: witness.ScriptHash(),
|
||||
Script: witness,
|
||||
|
@ -138,7 +138,7 @@ func newDumbBlock() *block.Block {
|
|||
Nonce: 1111,
|
||||
},
|
||||
Transactions: []*transaction.Transaction{
|
||||
{Type: transaction.IssueType},
|
||||
transaction.New([]byte{byte(opcode.PUSH1)}, 0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -165,19 +165,21 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
return testNonce
|
||||
}
|
||||
|
||||
var neoAmount = util.Fixed8FromInt64(99999000)
|
||||
var neoRemainder = util.Fixed8FromInt64(100000000) - neoAmount
|
||||
const neoAmount = 99999000
|
||||
bc := newTestChain(t)
|
||||
defer bc.Close()
|
||||
|
||||
gasHash := bc.contracts.GAS.Hash
|
||||
neoHash := bc.contracts.NEO.Hash
|
||||
t.Logf("native GAS hash: %v", gasHash)
|
||||
t.Logf("native NEO hash: %v", neoHash)
|
||||
|
||||
priv0 := testchain.PrivateKeyByID(0)
|
||||
priv0ScriptHash := priv0.GetScriptHash()
|
||||
|
||||
// Move almost all NEO and some nep5 GAS to one simple account.
|
||||
txMoveNeo := newNEP5Transfer(gasHash, neoOwner, priv0ScriptHash, 1000000000)
|
||||
require.Equal(t, util.Fixed8FromInt64(0), bc.GetUtilityTokenBalance(priv0ScriptHash))
|
||||
// Move some NEO to one simple account.
|
||||
txMoveNeo := newNEP5Transfer(neoHash, neoOwner, priv0ScriptHash, neoAmount)
|
||||
txMoveNeo.ValidUntilBlock = validUntilBlock
|
||||
txMoveNeo.Nonce = getNextNonce()
|
||||
txMoveNeo.Sender = neoOwner
|
||||
|
@ -187,93 +189,34 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
}}
|
||||
|
||||
// use output of issue tx from genesis block as an input
|
||||
genesisBlock, err := bc.GetBlock(bc.GetHeaderHash(0))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 4, len(genesisBlock.Transactions))
|
||||
h := genesisBlock.Transactions[2].Hash()
|
||||
txMoveNeo.AddInput(&transaction.Input{
|
||||
PrevHash: h,
|
||||
PrevIndex: 0,
|
||||
})
|
||||
txMoveNeo.AddOutput(&transaction.Output{
|
||||
AssetID: GoverningTokenID(),
|
||||
Amount: neoAmount,
|
||||
ScriptHash: priv0ScriptHash,
|
||||
Position: 0,
|
||||
})
|
||||
txMoveNeo.AddOutput(&transaction.Output{
|
||||
AssetID: GoverningTokenID(),
|
||||
Amount: neoRemainder,
|
||||
ScriptHash: neoOwner,
|
||||
Position: 1,
|
||||
})
|
||||
require.NoError(t, signTx(bc, txMoveNeo))
|
||||
b := bc.newBlock(txMoveNeo)
|
||||
// Move some GAS to one simple account.
|
||||
txMoveGas := newNEP5Transfer(gasHash, neoOwner, priv0ScriptHash, int64(util.Fixed8FromInt64(1000)))
|
||||
txMoveGas.ValidUntilBlock = validUntilBlock
|
||||
txMoveGas.Nonce = getNextNonce()
|
||||
txMoveGas.Sender = neoOwner
|
||||
txMoveGas.Cosigners = []transaction.Cosigner{{
|
||||
Account: neoOwner,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
}}
|
||||
require.NoError(t, signTx(bc, txMoveGas))
|
||||
b := bc.newBlock(txMoveNeo, txMoveGas)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE())
|
||||
t.Logf("txMoveGas: %s", txMoveGas.Hash().StringLE())
|
||||
|
||||
require.Equal(t, util.Fixed8FromInt64(1000), bc.GetUtilityTokenBalance(priv0ScriptHash))
|
||||
// info for getblockheader rpc tests
|
||||
t.Logf("header hash: %s", b.Hash().StringLE())
|
||||
buf := io.NewBufBinWriter()
|
||||
b.Header().EncodeBinary(buf.BinWriter)
|
||||
t.Logf("header: %s", hex.EncodeToString(buf.Bytes()))
|
||||
|
||||
// Generate some blocks to be able to claim GAS for them.
|
||||
_, err = bc.genBlocks(numOfEmptyBlocks)
|
||||
require.NoError(t, err)
|
||||
|
||||
acc0, err := wallet.NewAccountFromWIF(priv0.WIF())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make a NEO roundtrip (send to myself) and claim GAS.
|
||||
txNeoRound := transaction.NewContractTX()
|
||||
txNeoRound.Nonce = getNextNonce()
|
||||
txNeoRound.Sender = priv0ScriptHash
|
||||
txNeoRound.ValidUntilBlock = validUntilBlock
|
||||
txNeoRound.AddInput(&transaction.Input{
|
||||
PrevHash: txMoveNeo.Hash(),
|
||||
PrevIndex: 0,
|
||||
})
|
||||
txNeoRound.AddOutput(&transaction.Output{
|
||||
AssetID: GoverningTokenID(),
|
||||
Amount: neoAmount,
|
||||
ScriptHash: priv0.GetScriptHash(),
|
||||
Position: 0,
|
||||
})
|
||||
txNeoRound.Data = new(transaction.ContractTX)
|
||||
require.NoError(t, addNetworkFee(bc, txNeoRound, acc0))
|
||||
require.NoError(t, acc0.SignTx(txNeoRound))
|
||||
b = bc.newBlock(txNeoRound)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
t.Logf("txNeoRound: %s", txNeoRound.Hash().StringLE())
|
||||
|
||||
claim := new(transaction.ClaimTX)
|
||||
claim.Claims = append(claim.Claims, transaction.Input{
|
||||
PrevHash: txMoveNeo.Hash(),
|
||||
PrevIndex: 0,
|
||||
})
|
||||
txClaim := transaction.NewClaimTX(claim)
|
||||
txClaim.Nonce = getNextNonce()
|
||||
txClaim.ValidUntilBlock = validUntilBlock
|
||||
txClaim.Sender = priv0ScriptHash
|
||||
txClaim.Data = claim
|
||||
neoGas, sysGas, err := bc.CalculateClaimable(neoAmount, 1, bc.BlockHeight())
|
||||
require.NoError(t, err)
|
||||
gasOwned := neoGas + sysGas
|
||||
txClaim.AddOutput(&transaction.Output{
|
||||
AssetID: UtilityTokenID(),
|
||||
Amount: gasOwned,
|
||||
ScriptHash: priv0.GetScriptHash(),
|
||||
Position: 0,
|
||||
})
|
||||
require.NoError(t, addNetworkFee(bc, txClaim, acc0))
|
||||
require.NoError(t, acc0.SignTx(txClaim))
|
||||
b = bc.newBlock(txClaim)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
t.Logf("txClaim: %s", txClaim.Hash().StringLE())
|
||||
|
||||
// Push some contract into the chain.
|
||||
avm, err := ioutil.ReadFile(prefix + "test_contract.avm")
|
||||
require.NoError(t, err)
|
||||
|
@ -297,21 +240,10 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
txScript := script.Bytes()
|
||||
|
||||
invFee := util.Fixed8FromFloat(100)
|
||||
txDeploy := transaction.NewInvocationTX(txScript, invFee)
|
||||
txDeploy := transaction.New(txScript, invFee)
|
||||
txDeploy.Nonce = getNextNonce()
|
||||
txDeploy.ValidUntilBlock = validUntilBlock
|
||||
txDeploy.Sender = priv0ScriptHash
|
||||
txDeploy.AddInput(&transaction.Input{
|
||||
PrevHash: txClaim.Hash(),
|
||||
PrevIndex: 0,
|
||||
})
|
||||
txDeploy.AddOutput(&transaction.Output{
|
||||
AssetID: UtilityTokenID(),
|
||||
Amount: gasOwned - invFee,
|
||||
ScriptHash: priv0.GetScriptHash(),
|
||||
Position: 0,
|
||||
})
|
||||
gasOwned -= invFee
|
||||
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
|
||||
require.NoError(t, acc0.SignTx(txDeploy))
|
||||
b = bc.newBlock(txDeploy)
|
||||
|
@ -322,7 +254,7 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
script = io.NewBufBinWriter()
|
||||
emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "Put", "testkey", "testvalue")
|
||||
|
||||
txInv := transaction.NewInvocationTX(script.Bytes(), 0)
|
||||
txInv := transaction.New(script.Bytes(), 0)
|
||||
txInv.Nonce = getNextNonce()
|
||||
txInv.ValidUntilBlock = validUntilBlock
|
||||
txInv.Sender = priv0ScriptHash
|
||||
|
@ -333,26 +265,18 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
t.Logf("txInv: %s", txInv.Hash().StringLE())
|
||||
|
||||
priv1 := testchain.PrivateKeyByID(1)
|
||||
txNeo0to1 := transaction.NewContractTX()
|
||||
txNeo0to1 := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), 1000)
|
||||
txNeo0to1.Nonce = getNextNonce()
|
||||
txNeo0to1.ValidUntilBlock = validUntilBlock
|
||||
txNeo0to1.Sender = priv0ScriptHash
|
||||
txNeo0to1.Data = new(transaction.ContractTX)
|
||||
txNeo0to1.AddInput(&transaction.Input{
|
||||
PrevHash: txNeoRound.Hash(),
|
||||
PrevIndex: 0,
|
||||
})
|
||||
txNeo0to1.AddOutput(&transaction.Output{
|
||||
AssetID: GoverningTokenID(),
|
||||
Amount: util.Fixed8FromInt64(1000),
|
||||
ScriptHash: priv1.GetScriptHash(),
|
||||
})
|
||||
txNeo0to1.AddOutput(&transaction.Output{
|
||||
AssetID: GoverningTokenID(),
|
||||
Amount: neoAmount - util.Fixed8FromInt64(1000),
|
||||
ScriptHash: priv0.GetScriptHash(),
|
||||
})
|
||||
|
||||
txNeo0to1.Cosigners = []transaction.Cosigner{
|
||||
{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
},
|
||||
}
|
||||
require.NoError(t, addNetworkFee(bc, txNeo0to1, acc0))
|
||||
require.NoError(t, acc0.SignTx(txNeo0to1))
|
||||
b = bc.newBlock(txNeo0to1)
|
||||
|
@ -361,7 +285,7 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
sh := hash.Hash160(avm)
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCallWithOperationAndArgs(w.BinWriter, sh, "init")
|
||||
initTx := transaction.NewInvocationTX(w.Bytes(), 0)
|
||||
initTx := transaction.New(w.Bytes(), 0)
|
||||
initTx.Nonce = getNextNonce()
|
||||
initTx.ValidUntilBlock = validUntilBlock
|
||||
initTx.Sender = priv0ScriptHash
|
||||
|
@ -384,7 +308,7 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
|
||||
b = bc.newBlock(initTx, transferTx)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringBE())
|
||||
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE())
|
||||
|
||||
transferTx = newNEP5Transfer(sh, priv0.GetScriptHash(), priv1.GetScriptHash(), 123)
|
||||
transferTx.Nonce = getNextNonce()
|
||||
|
@ -403,7 +327,7 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
|
||||
b = bc.newBlock(transferTx)
|
||||
require.NoError(t, bc.AddBlock(b))
|
||||
t.Logf("sendRublesTx: %v", transferTx.Hash().StringBE())
|
||||
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
|
||||
|
||||
if saveChain {
|
||||
outStream, err := os.Create(prefix + "testblocks.acc")
|
||||
|
@ -427,26 +351,21 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Make a NEO roundtrip (send to myself) and claim GAS.
|
||||
txNeoRound = transaction.NewContractTX()
|
||||
txNeoRound.Nonce = getNextNonce()
|
||||
txNeoRound.ValidUntilBlock = validUntilBlock
|
||||
txNeoRound.Sender = priv0ScriptHash
|
||||
txNeoRound.AddInput(&transaction.Input{
|
||||
PrevHash: txNeo0to1.Hash(),
|
||||
PrevIndex: 1,
|
||||
})
|
||||
txNeoRound.AddOutput(&transaction.Output{
|
||||
AssetID: GoverningTokenID(),
|
||||
Amount: neoAmount - util.Fixed8FromInt64(1000),
|
||||
ScriptHash: priv0.GetScriptHash(),
|
||||
Position: 0,
|
||||
})
|
||||
txNeoRound.Data = new(transaction.ContractTX)
|
||||
require.NoError(t, addNetworkFee(bc, txNeoRound, acc0))
|
||||
require.NoError(t, acc0.SignTx(txNeoRound))
|
||||
// Prepare some transaction for future submission.
|
||||
txSendRaw := newNEP5Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), int64(util.Fixed8FromInt64(1000)))
|
||||
txSendRaw.ValidUntilBlock = validUntilBlock
|
||||
txSendRaw.Nonce = getNextNonce()
|
||||
txSendRaw.Sender = priv0ScriptHash
|
||||
txSendRaw.Cosigners = []transaction.Cosigner{{
|
||||
Account: priv0ScriptHash,
|
||||
Scopes: transaction.CalledByEntry,
|
||||
AllowedContracts: nil,
|
||||
AllowedGroups: nil,
|
||||
}}
|
||||
require.NoError(t, addNetworkFee(bc, txSendRaw, acc0))
|
||||
require.NoError(t, acc0.SignTx(txSendRaw))
|
||||
bw := io.NewBufBinWriter()
|
||||
txNeoRound.EncodeBinary(bw.BinWriter)
|
||||
txSendRaw.EncodeBinary(bw.BinWriter)
|
||||
t.Logf("sendrawtransaction: %s", hex.EncodeToString(bw.Bytes()))
|
||||
}
|
||||
|
||||
|
@ -456,7 +375,7 @@ func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Trans
|
|||
emit.Opcode(w.BinWriter, opcode.ASSERT)
|
||||
|
||||
script := w.Bytes()
|
||||
return transaction.NewInvocationTX(script, 0)
|
||||
return transaction.New(script, 0)
|
||||
}
|
||||
|
||||
func addSender(txs ...*transaction.Transaction) error {
|
||||
|
|
|
@ -4,20 +4,15 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
gherr "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -29,15 +24,6 @@ const (
|
|||
MaxContractParametersNum = 252
|
||||
// MaxContractStringLen is the maximum length for contract metadata strings.
|
||||
MaxContractStringLen = 252
|
||||
// MaxAssetNameLen is the maximum length of asset name.
|
||||
MaxAssetNameLen = 1024
|
||||
// MaxAssetPrecision is the maximum precision of asset.
|
||||
MaxAssetPrecision = 8
|
||||
// BlocksPerYear is a multiplier for asset renewal.
|
||||
BlocksPerYear = 2000000
|
||||
// DefaultAssetLifetime is the default lifetime of an asset (which differs
|
||||
// from assets created by register tx).
|
||||
DefaultAssetLifetime = 1 + BlocksPerYear
|
||||
)
|
||||
|
||||
// headerGetVersion returns version from the header.
|
||||
|
@ -88,106 +74,6 @@ func txGetAttributes(ic *interop.Context, v *vm.VM) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// txGetInputs returns current transaction inputs.
|
||||
func txGetInputs(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
if len(tx.Inputs) > vm.MaxArraySize {
|
||||
return errors.New("too many inputs")
|
||||
}
|
||||
inputs := make([]vm.StackItem, 0, len(tx.Inputs))
|
||||
for i := range tx.Inputs {
|
||||
inputs = append(inputs, vm.NewInteropItem(&tx.Inputs[i]))
|
||||
}
|
||||
v.Estack().PushVal(inputs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetOutputs returns current transaction outputs.
|
||||
func txGetOutputs(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
if len(tx.Outputs) > vm.MaxArraySize {
|
||||
return errors.New("too many outputs")
|
||||
}
|
||||
outputs := make([]vm.StackItem, 0, len(tx.Outputs))
|
||||
for i := range tx.Outputs {
|
||||
outputs = append(outputs, vm.NewInteropItem(&tx.Outputs[i]))
|
||||
}
|
||||
v.Estack().PushVal(outputs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetReferences returns current transaction references.
|
||||
func txGetReferences(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return fmt.Errorf("type mismatch: %T is not a Transaction", txInterface)
|
||||
}
|
||||
refs, err := ic.Chain.References(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(refs) > vm.MaxArraySize {
|
||||
return errors.New("too many references")
|
||||
}
|
||||
|
||||
stackrefs := make([]vm.StackItem, 0, len(refs))
|
||||
for i := range tx.Inputs {
|
||||
for j := range refs {
|
||||
if refs[j].In == tx.Inputs[i] {
|
||||
stackrefs = append(stackrefs, vm.NewInteropItem(refs[j]))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
v.Estack().PushVal(stackrefs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetType returns current transaction type.
|
||||
func txGetType(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
v.Estack().PushVal(int(tx.Type))
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetUnspentCoins returns current transaction unspent coins.
|
||||
func txGetUnspentCoins(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
ucs, err := ic.DAO.GetUnspentCoinState(tx.Hash())
|
||||
if err == storage.ErrKeyNotFound {
|
||||
v.Estack().PushVal([]vm.StackItem{})
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return errors.New("no unspent coin state found")
|
||||
}
|
||||
|
||||
items := make([]vm.StackItem, 0, len(ucs.States))
|
||||
for i := range items {
|
||||
if ucs.States[i].State&state.CoinSpent == 0 {
|
||||
items = append(items, vm.NewInteropItem(&ucs.States[i].Output))
|
||||
}
|
||||
}
|
||||
v.Estack().PushVal(items)
|
||||
return nil
|
||||
}
|
||||
|
||||
// txGetWitnesses returns current transaction witnesses.
|
||||
func txGetWitnesses(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
|
@ -206,24 +92,6 @@ func txGetWitnesses(ic *interop.Context, v *vm.VM) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// invocationTx_GetScript returns invocation script from the current transaction.
|
||||
func invocationTxGetScript(ic *interop.Context, v *vm.VM) error {
|
||||
txInterface := v.Estack().Pop().Value()
|
||||
tx, ok := txInterface.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return errors.New("value is not a transaction")
|
||||
}
|
||||
inv, ok := tx.Data.(*transaction.InvocationTX)
|
||||
if tx.Type != transaction.InvocationType || !ok {
|
||||
return errors.New("value is not an invocation transaction")
|
||||
}
|
||||
// It's important not to share inv.Script slice with the code running in VM.
|
||||
script := make([]byte, len(inv.Script))
|
||||
copy(script, inv.Script)
|
||||
v.Estack().PushVal(script)
|
||||
return nil
|
||||
}
|
||||
|
||||
// witnessGetVerificationScript returns current witness' script.
|
||||
func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
|
||||
witInterface := v.Estack().Pop().Value()
|
||||
|
@ -238,84 +106,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// popInputFromVM returns transaction.Input from the first estack element.
|
||||
func popInputFromVM(v *vm.VM) (*transaction.Input, error) {
|
||||
inInterface := v.Estack().Pop().Value()
|
||||
input, ok := inInterface.(*transaction.Input)
|
||||
if !ok {
|
||||
txio, ok := inInterface.(transaction.InOut)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type mismatch: %T is not an Input or InOut", inInterface)
|
||||
}
|
||||
input = &txio.In
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// inputGetHash returns hash from the given input.
|
||||
func inputGetHash(ic *interop.Context, v *vm.VM) error {
|
||||
input, err := popInputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(input.PrevHash.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// inputGetIndex returns index from the given input.
|
||||
func inputGetIndex(ic *interop.Context, v *vm.VM) error {
|
||||
input, err := popInputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(input.PrevIndex)
|
||||
return nil
|
||||
}
|
||||
|
||||
// popOutputFromVM returns transaction.Input from the first estack element.
|
||||
func popOutputFromVM(v *vm.VM) (*transaction.Output, error) {
|
||||
outInterface := v.Estack().Pop().Value()
|
||||
output, ok := outInterface.(*transaction.Output)
|
||||
if !ok {
|
||||
txio, ok := outInterface.(transaction.InOut)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type mismatch: %T is not an Output or InOut", outInterface)
|
||||
}
|
||||
output = &txio.Out
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// outputGetAssetId returns asset ID from the given output.
|
||||
func outputGetAssetID(ic *interop.Context, v *vm.VM) error {
|
||||
output, err := popOutputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(output.AssetID.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// outputGetScriptHash returns scripthash from the given output.
|
||||
func outputGetScriptHash(ic *interop.Context, v *vm.VM) error {
|
||||
output, err := popOutputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(output.ScriptHash.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// outputGetValue returns value (amount) from the given output.
|
||||
func outputGetValue(ic *interop.Context, v *vm.VM) error {
|
||||
output, err := popOutputFromVM(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Estack().PushVal(int64(output.Amount))
|
||||
return nil
|
||||
}
|
||||
|
||||
// attrGetData returns tx attribute data.
|
||||
func attrGetData(ic *interop.Context, v *vm.VM) error {
|
||||
attrInterface := v.Estack().Pop().Value()
|
||||
|
@ -353,21 +143,6 @@ func bcGetAccount(ic *interop.Context, v *vm.VM) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// bcGetAsset returns an asset.
|
||||
func bcGetAsset(ic *interop.Context, v *vm.VM) error {
|
||||
asbytes := v.Estack().Pop().Bytes()
|
||||
ashash, err := util.Uint256DecodeBytesBE(asbytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
as, err := ic.DAO.GetAssetState(ashash)
|
||||
if err != nil {
|
||||
return errors.New("asset not found")
|
||||
}
|
||||
v.Estack().PushVal(vm.NewInteropItem(as))
|
||||
return nil
|
||||
}
|
||||
|
||||
// accountGetBalance returns balance for a given account.
|
||||
func accountGetBalance(ic *interop.Context, v *vm.VM) error {
|
||||
accInterface := v.Estack().Pop().Value()
|
||||
|
@ -571,203 +346,6 @@ func contractMigrate(ic *interop.Context, v *vm.VM) error {
|
|||
return contractDestroy(ic, v)
|
||||
}
|
||||
|
||||
// assetCreate creates an asset.
|
||||
func assetCreate(ic *interop.Context, v *vm.VM) error {
|
||||
if ic.Trigger != trigger.Application {
|
||||
return errors.New("can't create asset when not triggered by an application")
|
||||
}
|
||||
atype := transaction.AssetType(v.Estack().Pop().BigInt().Int64())
|
||||
switch atype {
|
||||
case transaction.Currency, transaction.Share, transaction.Invoice, transaction.Token:
|
||||
// ok
|
||||
default:
|
||||
return fmt.Errorf("wrong asset type: %x", atype)
|
||||
}
|
||||
name := string(v.Estack().Pop().Bytes())
|
||||
if len(name) > MaxAssetNameLen {
|
||||
return errors.New("too big name")
|
||||
}
|
||||
amount := util.Fixed8(v.Estack().Pop().BigInt().Int64())
|
||||
if amount == util.Fixed8(0) {
|
||||
return errors.New("asset amount can't be zero")
|
||||
}
|
||||
if amount < -util.Satoshi() {
|
||||
return errors.New("asset amount can't be negative (except special -Satoshi value")
|
||||
}
|
||||
if atype == transaction.Invoice && amount != -util.Satoshi() {
|
||||
return errors.New("invoice assets can only have -Satoshi amount")
|
||||
}
|
||||
precision := byte(v.Estack().Pop().BigInt().Int64())
|
||||
if precision > MaxAssetPrecision {
|
||||
return fmt.Errorf("can't have asset precision of more than %d", MaxAssetPrecision)
|
||||
}
|
||||
if atype == transaction.Share && precision != 0 {
|
||||
return errors.New("share assets can only have zero precision")
|
||||
}
|
||||
if amount != -util.Satoshi() && (int64(amount)%int64(math.Pow10(int(MaxAssetPrecision-precision))) != 0) {
|
||||
return errors.New("given asset amount has fractional component")
|
||||
}
|
||||
owner, err := keys.NewPublicKeyFromBytes(v.Estack().Pop().Bytes())
|
||||
if err != nil {
|
||||
return gherr.Wrap(err, "failed to get owner key")
|
||||
}
|
||||
if owner.IsInfinity() {
|
||||
return errors.New("can't have infinity as an owner key")
|
||||
}
|
||||
witnessOk, err := runtime.CheckKeyedWitness(ic, v, owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !witnessOk {
|
||||
return errors.New("witness check didn't succeed")
|
||||
}
|
||||
admin, err := util.Uint160DecodeBytesBE(v.Estack().Pop().Bytes())
|
||||
if err != nil {
|
||||
return gherr.Wrap(err, "failed to get admin")
|
||||
}
|
||||
issuer, err := util.Uint160DecodeBytesBE(v.Estack().Pop().Bytes())
|
||||
if err != nil {
|
||||
return gherr.Wrap(err, "failed to get issuer")
|
||||
}
|
||||
asset := &state.Asset{
|
||||
ID: ic.Tx.Hash(),
|
||||
AssetType: atype,
|
||||
Name: name,
|
||||
Amount: amount,
|
||||
Precision: precision,
|
||||
Owner: *owner,
|
||||
Admin: admin,
|
||||
Issuer: issuer,
|
||||
Expiration: ic.Chain.BlockHeight() + DefaultAssetLifetime,
|
||||
}
|
||||
err = ic.DAO.PutAssetState(asset)
|
||||
if err != nil {
|
||||
return gherr.Wrap(err, "failed to Store asset")
|
||||
}
|
||||
v.Estack().PushVal(vm.NewInteropItem(asset))
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetAdmin returns asset admin.
|
||||
func assetGetAdmin(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(as.Admin.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetAmount returns the overall amount of asset available.
|
||||
func assetGetAmount(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(int64(as.Amount))
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetAssetId returns the id of an asset.
|
||||
func assetGetAssetID(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(as.ID.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetAssetType returns type of an asset.
|
||||
func assetGetAssetType(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(int(as.AssetType))
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetAvailable returns available (not yet issued) amount of asset.
|
||||
func assetGetAvailable(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(int(as.Available))
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetIssuer returns issuer of an asset.
|
||||
func assetGetIssuer(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(as.Issuer.BytesBE())
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetOwner returns owner of an asset.
|
||||
func assetGetOwner(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(as.Owner.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetGetPrecision returns precision used to measure this asset.
|
||||
func assetGetPrecision(ic *interop.Context, v *vm.VM) error {
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
v.Estack().PushVal(int(as.Precision))
|
||||
return nil
|
||||
}
|
||||
|
||||
// assetRenew updates asset expiration date.
|
||||
func assetRenew(ic *interop.Context, v *vm.VM) error {
|
||||
if ic.Trigger != trigger.Application {
|
||||
return errors.New("can't create asset when not triggered by an application")
|
||||
}
|
||||
asInterface := v.Estack().Pop().Value()
|
||||
as, ok := asInterface.(*state.Asset)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not an asset state", as)
|
||||
}
|
||||
years := byte(v.Estack().Pop().BigInt().Int64())
|
||||
// Not sure why C# code regets an asset from the Store, but we also do it.
|
||||
asset, err := ic.DAO.GetAssetState(as.ID)
|
||||
if err != nil {
|
||||
return errors.New("can't renew non-existent asset")
|
||||
}
|
||||
if asset.Expiration < ic.Chain.BlockHeight()+1 {
|
||||
asset.Expiration = ic.Chain.BlockHeight() + 1
|
||||
}
|
||||
expiration := uint64(asset.Expiration) + uint64(years)*BlocksPerYear
|
||||
if expiration > math.MaxUint32 {
|
||||
expiration = math.MaxUint32
|
||||
}
|
||||
asset.Expiration = uint32(expiration)
|
||||
err = ic.DAO.PutAssetState(asset)
|
||||
if err != nil {
|
||||
return gherr.Wrap(err, "failed to Store asset")
|
||||
}
|
||||
v.Estack().PushVal(expiration)
|
||||
return nil
|
||||
}
|
||||
|
||||
// runtimeSerialize serializes top stack item into a ByteArray.
|
||||
func runtimeSerialize(_ *interop.Context, v *vm.VM) error {
|
||||
return vm.RuntimeSerialize(v)
|
||||
|
|
|
@ -25,18 +25,13 @@ import (
|
|||
)
|
||||
|
||||
/* Missing tests:
|
||||
* TestTxGetReferences
|
||||
* TestTxGetUnspentCoins
|
||||
* TestTxGetWitnesses
|
||||
* TestBcGetAccount
|
||||
* TestBcGetAsset
|
||||
* TestAccountGetBalance
|
||||
* TestAccountIsStandard
|
||||
* TestCreateContractStateFromVM
|
||||
* TestContractCreate
|
||||
* TestContractMigrate
|
||||
* TestAssetCreate
|
||||
* TestAssetRenew
|
||||
* TestRuntimeSerialize
|
||||
* TestRuntimeDeserialize
|
||||
*/
|
||||
|
@ -192,47 +187,6 @@ func TestTxGetAttributes(t *testing.T) {
|
|||
require.Equal(t, tx.Attributes[0].Usage, value[0].Value().(*transaction.Attribute).Usage)
|
||||
}
|
||||
|
||||
func TestTxGetInputs(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndPushTX(t)
|
||||
defer chain.Close()
|
||||
|
||||
err := txGetInputs(context, v)
|
||||
require.NoError(t, err)
|
||||
value := v.Estack().Pop().Value().([]vm.StackItem)
|
||||
require.Equal(t, tx.Inputs[0], *value[0].Value().(*transaction.Input))
|
||||
}
|
||||
|
||||
func TestTxGetOutputs(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndPushTX(t)
|
||||
defer chain.Close()
|
||||
|
||||
err := txGetOutputs(context, v)
|
||||
require.NoError(t, err)
|
||||
value := v.Estack().Pop().Value().([]vm.StackItem)
|
||||
require.Equal(t, tx.Outputs[0], *value[0].Value().(*transaction.Output))
|
||||
}
|
||||
|
||||
func TestTxGetType(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndPushTX(t)
|
||||
defer chain.Close()
|
||||
|
||||
err := txGetType(context, v)
|
||||
require.NoError(t, err)
|
||||
value := v.Estack().Pop().Value().(*big.Int)
|
||||
require.Equal(t, big.NewInt(int64(tx.Type)), value)
|
||||
}
|
||||
|
||||
func TestInvocationTxGetScript(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndPushTX(t)
|
||||
defer chain.Close()
|
||||
|
||||
err := invocationTxGetScript(context, v)
|
||||
require.NoError(t, err)
|
||||
value := v.Estack().Pop().Value().([]byte)
|
||||
inv := tx.Data.(*transaction.InvocationTX)
|
||||
require.Equal(t, inv.Script, value)
|
||||
}
|
||||
|
||||
func TestWitnessGetVerificationScript(t *testing.T) {
|
||||
v := vm.New()
|
||||
script := []byte{byte(opcode.PUSHM1), byte(opcode.RET)}
|
||||
|
@ -290,14 +244,14 @@ func TestECDSAVerify(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("signed interop item", func(t *testing.T) {
|
||||
tx := transaction.NewInvocationTX([]byte{0, 1, 2}, 1)
|
||||
tx := transaction.New([]byte{0, 1, 2}, 1)
|
||||
msg := tx.GetSignedPart()
|
||||
sign := priv.Sign(msg)
|
||||
runCase(t, false, true, sign, priv.PublicKey().Bytes(), vm.NewInteropItem(tx))
|
||||
})
|
||||
|
||||
t.Run("signed script container", func(t *testing.T) {
|
||||
tx := transaction.NewInvocationTX([]byte{0, 1, 2}, 1)
|
||||
tx := transaction.New([]byte{0, 1, 2}, 1)
|
||||
msg := tx.GetSignedPart()
|
||||
sign := priv.Sign(msg)
|
||||
ic.Container = tx
|
||||
|
@ -325,81 +279,6 @@ func TestECDSAVerify(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestPopInputFromVM(t *testing.T) {
|
||||
v, tx, _, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||
|
||||
input, err := popInputFromVM(v)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tx.Inputs[0], *input)
|
||||
}
|
||||
|
||||
func TestInputGetHash(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||
|
||||
err := inputGetHash(context, v)
|
||||
require.NoError(t, err)
|
||||
hash := v.Estack().Pop().Value()
|
||||
require.Equal(t, tx.Inputs[0].PrevHash.BytesBE(), hash)
|
||||
}
|
||||
|
||||
func TestInputGetIndex(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Inputs[0]))
|
||||
|
||||
err := inputGetIndex(context, v)
|
||||
require.NoError(t, err)
|
||||
index := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(tx.Inputs[0].PrevIndex)), index)
|
||||
}
|
||||
|
||||
func TestPopOutputFromVM(t *testing.T) {
|
||||
v, tx, _, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
output, err := popOutputFromVM(v)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tx.Outputs[0], *output)
|
||||
}
|
||||
|
||||
func TestOutputGetAssetID(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
err := outputGetAssetID(context, v)
|
||||
require.NoError(t, err)
|
||||
assetID := v.Estack().Pop().Value()
|
||||
require.Equal(t, tx.Outputs[0].AssetID.BytesBE(), assetID)
|
||||
}
|
||||
|
||||
func TestOutputGetScriptHash(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
err := outputGetScriptHash(context, v)
|
||||
require.NoError(t, err)
|
||||
scriptHash := v.Estack().Pop().Value()
|
||||
require.Equal(t, tx.Outputs[0].ScriptHash.BytesBE(), scriptHash)
|
||||
}
|
||||
|
||||
func TestOutputGetValue(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(&tx.Outputs[0]))
|
||||
|
||||
err := outputGetValue(context, v)
|
||||
require.NoError(t, err)
|
||||
amount := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(tx.Outputs[0].Amount)), amount)
|
||||
}
|
||||
|
||||
func TestAttrGetData(t *testing.T) {
|
||||
v, tx, context, chain := createVMAndTX(t)
|
||||
defer chain.Close()
|
||||
|
@ -455,95 +334,7 @@ func TestContractIsPayable(t *testing.T) {
|
|||
require.Equal(t, contractState.IsPayable(), isPayable)
|
||||
}
|
||||
|
||||
func TestAssetGetAdmin(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetAdmin(context, v)
|
||||
require.NoError(t, err)
|
||||
admin := v.Estack().Pop().Value()
|
||||
require.Equal(t, assetState.Admin.BytesBE(), admin)
|
||||
}
|
||||
|
||||
func TestAssetGetAmount(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetAmount(context, v)
|
||||
require.NoError(t, err)
|
||||
amount := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(assetState.Amount)), amount)
|
||||
}
|
||||
|
||||
func TestAssetGetAssetID(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetAssetID(context, v)
|
||||
require.NoError(t, err)
|
||||
assetID := v.Estack().Pop().Value()
|
||||
require.Equal(t, assetState.ID.BytesBE(), assetID)
|
||||
}
|
||||
|
||||
func TestAssetGetAssetType(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetAssetType(context, v)
|
||||
require.NoError(t, err)
|
||||
assetType := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(assetState.AssetType)), assetType)
|
||||
}
|
||||
|
||||
func TestAssetGetAvailable(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetAvailable(context, v)
|
||||
require.NoError(t, err)
|
||||
available := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(assetState.Available)), available)
|
||||
}
|
||||
|
||||
func TestAssetGetIssuer(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetIssuer(context, v)
|
||||
require.NoError(t, err)
|
||||
issuer := v.Estack().Pop().Value()
|
||||
require.Equal(t, assetState.Issuer.BytesBE(), issuer)
|
||||
}
|
||||
|
||||
func TestAssetGetOwner(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetOwner(context, v)
|
||||
require.NoError(t, err)
|
||||
owner := v.Estack().Pop().Value()
|
||||
require.Equal(t, assetState.Owner.Bytes(), owner)
|
||||
}
|
||||
|
||||
func TestAssetGetPrecision(t *testing.T) {
|
||||
v, assetState, context, chain := createVMAndAssetState(t)
|
||||
defer chain.Close()
|
||||
v.Estack().PushVal(vm.NewInteropItem(assetState))
|
||||
|
||||
err := assetGetPrecision(context, v)
|
||||
require.NoError(t, err)
|
||||
precision := v.Estack().Pop().Value()
|
||||
require.Equal(t, big.NewInt(int64(assetState.Precision)), precision)
|
||||
}
|
||||
|
||||
// Helper functions to create VM, InteropContext, TX, Account, Contract, Asset.
|
||||
// Helper functions to create VM, InteropContext, TX, Account, Contract.
|
||||
|
||||
func createVMAndPushBlock(t *testing.T) (*vm.VM, *block.Block, *interop.Context, *Blockchain) {
|
||||
v := vm.New()
|
||||
|
@ -560,29 +351,6 @@ func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop
|
|||
return v, tx, context, chain
|
||||
}
|
||||
|
||||
func createVMAndAssetState(t *testing.T) (*vm.VM, *state.Asset, *interop.Context, *Blockchain) {
|
||||
v := vm.New()
|
||||
assetState := &state.Asset{
|
||||
ID: util.Uint256{},
|
||||
AssetType: transaction.GoverningToken,
|
||||
Name: "TestAsset",
|
||||
Amount: 1,
|
||||
Available: 2,
|
||||
Precision: 1,
|
||||
FeeMode: 1,
|
||||
FeeAddress: random.Uint160(),
|
||||
Owner: keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)},
|
||||
Admin: random.Uint160(),
|
||||
Issuer: random.Uint160(),
|
||||
Expiration: 10,
|
||||
IsFrozen: false,
|
||||
}
|
||||
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)
|
||||
return v, assetState, context, chain
|
||||
}
|
||||
|
||||
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
|
||||
v := vm.New()
|
||||
contractState := &state.Contract{
|
||||
|
@ -617,7 +385,7 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context
|
|||
func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Context, *Blockchain) {
|
||||
v := vm.New()
|
||||
script := []byte{byte(opcode.PUSH1), byte(opcode.RET)}
|
||||
tx := transaction.NewInvocationTX(script, 0)
|
||||
tx := transaction.New(script, 0)
|
||||
|
||||
bytes := make([]byte, 1)
|
||||
attributes := append(tx.Attributes, transaction.Attribute{
|
||||
|
@ -625,21 +393,7 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop.Con
|
|||
Data: bytes,
|
||||
})
|
||||
|
||||
inputs := append(tx.Inputs, transaction.Input{
|
||||
PrevHash: random.Uint256(),
|
||||
PrevIndex: 1,
|
||||
})
|
||||
|
||||
outputs := append(tx.Outputs, transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: 10,
|
||||
ScriptHash: random.Uint160(),
|
||||
Position: 1,
|
||||
})
|
||||
|
||||
tx.Attributes = attributes
|
||||
tx.Inputs = inputs
|
||||
tx.Outputs = outputs
|
||||
chain := newTestChain(t)
|
||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, tx)
|
||||
return v, tx, context, chain
|
||||
|
|
|
@ -105,23 +105,12 @@ var neoInterops = []interop.Function{
|
|||
{Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
||||
{Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
||||
{Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100},
|
||||
{Name: "Neo.Asset.Create", Func: assetCreate, Price: 0},
|
||||
{Name: "Neo.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
|
||||
{Name: "Neo.Asset.GetAmount", Func: assetGetAmount, Price: 1},
|
||||
{Name: "Neo.Asset.GetAssetId", Func: assetGetAssetID, Price: 1},
|
||||
{Name: "Neo.Asset.GetAssetType", Func: assetGetAssetType, Price: 1},
|
||||
{Name: "Neo.Asset.GetAvailable", Func: assetGetAvailable, Price: 1},
|
||||
{Name: "Neo.Asset.GetIssuer", Func: assetGetIssuer, Price: 1},
|
||||
{Name: "Neo.Asset.GetOwner", Func: assetGetOwner, Price: 1},
|
||||
{Name: "Neo.Asset.GetPrecision", Func: assetGetPrecision, Price: 1},
|
||||
{Name: "Neo.Asset.Renew", Func: assetRenew, Price: 0},
|
||||
{Name: "Neo.Attribute.GetData", Func: attrGetData, Price: 1},
|
||||
{Name: "Neo.Attribute.GetUsage", Func: attrGetUsage, Price: 1},
|
||||
{Name: "Neo.Block.GetTransaction", Func: blockGetTransaction, Price: 1},
|
||||
{Name: "Neo.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1},
|
||||
{Name: "Neo.Block.GetTransactions", Func: blockGetTransactions, Price: 1},
|
||||
{Name: "Neo.Blockchain.GetAccount", Func: bcGetAccount, Price: 100},
|
||||
{Name: "Neo.Blockchain.GetAsset", Func: bcGetAsset, Price: 100},
|
||||
{Name: "Neo.Blockchain.GetBlock", Func: bcGetBlock, Price: 200},
|
||||
{Name: "Neo.Blockchain.GetContract", Func: bcGetContract, Price: 100},
|
||||
{Name: "Neo.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
||||
|
@ -148,18 +137,12 @@ var neoInterops = []interop.Function{
|
|||
{Name: "Neo.Header.GetPrevHash", Func: headerGetPrevHash, Price: 1},
|
||||
{Name: "Neo.Header.GetTimestamp", Func: headerGetTimestamp, Price: 1},
|
||||
{Name: "Neo.Header.GetVersion", Func: headerGetVersion, Price: 1},
|
||||
{Name: "Neo.Input.GetHash", Func: inputGetHash, Price: 1},
|
||||
{Name: "Neo.Input.GetIndex", Func: inputGetIndex, Price: 1},
|
||||
{Name: "Neo.InvocationTransaction.GetScript", Func: invocationTxGetScript, Price: 1},
|
||||
{Name: "Neo.Iterator.Concat", Func: iterator.Concat, Price: 1},
|
||||
{Name: "Neo.Iterator.Create", Func: iterator.Create, Price: 1},
|
||||
{Name: "Neo.Iterator.Key", Func: iterator.Key, Price: 1},
|
||||
{Name: "Neo.Iterator.Keys", Func: iterator.Keys, Price: 1},
|
||||
{Name: "Neo.Iterator.Values", Func: iterator.Values, Price: 1},
|
||||
{Name: "Neo.Native.Deploy", Func: native.Deploy, Price: 1},
|
||||
{Name: "Neo.Output.GetAssetId", Func: outputGetAssetID, Price: 1},
|
||||
{Name: "Neo.Output.GetScriptHash", Func: outputGetScriptHash, Price: 1},
|
||||
{Name: "Neo.Output.GetValue", Func: outputGetValue, Price: 1},
|
||||
{Name: "Neo.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200},
|
||||
{Name: "Neo.Runtime.Deserialize", Func: runtimeDeserialize, Price: 1},
|
||||
{Name: "Neo.Runtime.GetTime", Func: runtimeGetTime, Price: 1},
|
||||
|
@ -176,11 +159,6 @@ var neoInterops = []interop.Function{
|
|||
{Name: "Neo.StorageContext.AsReadOnly", Func: storageContextAsReadOnly, Price: 1},
|
||||
{Name: "Neo.Transaction.GetAttributes", Func: txGetAttributes, Price: 1},
|
||||
{Name: "Neo.Transaction.GetHash", Func: txGetHash, Price: 1},
|
||||
{Name: "Neo.Transaction.GetInputs", Func: txGetInputs, Price: 1},
|
||||
{Name: "Neo.Transaction.GetOutputs", Func: txGetOutputs, Price: 1},
|
||||
{Name: "Neo.Transaction.GetReferences", Func: txGetReferences, Price: 200},
|
||||
{Name: "Neo.Transaction.GetType", Func: txGetType, Price: 1},
|
||||
{Name: "Neo.Transaction.GetUnspentCoins", Func: txGetUnspentCoins, Price: 200},
|
||||
{Name: "Neo.Transaction.GetWitnesses", Func: txGetWitnesses, Price: 200},
|
||||
{Name: "Neo.Witness.GetVerificationScript", Func: witnessGetVerificationScript, Price: 100},
|
||||
|
||||
|
@ -191,23 +169,12 @@ var neoInterops = []interop.Function{
|
|||
// Old compatibility APIs.
|
||||
{Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
||||
{Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
||||
{Name: "AntShares.Asset.Create", Func: assetCreate, Price: 0},
|
||||
{Name: "AntShares.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
|
||||
{Name: "AntShares.Asset.GetAmount", Func: assetGetAmount, Price: 1},
|
||||
{Name: "AntShares.Asset.GetAssetId", Func: assetGetAssetID, Price: 1},
|
||||
{Name: "AntShares.Asset.GetAssetType", Func: assetGetAssetType, Price: 1},
|
||||
{Name: "AntShares.Asset.GetAvailable", Func: assetGetAvailable, Price: 1},
|
||||
{Name: "AntShares.Asset.GetIssuer", Func: assetGetIssuer, Price: 1},
|
||||
{Name: "AntShares.Asset.GetOwner", Func: assetGetOwner, Price: 1},
|
||||
{Name: "AntShares.Asset.GetPrecision", Func: assetGetPrecision, Price: 1},
|
||||
{Name: "AntShares.Asset.Renew", Func: assetRenew, Price: 0},
|
||||
{Name: "AntShares.Attribute.GetData", Func: attrGetData, Price: 1},
|
||||
{Name: "AntShares.Attribute.GetUsage", Func: attrGetUsage, Price: 1},
|
||||
{Name: "AntShares.Block.GetTransaction", Func: blockGetTransaction, Price: 1},
|
||||
{Name: "AntShares.Block.GetTransactionCount", Func: blockGetTransactionCount, Price: 1},
|
||||
{Name: "AntShares.Block.GetTransactions", Func: blockGetTransactions, Price: 1},
|
||||
{Name: "AntShares.Blockchain.GetAccount", Func: bcGetAccount, Price: 100},
|
||||
{Name: "AntShares.Blockchain.GetAsset", Func: bcGetAsset, Price: 100},
|
||||
{Name: "AntShares.Blockchain.GetBlock", Func: bcGetBlock, Price: 200},
|
||||
{Name: "AntShares.Blockchain.GetContract", Func: bcGetContract, Price: 100},
|
||||
{Name: "AntShares.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
||||
|
@ -224,11 +191,6 @@ var neoInterops = []interop.Function{
|
|||
{Name: "AntShares.Header.GetPrevHash", Func: headerGetPrevHash, Price: 1},
|
||||
{Name: "AntShares.Header.GetTimestamp", Func: headerGetTimestamp, Price: 1},
|
||||
{Name: "AntShares.Header.GetVersion", Func: headerGetVersion, Price: 1},
|
||||
{Name: "AntShares.Input.GetHash", Func: inputGetHash, Price: 1},
|
||||
{Name: "AntShares.Input.GetIndex", Func: inputGetIndex, Price: 1},
|
||||
{Name: "AntShares.Output.GetAssetId", Func: outputGetAssetID, Price: 1},
|
||||
{Name: "AntShares.Output.GetScriptHash", Func: outputGetScriptHash, Price: 1},
|
||||
{Name: "AntShares.Output.GetValue", Func: outputGetValue, Price: 1},
|
||||
{Name: "AntShares.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200},
|
||||
{Name: "AntShares.Runtime.Log", Func: runtimeLog, Price: 1},
|
||||
{Name: "AntShares.Runtime.Notify", Func: runtimeNotify, Price: 1},
|
||||
|
@ -238,10 +200,6 @@ var neoInterops = []interop.Function{
|
|||
{Name: "AntShares.Storage.Put", Func: storagePut, Price: 0},
|
||||
{Name: "AntShares.Transaction.GetAttributes", Func: txGetAttributes, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetHash", Func: txGetHash, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetInputs", Func: txGetInputs, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetOutputs", Func: txGetOutputs, Price: 1},
|
||||
{Name: "AntShares.Transaction.GetReferences", Func: txGetReferences, Price: 200},
|
||||
{Name: "AntShares.Transaction.GetType", Func: txGetType, Price: 1},
|
||||
}
|
||||
|
||||
// initIDinInteropsSlice initializes IDs from names in one given
|
||||
|
|
|
@ -34,15 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
funcs := []func(*interop.Context, *vm.VM) error{
|
||||
accountGetBalance,
|
||||
accountGetScriptHash,
|
||||
assetGetAdmin,
|
||||
assetGetAmount,
|
||||
assetGetAssetID,
|
||||
assetGetAssetType,
|
||||
assetGetAvailable,
|
||||
assetGetIssuer,
|
||||
assetGetOwner,
|
||||
assetGetPrecision,
|
||||
assetRenew,
|
||||
attrGetData,
|
||||
attrGetUsage,
|
||||
blockGetTransaction,
|
||||
|
@ -58,12 +49,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
headerGetPrevHash,
|
||||
headerGetTimestamp,
|
||||
headerGetVersion,
|
||||
inputGetHash,
|
||||
inputGetIndex,
|
||||
invocationTxGetScript,
|
||||
outputGetAssetID,
|
||||
outputGetScriptHash,
|
||||
outputGetValue,
|
||||
storageContextAsReadOnly,
|
||||
storageDelete,
|
||||
storageFind,
|
||||
|
@ -72,11 +57,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
storagePutEx,
|
||||
txGetAttributes,
|
||||
txGetHash,
|
||||
txGetInputs,
|
||||
txGetOutputs,
|
||||
txGetReferences,
|
||||
txGetType,
|
||||
txGetUnspentCoins,
|
||||
txGetWitnesses,
|
||||
witnessGetVerificationScript,
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
|
||||
var (
|
||||
// ErrConflict is returned when transaction being added is incompatible
|
||||
// with the contents of the memory pool (using the same inputs as some
|
||||
// other transaction in the pool)
|
||||
// with the contents of the memory pool (Sender doesn't have enough GAS
|
||||
// to pay for all transactions in the pool).
|
||||
ErrConflict = errors.New("conflicts with the memory pool")
|
||||
// ErrDup is returned when transaction being added is already present
|
||||
// in the memory pool.
|
||||
|
@ -33,12 +33,6 @@ type item struct {
|
|||
// items is a slice of item.
|
||||
type items []*item
|
||||
|
||||
// TxWithFee combines transaction and its precalculated network fee.
|
||||
type TxWithFee struct {
|
||||
Tx *transaction.Transaction
|
||||
Fee util.Fixed8
|
||||
}
|
||||
|
||||
// utilityBalanceAndFees stores sender's balance and overall fees of
|
||||
// sender's transactions which are currently in mempool
|
||||
type utilityBalanceAndFees struct {
|
||||
|
@ -51,8 +45,6 @@ type Pool struct {
|
|||
lock sync.RWMutex
|
||||
verifiedMap map[util.Uint256]*item
|
||||
verifiedTxes items
|
||||
inputs []*transaction.Input
|
||||
claims []*transaction.Input
|
||||
fees map[util.Uint160]utilityBalanceAndFees
|
||||
|
||||
capacity int
|
||||
|
@ -79,20 +71,6 @@ func (p *item) CompareTo(otherP *item) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
if p.isLowPrio && otherP.isLowPrio {
|
||||
thisIsClaimTx := p.txn.Type == transaction.ClaimType
|
||||
otherIsClaimTx := otherP.txn.Type == transaction.ClaimType
|
||||
|
||||
if thisIsClaimTx != otherIsClaimTx {
|
||||
// This is a claim Tx and other isn't.
|
||||
if thisIsClaimTx {
|
||||
return 1
|
||||
}
|
||||
// The other is claim Tx and this isn't.
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// Fees sorted ascending.
|
||||
if ret := p.txn.FeePerByte().CompareTo(otherP.txn.FeePerByte()); ret != 0 {
|
||||
return ret
|
||||
|
@ -135,35 +113,6 @@ func (mp *Pool) containsKey(hash util.Uint256) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// findIndexForInput finds an index in a sorted Input pointers slice that is
|
||||
// appropriate to place this input into (or which contains an identical Input).
|
||||
func findIndexForInput(slice []*transaction.Input, input *transaction.Input) int {
|
||||
return sort.Search(len(slice), func(n int) bool {
|
||||
return input.Cmp(slice[n]) <= 0
|
||||
})
|
||||
}
|
||||
|
||||
// pushInputToSortedSlice pushes new Input into the given slice.
|
||||
func pushInputToSortedSlice(slice *[]*transaction.Input, input *transaction.Input) {
|
||||
n := findIndexForInput(*slice, input)
|
||||
*slice = append(*slice, input)
|
||||
if n != len(*slice)-1 {
|
||||
copy((*slice)[n+1:], (*slice)[n:])
|
||||
(*slice)[n] = input
|
||||
}
|
||||
}
|
||||
|
||||
// dropInputFromSortedSlice removes given input from the given slice.
|
||||
func dropInputFromSortedSlice(slice *[]*transaction.Input, input *transaction.Input) {
|
||||
n := findIndexForInput(*slice, input)
|
||||
if n == len(*slice) || *input != *(*slice)[n] {
|
||||
// Not present.
|
||||
return
|
||||
}
|
||||
copy((*slice)[n:], (*slice)[n+1:])
|
||||
*slice = (*slice)[:len(*slice)-1]
|
||||
}
|
||||
|
||||
// tryAddSendersFee tries to add system fee and network fee to the total sender`s fee in mempool
|
||||
// and returns false if sender has not enough GAS to pay
|
||||
func (mp *Pool) tryAddSendersFee(tx *transaction.Transaction, feer Feer) bool {
|
||||
|
@ -244,18 +193,6 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
|
|||
}
|
||||
mp.addSendersFee(pItem.txn)
|
||||
|
||||
// For lots of inputs it might be easier to push them all and sort
|
||||
// afterwards, but that requires benchmarking.
|
||||
for i := range t.Inputs {
|
||||
pushInputToSortedSlice(&mp.inputs, &t.Inputs[i])
|
||||
}
|
||||
if t.Type == transaction.ClaimType {
|
||||
claim := t.Data.(*transaction.ClaimTX)
|
||||
for i := range claim.Claims {
|
||||
pushInputToSortedSlice(&mp.claims, &claim.Claims[i])
|
||||
}
|
||||
}
|
||||
|
||||
updateMempoolMetrics(len(mp.verifiedTxes))
|
||||
mp.lock.Unlock()
|
||||
return nil
|
||||
|
@ -281,15 +218,6 @@ func (mp *Pool) Remove(hash util.Uint256) {
|
|||
senderFee := mp.fees[it.txn.Sender]
|
||||
senderFee.feeSum -= it.txn.SystemFee + it.txn.NetworkFee
|
||||
mp.fees[it.txn.Sender] = senderFee
|
||||
for i := range it.txn.Inputs {
|
||||
dropInputFromSortedSlice(&mp.inputs, &it.txn.Inputs[i])
|
||||
}
|
||||
if it.txn.Type == transaction.ClaimType {
|
||||
claim := it.txn.Data.(*transaction.ClaimTX)
|
||||
for i := range claim.Claims {
|
||||
dropInputFromSortedSlice(&mp.claims, &claim.Claims[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
updateMempoolMetrics(len(mp.verifiedTxes))
|
||||
mp.lock.Unlock()
|
||||
|
@ -303,34 +231,15 @@ func (mp *Pool) RemoveStale(isOK func(*transaction.Transaction) bool, feer Feer)
|
|||
// We can reuse already allocated slice
|
||||
// because items are iterated one-by-one in increasing order.
|
||||
newVerifiedTxes := mp.verifiedTxes[:0]
|
||||
newInputs := mp.inputs[:0]
|
||||
newClaims := mp.claims[:0]
|
||||
mp.fees = make(map[util.Uint160]utilityBalanceAndFees) // it'd be nice to reuse existing map, but we can't easily clear it
|
||||
for _, itm := range mp.verifiedTxes {
|
||||
if isOK(itm.txn) && mp.tryAddSendersFee(itm.txn, feer) {
|
||||
newVerifiedTxes = append(newVerifiedTxes, itm)
|
||||
for i := range itm.txn.Inputs {
|
||||
newInputs = append(newInputs, &itm.txn.Inputs[i])
|
||||
}
|
||||
if itm.txn.Type == transaction.ClaimType {
|
||||
claim := itm.txn.Data.(*transaction.ClaimTX)
|
||||
for i := range claim.Claims {
|
||||
newClaims = append(newClaims, &claim.Claims[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
delete(mp.verifiedMap, itm.txn.Hash())
|
||||
}
|
||||
}
|
||||
sort.Slice(newInputs, func(i, j int) bool {
|
||||
return newInputs[i].Cmp(newInputs[j]) < 0
|
||||
})
|
||||
sort.Slice(newClaims, func(i, j int) bool {
|
||||
return newClaims[i].Cmp(newClaims[j]) < 0
|
||||
})
|
||||
mp.verifiedTxes = newVerifiedTxes
|
||||
mp.inputs = newInputs
|
||||
mp.claims = newClaims
|
||||
mp.lock.Unlock()
|
||||
}
|
||||
|
||||
|
@ -345,74 +254,39 @@ func NewMemPool(capacity int) Pool {
|
|||
}
|
||||
|
||||
// TryGetValue returns a transaction and its fee if it exists in the memory pool.
|
||||
func (mp *Pool) TryGetValue(hash util.Uint256) (*transaction.Transaction, util.Fixed8, bool) {
|
||||
func (mp *Pool) TryGetValue(hash util.Uint256) (*transaction.Transaction, bool) {
|
||||
mp.lock.RLock()
|
||||
defer mp.lock.RUnlock()
|
||||
if pItem, ok := mp.verifiedMap[hash]; ok {
|
||||
return pItem.txn, pItem.txn.NetworkFee, ok
|
||||
return pItem.txn, ok
|
||||
}
|
||||
|
||||
return nil, 0, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// GetVerifiedTransactions returns a slice of Input from all the transactions in the memory pool
|
||||
// whose hash is not included in excludedHashes.
|
||||
func (mp *Pool) GetVerifiedTransactions() []TxWithFee {
|
||||
// GetVerifiedTransactions returns a slice of transactions with their fees.
|
||||
func (mp *Pool) GetVerifiedTransactions() []*transaction.Transaction {
|
||||
mp.lock.RLock()
|
||||
defer mp.lock.RUnlock()
|
||||
|
||||
var t = make([]TxWithFee, len(mp.verifiedTxes))
|
||||
var t = make([]*transaction.Transaction, len(mp.verifiedTxes))
|
||||
|
||||
for i := range mp.verifiedTxes {
|
||||
t[i].Tx = mp.verifiedTxes[i].txn
|
||||
t[i].Fee = mp.verifiedTxes[i].txn.NetworkFee
|
||||
t[i] = mp.verifiedTxes[i].txn
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// areInputsInPool tries to find inputs in a given sorted pool and returns true
|
||||
// if it finds any.
|
||||
func areInputsInPool(inputs []transaction.Input, pool []*transaction.Input) bool {
|
||||
for i := range inputs {
|
||||
n := findIndexForInput(pool, &inputs[i])
|
||||
if n < len(pool) && *pool[n] == inputs[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkTxConflicts is an internal unprotected version of Verify.
|
||||
func (mp *Pool) checkTxConflicts(tx *transaction.Transaction, fee Feer) bool {
|
||||
if areInputsInPool(tx.Inputs, mp.inputs) {
|
||||
return false
|
||||
}
|
||||
if !mp.checkBalanceAndUpdate(tx, fee) {
|
||||
return false
|
||||
}
|
||||
switch tx.Type {
|
||||
case transaction.ClaimType:
|
||||
claim := tx.Data.(*transaction.ClaimTX)
|
||||
if areInputsInPool(claim.Claims, mp.claims) {
|
||||
return false
|
||||
}
|
||||
case transaction.IssueType:
|
||||
// It's a hack, because technically we could check for
|
||||
// available asset amount, but these transactions are so rare
|
||||
// that no one really cares about this restriction.
|
||||
for i := range mp.verifiedTxes {
|
||||
if mp.verifiedTxes[i].txn.Type == transaction.IssueType {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
return mp.checkBalanceAndUpdate(tx, fee)
|
||||
}
|
||||
|
||||
// Verify verifies if the inputs of a transaction tx are already used in any other transaction in the memory pool.
|
||||
// If yes, the transaction tx is not a valid transaction and the function return false.
|
||||
// If no, the transaction tx is a valid transaction and the function return true.
|
||||
// Verify checks if a Sender of tx is able to pay for it (and all the other
|
||||
// transactions in the pool). If yes, the transaction tx is a valid
|
||||
// transaction and the function returns true. If no, the transaction tx is
|
||||
// considered to be invalid the function returns false.
|
||||
func (mp *Pool) Verify(tx *transaction.Transaction, feer Feer) bool {
|
||||
mp.lock.RLock()
|
||||
defer mp.lock.RUnlock()
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -30,18 +30,18 @@ func (fs *FeerStub) GetUtilityTokenBalance(uint160 util.Uint160) util.Fixed8 {
|
|||
|
||||
func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) {
|
||||
mp := NewMemPool(10)
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = 0
|
||||
_, _, ok := mp.TryGetValue(tx.Hash())
|
||||
_, ok := mp.TryGetValue(tx.Hash())
|
||||
require.Equal(t, false, ok)
|
||||
require.NoError(t, mp.Add(tx, fs))
|
||||
// Re-adding should fail.
|
||||
require.Error(t, mp.Add(tx, fs))
|
||||
tx2, _, ok := mp.TryGetValue(tx.Hash())
|
||||
tx2, ok := mp.TryGetValue(tx.Hash())
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, tx, tx2)
|
||||
mp.Remove(tx.Hash())
|
||||
_, _, ok = mp.TryGetValue(tx.Hash())
|
||||
_, ok = mp.TryGetValue(tx.Hash())
|
||||
require.Equal(t, false, ok)
|
||||
// Make sure nothing left in the mempool after removal.
|
||||
assert.Equal(t, 0, len(mp.verifiedMap))
|
||||
|
@ -55,162 +55,13 @@ func TestMemPoolAddRemove(t *testing.T) {
|
|||
t.Run("high priority", func(t *testing.T) { testMemPoolAddRemoveWithFeer(t, fs) })
|
||||
}
|
||||
|
||||
func TestMemPoolAddRemoveWithInputsAndClaims(t *testing.T) {
|
||||
mp := NewMemPool(50)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
mpLessInputs := func(i, j int) bool {
|
||||
return mp.inputs[i].Cmp(mp.inputs[j]) < 0
|
||||
}
|
||||
mpLessClaims := func(i, j int) bool {
|
||||
return mp.claims[i].Cmp(mp.claims[j]) < 0
|
||||
}
|
||||
txm1 := transaction.NewContractTX()
|
||||
txm1.Nonce = 1
|
||||
txc1, claim1 := newClaimTX()
|
||||
for i := 0; i < 5; i++ {
|
||||
txm1.Inputs = append(txm1.Inputs, transaction.Input{PrevHash: hash1, PrevIndex: uint16(100 - i)})
|
||||
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash1, PrevIndex: uint16(100 - i)})
|
||||
}
|
||||
require.NoError(t, mp.Add(txm1, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(txc1, &FeerStub{}))
|
||||
// Look inside.
|
||||
assert.Equal(t, len(txm1.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
assert.Equal(t, len(claim1.Claims), len(mp.claims))
|
||||
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
|
||||
|
||||
txm2 := transaction.NewContractTX()
|
||||
txm2.Nonce = 1
|
||||
txc2, claim2 := newClaimTX()
|
||||
for i := 0; i < 10; i++ {
|
||||
txm2.Inputs = append(txm2.Inputs, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
claim2.Claims = append(claim2.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
}
|
||||
require.NoError(t, mp.Add(txm2, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(txc2, &FeerStub{}))
|
||||
assert.Equal(t, len(txm1.Inputs)+len(txm2.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
assert.Equal(t, len(claim1.Claims)+len(claim2.Claims), len(mp.claims))
|
||||
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
|
||||
|
||||
mp.Remove(txm1.Hash())
|
||||
mp.Remove(txc2.Hash())
|
||||
assert.Equal(t, len(txm2.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
assert.Equal(t, len(claim1.Claims), len(mp.claims))
|
||||
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
|
||||
|
||||
require.NoError(t, mp.Add(txm1, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(txc2, &FeerStub{}))
|
||||
assert.Equal(t, len(txm1.Inputs)+len(txm2.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
assert.Equal(t, len(claim1.Claims)+len(claim2.Claims), len(mp.claims))
|
||||
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
|
||||
|
||||
mp.RemoveStale(func(t *transaction.Transaction) bool {
|
||||
if t.Hash() == txc1.Hash() || t.Hash() == txm2.Hash() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, &FeerStub{})
|
||||
assert.Equal(t, len(txm1.Inputs), len(mp.inputs))
|
||||
assert.True(t, sort.SliceIsSorted(mp.inputs, mpLessInputs))
|
||||
assert.Equal(t, len(claim2.Claims), len(mp.claims))
|
||||
assert.True(t, sort.SliceIsSorted(mp.claims, mpLessClaims))
|
||||
}
|
||||
|
||||
func TestMemPoolVerifyInputs(t *testing.T) {
|
||||
mp := NewMemPool(10)
|
||||
tx := transaction.NewContractTX()
|
||||
tx.Nonce = 1
|
||||
inhash1 := random.Uint256()
|
||||
tx.Inputs = append(tx.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 0})
|
||||
require.Equal(t, true, mp.Verify(tx, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(tx, &FeerStub{}))
|
||||
|
||||
tx2 := transaction.NewContractTX()
|
||||
tx2.Nonce = 2
|
||||
inhash2 := random.Uint256()
|
||||
tx2.Inputs = append(tx2.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
||||
require.Equal(t, true, mp.Verify(tx2, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||
|
||||
tx3 := transaction.NewContractTX()
|
||||
tx3.Nonce = 3
|
||||
// Different index number, but the same PrevHash as in tx1.
|
||||
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash1, PrevIndex: 1})
|
||||
require.Equal(t, true, mp.Verify(tx3, &FeerStub{}))
|
||||
// The same input as in tx2.
|
||||
tx3.Inputs = append(tx3.Inputs, transaction.Input{PrevHash: inhash2, PrevIndex: 0})
|
||||
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
|
||||
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
||||
}
|
||||
|
||||
func TestMemPoolVerifyClaims(t *testing.T) {
|
||||
mp := NewMemPool(50)
|
||||
tx1, claim1 := newClaimTX()
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
for i := 0; i < 10; i++ {
|
||||
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
||||
claim1.Claims = append(claim1.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
}
|
||||
require.Equal(t, true, mp.Verify(tx1, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
||||
|
||||
tx2, claim2 := newClaimTX()
|
||||
for i := 0; i < 10; i++ {
|
||||
claim2.Claims = append(claim2.Claims, transaction.Input{PrevHash: hash2, PrevIndex: uint16(i + 10)})
|
||||
}
|
||||
require.Equal(t, true, mp.Verify(tx2, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||
|
||||
tx3, claim3 := newClaimTX()
|
||||
claim3.Claims = append(claim3.Claims, transaction.Input{PrevHash: hash1, PrevIndex: 0})
|
||||
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
|
||||
require.Error(t, mp.Add(tx3, &FeerStub{}))
|
||||
}
|
||||
|
||||
func TestMemPoolVerifyIssue(t *testing.T) {
|
||||
mp := NewMemPool(50)
|
||||
tx1 := newIssueTX()
|
||||
require.Equal(t, true, mp.Verify(tx1, &FeerStub{}))
|
||||
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
||||
|
||||
tx2 := newIssueTX()
|
||||
require.Equal(t, false, mp.Verify(tx2, &FeerStub{}))
|
||||
require.Error(t, mp.Add(tx2, &FeerStub{}))
|
||||
}
|
||||
|
||||
func newIssueTX() *transaction.Transaction {
|
||||
tx := transaction.NewIssueTX()
|
||||
tx.Outputs = []transaction.Output{
|
||||
{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8FromInt64(42),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
func newClaimTX() (*transaction.Transaction, *transaction.ClaimTX) {
|
||||
cl := &transaction.ClaimTX{}
|
||||
return transaction.NewClaimTX(cl), cl
|
||||
}
|
||||
|
||||
func TestOverCapacity(t *testing.T) {
|
||||
var fs = &FeerStub{lowPriority: true}
|
||||
const mempoolSize = 10
|
||||
mp := NewMemPool(mempoolSize)
|
||||
|
||||
for i := 0; i < mempoolSize; i++ {
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = uint32(i)
|
||||
require.NoError(t, mp.Add(tx, fs))
|
||||
}
|
||||
|
@ -218,19 +69,9 @@ func TestOverCapacity(t *testing.T) {
|
|||
require.Equal(t, mempoolSize, mp.Count())
|
||||
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
||||
|
||||
// Claim TX has more priority than ordinary lowprio, so it should easily
|
||||
// fit into the pool.
|
||||
claim := &transaction.Transaction{
|
||||
Type: transaction.ClaimType,
|
||||
Data: &transaction.ClaimTX{},
|
||||
}
|
||||
require.NoError(t, mp.Add(claim, fs))
|
||||
require.Equal(t, mempoolSize, mp.Count())
|
||||
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
||||
|
||||
// Fees are also prioritized.
|
||||
for i := 0; i < mempoolSize-1; i++ {
|
||||
tx := transaction.NewContractTX()
|
||||
for i := 0; i < mempoolSize; i++ {
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Attributes = append(tx.Attributes, transaction.Attribute{
|
||||
Usage: transaction.Hash1,
|
||||
Data: util.Uint256{1, 2, 3, 4}.BytesBE(),
|
||||
|
@ -244,23 +85,20 @@ func TestOverCapacity(t *testing.T) {
|
|||
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
||||
}
|
||||
// Less prioritized txes are not allowed anymore.
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Attributes = append(tx.Attributes, transaction.Attribute{
|
||||
Usage: transaction.Hash1,
|
||||
Data: util.Uint256{1, 2, 3, 4}.BytesBE(),
|
||||
})
|
||||
tx.NetworkFee = util.Fixed8FromFloat(0.00001)
|
||||
tx.NetworkFee = util.Fixed8FromFloat(0.000001)
|
||||
tx.Nonce = txcnt
|
||||
txcnt++
|
||||
require.Error(t, mp.Add(tx, fs))
|
||||
require.Equal(t, mempoolSize, mp.Count())
|
||||
require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
|
||||
|
||||
// But claim tx should still be there.
|
||||
require.True(t, mp.ContainsKey(claim.Hash()))
|
||||
|
||||
// Low net fee, but higher per-byte fee is still a better combination.
|
||||
tx = transaction.NewContractTX()
|
||||
tx = transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = txcnt
|
||||
tx.NetworkFee = util.Fixed8FromFloat(0.00007)
|
||||
txcnt++
|
||||
|
@ -273,7 +111,7 @@ func TestOverCapacity(t *testing.T) {
|
|||
// High priority always wins over low priority.
|
||||
fs.lowPriority = false
|
||||
for i := 0; i < mempoolSize; i++ {
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = txcnt
|
||||
txcnt++
|
||||
require.NoError(t, mp.Add(tx, fs))
|
||||
|
@ -282,7 +120,7 @@ func TestOverCapacity(t *testing.T) {
|
|||
}
|
||||
// Good luck with low priority now.
|
||||
fs.lowPriority = true
|
||||
tx = transaction.NewContractTX()
|
||||
tx = transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = txcnt
|
||||
require.Error(t, mp.Add(tx, fs))
|
||||
require.Equal(t, mempoolSize, mp.Count())
|
||||
|
@ -296,7 +134,7 @@ func TestGetVerified(t *testing.T) {
|
|||
|
||||
txes := make([]*transaction.Transaction, 0, mempoolSize)
|
||||
for i := 0; i < mempoolSize; i++ {
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = uint32(i)
|
||||
txes = append(txes, tx)
|
||||
require.NoError(t, mp.Add(tx, fs))
|
||||
|
@ -304,9 +142,7 @@ func TestGetVerified(t *testing.T) {
|
|||
require.Equal(t, mempoolSize, mp.Count())
|
||||
verTxes := mp.GetVerifiedTransactions()
|
||||
require.Equal(t, mempoolSize, len(verTxes))
|
||||
for _, txf := range verTxes {
|
||||
require.Contains(t, txes, txf.Tx)
|
||||
}
|
||||
require.ElementsMatch(t, txes, verTxes)
|
||||
for _, tx := range txes {
|
||||
mp.Remove(tx.Hash())
|
||||
}
|
||||
|
@ -322,7 +158,7 @@ func TestRemoveStale(t *testing.T) {
|
|||
txes1 := make([]*transaction.Transaction, 0, mempoolSize/2)
|
||||
txes2 := make([]*transaction.Transaction, 0, mempoolSize/2)
|
||||
for i := 0; i < mempoolSize; i++ {
|
||||
tx := transaction.NewContractTX()
|
||||
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx.Nonce = uint32(i)
|
||||
if i%2 == 0 {
|
||||
txes1 = append(txes1, tx)
|
||||
|
@ -343,15 +179,15 @@ func TestRemoveStale(t *testing.T) {
|
|||
require.Equal(t, mempoolSize/2, mp.Count())
|
||||
verTxes := mp.GetVerifiedTransactions()
|
||||
for _, txf := range verTxes {
|
||||
require.NotContains(t, txes1, txf.Tx)
|
||||
require.Contains(t, txes2, txf.Tx)
|
||||
require.NotContains(t, txes1, txf)
|
||||
require.Contains(t, txes2, txf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemPoolFees(t *testing.T) {
|
||||
mp := NewMemPool(10)
|
||||
sender0 := util.Uint160{1, 2, 3}
|
||||
tx0 := transaction.NewContractTX()
|
||||
tx0 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx0.NetworkFee = util.Fixed8FromInt64(11000)
|
||||
tx0.Sender = sender0
|
||||
// insufficient funds to add transaction, but balance should be stored
|
||||
|
@ -364,7 +200,7 @@ func TestMemPoolFees(t *testing.T) {
|
|||
}, mp.fees[sender0])
|
||||
|
||||
// no problems with adding another transaction with lower fee
|
||||
tx1 := transaction.NewContractTX()
|
||||
tx1 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx1.NetworkFee = util.Fixed8FromInt64(7000)
|
||||
tx1.Sender = sender0
|
||||
require.NoError(t, mp.Add(tx1, &FeerStub{}))
|
||||
|
@ -375,7 +211,7 @@ func TestMemPoolFees(t *testing.T) {
|
|||
}, mp.fees[sender0])
|
||||
|
||||
// balance shouldn't change after adding one more transaction
|
||||
tx2 := transaction.NewContractTX()
|
||||
tx2 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx2.NetworkFee = util.Fixed8FromFloat(3000)
|
||||
tx2.Sender = sender0
|
||||
require.NoError(t, mp.Add(tx2, &FeerStub{}))
|
||||
|
@ -387,7 +223,7 @@ func TestMemPoolFees(t *testing.T) {
|
|||
}, mp.fees[sender0])
|
||||
|
||||
// can't add more transactions as we don't have enough GAS
|
||||
tx3 := transaction.NewContractTX()
|
||||
tx3 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
||||
tx3.NetworkFee = util.Fixed8FromFloat(0.5)
|
||||
tx3.Sender = sender0
|
||||
require.Equal(t, false, mp.Verify(tx3, &FeerStub{}))
|
||||
|
|
|
@ -10,9 +10,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
)
|
||||
|
||||
// GAS represents GAS native contract.
|
||||
|
@ -40,11 +38,6 @@ func NewGAS() *GAS {
|
|||
|
||||
g.nep5TokenNative = *nep5
|
||||
|
||||
desc := newDescriptor("getSysFeeAmount", smartcontract.IntegerType,
|
||||
manifest.NewParameter("index", smartcontract.IntegerType))
|
||||
md := newMethodAndPrice(g.getSysFeeAmount, 1, smartcontract.NoneFlag)
|
||||
g.AddMethod(md, desc, true)
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
|
@ -98,16 +91,6 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
index := toBigInt(args[0])
|
||||
h := ic.Chain.GetHeaderHash(int(index.Int64()))
|
||||
_, sf, err := ic.DAO.GetBlock(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vm.NewBigIntegerItem(big.NewInt(int64(sf)))
|
||||
}
|
||||
|
||||
func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, []*keys.PublicKey, error) {
|
||||
vs, err := ic.Chain.GetStandByValidators()
|
||||
if err != nil {
|
||||
|
|
|
@ -188,12 +188,9 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
|
|||
if ic.Block == nil || ic.Block.Index == 0 {
|
||||
return nil
|
||||
}
|
||||
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.Balance.Int64()), acc.BalanceHeight, ic.Block.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gen := ic.Chain.CalculateClaimable(acc.Balance.Int64(), acc.BalanceHeight, ic.Block.Index)
|
||||
acc.BalanceHeight = ic.Block.Index
|
||||
n.GAS.mint(ic, h, big.NewInt(int64(sys+net)))
|
||||
n.GAS.mint(ic, h, big.NewInt(int64(gen)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -206,11 +203,8 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []vm.StackItem) vm.StackIte
|
|||
}
|
||||
tr := bs.Trackers[n.Hash]
|
||||
|
||||
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(tr.Balance), tr.LastUpdatedBlock, end)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vm.NewBigIntegerItem(big.NewInt(int64(sys.Add(net))))
|
||||
gen := ic.Chain.CalculateClaimable(tr.Balance, tr.LastUpdatedBlock, end)
|
||||
return vm.NewBigIntegerItem(big.NewInt(int64(gen)))
|
||||
}
|
||||
|
||||
func (n *NEO) registerValidator(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
|
|
|
@ -169,6 +169,8 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
|
|||
inc := amount
|
||||
if isEmpty {
|
||||
inc = big.NewInt(0)
|
||||
} else {
|
||||
inc = new(big.Int).Neg(inc)
|
||||
}
|
||||
if err := c.incBalance(ic, from, siFrom, inc); err != nil {
|
||||
return err
|
||||
|
@ -206,11 +208,17 @@ func (c *nep5TokenNative) balanceOf(ic *interop.Context, args []vm.StackItem) vm
|
|||
}
|
||||
|
||||
func (c *nep5TokenNative) mint(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||
if amount.Sign() == 0 {
|
||||
return
|
||||
}
|
||||
c.addTokens(ic, h, amount)
|
||||
c.emitTransfer(ic, nil, &h, amount)
|
||||
}
|
||||
|
||||
func (c *nep5TokenNative) burn(ic *interop.Context, h util.Uint160, amount *big.Int) {
|
||||
if amount.Sign() == 0 {
|
||||
return
|
||||
}
|
||||
amount = new(big.Int).Neg(amount)
|
||||
c.addTokens(ic, h, amount)
|
||||
c.emitTransfer(ic, &h, nil, amount)
|
||||
|
|
|
@ -92,7 +92,7 @@ func TestNativeContract_Invoke(t *testing.T) {
|
|||
w := io.NewBufBinWriter()
|
||||
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
||||
script := w.Bytes()
|
||||
tx := transaction.NewInvocationTX(script, 0)
|
||||
tx := transaction.New(script, 0)
|
||||
validUntil := chain.blockHeight + 1
|
||||
tx.ValidUntilBlock = validUntil
|
||||
require.NoError(t, addSender(tx))
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package core
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
|
||||
// spentCoin represents the state of a single spent coin output.
|
||||
type spentCoin struct {
|
||||
Output *transaction.Output
|
||||
StartHeight uint32
|
||||
EndHeight uint32
|
||||
}
|
|
@ -13,16 +13,6 @@ type UnspentBalance struct {
|
|||
Value util.Fixed8 `json:"value"`
|
||||
}
|
||||
|
||||
// UnclaimedBalance represents transaction output which was spent and
|
||||
// can be claimed.
|
||||
type UnclaimedBalance struct {
|
||||
Tx util.Uint256
|
||||
Index uint16
|
||||
Start uint32
|
||||
End uint32
|
||||
Value util.Fixed8
|
||||
}
|
||||
|
||||
// UnspentBalances is a slice of UnspentBalance (mostly needed to sort them).
|
||||
type UnspentBalances []UnspentBalance
|
||||
|
||||
|
@ -32,7 +22,6 @@ type Account struct {
|
|||
ScriptHash util.Uint160
|
||||
IsFrozen bool
|
||||
Balances map[util.Uint256][]UnspentBalance
|
||||
Unclaimed UnclaimedBalances
|
||||
}
|
||||
|
||||
// NewAccount returns a new Account object.
|
||||
|
@ -42,7 +31,6 @@ func NewAccount(scriptHash util.Uint160) *Account {
|
|||
ScriptHash: scriptHash,
|
||||
IsFrozen: false,
|
||||
Balances: make(map[util.Uint256][]UnspentBalance),
|
||||
Unclaimed: UnclaimedBalances{Raw: []byte{}},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,10 +52,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
|
|||
}
|
||||
s.Balances[key] = ubs
|
||||
}
|
||||
|
||||
lenBalances = br.ReadVarUint()
|
||||
s.Unclaimed.Raw = make([]byte, lenBalances*UnclaimedBalanceSize)
|
||||
br.ReadBytes(s.Unclaimed.Raw)
|
||||
}
|
||||
|
||||
// EncodeBinary encodes Account to the given BinWriter.
|
||||
|
@ -84,9 +68,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
|
|||
v[i].EncodeBinary(bw)
|
||||
}
|
||||
}
|
||||
|
||||
bw.WriteVarUint(uint64(s.Unclaimed.Size()))
|
||||
bw.WriteBytes(s.Unclaimed.Raw)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
|
@ -103,24 +84,6 @@ func (u *UnspentBalance) EncodeBinary(w *io.BinWriter) {
|
|||
u.Value.EncodeBinary(w)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (u *UnclaimedBalance) DecodeBinary(r *io.BinReader) {
|
||||
u.Tx.DecodeBinary(r)
|
||||
u.Index = r.ReadU16LE()
|
||||
u.Start = r.ReadU32LE()
|
||||
u.End = r.ReadU32LE()
|
||||
u.Value.DecodeBinary(r)
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (u *UnclaimedBalance) EncodeBinary(w *io.BinWriter) {
|
||||
u.Tx.EncodeBinary(w)
|
||||
w.WriteU16LE(u.Index)
|
||||
w.WriteU32LE(u.Start)
|
||||
w.WriteU32LE(u.End)
|
||||
u.Value.EncodeBinary(w)
|
||||
}
|
||||
|
||||
// GetBalanceValues sums all unspent outputs and returns a map of asset IDs to
|
||||
// overall balances.
|
||||
func (s *Account) GetBalanceValues() map[util.Uint256]util.Fixed8 {
|
||||
|
|
|
@ -30,7 +30,6 @@ func TestDecodeEncodeAccountState(t *testing.T) {
|
|||
ScriptHash: random.Uint160(),
|
||||
IsFrozen: true,
|
||||
Balances: balances,
|
||||
Unclaimed: UnclaimedBalances{Raw: []byte{}},
|
||||
}
|
||||
|
||||
testserdes.EncodeDecodeBinary(t, a, new(Account))
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
const feeMode = 0x0
|
||||
|
||||
// Asset represents the state of an NEO registered Asset.
|
||||
type Asset struct {
|
||||
ID util.Uint256
|
||||
AssetType transaction.AssetType
|
||||
Name string
|
||||
Amount util.Fixed8
|
||||
Available util.Fixed8
|
||||
Precision uint8
|
||||
FeeMode uint8
|
||||
FeeAddress util.Uint160
|
||||
Owner keys.PublicKey
|
||||
Admin util.Uint160
|
||||
Issuer util.Uint160
|
||||
Expiration uint32
|
||||
IsFrozen bool
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (a *Asset) DecodeBinary(br *io.BinReader) {
|
||||
br.ReadBytes(a.ID[:])
|
||||
a.AssetType = transaction.AssetType(br.ReadB())
|
||||
|
||||
a.Name = br.ReadString()
|
||||
|
||||
a.Amount.DecodeBinary(br)
|
||||
a.Available.DecodeBinary(br)
|
||||
a.Precision = uint8(br.ReadB())
|
||||
a.FeeMode = uint8(br.ReadB())
|
||||
a.FeeAddress.DecodeBinary(br)
|
||||
|
||||
a.Owner.DecodeBinary(br)
|
||||
a.Admin.DecodeBinary(br)
|
||||
a.Issuer.DecodeBinary(br)
|
||||
a.Expiration = br.ReadU32LE()
|
||||
a.IsFrozen = br.ReadBool()
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (a *Asset) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteBytes(a.ID[:])
|
||||
bw.WriteB(byte(a.AssetType))
|
||||
bw.WriteString(a.Name)
|
||||
a.Amount.EncodeBinary(bw)
|
||||
a.Available.EncodeBinary(bw)
|
||||
bw.WriteB(byte(a.Precision))
|
||||
bw.WriteB(byte(a.FeeMode))
|
||||
a.FeeAddress.EncodeBinary(bw)
|
||||
|
||||
a.Owner.EncodeBinary(bw)
|
||||
|
||||
a.Admin.EncodeBinary(bw)
|
||||
a.Issuer.EncodeBinary(bw)
|
||||
bw.WriteU32LE(a.Expiration)
|
||||
bw.WriteBool(a.IsFrozen)
|
||||
}
|
||||
|
||||
// GetName returns the asset name based on its type.
|
||||
func (a *Asset) GetName() string {
|
||||
|
||||
if a.AssetType == transaction.GoverningToken {
|
||||
return "NEO"
|
||||
} else if a.AssetType == transaction.UtilityToken {
|
||||
return "NEOGas"
|
||||
}
|
||||
|
||||
return a.Name
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEncodeDecodeAssetState(t *testing.T) {
|
||||
asset := &Asset{
|
||||
ID: random.Uint256(),
|
||||
AssetType: transaction.Token,
|
||||
Name: "super cool token",
|
||||
Amount: util.Fixed8(1000000),
|
||||
Available: util.Fixed8(100),
|
||||
Precision: 0,
|
||||
FeeMode: feeMode,
|
||||
Owner: keys.PublicKey{},
|
||||
Admin: random.Uint160(),
|
||||
Issuer: random.Uint160(),
|
||||
Expiration: 10,
|
||||
IsFrozen: false,
|
||||
}
|
||||
|
||||
testserdes.EncodeDecodeBinary(t, asset, new(Asset))
|
||||
}
|
||||
|
||||
func TestAssetState_GetName_NEO(t *testing.T) {
|
||||
asset := &Asset{AssetType: transaction.GoverningToken}
|
||||
assert.Equal(t, "NEO", asset.GetName())
|
||||
}
|
||||
|
||||
func TestAssetState_GetName_NEOGas(t *testing.T) {
|
||||
asset := &Asset{AssetType: transaction.UtilityToken}
|
||||
assert.Equal(t, "NEOGas", asset.GetName())
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// UnclaimedBalanceSize is a size of the UnclaimedBalance struct in bytes.
|
||||
const UnclaimedBalanceSize = util.Uint256Size + 2 + 4 + 4 + 8
|
||||
|
||||
// UnclaimedBalances is a slice of UnclaimedBalance.
|
||||
type UnclaimedBalances struct {
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
// Size returns an amount of store unclaimed balances.
|
||||
func (bs *UnclaimedBalances) Size() int {
|
||||
return len(bs.Raw) / UnclaimedBalanceSize
|
||||
}
|
||||
|
||||
// ForEach iterates over all unclaimed balances.
|
||||
func (bs *UnclaimedBalances) ForEach(f func(*UnclaimedBalance) error) error {
|
||||
b := new(UnclaimedBalance)
|
||||
for i := 0; i < len(bs.Raw); i += UnclaimedBalanceSize {
|
||||
r := io.NewBinReaderFromBuf(bs.Raw[i : i+UnclaimedBalanceSize])
|
||||
b.DecodeBinary(r)
|
||||
if r.Err != nil {
|
||||
return r.Err
|
||||
} else if err := f(b); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes specified unclaim from the list and returns
|
||||
// false if it wasn't found.
|
||||
func (bs *UnclaimedBalances) Remove(tx util.Uint256, index uint16) bool {
|
||||
const keySize = util.Uint256Size + 2
|
||||
key := make([]byte, keySize)
|
||||
copy(key, tx[:])
|
||||
binary.LittleEndian.PutUint16(key[util.Uint256Size:], index)
|
||||
|
||||
for i := 0; i < len(bs.Raw); i += UnclaimedBalanceSize {
|
||||
if bytes.Equal(bs.Raw[i:i+keySize], key) {
|
||||
lastIndex := len(bs.Raw) - UnclaimedBalanceSize
|
||||
if i != lastIndex {
|
||||
copy(bs.Raw[i:i+UnclaimedBalanceSize], bs.Raw[lastIndex:])
|
||||
}
|
||||
bs.Raw = bs.Raw[:lastIndex]
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Put puts new unclaim in a list.
|
||||
func (bs *UnclaimedBalances) Put(b *UnclaimedBalance) error {
|
||||
w := io.NewBufBinWriter()
|
||||
b.EncodeBinary(w.BinWriter)
|
||||
if w.Err != nil {
|
||||
return w.Err
|
||||
}
|
||||
bs.Raw = append(bs.Raw, w.Bytes()...)
|
||||
return nil
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnclaimedBalance_Structure(t *testing.T) {
|
||||
b := randomUnclaimed(t)
|
||||
buf, err := testserdes.EncodeBinary(b)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, UnclaimedBalanceSize, len(buf))
|
||||
require.Equal(t, b.Tx.BytesBE(), buf[:util.Uint256Size])
|
||||
require.Equal(t, b.Index, binary.LittleEndian.Uint16(buf[util.Uint256Size:]))
|
||||
}
|
||||
|
||||
func TestUnclaimedBalances_Put(t *testing.T) {
|
||||
bs := new(UnclaimedBalances)
|
||||
b1 := randomUnclaimed(t)
|
||||
b2 := randomUnclaimed(t)
|
||||
b3 := randomUnclaimed(t)
|
||||
|
||||
require.NoError(t, bs.Put(b1))
|
||||
require.Equal(t, 1, bs.Size())
|
||||
require.NoError(t, bs.Put(b2))
|
||||
require.Equal(t, 2, bs.Size())
|
||||
require.NoError(t, bs.Put(b3))
|
||||
require.Equal(t, 3, bs.Size())
|
||||
require.True(t, bs.Remove(b2.Tx, b2.Index))
|
||||
require.Equal(t, 2, bs.Size())
|
||||
require.False(t, bs.Remove(b2.Tx, b2.Index))
|
||||
require.Equal(t, 2, bs.Size())
|
||||
require.True(t, bs.Remove(b1.Tx, b1.Index))
|
||||
require.Equal(t, 1, bs.Size())
|
||||
require.True(t, bs.Remove(b3.Tx, b3.Index))
|
||||
require.Equal(t, 0, bs.Size())
|
||||
}
|
||||
|
||||
func TestUnclaimedBalances_ForEach(t *testing.T) {
|
||||
bs := new(UnclaimedBalances)
|
||||
b1 := randomUnclaimed(t)
|
||||
b2 := randomUnclaimed(t)
|
||||
b3 := randomUnclaimed(t)
|
||||
|
||||
require.NoError(t, bs.Put(b1))
|
||||
require.NoError(t, bs.Put(b2))
|
||||
require.NoError(t, bs.Put(b3))
|
||||
|
||||
var indices []uint16
|
||||
err := bs.ForEach(func(b *UnclaimedBalance) error {
|
||||
indices = append(indices, b.Index)
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []uint16{b1.Index, b2.Index, b3.Index}, indices)
|
||||
}
|
||||
|
||||
func randomUnclaimed(t *testing.T) *UnclaimedBalance {
|
||||
b := new(UnclaimedBalance)
|
||||
b.Tx = random.Uint256()
|
||||
b.Index = uint16(rand.Uint32())
|
||||
b.Start = rand.Uint32()
|
||||
b.End = rand.Uint32()
|
||||
b.Value = util.Fixed8(rand.Int63())
|
||||
|
||||
return b
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// UnspentCoin hold the state of a unspent coin.
|
||||
type UnspentCoin struct {
|
||||
Height uint32
|
||||
States []OutputState
|
||||
}
|
||||
|
||||
// OutputState combines transaction output (UTXO) and its state
|
||||
// (spent/claimed...) along with the height of spend (if it's spent).
|
||||
type OutputState struct {
|
||||
transaction.Output
|
||||
|
||||
SpendHeight uint32
|
||||
State Coin
|
||||
}
|
||||
|
||||
// NewUnspentCoin returns a new unspent coin state with N confirmed states.
|
||||
func NewUnspentCoin(height uint32, tx *transaction.Transaction) *UnspentCoin {
|
||||
u := &UnspentCoin{
|
||||
Height: height,
|
||||
States: make([]OutputState, len(tx.Outputs)),
|
||||
}
|
||||
for i := range tx.Outputs {
|
||||
u.States[i] = OutputState{Output: tx.Outputs[i]}
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// EncodeBinary encodes UnspentCoin to the given BinWriter.
|
||||
func (s *UnspentCoin) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteU32LE(s.Height)
|
||||
bw.WriteArray(s.States)
|
||||
bw.WriteVarUint(uint64(len(s.States)))
|
||||
}
|
||||
|
||||
// DecodeBinary decodes UnspentCoin from the given BinReader.
|
||||
func (s *UnspentCoin) DecodeBinary(br *io.BinReader) {
|
||||
s.Height = br.ReadU32LE()
|
||||
br.ReadArray(&s.States)
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (o *OutputState) EncodeBinary(w *io.BinWriter) {
|
||||
o.Output.EncodeBinary(w)
|
||||
w.WriteU32LE(o.SpendHeight)
|
||||
w.WriteB(byte(o.State))
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (o *OutputState) DecodeBinary(r *io.BinReader) {
|
||||
o.Output.DecodeBinary(r)
|
||||
o.SpendHeight = r.ReadU32LE()
|
||||
o.State = Coin(r.ReadB())
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
func TestDecodeEncodeUnspentCoin(t *testing.T) {
|
||||
unspent := &UnspentCoin{
|
||||
Height: 100500,
|
||||
States: []OutputState{
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(42),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 201000,
|
||||
State: CoinSpent,
|
||||
},
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(420),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 0,
|
||||
State: CoinConfirmed,
|
||||
},
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(4200),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 111000,
|
||||
State: CoinSpent & CoinClaimed,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testserdes.EncodeDecodeBinary(t, unspent, new(UnspentCoin))
|
||||
}
|
|
@ -10,10 +10,6 @@ const (
|
|||
DataBlock KeyPrefix = 0x01
|
||||
DataTransaction KeyPrefix = 0x02
|
||||
STAccount KeyPrefix = 0x40
|
||||
STCoin KeyPrefix = 0x44
|
||||
STSpentCoin KeyPrefix = 0x45
|
||||
STValidator KeyPrefix = 0x48
|
||||
STAsset KeyPrefix = 0x4c
|
||||
STNotification KeyPrefix = 0x4d
|
||||
STContract KeyPrefix = 0x50
|
||||
STStorage KeyPrefix = 0x70
|
||||
|
|
|
@ -11,9 +11,6 @@ var (
|
|||
DataBlock,
|
||||
DataTransaction,
|
||||
STAccount,
|
||||
STCoin,
|
||||
STValidator,
|
||||
STAsset,
|
||||
STContract,
|
||||
STStorage,
|
||||
IXHeaderHashList,
|
||||
|
@ -26,9 +23,6 @@ var (
|
|||
0x01,
|
||||
0x02,
|
||||
0x40,
|
||||
0x44,
|
||||
0x48,
|
||||
0x4c,
|
||||
0x50,
|
||||
0x70,
|
||||
0x80,
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package transaction
|
||||
|
||||
// AssetType represents a NEO asset type.
|
||||
type AssetType uint8
|
||||
|
||||
// Valid asset types.
|
||||
const (
|
||||
CreditFlag AssetType = 0x40
|
||||
DutyFlag AssetType = 0x80
|
||||
GoverningToken AssetType = 0x00
|
||||
UtilityToken AssetType = 0x01
|
||||
Currency AssetType = 0x08
|
||||
Share AssetType = DutyFlag | 0x10
|
||||
Invoice AssetType = DutyFlag | 0x18
|
||||
Token AssetType = CreditFlag | 0x20
|
||||
)
|
|
@ -1,38 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// ClaimTX represents a claim transaction.
|
||||
type ClaimTX struct {
|
||||
Claims []Input
|
||||
}
|
||||
|
||||
// NewClaimTX creates Transaction of ClaimType type.
|
||||
func NewClaimTX(claim *ClaimTX) *Transaction {
|
||||
return &Transaction{
|
||||
Type: ClaimType,
|
||||
Version: 0,
|
||||
Nonce: rand.Uint32(),
|
||||
Data: claim,
|
||||
Attributes: []Attribute{},
|
||||
Cosigners: []Cosigner{},
|
||||
Inputs: []Input{},
|
||||
Outputs: []Output{},
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (tx *ClaimTX) DecodeBinary(br *io.BinReader) {
|
||||
br.ReadArray(&tx.Claims)
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (tx *ClaimTX) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteArray(tx.Claims)
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// ContractTX represents a contract transaction.
|
||||
// This TX has not special attributes.
|
||||
type ContractTX struct{}
|
||||
|
||||
// NewContractTX creates Transaction of ContractType type.
|
||||
func NewContractTX() *Transaction {
|
||||
return &Transaction{
|
||||
Type: ContractType,
|
||||
Version: 0,
|
||||
Nonce: rand.Uint32(),
|
||||
Data: &ContractTX{},
|
||||
Attributes: []Attribute{},
|
||||
Cosigners: []Cosigner{},
|
||||
Inputs: []Input{},
|
||||
Outputs: []Output{},
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (tx *ContractTX) DecodeBinary(r *io.BinReader) {
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (tx *ContractTX) EncodeBinary(w *io.BinWriter) {
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package transaction
|
||||
|
||||
//TODO NEO3.0: Update binary
|
||||
/*
|
||||
func TestEncodeDecodeContract(t *testing.T) {
|
||||
// mainnet transaction: bdf6cc3b9af12a7565bda80933a75ee8cef1bc771d0d58effc08e4c8b436da79
|
||||
rawtx := "80000001888da99f8f497fd65c4325786a09511159c279af4e7eb532e9edd628c87cc1ee0000019b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50082167010000000a8666b4830229d6a1a9b80f6088059191c122d2b0141409e79e132290c82916a88f1a3db5cf9f3248b780cfece938ab0f0812d0e188f3a489c7d1a23def86bd69d863ae67de753b2c2392e9497eadc8eb9fc43aa52c645232103e2f6a334e05002624cf616f01a62cff2844c34a3b08ca16048c259097e315078ac"
|
||||
tx := decodeTransaction(rawtx, t)
|
||||
|
||||
assert.Equal(t, ContractType, tx.Type)
|
||||
assert.IsType(t, tx.Data, &ContractTX{})
|
||||
assert.Equal(t, 0, int(tx.Version))
|
||||
assert.Equal(t, 1, len(tx.Inputs))
|
||||
|
||||
input := tx.Inputs[0]
|
||||
|
||||
assert.Equal(t, "eec17cc828d6ede932b57e4eaf79c2591151096a7825435cd67f498f9fa98d88", input.PrevHash.StringLE())
|
||||
assert.Equal(t, 0, int(input.PrevIndex))
|
||||
assert.Equal(t, int64(706), tx.Outputs[0].Amount.IntegralValue())
|
||||
assert.Equal(t, int32(0), tx.Outputs[0].Amount.FractionalValue())
|
||||
assert.Equal(t, "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", tx.Outputs[0].AssetID.StringLE())
|
||||
assert.Equal(t, "a8666b4830229d6a1a9b80f6088059191c122d2b", tx.Outputs[0].ScriptHash.String())
|
||||
assert.Equal(t, "bdf6cc3b9af12a7565bda80933a75ee8cef1bc771d0d58effc08e4c8b436da79", tx.Hash().StringLE())
|
||||
|
||||
// Encode
|
||||
data, err := testserdes.EncodeBinary(tx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rawtx, hex.EncodeToString(data))
|
||||
}
|
||||
*/
|
|
@ -9,8 +9,6 @@ import (
|
|||
|
||||
var (
|
||||
//TODO NEO3.0: Update binary
|
||||
// https://neotracker.io/tx/2c6a45547b3898318e400e541628990a07acb00f3b9a15a8e966ae49525304da
|
||||
rawClaimTX = "020004bc67ba325d6412ff4c55b10f7e9afb54bbb2228d201b37363c3d697ac7c198f70300591cd454d7318d2087c0196abfbbd1573230380672f0f0cd004dcb4857e58cbd010031bcfbed573f5318437e95edd603922a4455ff3326a979fdd1c149a84c4cb0290000b51eb6159c58cac4fe23d90e292ad2bcb7002b0da2c474e81e1889c0649d2c490000000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c603b555f00000000005d9de59d99c0d1f6ed1496444473f4a0b538302f014140456349cec43053009accdb7781b0799c6b591c812768804ab0a0b56b5eae7a97694227fcd33e70899c075848b2cee8fae733faac6865b484d3f7df8949e2aadb232103945fae1ed3c31d778f149192b76734fcc951b400ba3598faa81ff92ebe477eacac"
|
||||
// https://neotracker.io/tx/fe4b3af60677204c57e573a57bdc97bc5059b05ad85b1474f84431f88d910f64
|
||||
rawInvocationTX = "d101590400b33f7114839c33710da24cf8e7d536b8d244f3991cf565c8146063795d3b9b3cd55aef026eae992b91063db0db53c1087472616e7366657267c5cc1cb5392019e2cc4e6d6b5ea54c8d4b6d11acf166cb072961424c54f6000000000000000001206063795d3b9b3cd55aef026eae992b91063db0db0000014140c6a131c55ca38995402dff8e92ac55d89cbed4b98dfebbcb01acbc01bd78fa2ce2061be921b8999a9ab79c2958875bccfafe7ce1bbbaf1f56580815ea3a4feed232102d41ddce2c97be4c9aa571b8a32cbc305aa29afffbcae71b0ef568db0e93929aaac"
|
||||
)
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package transaction
|
||||
|
||||
// InOut represents an Input bound to its corresponding Output which is a useful
|
||||
// combination for many purposes.
|
||||
type InOut struct {
|
||||
In Input
|
||||
Out Output
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Input represents a Transaction input (CoinReference).
|
||||
type Input struct {
|
||||
// The hash of the previous transaction.
|
||||
PrevHash util.Uint256 `json:"txid"`
|
||||
|
||||
// The index of the previous transaction.
|
||||
PrevIndex uint16 `json:"vout"`
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (in *Input) DecodeBinary(br *io.BinReader) {
|
||||
br.ReadBytes(in.PrevHash[:])
|
||||
in.PrevIndex = br.ReadU16LE()
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (in *Input) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteBytes(in.PrevHash[:])
|
||||
bw.WriteU16LE(in.PrevIndex)
|
||||
}
|
||||
|
||||
// Cmp compares two Inputs by their hash and index allowing to make a set of
|
||||
// transactions ordered.
|
||||
func (in *Input) Cmp(other *Input) int {
|
||||
hashcmp := in.PrevHash.CompareTo(other.PrevHash)
|
||||
if hashcmp == 0 {
|
||||
return int(in.PrevIndex) - int(other.PrevIndex)
|
||||
}
|
||||
return hashcmp
|
||||
}
|
||||
|
||||
// MapInputsToSorted maps given slice of inputs into a new slice of pointers
|
||||
// to inputs sorted by their PrevHash and PrevIndex.
|
||||
func MapInputsToSorted(ins []Input) []*Input {
|
||||
ptrs := make([]*Input, len(ins))
|
||||
for i := range ins {
|
||||
ptrs[i] = &ins[i]
|
||||
}
|
||||
sort.Slice(ptrs, func(i, j int) bool {
|
||||
return ptrs[i].Cmp(ptrs[j]) < 0
|
||||
})
|
||||
return ptrs
|
||||
}
|
||||
|
||||
// GroupInputsByPrevHash groups all TX inputs by their previous hash into
|
||||
// several slices (which actually are subslices of one new slice with pointers).
|
||||
// Each of these slices contains at least one element.
|
||||
func GroupInputsByPrevHash(ins []Input) [][]*Input {
|
||||
if len(ins) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ptrs := MapInputsToSorted(ins)
|
||||
var first int
|
||||
res := make([][]*Input, 0)
|
||||
currentHash := ptrs[0].PrevHash
|
||||
|
||||
for i := range ptrs {
|
||||
if !currentHash.Equals(ptrs[i].PrevHash) {
|
||||
res = append(res, ptrs[first:i])
|
||||
first = i
|
||||
currentHash = ptrs[i].PrevHash
|
||||
}
|
||||
}
|
||||
res = append(res, ptrs[first:])
|
||||
return res
|
||||
}
|
||||
|
||||
// HaveDuplicateInputs checks inputs for duplicates and returns true if there are
|
||||
// any.
|
||||
func HaveDuplicateInputs(ins []Input) bool {
|
||||
if len(ins) < 2 {
|
||||
return false
|
||||
}
|
||||
if len(ins) == 2 {
|
||||
return ins[0] == ins[1]
|
||||
}
|
||||
ptrs := MapInputsToSorted(ins)
|
||||
for i := 1; i < len(ptrs); i++ {
|
||||
if *ptrs[i] == *ptrs[i-1] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGroupInputsByPrevHash0(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
res := GroupInputsByPrevHash(inputs)
|
||||
require.Equal(t, 0, len(res))
|
||||
}
|
||||
|
||||
func TestGroupInputsByPrevHash1(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
res := GroupInputsByPrevHash(inputs)
|
||||
require.Equal(t, 1, len(res))
|
||||
require.Equal(t, 1, len(res[0]))
|
||||
assert.Equal(t, hash, res[0][0].PrevHash)
|
||||
assert.Equal(t, uint16(42), res[0][0].PrevIndex)
|
||||
}
|
||||
|
||||
func TestGroupInputsByPrevHashMany(t *testing.T) {
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
hash3, err := util.Uint256DecodeStringBE("caa41245c3e48ddc13dabe989ba8fbc59418e9228fef9efb62855b0b17d7448b")
|
||||
require.NoError(t, err)
|
||||
inputs := make([]Input, 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
||||
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
inputs = append(inputs, Input{PrevHash: hash3, PrevIndex: uint16(i)})
|
||||
}
|
||||
for i := 15; i < 20; i++ {
|
||||
inputs = append(inputs, Input{PrevHash: hash3, PrevIndex: uint16(i)})
|
||||
}
|
||||
for i := 10; i < 15; i++ {
|
||||
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
inputs = append(inputs, Input{PrevHash: hash3, PrevIndex: uint16(i)})
|
||||
}
|
||||
seen := make(map[uint16]bool)
|
||||
res := GroupInputsByPrevHash(inputs)
|
||||
require.Equal(t, 3, len(res))
|
||||
assert.Equal(t, hash2, res[0][0].PrevHash)
|
||||
assert.Equal(t, 15, len(res[0]))
|
||||
for i := range res[0] {
|
||||
assert.Equal(t, res[0][i].PrevHash, res[0][0].PrevHash)
|
||||
assert.Equal(t, false, seen[res[0][i].PrevIndex])
|
||||
seen[res[0][i].PrevIndex] = true
|
||||
}
|
||||
seen = make(map[uint16]bool)
|
||||
assert.Equal(t, hash1, res[1][0].PrevHash)
|
||||
assert.Equal(t, 10, len(res[1]))
|
||||
for i := range res[1] {
|
||||
assert.Equal(t, res[1][i].PrevHash, res[1][0].PrevHash)
|
||||
assert.Equal(t, false, seen[res[1][i].PrevIndex])
|
||||
seen[res[1][i].PrevIndex] = true
|
||||
}
|
||||
seen = make(map[uint16]bool)
|
||||
assert.Equal(t, hash3, res[2][0].PrevHash)
|
||||
assert.Equal(t, 20, len(res[2]))
|
||||
for i := range res[2] {
|
||||
assert.Equal(t, res[2][i].PrevHash, res[2][0].PrevHash)
|
||||
assert.Equal(t, false, seen[res[2][i].PrevIndex])
|
||||
seen[res[2][i].PrevIndex] = true
|
||||
}
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs0(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs1(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs2True(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
require.True(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs2FalseInd(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash, err := util.Uint256DecodeStringLE("46168f963d6d8168a870405f66cc9e13a235791013b8ee2f90cc20a8293bd1af")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 42})
|
||||
inputs = append(inputs, Input{PrevHash: hash, PrevIndex: 41})
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputs2FalseHash(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: 42})
|
||||
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: 42})
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputsMFalse(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
for i := 0; i < 10; i++ {
|
||||
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
||||
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
}
|
||||
require.False(t, HaveDuplicateInputs(inputs))
|
||||
}
|
||||
|
||||
func TestHaveDuplicateInputsMTrue(t *testing.T) {
|
||||
inputs := make([]Input, 0)
|
||||
hash1, err := util.Uint256DecodeStringBE("a83ba6ede918a501558d3170a124324aedc89909e64c4ff2c6f863094f980b25")
|
||||
require.NoError(t, err)
|
||||
hash2, err := util.Uint256DecodeStringBE("629397158f852e838077bb2715b13a2e29b0a51c2157e5466321b70ed7904ce9")
|
||||
require.NoError(t, err)
|
||||
for i := 0; i < 10; i++ {
|
||||
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: uint16(i)})
|
||||
inputs = append(inputs, Input{PrevHash: hash2, PrevIndex: uint16(i)})
|
||||
}
|
||||
inputs = append(inputs, Input{PrevHash: hash1, PrevIndex: 0})
|
||||
require.True(t, HaveDuplicateInputs(inputs))
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// InvocationTX represents a invocation transaction and is used to
|
||||
// deploy smart contract to the NEO blockchain.
|
||||
type InvocationTX struct {
|
||||
// Script output of the smart contract.
|
||||
Script []byte
|
||||
|
||||
// Gas cost of the smart contract.
|
||||
Gas util.Fixed8
|
||||
Version uint8
|
||||
}
|
||||
|
||||
// NewInvocationTX returns a new invocation transaction.
|
||||
func NewInvocationTX(script []byte, gas util.Fixed8) *Transaction {
|
||||
return &Transaction{
|
||||
Type: InvocationType,
|
||||
Version: 1,
|
||||
Nonce: rand.Uint32(),
|
||||
Data: &InvocationTX{
|
||||
Script: script,
|
||||
Gas: gas,
|
||||
Version: 1,
|
||||
},
|
||||
Attributes: []Attribute{},
|
||||
Cosigners: []Cosigner{},
|
||||
Inputs: []Input{},
|
||||
Outputs: []Output{},
|
||||
Scripts: []Witness{},
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (tx *InvocationTX) DecodeBinary(br *io.BinReader) {
|
||||
tx.Script = br.ReadVarBytes()
|
||||
if br.Err == nil && len(tx.Script) == 0 {
|
||||
br.Err = errors.New("no script")
|
||||
return
|
||||
}
|
||||
if tx.Version >= 1 {
|
||||
tx.Gas.DecodeBinary(br)
|
||||
if br.Err == nil && tx.Gas.LessThan(0) {
|
||||
br.Err = errors.New("negative gas")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
tx.Gas = util.Fixed8FromInt64(0)
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (tx *InvocationTX) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteVarBytes(tx.Script)
|
||||
if tx.Version >= 1 {
|
||||
tx.Gas.EncodeBinary(bw)
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInvocationZeroScript(t *testing.T) {
|
||||
// Zero-length script.
|
||||
in, err := hex.DecodeString("000000000000000000")
|
||||
require.NoError(t, err)
|
||||
|
||||
inv := &InvocationTX{Version: 1}
|
||||
assert.Error(t, testserdes.DecodeBinary(in, inv))
|
||||
|
||||
// PUSH1 script.
|
||||
in, err = hex.DecodeString("01510000000000000000")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NoError(t, testserdes.DecodeBinary(in, inv))
|
||||
}
|
||||
|
||||
func TestInvocationNegativeGas(t *testing.T) {
|
||||
// Negative GAS
|
||||
in, err := hex.DecodeString("015100000000000000ff")
|
||||
require.NoError(t, err)
|
||||
|
||||
inv := &InvocationTX{Version: 1}
|
||||
assert.Error(t, testserdes.DecodeBinary(in, inv))
|
||||
|
||||
// Positive GAS.
|
||||
in, err = hex.DecodeString("01510100000000000000")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NoError(t, testserdes.DecodeBinary(in, inv))
|
||||
assert.Equal(t, util.Fixed8(1), inv.Gas)
|
||||
}
|
||||
|
||||
func TestInvocationVersionZero(t *testing.T) {
|
||||
in, err := hex.DecodeString("0151")
|
||||
require.NoError(t, err)
|
||||
|
||||
inv := &InvocationTX{Version: 1}
|
||||
assert.Error(t, testserdes.DecodeBinary(in, inv))
|
||||
|
||||
inv = &InvocationTX{Version: 0}
|
||||
assert.NoError(t, testserdes.DecodeBinary(in, inv))
|
||||
assert.Equal(t, util.Fixed8(0), inv.Gas)
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// IssueTX represents a issue transaction.
|
||||
// This TX has not special attributes.
|
||||
type IssueTX struct{}
|
||||
|
||||
// NewIssueTX creates Transaction of IssueType type.
|
||||
func NewIssueTX() *Transaction {
|
||||
return &Transaction{
|
||||
Type: IssueType,
|
||||
Version: 0,
|
||||
Nonce: rand.Uint32(),
|
||||
Data: &IssueTX{},
|
||||
Attributes: []Attribute{},
|
||||
Cosigners: []Cosigner{},
|
||||
Inputs: []Input{},
|
||||
Outputs: []Output{},
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (tx *IssueTX) DecodeBinary(r *io.BinReader) {
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (tx *IssueTX) EncodeBinary(w *io.BinWriter) {
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Output represents a Transaction output.
|
||||
type Output struct {
|
||||
// The NEO asset id used in the transaction.
|
||||
AssetID util.Uint256 `json:"asset"`
|
||||
|
||||
// Amount of AssetType send or received.
|
||||
Amount util.Fixed8 `json:"value"`
|
||||
|
||||
// The address of the recipient.
|
||||
ScriptHash util.Uint160 `json:"address"`
|
||||
|
||||
// The position of the Output in slice []Output. This is actually set in NewTransactionOutputRaw
|
||||
// and used for displaying purposes.
|
||||
Position int `json:"n"`
|
||||
}
|
||||
|
||||
type outputAux struct {
|
||||
AssetID util.Uint256 `json:"asset"`
|
||||
Amount util.Fixed8 `json:"value"`
|
||||
ScriptHash string `json:"address"`
|
||||
Position int `json:"n"`
|
||||
}
|
||||
|
||||
// NewOutput returns a new transaction output.
|
||||
func NewOutput(assetID util.Uint256, amount util.Fixed8, scriptHash util.Uint160) *Output {
|
||||
return &Output{
|
||||
AssetID: assetID,
|
||||
Amount: amount,
|
||||
ScriptHash: scriptHash,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (out *Output) DecodeBinary(br *io.BinReader) {
|
||||
br.ReadBytes(out.AssetID[:])
|
||||
out.Amount.DecodeBinary(br)
|
||||
br.ReadBytes(out.ScriptHash[:])
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (out *Output) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteBytes(out.AssetID[:])
|
||||
out.Amount.EncodeBinary(bw)
|
||||
bw.WriteBytes(out.ScriptHash[:])
|
||||
}
|
||||
|
||||
// MarshalJSON implements the Marshaler interface.
|
||||
func (out *Output) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"asset": out.AssetID,
|
||||
"value": out.Amount,
|
||||
"address": address.Uint160ToString(out.ScriptHash),
|
||||
"n": out.Position,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (out *Output) UnmarshalJSON(data []byte) error {
|
||||
var outAux outputAux
|
||||
err := json.Unmarshal(data, &outAux)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.ScriptHash, err = address.StringToUint160(outAux.ScriptHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Amount = outAux.Amount
|
||||
out.AssetID = outAux.AssetID
|
||||
out.Position = outAux.Position
|
||||
return nil
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// RegisterTX represents a register transaction.
|
||||
// NOTE: This is deprecated.
|
||||
type RegisterTX struct {
|
||||
// The type of the asset being registered.
|
||||
AssetType AssetType
|
||||
|
||||
// Name of the asset being registered.
|
||||
Name string
|
||||
|
||||
// Amount registered.
|
||||
// Unlimited mode -0.00000001.
|
||||
Amount util.Fixed8
|
||||
|
||||
// Decimals.
|
||||
Precision uint8
|
||||
|
||||
// Public key of the owner.
|
||||
Owner keys.PublicKey
|
||||
|
||||
Admin util.Uint160
|
||||
}
|
||||
|
||||
// NewRegisterTX creates Transaction of RegisterType type.
|
||||
func NewRegisterTX(register *RegisterTX) *Transaction {
|
||||
return &Transaction{
|
||||
Type: RegisterType,
|
||||
Version: 0,
|
||||
Nonce: rand.Uint32(),
|
||||
Data: register,
|
||||
Attributes: []Attribute{},
|
||||
Cosigners: []Cosigner{},
|
||||
Inputs: []Input{},
|
||||
Outputs: []Output{},
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (tx *RegisterTX) DecodeBinary(br *io.BinReader) {
|
||||
tx.AssetType = AssetType(br.ReadB())
|
||||
|
||||
tx.Name = br.ReadString()
|
||||
|
||||
tx.Amount.DecodeBinary(br)
|
||||
tx.Precision = uint8(br.ReadB())
|
||||
|
||||
tx.Owner.DecodeBinary(br)
|
||||
|
||||
tx.Admin.DecodeBinary(br)
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (tx *RegisterTX) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteB(byte(tx.AssetType))
|
||||
bw.WriteString(tx.Name)
|
||||
tx.Amount.EncodeBinary(bw)
|
||||
bw.WriteB(byte(tx.Precision))
|
||||
bw.WriteBytes(tx.Owner.Bytes())
|
||||
tx.Admin.EncodeBinary(bw)
|
||||
}
|
||||
|
||||
// registeredAsset is a wrapper for RegisterTransaction
|
||||
type registeredAsset struct {
|
||||
AssetType AssetType `json:"type,omitempty"`
|
||||
Name json.RawMessage `json:"name,omitempty"`
|
||||
Amount util.Fixed8 `json:"amount,omitempty"`
|
||||
Precision uint8 `json:"precision,omitempty"`
|
||||
Owner keys.PublicKey `json:"owner,omitempty"`
|
||||
Admin string `json:"admin,omitempty"`
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
func TestRegisterTX(t *testing.T) {
|
||||
someuint160, _ := util.Uint160DecodeStringBE("4d3b96ae1bcc5a585e075e3b81920210dec16302")
|
||||
registerTx := &RegisterTX{
|
||||
AssetType: UtilityToken,
|
||||
Name: "this is some token I created",
|
||||
Amount: util.Fixed8FromInt64(1000000),
|
||||
Precision: 8,
|
||||
Admin: someuint160,
|
||||
}
|
||||
tx := NewRegisterTX(registerTx)
|
||||
_ = tx.Hash()
|
||||
|
||||
testserdes.EncodeDecodeBinary(t, tx, new(Transaction))
|
||||
}
|
||||
|
||||
//TODO NEO3.0: update binary
|
||||
/*
|
||||
func TestDecodeRegisterTXFromRawString(t *testing.T) {
|
||||
rawTX := "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000"
|
||||
b, err := hex.DecodeString(rawTX)
|
||||
require.NoError(t, err)
|
||||
|
||||
tx := &Transaction{}
|
||||
assert.NoError(t, testserdes.DecodeBinary(b, tx))
|
||||
assert.Equal(t, RegisterType, tx.Type)
|
||||
txData := tx.Data.(*RegisterTX)
|
||||
assert.Equal(t, GoverningToken, txData.AssetType)
|
||||
assert.Equal(t, "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]", txData.Name)
|
||||
assert.Equal(t, util.Fixed8FromInt64(100000000), txData.Amount)
|
||||
assert.Equal(t, uint8(0), txData.Precision)
|
||||
assert.Equal(t, keys.PublicKey{}, txData.Owner)
|
||||
assert.Equal(t, "Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt", address.Uint160ToString(txData.Admin))
|
||||
assert.Equal(t, "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", tx.Hash().StringLE())
|
||||
|
||||
testserdes.EncodeDecodeBinary(t, tx, new(Transaction))
|
||||
}
|
||||
*/
|
|
@ -1,9 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
||||
// Result represents the Result of a transaction.
|
||||
type Result struct {
|
||||
AssetID util.Uint256
|
||||
Amount util.Fixed8
|
||||
}
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
|
@ -26,9 +26,6 @@ const (
|
|||
|
||||
// Transaction is a process recorded in the NEO blockchain.
|
||||
type Transaction struct {
|
||||
// The type of the transaction.
|
||||
Type TXType
|
||||
|
||||
// The trading version which is currently 0.
|
||||
Version uint8
|
||||
|
||||
|
@ -48,9 +45,8 @@ type Transaction struct {
|
|||
// transaction should fail verification.
|
||||
ValidUntilBlock uint32
|
||||
|
||||
// Data specific to the type of the transaction.
|
||||
// This is always a pointer to a <Type>Transaction.
|
||||
Data TXer
|
||||
// Code to run in NeoVM for this transaction.
|
||||
Script []byte
|
||||
|
||||
// Transaction attributes.
|
||||
Attributes []Attribute
|
||||
|
@ -58,12 +54,6 @@ type Transaction struct {
|
|||
// Transaction cosigners (not include Sender).
|
||||
Cosigners []Cosigner
|
||||
|
||||
// The inputs of the transaction.
|
||||
Inputs []Input
|
||||
|
||||
// The outputs of the transaction.
|
||||
Outputs []Output
|
||||
|
||||
// The scripts that comes with this transaction.
|
||||
// Scripts exist out of the verification script
|
||||
// and invocation script.
|
||||
|
@ -89,6 +79,20 @@ func NewTrimmedTX(hash util.Uint256) *Transaction {
|
|||
}
|
||||
}
|
||||
|
||||
// New returns a new transaction to execute given script and pay given system
|
||||
// fee.
|
||||
func New(script []byte, gas util.Fixed8) *Transaction {
|
||||
return &Transaction{
|
||||
Version: 0,
|
||||
Nonce: rand.Uint32(),
|
||||
Script: script,
|
||||
SystemFee: gas,
|
||||
Attributes: []Attribute{},
|
||||
Cosigners: []Cosigner{},
|
||||
Scripts: []Witness{},
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns the hash of the transaction.
|
||||
func (t *Transaction) Hash() util.Uint256 {
|
||||
if t.hash.Equals(util.Uint256{}) {
|
||||
|
@ -109,20 +113,13 @@ func (t *Transaction) VerificationHash() util.Uint256 {
|
|||
return t.verificationHash
|
||||
}
|
||||
|
||||
// AddOutput adds the given output to the transaction outputs.
|
||||
func (t *Transaction) AddOutput(out *Output) {
|
||||
t.Outputs = append(t.Outputs, *out)
|
||||
}
|
||||
|
||||
// AddInput adds the given input to the transaction inputs.
|
||||
func (t *Transaction) AddInput(in *Input) {
|
||||
t.Inputs = append(t.Inputs, *in)
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
||||
t.Type = TXType(br.ReadB())
|
||||
t.Version = uint8(br.ReadB())
|
||||
if t.Version > 0 {
|
||||
br.Err = errors.New("only version 0 is supported")
|
||||
return
|
||||
}
|
||||
t.Nonce = br.ReadU32LE()
|
||||
t.Sender.DecodeBinary(br)
|
||||
t.SystemFee.DecodeBinary(br)
|
||||
|
@ -140,7 +137,6 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
|||
return
|
||||
}
|
||||
t.ValidUntilBlock = br.ReadU32LE()
|
||||
t.decodeData(br)
|
||||
|
||||
br.ReadArray(&t.Attributes)
|
||||
|
||||
|
@ -154,14 +150,12 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
|||
}
|
||||
}
|
||||
|
||||
br.ReadArray(&t.Inputs)
|
||||
br.ReadArray(&t.Outputs)
|
||||
for i := range t.Outputs {
|
||||
if t.Outputs[i].Amount.LessThan(0) {
|
||||
br.Err = errors.New("negative output")
|
||||
return
|
||||
}
|
||||
t.Script = br.ReadVarBytes()
|
||||
if br.Err == nil && len(t.Script) == 0 {
|
||||
br.Err = errors.New("no script")
|
||||
return
|
||||
}
|
||||
|
||||
br.ReadArray(&t.Scripts)
|
||||
|
||||
// Create the hash of the transaction at decode, so we dont need
|
||||
|
@ -171,28 +165,6 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *Transaction) decodeData(r *io.BinReader) {
|
||||
switch t.Type {
|
||||
case InvocationType:
|
||||
t.Data = &InvocationTX{Version: t.Version}
|
||||
t.Data.(*InvocationTX).DecodeBinary(r)
|
||||
case ClaimType:
|
||||
t.Data = &ClaimTX{}
|
||||
t.Data.(*ClaimTX).DecodeBinary(r)
|
||||
case ContractType:
|
||||
t.Data = &ContractTX{}
|
||||
t.Data.(*ContractTX).DecodeBinary(r)
|
||||
case RegisterType:
|
||||
t.Data = &RegisterTX{}
|
||||
t.Data.(*RegisterTX).DecodeBinary(r)
|
||||
case IssueType:
|
||||
t.Data = &IssueTX{}
|
||||
t.Data.(*IssueTX).DecodeBinary(r)
|
||||
default:
|
||||
r.Err = fmt.Errorf("invalid TX type %x", t.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (t *Transaction) EncodeBinary(bw *io.BinWriter) {
|
||||
t.encodeHashableFields(bw)
|
||||
|
@ -202,12 +174,10 @@ func (t *Transaction) EncodeBinary(bw *io.BinWriter) {
|
|||
// encodeHashableFields encodes the fields that are not used for
|
||||
// signing the transaction, which are all fields except the scripts.
|
||||
func (t *Transaction) encodeHashableFields(bw *io.BinWriter) {
|
||||
noData := t.Type == ContractType
|
||||
if t.Data == nil && !noData {
|
||||
bw.Err = errors.New("transaction has no data")
|
||||
if len(t.Script) == 0 {
|
||||
bw.Err = errors.New("transaction has no script")
|
||||
return
|
||||
}
|
||||
bw.WriteB(byte(t.Type))
|
||||
bw.WriteB(byte(t.Version))
|
||||
bw.WriteU32LE(t.Nonce)
|
||||
t.Sender.EncodeBinary(bw)
|
||||
|
@ -215,22 +185,13 @@ func (t *Transaction) encodeHashableFields(bw *io.BinWriter) {
|
|||
t.NetworkFee.EncodeBinary(bw)
|
||||
bw.WriteU32LE(t.ValidUntilBlock)
|
||||
|
||||
// Underlying TXer.
|
||||
if !noData {
|
||||
t.Data.EncodeBinary(bw)
|
||||
}
|
||||
|
||||
// Attributes
|
||||
bw.WriteArray(t.Attributes)
|
||||
|
||||
// Cosigners
|
||||
bw.WriteArray(t.Cosigners)
|
||||
|
||||
// Inputs
|
||||
bw.WriteArray(t.Inputs)
|
||||
|
||||
// Outputs
|
||||
bw.WriteArray(t.Outputs)
|
||||
bw.WriteVarBytes(t.Script)
|
||||
}
|
||||
|
||||
// createHash creates the hash of the transaction.
|
||||
|
@ -248,16 +209,6 @@ func (t *Transaction) createHash() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GroupOutputByAssetID groups all TX outputs by their assetID.
|
||||
func (t Transaction) GroupOutputByAssetID() map[util.Uint256][]*Output {
|
||||
m := make(map[util.Uint256][]*Output)
|
||||
for i := range t.Outputs {
|
||||
hash := t.Outputs[i].AssetID
|
||||
m[hash] = append(m[hash], &t.Outputs[i])
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// GetSignedPart returns a part of the transaction which must be signed.
|
||||
func (t *Transaction) GetSignedPart() []byte {
|
||||
buf := io.NewBufBinWriter()
|
||||
|
@ -300,7 +251,6 @@ func (t *Transaction) FeePerByte() util.Fixed8 {
|
|||
type transactionJSON struct {
|
||||
TxID util.Uint256 `json:"txid"`
|
||||
Size int `json:"size"`
|
||||
Type TXType `json:"type"`
|
||||
Version uint8 `json:"version"`
|
||||
Nonce uint32 `json:"nonce"`
|
||||
Sender string `json:"sender"`
|
||||
|
@ -309,14 +259,8 @@ type transactionJSON struct {
|
|||
ValidUntilBlock uint32 `json:"valid_until_block"`
|
||||
Attributes []Attribute `json:"attributes"`
|
||||
Cosigners []Cosigner `json:"cosigners"`
|
||||
Inputs []Input `json:"vin"`
|
||||
Outputs []Output `json:"vout"`
|
||||
Script string `json:"script"`
|
||||
Scripts []Witness `json:"scripts"`
|
||||
|
||||
Claims []Input `json:"claims,omitempty"`
|
||||
Script string `json:"script,omitempty"`
|
||||
Gas util.Fixed8 `json:"gas,omitempty"`
|
||||
Asset *registeredAsset `json:"asset,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface.
|
||||
|
@ -324,36 +268,17 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
|||
tx := transactionJSON{
|
||||
TxID: t.Hash(),
|
||||
Size: io.GetVarSize(t),
|
||||
Type: t.Type,
|
||||
Version: t.Version,
|
||||
Nonce: t.Nonce,
|
||||
Sender: address.Uint160ToString(t.Sender),
|
||||
ValidUntilBlock: t.ValidUntilBlock,
|
||||
Attributes: t.Attributes,
|
||||
Cosigners: t.Cosigners,
|
||||
Inputs: t.Inputs,
|
||||
Outputs: t.Outputs,
|
||||
Script: hex.EncodeToString(t.Script),
|
||||
Scripts: t.Scripts,
|
||||
SystemFee: t.SystemFee,
|
||||
NetworkFee: t.NetworkFee,
|
||||
}
|
||||
switch t.Type {
|
||||
case ClaimType:
|
||||
tx.Claims = t.Data.(*ClaimTX).Claims
|
||||
case InvocationType:
|
||||
tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script)
|
||||
tx.Gas = t.Data.(*InvocationTX).Gas
|
||||
case RegisterType:
|
||||
transaction := *t.Data.(*RegisterTX)
|
||||
tx.Asset = ®isteredAsset{
|
||||
AssetType: transaction.AssetType,
|
||||
Name: json.RawMessage(transaction.Name),
|
||||
Amount: transaction.Amount,
|
||||
Precision: transaction.Precision,
|
||||
Owner: transaction.Owner,
|
||||
Admin: address.Uint160ToString(transaction.Admin),
|
||||
}
|
||||
}
|
||||
return json.Marshal(tx)
|
||||
}
|
||||
|
||||
|
@ -363,14 +288,11 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
|
|||
if err := json.Unmarshal(data, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
t.Type = tx.Type
|
||||
t.Version = tx.Version
|
||||
t.Nonce = tx.Nonce
|
||||
t.ValidUntilBlock = tx.ValidUntilBlock
|
||||
t.Attributes = tx.Attributes
|
||||
t.Cosigners = tx.Cosigners
|
||||
t.Inputs = tx.Inputs
|
||||
t.Outputs = tx.Outputs
|
||||
t.Scripts = tx.Scripts
|
||||
t.SystemFee = tx.SystemFee
|
||||
t.NetworkFee = tx.NetworkFee
|
||||
|
@ -379,38 +301,9 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
|
|||
return errors.New("cannot unmarshal tx: bad sender")
|
||||
}
|
||||
t.Sender = sender
|
||||
switch tx.Type {
|
||||
case ClaimType:
|
||||
t.Data = &ClaimTX{
|
||||
Claims: tx.Claims,
|
||||
}
|
||||
case InvocationType:
|
||||
bytes, err := hex.DecodeString(tx.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Data = &InvocationTX{
|
||||
Script: bytes,
|
||||
Gas: tx.Gas,
|
||||
Version: tx.Version,
|
||||
}
|
||||
case RegisterType:
|
||||
admin, err := address.StringToUint160(tx.Asset.Admin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Data = &RegisterTX{
|
||||
AssetType: tx.Asset.AssetType,
|
||||
Name: string(tx.Asset.Name),
|
||||
Amount: tx.Asset.Amount,
|
||||
Precision: tx.Asset.Precision,
|
||||
Owner: tx.Asset.Owner,
|
||||
Admin: admin,
|
||||
}
|
||||
case ContractType:
|
||||
t.Data = &ContractTX{}
|
||||
case IssueType:
|
||||
t.Data = &IssueTX{}
|
||||
t.Script, err = hex.DecodeString(tx.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.Hash() != tx.TxID {
|
||||
return errors.New("txid doesn't match transaction hash")
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -36,31 +35,6 @@ func TestWitnessEncodeDecode(t *testing.T) {
|
|||
|
||||
// TODO NEO3.0: update binary
|
||||
/*
|
||||
func TestDecodeEncodeClaimTX(t *testing.T) {
|
||||
tx := decodeTransaction(rawClaimTX, t)
|
||||
assert.Equal(t, tx.Type, ClaimType)
|
||||
assert.IsType(t, tx.Data, &ClaimTX{})
|
||||
claimTX := tx.Data.(*ClaimTX)
|
||||
assert.Equal(t, 4, len(claimTX.Claims))
|
||||
assert.Equal(t, 0, len(tx.Attributes))
|
||||
assert.Equal(t, 0, len(tx.Inputs))
|
||||
assert.Equal(t, 1, len(tx.Outputs))
|
||||
assert.Equal(t, "AQJseD8iBmCD4sgfHRhMahmoi9zvopG6yz", address.Uint160ToString(tx.Outputs[0].ScriptHash))
|
||||
assert.Equal(t, "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7", tx.Outputs[0].AssetID.StringLE())
|
||||
assert.Equal(t, tx.Outputs[0].Amount.String(), "0.06247739")
|
||||
invoc := "40456349cec43053009accdb7781b0799c6b591c812768804ab0a0b56b5eae7a97694227fcd33e70899c075848b2cee8fae733faac6865b484d3f7df8949e2aadb"
|
||||
verif := "2103945fae1ed3c31d778f149192b76734fcc951b400ba3598faa81ff92ebe477eacac"
|
||||
assert.Equal(t, 1, len(tx.Scripts))
|
||||
assert.Equal(t, invoc, hex.EncodeToString(tx.Scripts[0].InvocationScript))
|
||||
assert.Equal(t, verif, hex.EncodeToString(tx.Scripts[0].VerificationScript))
|
||||
|
||||
data, err := testserdes.EncodeBinary(tx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rawClaimTX, hex.EncodeToString(data))
|
||||
|
||||
hash := "2c6a45547b3898318e400e541628990a07acb00f3b9a15a8e966ae49525304da"
|
||||
assert.Equal(t, hash, tx.hash.StringLE())
|
||||
}
|
||||
|
||||
func TestDecodeEncodeInvocationTX(t *testing.T) {
|
||||
tx := decodeTransaction(rawInvocationTX, t)
|
||||
|
@ -87,119 +61,35 @@ func TestDecodeEncodeInvocationTX(t *testing.T) {
|
|||
}
|
||||
*/
|
||||
|
||||
func TestNewInvocationTX(t *testing.T) {
|
||||
func TestNew(t *testing.T) {
|
||||
script := []byte{0x51}
|
||||
tx := NewInvocationTX(script, 1)
|
||||
txData := tx.Data.(*InvocationTX)
|
||||
assert.Equal(t, InvocationType, tx.Type)
|
||||
assert.Equal(t, tx.Version, txData.Version)
|
||||
assert.Equal(t, script, txData.Script)
|
||||
tx := New(script, 1)
|
||||
assert.Equal(t, util.Fixed8(1), tx.SystemFee)
|
||||
assert.Equal(t, script, tx.Script)
|
||||
// Update hash fields to match tx2 that is gonna autoupdate them on decode.
|
||||
_ = tx.Hash()
|
||||
testserdes.EncodeDecodeBinary(t, tx, new(Transaction))
|
||||
}
|
||||
|
||||
func TestEncodingTXWithNoData(t *testing.T) {
|
||||
func TestEncodingTXWithNoScript(t *testing.T) {
|
||||
_, err := testserdes.EncodeBinary(new(Transaction))
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalJSONContractTX(t *testing.T) {
|
||||
tx := NewContractTX()
|
||||
tx.Outputs = []Output{{
|
||||
AssetID: util.Uint256{1, 2, 3, 4},
|
||||
Amount: 567,
|
||||
ScriptHash: util.Uint160{7, 8, 9, 10},
|
||||
Position: 13,
|
||||
}}
|
||||
tx.Scripts = []Witness{{
|
||||
InvocationScript: []byte{5, 3, 1},
|
||||
VerificationScript: []byte{2, 4, 6},
|
||||
}}
|
||||
tx.Data = &ContractTX{}
|
||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalJSONClaimTX(t *testing.T) {
|
||||
tx := &Transaction{
|
||||
Type: ClaimType,
|
||||
Version: 0,
|
||||
Data: &ClaimTX{Claims: []Input{
|
||||
{
|
||||
PrevHash: util.Uint256{1, 2, 3, 4},
|
||||
PrevIndex: uint16(56),
|
||||
},
|
||||
}},
|
||||
Attributes: []Attribute{},
|
||||
Inputs: []Input{{
|
||||
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||
PrevIndex: uint16(12),
|
||||
}},
|
||||
Outputs: []Output{{
|
||||
AssetID: util.Uint256{1, 2, 3},
|
||||
Amount: util.Fixed8FromInt64(1),
|
||||
ScriptHash: util.Uint160{1, 2, 3},
|
||||
Position: 0,
|
||||
}},
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
|
||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||
func TestDecodingTXWithNoScript(t *testing.T) {
|
||||
txBin, err := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
require.NoError(t, err)
|
||||
err = testserdes.DecodeBinary(txBin, new(Transaction))
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
|
||||
tx := &Transaction{
|
||||
Type: InvocationType,
|
||||
Version: 3,
|
||||
Data: &InvocationTX{
|
||||
Script: []byte{1, 2, 3, 4},
|
||||
Gas: util.Fixed8FromFloat(100),
|
||||
Version: 3,
|
||||
},
|
||||
Version: 0,
|
||||
Script: []byte{1, 2, 3, 4},
|
||||
Attributes: []Attribute{},
|
||||
Inputs: []Input{{
|
||||
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||
PrevIndex: uint16(12),
|
||||
}},
|
||||
Outputs: []Output{{
|
||||
AssetID: util.Uint256{1, 2, 3},
|
||||
Amount: util.Fixed8FromInt64(1),
|
||||
ScriptHash: util.Uint160{1, 2, 3},
|
||||
Position: 0,
|
||||
}},
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
|
||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalJSONRegisterTX(t *testing.T) {
|
||||
tx := &Transaction{
|
||||
Type: RegisterType,
|
||||
Version: 5,
|
||||
Data: &RegisterTX{
|
||||
AssetType: 0,
|
||||
Name: `[{"lang":"zh-CN","name":"小蚁股"},{"lang":"en","name":"AntShare"}]`,
|
||||
Amount: 1000000,
|
||||
Precision: 0,
|
||||
Owner: keys.PublicKey{},
|
||||
Admin: util.Uint160{},
|
||||
},
|
||||
Attributes: []Attribute{},
|
||||
Inputs: []Input{{
|
||||
PrevHash: util.Uint256{5, 6, 7, 8},
|
||||
PrevIndex: uint16(12),
|
||||
}},
|
||||
Outputs: []Output{{
|
||||
AssetID: util.Uint256{1, 2, 3},
|
||||
Amount: util.Fixed8FromInt64(1),
|
||||
ScriptHash: util.Uint160{1, 2, 3},
|
||||
Position: 0,
|
||||
}},
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
Scripts: []Witness{},
|
||||
Trimmed: false,
|
||||
}
|
||||
|
||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/io"
|
||||
|
||||
// TXer is interface that can act as the underlying data of
|
||||
// a transaction.
|
||||
type TXer interface {
|
||||
io.Serializable
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package transaction
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// TXType is the type of a transaction.
|
||||
type TXType uint8
|
||||
|
||||
// Constants for all valid transaction types.
|
||||
const (
|
||||
IssueType TXType = 0x01
|
||||
ClaimType TXType = 0x02
|
||||
RegisterType TXType = 0x40
|
||||
ContractType TXType = 0x80
|
||||
InvocationType TXType = 0xd1
|
||||
)
|
||||
|
||||
// String implements the stringer interface.
|
||||
func (t TXType) String() string {
|
||||
switch t {
|
||||
case IssueType:
|
||||
return "IssueTransaction"
|
||||
case ClaimType:
|
||||
return "ClaimTransaction"
|
||||
case RegisterType:
|
||||
return "RegisterTransaction"
|
||||
case ContractType:
|
||||
return "ContractTransaction"
|
||||
case InvocationType:
|
||||
return "InvocationTransaction"
|
||||
default:
|
||||
return "UnknownTransaction"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json marshaller interface.
|
||||
func (t TXType) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + t.String() + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (t *TXType) UnmarshalJSON(data []byte) error {
|
||||
l := len(data)
|
||||
if l < 2 || data[0] != '"' || data[l-1] != '"' {
|
||||
return errors.New("wrong format")
|
||||
}
|
||||
var err error
|
||||
*t, err = TXTypeFromString(string(data[1 : l-1]))
|
||||
return err
|
||||
}
|
||||
|
||||
// TXTypeFromString searches for TXType by string name.
|
||||
func TXTypeFromString(jsonString string) (TXType, error) {
|
||||
switch jsonString = strings.TrimSpace(jsonString); jsonString {
|
||||
case "IssueTransaction":
|
||||
return IssueType, nil
|
||||
case "ClaimTransaction":
|
||||
return ClaimType, nil
|
||||
case "RegisterTransaction":
|
||||
return RegisterType, nil
|
||||
case "ContractTransaction":
|
||||
return ContractType, nil
|
||||
case "InvocationTransaction":
|
||||
return InvocationType, nil
|
||||
default:
|
||||
return 0, errors.New("unknown state")
|
||||
}
|
||||
}
|
|
@ -46,48 +46,18 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
|||
base := block.Base{
|
||||
Version: 0,
|
||||
PrevHash: util.Uint256{},
|
||||
Timestamp: uint64(time.Date(2016, 7, 15, 15, 8, 21, 0, time.UTC).Unix()),
|
||||
Timestamp: uint64(time.Date(2016, 7, 15, 15, 8, 21, 0, time.UTC).Unix()) * 1000, // Milliseconds.
|
||||
Index: 0,
|
||||
NextConsensus: nextConsensus,
|
||||
Script: transaction.Witness{
|
||||
InvocationScript: []byte{},
|
||||
VerificationScript: []byte{byte(opcode.OLDPUSH1)},
|
||||
},
|
||||
}
|
||||
|
||||
rawScript, err := smartcontract.CreateMultiSigRedeemScript(
|
||||
len(cfg.StandbyValidators)/2+1,
|
||||
validators,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scriptOut := hash.Hash160(rawScript)
|
||||
|
||||
issueTx := transaction.NewIssueTX()
|
||||
// TODO NEO3.0: nonce should be constant to avoid variability of genesis block
|
||||
issueTx.Nonce = 0
|
||||
issueTx.Sender = hash.Hash160([]byte{byte(opcode.OLDPUSH1)})
|
||||
issueTx.Outputs = []transaction.Output{
|
||||
{
|
||||
AssetID: governingTokenTX.Hash(),
|
||||
Amount: governingTokenTX.Data.(*transaction.RegisterTX).Amount,
|
||||
ScriptHash: scriptOut,
|
||||
},
|
||||
}
|
||||
issueTx.Scripts = []transaction.Witness{
|
||||
{
|
||||
InvocationScript: []byte{},
|
||||
VerificationScript: []byte{byte(opcode.OLDPUSH1)},
|
||||
VerificationScript: []byte{byte(opcode.PUSH1)},
|
||||
},
|
||||
}
|
||||
|
||||
b := &block.Block{
|
||||
Base: base,
|
||||
Transactions: []*transaction.Transaction{
|
||||
&governingTokenTX,
|
||||
&utilityTokenTX,
|
||||
issueTx,
|
||||
deployNativeContracts(),
|
||||
},
|
||||
ConsensusData: block.ConsensusData{
|
||||
|
@ -107,7 +77,7 @@ func deployNativeContracts() *transaction.Transaction {
|
|||
buf := io.NewBufBinWriter()
|
||||
emit.Syscall(buf.BinWriter, "Neo.Native.Deploy")
|
||||
script := buf.Bytes()
|
||||
tx := transaction.NewInvocationTX(script, 0)
|
||||
tx := transaction.New(script, 0)
|
||||
tx.Nonce = 0
|
||||
tx.Sender = hash.Hash160([]byte{byte(opcode.PUSH1)})
|
||||
tx.Scripts = []transaction.Witness{
|
||||
|
@ -119,45 +89,6 @@ func deployNativeContracts() *transaction.Transaction {
|
|||
return tx
|
||||
}
|
||||
|
||||
func init() {
|
||||
admin := hash.Hash160([]byte{byte(opcode.OLDPUSH1)})
|
||||
registerTX := &transaction.RegisterTX{
|
||||
AssetType: transaction.GoverningToken,
|
||||
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]",
|
||||
Amount: util.Fixed8FromInt64(100000000),
|
||||
Precision: 0,
|
||||
Admin: admin,
|
||||
}
|
||||
|
||||
governingTokenTX = *transaction.NewRegisterTX(registerTX)
|
||||
// TODO NEO3.0: nonce should be constant to avoid variability of token hash
|
||||
governingTokenTX.Nonce = 0
|
||||
governingTokenTX.Sender = hash.Hash160([]byte{byte(opcode.OLDPUSH1)})
|
||||
|
||||
admin = hash.Hash160([]byte{0x00})
|
||||
registerTX = &transaction.RegisterTX{
|
||||
AssetType: transaction.UtilityToken,
|
||||
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁币\"},{\"lang\":\"en\",\"name\":\"AntCoin\"}]",
|
||||
Amount: calculateUtilityAmount(),
|
||||
Precision: 8,
|
||||
Admin: admin,
|
||||
}
|
||||
utilityTokenTX = *transaction.NewRegisterTX(registerTX)
|
||||
// TODO NEO3.0: nonce should be constant to avoid variability of token hash
|
||||
utilityTokenTX.Nonce = 0
|
||||
utilityTokenTX.Sender = hash.Hash160([]byte{byte(opcode.OLDPUSH1)})
|
||||
}
|
||||
|
||||
// GoverningTokenID returns the governing token (NEO) hash.
|
||||
func GoverningTokenID() util.Uint256 {
|
||||
return governingTokenTX.Hash()
|
||||
}
|
||||
|
||||
// UtilityTokenID returns the utility token (GAS) hash.
|
||||
func UtilityTokenID() util.Uint256 {
|
||||
return utilityTokenTX.Hash()
|
||||
}
|
||||
|
||||
func getValidators(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
||||
validators := make([]*keys.PublicKey, len(cfg.StandbyValidators))
|
||||
for i, pubKeyStr := range cfg.StandbyValidators {
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
|
|||
// have been changed. Consequently, hash of the genesis block has been changed.
|
||||
// Update expected genesis block hash for better times.
|
||||
// Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"
|
||||
expect := "1d4156d233220b893797a684fbb827bb2163b5042edd10653bbc1b2769adbb8d"
|
||||
expect := "e4cfc549c87d4ab7b570c368d05853ffb70eb9ef0f7d9c7a2e6e9e5d713ebbf4"
|
||||
assert.Equal(t, expect, block.Hash().StringLE())
|
||||
}
|
||||
|
||||
|
@ -42,19 +42,3 @@ func TestGetConsensusAddressMainNet(t *testing.T) {
|
|||
assert.Equal(t, consensusScript, script.String())
|
||||
assert.Equal(t, consensusAddr, address.Uint160ToString(script))
|
||||
}
|
||||
|
||||
func TestUtilityTokenTX(t *testing.T) {
|
||||
//TODO: After we added Nonce field to transaction.Transaction, UtilityTockenTx hash
|
||||
// has been changed. Update it for better times.
|
||||
// Old hash is "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
||||
expect := "f882fb865bab84b99623f21eedd902286af7da8d8a4609d7acefce04c851dc1c"
|
||||
assert.Equal(t, expect, UtilityTokenID().StringLE())
|
||||
}
|
||||
|
||||
func TestGoverningTokenTX(t *testing.T) {
|
||||
//TODO: After we added Nonce field to transaction.Transaction, GoveringTockenTx hash
|
||||
// has been changed. Update it for better times.
|
||||
// Old hash is "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"
|
||||
expect := "1a5e0e3eac2abced7de9ee2de0820a5c85e63756fcdfc29b82fead86a7c07c78"
|
||||
assert.Equal(t, expect, GoverningTokenID().StringLE())
|
||||
}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
Package asset provides functions to work with regular UTXO assets (like NEO or GAS).
|
||||
Mostly these are getters for Asset structure, but you can also create new assets
|
||||
and renew them (although it's recommended to use NEP-5 standard for new tokens).
|
||||
*/
|
||||
package asset
|
||||
|
||||
// Asset represents NEO asset type that is used in interop functions, it's
|
||||
// an opaque data structure that you can get data from only using functions from
|
||||
// this package. It's similar in function to the Asset class in the Neo .net
|
||||
// framework. To be able to use it you either need to get an existing Asset via
|
||||
// blockchain.GetAsset function or create a new one via Create.
|
||||
type Asset struct{}
|
||||
|
||||
// GetAssetID returns ID (256-bit ID of Register transaction for this asset in BE
|
||||
// representation) of the given asset. It uses `Neo.Asset.GetAssetId` syscall
|
||||
// internally.
|
||||
func GetAssetID(a Asset) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAssetType returns type of the given asset as a byte value. The value
|
||||
// returned can be interpreted as a bit field with the following meaning:
|
||||
// CreditFlag = 0x40
|
||||
// DutyFlag = 0x80
|
||||
// SystemShare = 0x00
|
||||
// SystemCoin = 0x01
|
||||
// Currency = 0x08
|
||||
// Share = DutyFlag | 0x10
|
||||
// Invoice = DutyFlag | 0x18
|
||||
// Token = CreditFlag | 0x20
|
||||
// It uses `Neo.Asset.GetAssetType` syscall internally.
|
||||
func GetAssetType(a Asset) byte {
|
||||
return 0x00
|
||||
}
|
||||
|
||||
// GetAmount returns the total amount of the given asset as an integer
|
||||
// multiplied by 10⁸. This value is the maximum possible circulating quantity of
|
||||
// Asset. The function uses `Neo.Asset.GetAmount` syscall internally.
|
||||
func GetAmount(a Asset) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetAvailable returns the amount of Asset currently available on the
|
||||
// blockchain. It uses the same encoding as the result of GetAmount and its
|
||||
// value can never exceed the value returned by GetAmount. This function uses
|
||||
// `Neo.Asset.GetAvailable` syscall internally.
|
||||
func GetAvailable(a Asset) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetPrecision returns precision of the given Asset. It uses
|
||||
// `Neo.Asset.GetPrecision` syscall internally.
|
||||
func GetPrecision(a Asset) byte {
|
||||
return 0x00
|
||||
}
|
||||
|
||||
// GetOwner returns the owner of the given Asset. It's represented as a
|
||||
// serialized (in compressed form) public key (33 bytes long). This function
|
||||
// uses `Neo.Asset.GetOwner` syscall internally.
|
||||
func GetOwner(a Asset) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAdmin returns the admin of the given Asset represented as a 160 bit hash
|
||||
// in BE form (contract script hash). Admin can modify attributes of this Asset.
|
||||
// This function uses `Neo.Asset.GetAdmin` syscall internally.
|
||||
func GetAdmin(a Asset) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIssuer returns the issuer of the given Asset represented as a 160 bit hash
|
||||
// in BE form (contract script hash). Issuer can issue new tokens for this Asset.
|
||||
// This function uses `Neo.Asset.GetIssuer` syscall internally.
|
||||
func GetIssuer(a Asset) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create registers a new asset on the blockchain (similar to old Register
|
||||
// transaction). `assetType` parameter has the same set of possible values as
|
||||
// GetAssetType result, `amount` must be multiplied by 10⁸, `precision` limits
|
||||
// the smallest possible amount of new Asset to 10⁻ⁿ (where n is precision which
|
||||
// can't exceed 8), `owner` is a public key of the owner in compressed serialized
|
||||
// form (33 bytes), `admin` and `issuer` should be represented as 20-byte slices
|
||||
// storing 160-bit hash in BE form. Created Asset is set to expire in one year,
|
||||
// so you need to renew it in time. If successful, this function returns a new
|
||||
// Asset. It uses `Neo.Asset.Create` syscall internally.
|
||||
func Create(assetType byte, name string, amount int, precision byte, owner, admin, issuer []byte) Asset {
|
||||
return Asset{}
|
||||
}
|
||||
|
||||
// Renew renews (make available for use) existing asset by the specified number
|
||||
// of years. It returns the last block number when this asset will be active.
|
||||
// It uses `Neo.Asset.Renew` syscall internally.
|
||||
func Renew(asset Asset, years int) int {
|
||||
return 0
|
||||
}
|
|
@ -5,7 +5,6 @@ package blockchain
|
|||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/account"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/asset"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/header"
|
||||
|
@ -73,11 +72,3 @@ func GetAccount(scriptHash []byte) account.Account {
|
|||
func GetValidators() [][]byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAsset returns asset found by the given asset ID (256 bit in BE format
|
||||
// represented as a slice of 32 bytes). Refer to the `asset` package for
|
||||
// possible uses of returned structure. This function uses
|
||||
// `Neo.Blockchain.GetAsset` syscall.
|
||||
func GetAsset(assetID []byte) asset.Asset {
|
||||
return asset.Asset{}
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
Package input provides functions dealing with transaction inputs.
|
||||
*/
|
||||
package input
|
||||
|
||||
// Input is an opaque data structure that can only be created by
|
||||
// transaction.GetInputs and it represents transaction's input. It's similar
|
||||
// to Neo .net framework's TransactionInput.
|
||||
type Input struct{}
|
||||
|
||||
// GetHash returns the hash stored in the given input (which also is a
|
||||
// transaction ID represented as 32 byte slice containing 256 bit BE value).
|
||||
// It uses `Neo.Input.GetHash` syscall.
|
||||
func GetHash(in Input) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIndex returns the index stored in the given input (which is a
|
||||
// transaction's output number). It uses `Neo.Input.GetIndex` syscall.
|
||||
func GetIndex(in Input) int {
|
||||
return 0
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
Package output provides functions dealing with transaction outputs.
|
||||
*/
|
||||
package output
|
||||
|
||||
// Output is an opaque data structure that can only be created by
|
||||
// transaction.GetOutputs and it represents transaction's output. It's similar
|
||||
// to Neo .net framework's TransactionOutput.
|
||||
type Output struct{}
|
||||
|
||||
// GetAssetID returns the asset ID (256 bit BE value in a 32 byte slice) of the
|
||||
// given output. It uses `Neo.Output.GetAssetId` syscall.
|
||||
func GetAssetID(out Output) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValue returns the value (asset quantity) of the given output. It uses
|
||||
// `Neo.Output.GetValue` syscall.
|
||||
func GetValue(out Output) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetScriptHash returns the script hash (receiver's address represented as
|
||||
// 20 byte slice containing 160 bit BE value) of the given output. It uses
|
||||
// `Neo.Output.GetScriptHash` syscall.
|
||||
func GetScriptHash(out Output) []byte {
|
||||
return nil
|
||||
}
|
|
@ -5,8 +5,6 @@ package transaction
|
|||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/attribute"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/input"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/output"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/witness"
|
||||
)
|
||||
|
||||
|
@ -21,22 +19,6 @@ func GetHash(t Transaction) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetType returns the type of the given transaction. Possible values:
|
||||
// MinerTransaction = 0x00
|
||||
// IssueTransaction = 0x01
|
||||
// ClaimTransaction = 0x02
|
||||
// EnrollmentTransaction = 0x20
|
||||
// RegisterTransaction = 0x40
|
||||
// ContractTransaction = 0x80
|
||||
// StateType = 0x90
|
||||
// AgencyTransaction = 0xb0
|
||||
// PublishTransaction = 0xd0
|
||||
// InvocationTransaction = 0xd1
|
||||
// It uses `Neo.Transaction.GetType` syscall.
|
||||
func GetType(t Transaction) byte {
|
||||
return 0x00
|
||||
}
|
||||
|
||||
// GetAttributes returns a slice of attributes for agiven transaction. Refer to
|
||||
// attribute package on how to use them. This function uses
|
||||
// `Neo.Transaction.GetAttributes` syscall.
|
||||
|
@ -44,42 +26,6 @@ func GetAttributes(t Transaction) []attribute.Attribute {
|
|||
return []attribute.Attribute{}
|
||||
}
|
||||
|
||||
// GetReferences returns a slice of references for a given Transaction. Elements
|
||||
// of this slice can be casted to any of input.Input or output.Output, depending
|
||||
// on which information you're interested in (as reference technically contains
|
||||
// both input and corresponding output), refer to input and output package on
|
||||
// how to use them. This function uses `Neo.Transaction.GetReferences` syscall.
|
||||
func GetReferences(t Transaction) []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// GetUnspentCoins returns a slice of not yet spent ouputs of a given transaction.
|
||||
// This function uses `Neo.Transaction.GetUnspentCoint` syscall.
|
||||
func GetUnspentCoins(t Transaction) []output.Output {
|
||||
return []output.Output{}
|
||||
}
|
||||
|
||||
// GetInputs returns a slice of inputs of a given Transaction. Refer to input
|
||||
// package on how to use them. This function uses `Neo.Transaction.GetInputs`
|
||||
// syscall.
|
||||
func GetInputs(t Transaction) []input.Input {
|
||||
return []input.Input{}
|
||||
}
|
||||
|
||||
// GetOutputs returns a slice of outputs of a given Transaction. Refer to output
|
||||
// package on how to use them. This function uses `Neo.Transaction.GetOutputs`
|
||||
// syscall.
|
||||
func GetOutputs(t Transaction) []output.Output {
|
||||
return []output.Output{}
|
||||
}
|
||||
|
||||
// GetScript returns the script stored in a given Invocation transaction.
|
||||
// Calling it for any other Transaction type would lead to failure. It uses
|
||||
// `Neo.InvocationTransaction.GetScript` syscall.
|
||||
func GetScript(t Transaction) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetWitnesses returns a slice of witnesses of a given Transaction. Refer to
|
||||
// witness package on how to use them. This function uses
|
||||
// `Neo.Transaction.GetWitnesses` syscall.
|
||||
|
|
|
@ -25,17 +25,13 @@ type testChain struct {
|
|||
blockheight uint32
|
||||
}
|
||||
|
||||
func (chain testChain) ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithFee {
|
||||
func (chain testChain) ApplyPolicyToTxSet([]*transaction.Transaction) []*transaction.Transaction {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetConfig() config.ProtocolConfiguration {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) CalculateClaimable(util.Fixed8, uint32, uint32) (util.Fixed8, util.Fixed8, error) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) References(t *transaction.Transaction) ([]transaction.InOut, error) {
|
||||
func (chain testChain) CalculateClaimable(int64, uint32, uint32) util.Fixed8 {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
|
@ -77,9 +73,6 @@ func (chain testChain) GetHeader(hash util.Uint256) (*block.Header, error) {
|
|||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetAssetState(util.Uint256) *state.Asset {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetAccountState(util.Uint160) *state.Account {
|
||||
panic("TODO")
|
||||
}
|
||||
|
@ -126,10 +119,6 @@ func (chain testChain) GetTransaction(util.Uint256) (*transaction.Transaction, u
|
|||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetUnspentCoinState(util.Uint256) *state.UnspentCoin {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetMemPool() *mempool.Pool {
|
||||
panic("TODO")
|
||||
}
|
||||
|
@ -138,6 +127,10 @@ func (chain testChain) IsLowPriority(util.Fixed8) bool {
|
|||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetGoverningTokenBalance(acc util.Uint160) (util.Fixed8, uint32) {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) GetUtilityTokenBalance(uint160 util.Uint160) util.Fixed8 {
|
||||
panic("TODO")
|
||||
}
|
||||
|
|
|
@ -11,12 +11,9 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -45,13 +42,6 @@ type Client struct {
|
|||
// All values are optional. If any duration is not specified
|
||||
// a default of 4 seconds will be used.
|
||||
type Options struct {
|
||||
// Balancer is an implementation of request.BalanceGetter interface,
|
||||
// if not set then the default Client's implementation will be used, but
|
||||
// it relies on server support for `getunspents` RPC call which is
|
||||
// standard for neo-go, but only implemented as a plugin for C# node. So
|
||||
// you can override it here to use NeoScanServer for example.
|
||||
Balancer request.BalanceGetter
|
||||
|
||||
// Cert is a client-side certificate, it doesn't work at the moment along
|
||||
// with the other two options below.
|
||||
Cert string
|
||||
|
@ -107,9 +97,6 @@ func New(ctx context.Context, endpoint string, opts Options) (*Client, error) {
|
|||
wifMu: new(sync.Mutex),
|
||||
endpoint: url,
|
||||
}
|
||||
if opts.Balancer == nil {
|
||||
opts.Balancer = cl
|
||||
}
|
||||
cl.opts = opts
|
||||
cl.requestF = cl.makeHTTPRequest
|
||||
return cl, nil
|
||||
|
@ -140,27 +127,6 @@ func (c *Client) SetWIF(wif string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// CalculateInputs implements request.BalanceGetter interface and returns inputs
|
||||
// array for the specified amount of given asset belonging to specified address.
|
||||
// This implementation uses GetUnspents JSON-RPC call internally, so make sure
|
||||
// your RPC server supports that.
|
||||
func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.Fixed8) ([]transaction.Input, util.Fixed8, error) {
|
||||
var utxos state.UnspentBalances
|
||||
|
||||
resp, err := c.GetUnspents(address)
|
||||
if err != nil {
|
||||
return nil, util.Fixed8(0), errors.Wrapf(err, "cannot get balance for address %v", address)
|
||||
}
|
||||
for _, ubi := range resp.Balance {
|
||||
if asset.Equals(ubi.AssetHash) {
|
||||
utxos = ubi.Unspents
|
||||
break
|
||||
}
|
||||
}
|
||||
return unspentsToInputs(utxos, cost)
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) performRequest(method string, p request.RawParams, v interface{}) error {
|
||||
var r = request.Raw{
|
||||
JSONRPC: request.JSONRPCVersion,
|
||||
|
|
|
@ -18,16 +18,13 @@ TODO:
|
|||
|
||||
Supported methods
|
||||
|
||||
getaccountstate
|
||||
getapplicationlog
|
||||
getassetstate
|
||||
getbestblockhash
|
||||
getblock
|
||||
getblockcount
|
||||
getblockhash
|
||||
getblockheader
|
||||
getblocksysfee
|
||||
getclaimable
|
||||
getconnectioncount
|
||||
getcontractstate
|
||||
getnep5balances
|
||||
|
@ -37,9 +34,7 @@ Supported methods
|
|||
getrawtransaction
|
||||
getstorage
|
||||
gettransactionheight
|
||||
gettxout
|
||||
getunclaimed
|
||||
getunspents
|
||||
getunclaimedgas
|
||||
getvalidators
|
||||
getversion
|
||||
invoke
|
||||
|
@ -56,7 +51,6 @@ Unsupported methods
|
|||
getbalance
|
||||
getmetricblocktimestamp
|
||||
getnewaddress
|
||||
getunclaimedgas
|
||||
getwalletheight
|
||||
importprivkey
|
||||
listaddress
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||
)
|
||||
|
||||
|
@ -23,11 +24,16 @@ func Example() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
resp, err := c.GetAccountState("ATySFJAbLW7QHsZGHScLhxq6EyNBxx3eFP")
|
||||
addr, err := address.StringToUint160("ATySFJAbLW7QHsZGHScLhxq6EyNBxx3eFP")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(resp.ScriptHash)
|
||||
resp, err := c.GetNEP5Balances(addr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(resp.Address)
|
||||
fmt.Println(resp.Balances)
|
||||
}
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
errs "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
/*
|
||||
Definition of types, helper functions and variables
|
||||
required for calculation of transaction inputs using
|
||||
NeoScan API.
|
||||
*/
|
||||
|
||||
type (
|
||||
// NeoScanServer stores NEOSCAN URL and API path.
|
||||
NeoScanServer struct {
|
||||
URL string // "protocol://host:port/"
|
||||
Path string // path to API endpoint without wallet address
|
||||
}
|
||||
|
||||
// Unspent stores Unspents per asset
|
||||
Unspent struct {
|
||||
Unspent state.UnspentBalances
|
||||
Asset string // "NEO" / "GAS"
|
||||
Amount util.Fixed8 // total unspent of this asset
|
||||
}
|
||||
|
||||
// NeoScanBalance is a struct of NeoScan response to 'get_balance' request
|
||||
NeoScanBalance struct {
|
||||
Balance []*Unspent
|
||||
Address string
|
||||
}
|
||||
)
|
||||
|
||||
// GetBalance performs a request to get balance for the address specified.
|
||||
func (s NeoScanServer) GetBalance(address string) ([]*Unspent, error) {
|
||||
var (
|
||||
err error
|
||||
req *http.Request
|
||||
res *http.Response
|
||||
balance NeoScanBalance
|
||||
client = http.Client{}
|
||||
balanceURL = s.URL + s.Path
|
||||
)
|
||||
|
||||
if req, err = http.NewRequest(http.MethodGet, balanceURL+address, nil); err != nil {
|
||||
return nil, errs.Wrap(err, "Failed to compose HTTP request")
|
||||
}
|
||||
|
||||
if res, err = client.Do(req); err != nil {
|
||||
return nil, errs.Wrap(err, "Failed to perform HTTP request")
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
if err = json.NewDecoder(res.Body).Decode(&balance); err != nil {
|
||||
return nil, errs.Wrap(err, "Failed to decode HTTP response")
|
||||
}
|
||||
return balance.Balance, nil
|
||||
}
|
||||
|
||||
func filterSpecificAsset(asset string, balance []*Unspent, assetBalance *Unspent) {
|
||||
for _, us := range balance {
|
||||
if us.Asset == asset {
|
||||
assetBalance.Unspent = us.Unspent
|
||||
assetBalance.Asset = us.Asset
|
||||
assetBalance.Amount = us.Amount
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CalculateInputs creates input transactions for the specified amount of given asset belonging to specified address.
|
||||
func (s NeoScanServer) CalculateInputs(address string, assetIDUint util.Uint256, cost util.Fixed8) ([]transaction.Input, util.Fixed8, error) {
|
||||
var (
|
||||
err error
|
||||
us []*Unspent
|
||||
assetUnspent Unspent
|
||||
assetID = result.GlobalAssets[assetIDUint.StringLE()]
|
||||
)
|
||||
if us, err = s.GetBalance(address); err != nil {
|
||||
return nil, util.Fixed8(0), errs.Wrapf(err, "Cannot get balance for address %v", address)
|
||||
}
|
||||
filterSpecificAsset(assetID, us, &assetUnspent)
|
||||
return unspentsToInputs(assetUnspent.Unspent, cost)
|
||||
}
|
||||
|
||||
// unspentsToInputs uses UnspentBalances to create a slice of inputs for a new
|
||||
// transcation containing the required amount of asset.
|
||||
func unspentsToInputs(utxos state.UnspentBalances, required util.Fixed8) ([]transaction.Input, util.Fixed8, error) {
|
||||
var (
|
||||
num, i uint16
|
||||
selected = util.Fixed8(0)
|
||||
)
|
||||
sort.Sort(utxos)
|
||||
|
||||
for _, us := range utxos {
|
||||
if selected >= required {
|
||||
break
|
||||
}
|
||||
selected += us.Value
|
||||
num++
|
||||
}
|
||||
if selected < required {
|
||||
return nil, util.Fixed8(0), errors.New("cannot compose inputs for transaction; check sender balance")
|
||||
}
|
||||
|
||||
inputs := make([]transaction.Input, 0, num)
|
||||
for i = 0; i < num; i++ {
|
||||
inputs = append(inputs, transaction.Input{
|
||||
PrevHash: utxos[i].Tx,
|
||||
PrevIndex: utxos[i].Index,
|
||||
})
|
||||
}
|
||||
|
||||
return inputs, selected, nil
|
||||
}
|
|
@ -5,11 +5,9 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
|
@ -94,22 +92,23 @@ func (c *Client) NEP5TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
|
|||
return wallet.NewToken(tokenHash, name, symbol, decimals), nil
|
||||
}
|
||||
|
||||
// TransferNEP5 creates an invocation transaction that invokes 'transfer' method
|
||||
// on a given token to move specified amount of NEP5 assets (in FixedN format
|
||||
// using contract's number of decimals) to given account.
|
||||
func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token *wallet.Token, amount int64, gas util.Fixed8) (util.Uint256, error) {
|
||||
// CreateNEP5TransferTx creates an invocation transaction for the 'transfer'
|
||||
// method of a given contract (token) to move specified amount of NEP5 assets
|
||||
// (in FixedN format using contract's number of decimals) to given account and
|
||||
// returns it. The returned transaction is not signed.
|
||||
func (c *Client) CreateNEP5TransferTx(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas util.Fixed8) (*transaction.Transaction, error) {
|
||||
from, err := address.StringToUint160(acc.Address)
|
||||
if err != nil {
|
||||
return util.Uint256{}, fmt.Errorf("bad account address: %v", err)
|
||||
return nil, fmt.Errorf("bad account address: %v", err)
|
||||
}
|
||||
// Note: we don't use invoke function here because it requires
|
||||
// 2 round trips instead of one.
|
||||
w := io.NewBufBinWriter()
|
||||
emit.AppCallWithOperationAndArgs(w.BinWriter, token.Hash, "transfer", from, to, amount)
|
||||
emit.AppCallWithOperationAndArgs(w.BinWriter, token, "transfer", from, to, amount)
|
||||
emit.Opcode(w.BinWriter, opcode.ASSERT)
|
||||
|
||||
script := w.Bytes()
|
||||
tx := transaction.NewInvocationTX(script, gas)
|
||||
tx := transaction.New(script, gas)
|
||||
tx.Sender = from
|
||||
tx.Cosigners = []transaction.Cosigner{
|
||||
{
|
||||
|
@ -122,11 +121,11 @@ func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token *walle
|
|||
|
||||
result, err := c.InvokeScript(hex.EncodeToString(script))
|
||||
if err != nil {
|
||||
return util.Uint256{}, fmt.Errorf("can't add system fee to transaction: %v", err)
|
||||
return nil, fmt.Errorf("can't add system fee to transaction: %v", err)
|
||||
}
|
||||
gasConsumed, err := util.Fixed8FromString(result.GasConsumed)
|
||||
if err != nil {
|
||||
return util.Uint256{}, fmt.Errorf("can't add system fee to transaction: %v", err)
|
||||
return nil, fmt.Errorf("can't add system fee to transaction: %v", err)
|
||||
}
|
||||
if gasConsumed > 0 {
|
||||
tx.SystemFee = gasConsumed
|
||||
|
@ -134,16 +133,25 @@ func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token *walle
|
|||
|
||||
tx.ValidUntilBlock, err = c.CalculateValidUntilBlock()
|
||||
if err != nil {
|
||||
return util.Uint256{}, fmt.Errorf("can't calculate validUntilBlock: %v", err)
|
||||
}
|
||||
|
||||
if err := request.AddInputsAndUnspentsToTx(tx, acc.Address, core.UtilityTokenID(), gas, c); err != nil {
|
||||
return util.Uint256{}, fmt.Errorf("can't add GAS to transaction: %v", err)
|
||||
return nil, fmt.Errorf("can't calculate validUntilBlock: %v", err)
|
||||
}
|
||||
|
||||
err = c.AddNetworkFee(tx, acc)
|
||||
if err != nil {
|
||||
return util.Uint256{}, fmt.Errorf("can't add network fee to transaction: %v", err)
|
||||
return nil, fmt.Errorf("can't add network fee to transaction: %v", err)
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// TransferNEP5 creates an invocation transaction that invokes 'transfer' method
|
||||
// on a given token to move specified amount of NEP5 assets (in FixedN format
|
||||
// using contract's number of decimals) to given account and sends it to the
|
||||
// network returning just a hash of it.
|
||||
func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas util.Fixed8) (util.Uint256, error) {
|
||||
tx, err := c.CreateNEP5TransferTx(acc, to, token, amount, gas)
|
||||
if err != nil {
|
||||
return util.Uint256{}, err
|
||||
}
|
||||
|
||||
if err := acc.SignTx(tx); err != nil {
|
||||
|
|
|
@ -2,6 +2,7 @@ package client
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strconv"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
|
@ -16,18 +17,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// GetAccountState returns detailed information about a NEO account.
|
||||
func (c *Client) GetAccountState(address string) (*result.AccountState, error) {
|
||||
var (
|
||||
params = request.NewRawParams(address)
|
||||
resp = &result.AccountState{}
|
||||
)
|
||||
if err := c.performRequest("getaccountstate", params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetApplicationLog returns the contract log based on the specified txid.
|
||||
func (c *Client) GetApplicationLog(hash util.Uint256) (*result.ApplicationLog, error) {
|
||||
var (
|
||||
|
@ -40,18 +29,6 @@ func (c *Client) GetApplicationLog(hash util.Uint256) (*result.ApplicationLog, e
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAssetState queries the asset information, based on the specified asset number.
|
||||
func (c *Client) GetAssetState(hash util.Uint256) (*result.AssetState, error) {
|
||||
var (
|
||||
params = request.NewRawParams(hash.StringLE())
|
||||
resp = &result.AssetState{}
|
||||
)
|
||||
if err := c.performRequest("getassetstate", params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetBestBlockHash returns the hash of the tallest block in the main chain.
|
||||
func (c *Client) GetBestBlockHash() (util.Uint256, error) {
|
||||
var resp = util.Uint256{}
|
||||
|
@ -187,16 +164,6 @@ func (c *Client) GetBlockSysFee(index uint32) (util.Fixed8, error) {
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
// GetClaimable returns tx outputs which can be claimed.
|
||||
func (c *Client) GetClaimable(address string) (*result.ClaimableInfo, error) {
|
||||
params := request.NewRawParams(address)
|
||||
resp := new(result.ClaimableInfo)
|
||||
if err := c.performRequest("getclaimable", params, resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetConnectionCount returns the current number of connections for the node.
|
||||
func (c *Client) GetConnectionCount() (int, error) {
|
||||
var (
|
||||
|
@ -329,41 +296,20 @@ func (c *Client) GetTransactionHeight(hash util.Uint256) (uint32, error) {
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
// GetTxOut returns the corresponding unspent transaction output information (returned change),
|
||||
// based on the specified hash and index.
|
||||
func (c *Client) GetTxOut(hash util.Uint256, num int) (*result.TransactionOutput, error) {
|
||||
var (
|
||||
params = request.NewRawParams(hash.StringLE(), num)
|
||||
resp = &result.TransactionOutput{}
|
||||
)
|
||||
if err := c.performRequest("gettxout", params, resp); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetUnclaimed returns unclaimed GAS amount of the specified address.
|
||||
func (c *Client) GetUnclaimed(address string) (*result.Unclaimed, error) {
|
||||
// GetUnclaimedGas returns unclaimed GAS amount for the specified address.
|
||||
func (c *Client) GetUnclaimedGas(address string) (util.Fixed8, error) {
|
||||
var (
|
||||
params = request.NewRawParams(address)
|
||||
resp = &result.Unclaimed{}
|
||||
resp string
|
||||
)
|
||||
if err := c.performRequest("getunclaimed", params, resp); err != nil {
|
||||
return nil, err
|
||||
if err := c.performRequest("getunclaimedgas", params, &resp); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetUnspents returns UTXOs for the given NEO account.
|
||||
func (c *Client) GetUnspents(address string) (*result.Unspents, error) {
|
||||
var (
|
||||
params = request.NewRawParams(address)
|
||||
resp = &result.Unspents{}
|
||||
)
|
||||
if err := c.performRequest("getunspents", params, resp); err != nil {
|
||||
return nil, err
|
||||
i, err := strconv.ParseInt(resp, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp, nil
|
||||
return util.Fixed8(i), nil
|
||||
}
|
||||
|
||||
// GetValidators returns the current NEO consensus nodes information and voting status.
|
||||
|
@ -470,39 +416,6 @@ func (c *Client) SubmitBlock(b block.Block) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TransferAsset sends an amount of specific asset to a given address.
|
||||
// This call requires open wallet. (`wif` key in client struct.)
|
||||
// If response.Result is `true` then transaction was formed correctly and was written in blockchain.
|
||||
func (c *Client) TransferAsset(asset util.Uint256, address string, amount util.Fixed8) (util.Uint256, error) {
|
||||
var (
|
||||
err error
|
||||
rawTx *transaction.Transaction
|
||||
txParams = request.ContractTxParams{
|
||||
AssetID: asset,
|
||||
Address: address,
|
||||
Value: amount,
|
||||
WIF: c.WIF(),
|
||||
Balancer: c.opts.Balancer,
|
||||
}
|
||||
resp util.Uint256
|
||||
)
|
||||
|
||||
if rawTx, err = request.CreateRawContractTransaction(txParams); err != nil {
|
||||
return resp, errors.Wrap(err, "failed to create raw transaction")
|
||||
}
|
||||
|
||||
validUntilBlock, err := c.CalculateValidUntilBlock()
|
||||
if err != nil {
|
||||
return resp, errors.Wrap(err, "failed to add validUntilBlock to raw transaction")
|
||||
}
|
||||
rawTx.ValidUntilBlock = validUntilBlock
|
||||
|
||||
if err = c.SendRawTransaction(rawTx); err != nil {
|
||||
return resp, errors.Wrap(err, "failed to send raw transaction")
|
||||
}
|
||||
return rawTx.Hash(), nil
|
||||
}
|
||||
|
||||
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
||||
// transaction using given wif to sign it and spending the amount of gas
|
||||
// specified. It returns a hash of the invocation transaction and an error.
|
||||
|
@ -510,7 +423,7 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys
|
|||
var txHash util.Uint256
|
||||
var err error
|
||||
|
||||
tx := transaction.NewInvocationTX(script, sysfee)
|
||||
tx := transaction.New(script, sysfee)
|
||||
tx.SystemFee = sysfee
|
||||
|
||||
validUntilBlock, err := c.CalculateValidUntilBlock()
|
||||
|
@ -525,14 +438,6 @@ func (c *Client) SignAndPushInvocationTx(script []byte, acc *wallet.Account, sys
|
|||
}
|
||||
tx.Sender = addr
|
||||
|
||||
gas := sysfee + netfee
|
||||
|
||||
if gas > 0 {
|
||||
if err = request.AddInputsAndUnspentsToTx(tx, acc.Address, core.UtilityTokenID(), gas, c); err != nil {
|
||||
return txHash, errors.Wrap(err, "failed to add inputs and unspents to transaction")
|
||||
}
|
||||
}
|
||||
|
||||
err = c.AddNetworkFee(tx, acc)
|
||||
if err != nil {
|
||||
return txHash, errors.Wrapf(err, "failed to add network fee")
|
||||
|
|
|
@ -3,7 +3,6 @@ package client
|
|||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
@ -11,14 +10,15 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -31,113 +31,60 @@ type rpcClientTestCase struct {
|
|||
check func(t *testing.T, c *Client, result interface{})
|
||||
}
|
||||
|
||||
// getResultBlock202 returns data for block number 1 which is used by several tests.
|
||||
func getResultBlock202() *result.Block {
|
||||
nextBlockHash, err := util.Uint256DecodeStringLE("13283c93aec07dc90be3ddd65e2de15e9212f1b3205303f688d6df85129f6b22")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
prevBlockHash, err := util.Uint256DecodeStringLE("93b540424c1173e487a47582a652686b2885f959ffd895b30e184842403990ef")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
merkleRoot, err := util.Uint256DecodeStringLE("b8e923148dede20901d6fb225579f6d430cc58f24461d1b0f860ee32abbfcc8d")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
invScript, err := hex.DecodeString("0c403620ef8f02d7884c553fb6c54d2fe717cfddd9450886c5fc88a669a29a82fa1a7c715076996567a5a56747f20f10d7e4db071d73b306ccbf17f9a916fcfa1d020c4099e27d87bbb3fb4ce1c77dca85cf3eac46c9c3de87d8022ef7ad2b0a2bb980339293849cf85e5a0a5615ea7bc5bb0a7f28e31f278dc19d628f64c49b888df4c60c40616eefc9286843c2f3f2cf1815988356e409b3f10ffaf60b3468dc0a92dd929cbc8d5da74052c303e7474412f6beaddd551e9056c4e7a5fccdc06107e48f3fe10c40fd2d25d4156e969345c0522669b509e5ced70e4265066eadaf85eea3919d5ded525f8f52d6f0dfa0186c964dd0302fca5bc2dc0540b4ed21085be478c3123996")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
verifScript, err := hex.DecodeString("130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sender, err := address.StringToUint160("ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
txInvScript, err := hex.DecodeString("0c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
txVerifScript, err := hex.DecodeString("0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vin, err := util.Uint256DecodeStringLE("33e045101301854a0e07ff96a92ca1ba9b23c19501f1b7eb15ae9eea07b5f370")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
outAddress, err := address.StringToUint160("ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tx := transaction.NewContractTX()
|
||||
tx.Nonce = 3
|
||||
tx.ValidUntilBlock = 1200
|
||||
tx.Sender = sender
|
||||
tx.Scripts = []transaction.Witness{
|
||||
{
|
||||
InvocationScript: txInvScript,
|
||||
VerificationScript: txVerifScript,
|
||||
},
|
||||
}
|
||||
tx.Inputs = []transaction.Input{
|
||||
{
|
||||
PrevHash: vin,
|
||||
PrevIndex: 0,
|
||||
},
|
||||
}
|
||||
tx.Outputs = []transaction.Output{
|
||||
{
|
||||
AssetID: core.GoverningTokenID(),
|
||||
Amount: util.Fixed8FromInt64(99999000),
|
||||
ScriptHash: outAddress,
|
||||
Position: 0,
|
||||
},
|
||||
}
|
||||
const hexB1 = "00000000e862e7907fc987cd58ddb3abb754aeb8812c9377c45e737a036fe88a622c3b8f301f2e84a86b207270830e7929530ccb841a3df7379fe6f0ac8865b33316839501cdd0847201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c994130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb030057040000000000000002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f394130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb0003000000abec5362f11e75b6e02e407bb98d63675d1438410000000000000000de6e0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d143841015d0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b523801fd08010c4063fb12aa9f3fb83f6324ea9c7ec11fa9e995b51140f480409d68cf4d625e598b0632d6610602984bfa2f5e5ea9bcc62a0e6d818dd271b38530c0d1b8a71b4e0c0c4013e091eac6f304668d647c5c032fd1020597ea5204545e21c38655a6343d58492118f1231ede91af848af7e1d987d1a8816966f5fc1a7821c6c6f62734267bde0c40daadd04a7a4141d96c58de2d373e672ca071e2b82138ef52df016ac522710385db2ac73743d2fe73061fa5d6cb0ff73a7ec7f0667e4c8bff6aa0d5783128d36e0c40dab85cd87d3f92be9532292bdc6f420b0ecbf2f877c70c6a9921ee0fc900dfc53998cf020a51fa9af3d0608f6a2b9048cea3c0b586485802bbd278b261eee8a494130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
|
||||
|
||||
var nonce uint64
|
||||
i, err := fmt.Sscanf("0000000000000457", "%016x", &nonce)
|
||||
if i != 1 {
|
||||
panic("can't decode nonce")
|
||||
}
|
||||
const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f394130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
|
||||
|
||||
const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1681,"nextblockhash":"0xf2afe371a27c9dbac4f4a8ad8eba750898b7c04aa298e64fe9e488e947976045","confirmations":6,"hash":"0xbd178d8d4a28ec082c034f817ce2423221281a31e7e00014dbf732c4053033d2","version":0,"previousblockhash":"0x8f3b2c628ae86f037a735ec477932c81b8ae54b7abb3dd58cd87c97f90e762e8","merkleroot":"0x95831633b36588acf0e69f37f73d1a84cb0c5329790e837072206ba8842e1f30","time":1591366176001,"index":1,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c9","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0x8af9ccb8e7e0f0a73e77b78dc52750e77c50f78b09ecc2f0669c0b459cc7dd89","size":575,"version":0,"nonce":2,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238","scripts":[{"invocation":"0c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f3","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]},{"txid":"0xe7cff9e4820e53232dae619a3e6f57a9430dc240b5ed7b5c0ea2cfee3e90c985","size":579,"version":0,"nonce":3,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0088035","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b5238","scripts":[{"invocation":"0c4063fb12aa9f3fb83f6324ea9c7ec11fa9e995b51140f480409d68cf4d625e598b0632d6610602984bfa2f5e5ea9bcc62a0e6d818dd271b38530c0d1b8a71b4e0c0c4013e091eac6f304668d647c5c032fd1020597ea5204545e21c38655a6343d58492118f1231ede91af848af7e1d987d1a8816966f5fc1a7821c6c6f62734267bde0c40daadd04a7a4141d96c58de2d373e672ca071e2b82138ef52df016ac522710385db2ac73743d2fe73061fa5d6cb0ff73a7ec7f0667e4c8bff6aa0d5783128d36e0c40dab85cd87d3f92be9532292bdc6f420b0ecbf2f877c70c6a9921ee0fc900dfc53998cf020a51fa9af3d0608f6a2b9048cea3c0b586485802bbd278b261eee8a4","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]}]}}`
|
||||
|
||||
const hexHeader1 = "00000000e862e7907fc987cd58ddb3abb754aeb8812c9377c45e737a036fe88a622c3b8f301f2e84a86b207270830e7929530ccb841a3df7379fe6f0ac8865b33316839501cdd0847201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c994130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb00"
|
||||
|
||||
const header1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"hash":"0xbd178d8d4a28ec082c034f817ce2423221281a31e7e00014dbf732c4053033d2","size":518,"version":0,"previousblockhash":"0x8f3b2c628ae86f037a735ec477932c81b8ae54b7abb3dd58cd87c97f90e762e8","merkleroot":"0x95831633b36588acf0e69f37f73d1a84cb0c5329790e837072206ba8842e1f30","time":1591366176001,"index":1,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40ab634ce91590e77b246cee8b204e8a270268ee1ef32434cece73f425a7dbc90f1bed1dbe914edcaa2653167ad170ae10e16a9b2c6b7e0af1f711fb848fbb1b7f0c40232de6ad07ee3846bafa96302d37602349501a556df575e7df0743e45b076d6a0c6c6dd4cad3898f9e8848dd054abd303b229fd12984042f241f0e668f39a0fb0c408b4af43057df189a9d471010b5150bab442040403147c5e502bda38cde3ff8bce803f01245e07e2bfb95d57349c55dcc27e3710b82f2735d0f40eb4342908e330c40cda66f743d4ed8d856f5376953f9169581c668a9370245aef16202ebef9bb3f7f81234be62ec287d701ad7d8bf5042648019af9fe5baa0a8e05d279bfdb1d4c9","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"confirmations":6,"nextblockhash":"0xf2afe371a27c9dbac4f4a8ad8eba750898b7c04aa298e64fe9e488e947976045"}}`
|
||||
|
||||
const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0xbd178d8d4a28ec082c034f817ce2423221281a31e7e00014dbf732c4053033d2","confirmations":6,"blocktime":1591366176001,"txid":"0x8af9ccb8e7e0f0a73e77b78dc52750e77c50f78b09ecc2f0669c0b459cc7dd89","size":575,"version":0,"nonce":2,"sender":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":1}],"script":"0218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b5238","scripts":[{"invocation":"0c402d96d8fde4ba266f89bc71ef117361967e0d11ed84cd60942a27bc99994dc58adf36a0e74ce976aac657a87a3c19c38e8ca450c67420046b81d98c60fd8feb040c40b3c15d5d23e0403a36cf559caee2979ca6ef00fe255df0e5c3daac4da051016b41eba42668934cd3308359451bafdd5419d059179fd40859684a3b91388bf9d80c407ac048cf8540b091955a374a0f36dae560c92c0134886507a589edf58b9dfbb4e3dbd5450be34e269d2e5454eb14eb7d6280d6101b4529410f829d37634849be0c403bba4113a687ff8507c1753f8519557531cf9df51ecc20deeb2c2b003ec5a1f7588cdd50b99e40b4f8039bb56c5df7ec9e7d6ea4b02fe23792510da21c7557f3","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]}}`
|
||||
|
||||
// getResultBlock1 returns data for block number 1 which is used by several tests.
|
||||
func getResultBlock1() *result.Block {
|
||||
binB, err := hex.DecodeString(hexB1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
nextCon, err := address.StringToUint160("AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL")
|
||||
b := new(block.Block)
|
||||
err = testserdes.DecodeBinary(binB, b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
blck := &block.Block{
|
||||
Base: block.Base{
|
||||
Version: 0,
|
||||
PrevHash: prevBlockHash,
|
||||
MerkleRoot: merkleRoot,
|
||||
Timestamp: 1589300496,
|
||||
Index: 202,
|
||||
NextConsensus: nextCon,
|
||||
Script: transaction.Witness{
|
||||
InvocationScript: invScript,
|
||||
VerificationScript: verifScript,
|
||||
},
|
||||
},
|
||||
ConsensusData: block.ConsensusData{
|
||||
PrimaryIndex: 0,
|
||||
Nonce: nonce,
|
||||
},
|
||||
Transactions: []*transaction.Transaction{tx},
|
||||
b2Hash, err := util.Uint256DecodeStringLE("f2afe371a27c9dbac4f4a8ad8eba750898b7c04aa298e64fe9e488e947976045")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Update hashes for correct result comparison.
|
||||
_ = tx.Hash()
|
||||
_ = blck.Hash()
|
||||
return &result.Block{
|
||||
Block: blck,
|
||||
Block: b,
|
||||
BlockMetadata: result.BlockMetadata{
|
||||
Size: 781,
|
||||
Size: 1681,
|
||||
NextBlockHash: &b2Hash,
|
||||
Confirmations: 6,
|
||||
NextBlockHash: &nextBlockHash,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getTxMoveNeo() *result.TransactionOutputRaw {
|
||||
b1 := getResultBlock1()
|
||||
txBin, err := hex.DecodeString(hexTxMoveNeo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tx := new(transaction.Transaction)
|
||||
err = testserdes.DecodeBinary(txBin, tx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &result.TransactionOutputRaw{
|
||||
Transaction: tx,
|
||||
TransactionMetadata: result.TransactionMetadata{
|
||||
Timestamp: b1.Timestamp,
|
||||
Blockhash: b1.Block.Hash(),
|
||||
Confirmations: int(b1.Confirmations),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -146,32 +93,6 @@ func getResultBlock202() *result.Block {
|
|||
// published in official C# JSON-RPC API v2.10.3 reference
|
||||
// (see https://docs.neo.org/docs/en-us/reference/rpc/latest-version/api.html)
|
||||
var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||
"getaccountstate": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetAccountState("")
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id": 1,"result":{"version":0,"script_hash":"0x1179716da2e9523d153a35fb3ad10c561b1e5b1a","frozen":false,"votes":[],"balances":[{"asset":"0x1a5e0e3eac2abced7de9ee2de0820a5c85e63756fcdfc29b82fead86a7c07c78","value":"94"}]}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
scriptHash, err := util.Uint160DecodeStringLE("1179716da2e9523d153a35fb3ad10c561b1e5b1a")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &result.AccountState{
|
||||
Version: 0,
|
||||
ScriptHash: scriptHash,
|
||||
IsFrozen: false,
|
||||
Balances: result.Balances{
|
||||
result.Balance{
|
||||
Asset: core.GoverningTokenID(),
|
||||
Value: util.Fixed8FromInt64(94),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"getapplicationlog": {
|
||||
{
|
||||
name: "positive",
|
||||
|
@ -204,30 +125,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
},
|
||||
},
|
||||
},
|
||||
"getassetstate": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetAssetState(util.Uint256{})
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":"0x1a5e0e3eac2abced7de9ee2de0820a5c85e63756fcdfc29b82fead86a7c07c78","type":0,"name":"NEO","amount":"100000000","available":"100000000","precision":0,"owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":4000000,"is_frozen":false}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
return &result.AssetState{
|
||||
ID: core.GoverningTokenID(),
|
||||
AssetType: 0,
|
||||
Name: "NEO",
|
||||
Amount: util.Fixed8FromInt64(100000000),
|
||||
Available: util.Fixed8FromInt64(100000000),
|
||||
Precision: 0,
|
||||
Owner: "00",
|
||||
Admin: "Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt",
|
||||
Issuer: "AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM",
|
||||
Expiration: 4000000,
|
||||
IsFrozen: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"getbestblockhash": {
|
||||
{
|
||||
name: "positive",
|
||||
|
@ -248,51 +145,37 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
{
|
||||
name: "byIndex_positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetBlockByIndex(202)
|
||||
return c.GetBlockByIndex(1)
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"00000000ef9039404248180eb395d8ff59f985286b6852a68275a487e473114c4240b5938dccbfab32ee60f8b0d16144f258cc30d4f6795522fbd60109e2ed8d1423e9b810cdba5e00000000ca000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c403620ef8f02d7884c553fb6c54d2fe717cfddd9450886c5fc88a669a29a82fa1a7c715076996567a5a56747f20f10d7e4db071d73b306ccbf17f9a916fcfa1d020c4099e27d87bbb3fb4ce1c77dca85cf3eac46c9c3de87d8022ef7ad2b0a2bb980339293849cf85e5a0a5615ea7bc5bb0a7f28e31f278dc19d628f64c49b888df4c60c40616eefc9286843c2f3f2cf1815988356e409b3f10ffaf60b3468dc0a92dd929cbc8d5da74052c303e7474412f6beaddd551e9056c4e7a5fccdc06107e48f3fe10c40fd2d25d4156e969345c0522669b509e5ced70e4265066eadaf85eea3919d5ded525f8f52d6f0dfa0186c964dd0302fca5bc2dc0540b4ed21085be478c312399694130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb02005704000000000000800003000000316e851039019d39dfc2c37d6c3fee19fd58098700000000000000000000000000000000b004000000000170f3b507ea9eae15ebb7f10195c1239bbaa12ca996ff070e4a8501131045e033000001787cc0a786adfe829bc2dffc5637e6855c0a82e02deee97dedbc2aac3e0e5e1a00184a27db862300316e851039019d39dfc2c37d6c3fee19fd58098701420c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}`,
|
||||
result: func(c *Client) interface{} { return &block.Block{} },
|
||||
check: func(t *testing.T, c *Client, result interface{}) {
|
||||
res, ok := result.(*block.Block)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, uint32(0), res.Version)
|
||||
assert.Equal(t, "cbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86", res.Hash().StringLE())
|
||||
assert.Equal(t, "93b540424c1173e487a47582a652686b2885f959ffd895b30e184842403990ef", res.PrevHash.StringLE())
|
||||
assert.Equal(t, "b8e923148dede20901d6fb225579f6d430cc58f24461d1b0f860ee32abbfcc8d", res.MerkleRoot.StringLE())
|
||||
assert.Equal(t, 1, len(res.Transactions))
|
||||
assert.Equal(t, "96ef00d2efe03101f5b302f7fc3c8fcd22688944bdc83ab99071d77edbb08581", res.Transactions[0].Hash().StringLE())
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexB1 + `"}`,
|
||||
result: func(c *Client) interface{} {
|
||||
b := getResultBlock1()
|
||||
return b.Block
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byIndex_verbose_positive",
|
||||
invoke: func(c *Client) (i interface{}, err error) {
|
||||
return c.GetBlockByIndexVerbose(202)
|
||||
return c.GetBlockByIndexVerbose(1)
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0xcbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86","size":781,"version":0,"nextblockhash":"0x13283c93aec07dc90be3ddd65e2de15e9212f1b3205303f688d6df85129f6b22","previousblockhash":"0x93b540424c1173e487a47582a652686b2885f959ffd895b30e184842403990ef","merkleroot":"0xb8e923148dede20901d6fb225579f6d430cc58f24461d1b0f860ee32abbfcc8d","time":1589300496,"index":202,"consensus_data":{"primary":0,"nonce":"0000000000000457"},"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","confirmations":6,"witnesses":[{"invocation":"0c403620ef8f02d7884c553fb6c54d2fe717cfddd9450886c5fc88a669a29a82fa1a7c715076996567a5a56747f20f10d7e4db071d73b306ccbf17f9a916fcfa1d020c4099e27d87bbb3fb4ce1c77dca85cf3eac46c9c3de87d8022ef7ad2b0a2bb980339293849cf85e5a0a5615ea7bc5bb0a7f28e31f278dc19d628f64c49b888df4c60c40616eefc9286843c2f3f2cf1815988356e409b3f10ffaf60b3468dc0a92dd929cbc8d5da74052c303e7474412f6beaddd551e9056c4e7a5fccdc06107e48f3fe10c40fd2d25d4156e969345c0522669b509e5ced70e4265066eadaf85eea3919d5ded525f8f52d6f0dfa0186c964dd0302fca5bc2dc0540b4ed21085be478c3123996","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"tx":[{"txid":"0x96ef00d2efe03101f5b302f7fc3c8fcd22688944bdc83ab99071d77edbb08581","size":254,"type":"ContractTransaction","version":0,"nonce":3,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0","valid_until_block":1200,"attributes":[],"cosigners":[],"vin":[{"txid":"0x33e045101301854a0e07ff96a92ca1ba9b23c19501f1b7eb15ae9eea07b5f370","vout":0}],"vout":[{"address":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","asset":"0x1a5e0e3eac2abced7de9ee2de0820a5c85e63756fcdfc29b82fead86a7c07c78","n":0,"value":"99999000"}],"scripts":[{"invocation":"0c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}}`,
|
||||
serverResponse: b1Verbose,
|
||||
result: func(c *Client) interface{} {
|
||||
return getResultBlock202()
|
||||
return getResultBlock1()
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byHash_positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
hash, err := util.Uint256DecodeStringLE("86fe1061140b2ea791b0739fb9732abc6e5e47de4927228a1ac41de3d93eb7cb")
|
||||
hash, err := util.Uint256DecodeStringLE("d151651e86680a7ecbc87babf3346a42e7bc9974414ce192c9c22ac4f2e9d043")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c.GetBlockByHash(hash)
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"00000000ef9039404248180eb395d8ff59f985286b6852a68275a487e473114c4240b5938dccbfab32ee60f8b0d16144f258cc30d4f6795522fbd60109e2ed8d1423e9b810cdba5e00000000ca000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c403620ef8f02d7884c553fb6c54d2fe717cfddd9450886c5fc88a669a29a82fa1a7c715076996567a5a56747f20f10d7e4db071d73b306ccbf17f9a916fcfa1d020c4099e27d87bbb3fb4ce1c77dca85cf3eac46c9c3de87d8022ef7ad2b0a2bb980339293849cf85e5a0a5615ea7bc5bb0a7f28e31f278dc19d628f64c49b888df4c60c40616eefc9286843c2f3f2cf1815988356e409b3f10ffaf60b3468dc0a92dd929cbc8d5da74052c303e7474412f6beaddd551e9056c4e7a5fccdc06107e48f3fe10c40fd2d25d4156e969345c0522669b509e5ced70e4265066eadaf85eea3919d5ded525f8f52d6f0dfa0186c964dd0302fca5bc2dc0540b4ed21085be478c312399694130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb02005704000000000000800003000000316e851039019d39dfc2c37d6c3fee19fd58098700000000000000000000000000000000b004000000000170f3b507ea9eae15ebb7f10195c1239bbaa12ca996ff070e4a8501131045e033000001787cc0a786adfe829bc2dffc5637e6855c0a82e02deee97dedbc2aac3e0e5e1a00184a27db862300316e851039019d39dfc2c37d6c3fee19fd58098701420c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}`,
|
||||
result: func(c *Client) interface{} { return &block.Block{} },
|
||||
check: func(t *testing.T, c *Client, result interface{}) {
|
||||
res, ok := result.(*block.Block)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, uint32(0), res.Version)
|
||||
assert.Equal(t, "cbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86", res.Hash().StringLE())
|
||||
assert.Equal(t, "93b540424c1173e487a47582a652686b2885f959ffd895b30e184842403990ef", res.PrevHash.StringLE())
|
||||
assert.Equal(t, "b8e923148dede20901d6fb225579f6d430cc58f24461d1b0f860ee32abbfcc8d", res.MerkleRoot.StringLE())
|
||||
assert.Equal(t, 1, len(res.Transactions))
|
||||
assert.Equal(t, "96ef00d2efe03101f5b302f7fc3c8fcd22688944bdc83ab99071d77edbb08581", res.Transactions[0].Hash().StringLE())
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexB1 + `"}`,
|
||||
result: func(c *Client) interface{} {
|
||||
b := getResultBlock1()
|
||||
return b.Block
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -304,9 +187,9 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
}
|
||||
return c.GetBlockByHashVerbose(hash)
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0xcbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86","size":781,"version":0,"nextblockhash":"0x13283c93aec07dc90be3ddd65e2de15e9212f1b3205303f688d6df85129f6b22","previousblockhash":"0x93b540424c1173e487a47582a652686b2885f959ffd895b30e184842403990ef","merkleroot":"0xb8e923148dede20901d6fb225579f6d430cc58f24461d1b0f860ee32abbfcc8d","time":1589300496,"index":202,"consensus_data":{"primary":0,"nonce":"0000000000000457"},"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","confirmations":6,"witnesses":[{"invocation":"0c403620ef8f02d7884c553fb6c54d2fe717cfddd9450886c5fc88a669a29a82fa1a7c715076996567a5a56747f20f10d7e4db071d73b306ccbf17f9a916fcfa1d020c4099e27d87bbb3fb4ce1c77dca85cf3eac46c9c3de87d8022ef7ad2b0a2bb980339293849cf85e5a0a5615ea7bc5bb0a7f28e31f278dc19d628f64c49b888df4c60c40616eefc9286843c2f3f2cf1815988356e409b3f10ffaf60b3468dc0a92dd929cbc8d5da74052c303e7474412f6beaddd551e9056c4e7a5fccdc06107e48f3fe10c40fd2d25d4156e969345c0522669b509e5ced70e4265066eadaf85eea3919d5ded525f8f52d6f0dfa0186c964dd0302fca5bc2dc0540b4ed21085be478c3123996","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"tx":[{"txid":"0x96ef00d2efe03101f5b302f7fc3c8fcd22688944bdc83ab99071d77edbb08581","size":254,"type":"ContractTransaction","version":0,"nonce":3,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0","valid_until_block":1200,"attributes":[],"cosigners":[],"vin":[{"txid":"0x33e045101301854a0e07ff96a92ca1ba9b23c19501f1b7eb15ae9eea07b5f370","vout":0}],"vout":[{"address":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","asset":"0x1a5e0e3eac2abced7de9ee2de0820a5c85e63756fcdfc29b82fead86a7c07c78","n":0,"value":"99999000"}],"scripts":[{"invocation":"0c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}}`,
|
||||
serverResponse: b1Verbose,
|
||||
result: func(c *Client) interface{} {
|
||||
return getResultBlock202()
|
||||
return getResultBlock1()
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -348,15 +231,10 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
}
|
||||
return c.GetBlockHeader(hash)
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"00000000d039da5e49d63eb0533437d24ff8ceb6aeacf88680599c39f0ffca8948dfcdb94a3def1fca91cf45d69358414e3be77f7621e557f4cebbdb79a47d3cf56ac007f920a05e0000000001000000d60ac443bb800fb08261e75fa5925d747d48586101fd04014055041db6a59c99ab98137cc57e1e56a0a89856a311b2d2fc0aec76ec714c7616edc8fc5c9b81b27f25b7db1a61f64be0730a9cc103efcea1195cc3fe55843e264027e49c647f48bb08d3c32b79ee3432005ea577d7e497f78b46f1e81858848f961b557fb42a92e8eb4433fed203c917cbebb2138a31ed86750fb769d1e70956c0404c20054aa8bd45b520cba9410a9dd6c256481066bb657d7793fbba5551898c91b6dde81285fac841753ccfdd3193d08f19d5431313fa0d926ca965072a5fa3384026b0705078409bcc62fb98bb985edc387edeaaeba37bb7642d88a90762b2c2a62d9b61d53c097d548a368e450c4d995a178d5af28d4c93698233c52de05e3f0094534c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e4c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd624c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc24c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee6995450683073b3bb00"}`,
|
||||
result: func(c *Client) interface{} { return &block.Header{} },
|
||||
check: func(t *testing.T, c *Client, result interface{}) {
|
||||
res, ok := result.(*block.Header)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, uint32(0), res.Version)
|
||||
assert.Equal(t, "68e4bd688b852e807eef13a0ff7da7b02223e359a35153667e88f9cb4a3b0801", res.Hash().StringLE())
|
||||
assert.Equal(t, "b9cddf4889cafff0399c598086f8acaeb6cef84fd2373453b03ed6495eda39d0", res.PrevHash.StringLE())
|
||||
assert.Equal(t, "07c06af53c7da479dbbbcef457e521767fe73b4e415893d645cf91ca1fef3d4a", res.MerkleRoot.StringLE())
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexHeader1 + `"}`,
|
||||
result: func(c *Client) interface{} {
|
||||
b := getResultBlock1()
|
||||
return b.Header()
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -368,12 +246,12 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
}
|
||||
return c.GetBlockHeaderVerbose(hash)
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0xcbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86","size":781,"version":0,"nextblockhash":"0x13283c93aec07dc90be3ddd65e2de15e9212f1b3205303f688d6df85129f6b22","previousblockhash":"0x93b540424c1173e487a47582a652686b2885f959ffd895b30e184842403990ef","merkleroot":"0xb8e923148dede20901d6fb225579f6d430cc58f24461d1b0f860ee32abbfcc8d","time":1589300496,"index":202,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","confirmations":6,"witnesses":[{"invocation":"0c403620ef8f02d7884c553fb6c54d2fe717cfddd9450886c5fc88a669a29a82fa1a7c715076996567a5a56747f20f10d7e4db071d73b306ccbf17f9a916fcfa1d020c4099e27d87bbb3fb4ce1c77dca85cf3eac46c9c3de87d8022ef7ad2b0a2bb980339293849cf85e5a0a5615ea7bc5bb0a7f28e31f278dc19d628f64c49b888df4c60c40616eefc9286843c2f3f2cf1815988356e409b3f10ffaf60b3468dc0a92dd929cbc8d5da74052c303e7474412f6beaddd551e9056c4e7a5fccdc06107e48f3fe10c40fd2d25d4156e969345c0522669b509e5ced70e4265066eadaf85eea3919d5ded525f8f52d6f0dfa0186c964dd0302fca5bc2dc0540b4ed21085be478c3123996","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}]}}`,
|
||||
serverResponse: header1Verbose,
|
||||
result: func(c *Client) interface{} {
|
||||
b := getResultBlock202()
|
||||
b := getResultBlock1()
|
||||
return &result.Header{
|
||||
Hash: b.Hash(),
|
||||
Size: 781,
|
||||
Size: 518,
|
||||
Version: b.Version,
|
||||
NextBlockHash: b.NextBlockHash,
|
||||
PrevBlockHash: b.PrevHash,
|
||||
|
@ -399,36 +277,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
},
|
||||
},
|
||||
},
|
||||
"getclaimable": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetClaimable("AGofsxAUDwt52KjaB664GYsqVAkULYvKNt")
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"claimable":[{"txid":"52ba70ef18e879785572c917795cd81422c3820b8cf44c24846a30ee7376fd77","n":1,"value":800000,"start_height":476496,"end_height":488154,"generated":746.112,"sys_fee": 3.92,"unclaimed":750.032}],"address":"AGofsxAUDwt52KjaB664GYsqVAkULYvKNt","unclaimed": 750.032}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
txID, err := util.Uint256DecodeStringLE("52ba70ef18e879785572c917795cd81422c3820b8cf44c24846a30ee7376fd77")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &result.ClaimableInfo{
|
||||
Spents: []result.Claimable{
|
||||
{
|
||||
Tx: txID,
|
||||
N: 1,
|
||||
Value: util.Fixed8FromInt64(800000),
|
||||
StartHeight: 476496,
|
||||
EndHeight: 488154,
|
||||
Generated: util.Fixed8FromFloat(746.112),
|
||||
SysFee: util.Fixed8FromFloat(3.92),
|
||||
Unclaimed: util.Fixed8FromFloat(750.032),
|
||||
}},
|
||||
Address: "AGofsxAUDwt52KjaB664GYsqVAkULYvKNt",
|
||||
Unclaimed: util.Fixed8FromFloat(750.032),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"getconnectioncount": {
|
||||
{
|
||||
name: "positive",
|
||||
|
@ -593,21 +441,16 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (i interface{}, err error) {
|
||||
hash, err := util.Uint256DecodeStringLE("8185b0db7ed77190b93ac8bd44896822cd8f3cfcf702b3f50131e0efd200ef96")
|
||||
hash, err := util.Uint256DecodeStringLE("ca23bd5df3249836849309ca2afe972bfd288b0a7ae61302c8fd545daa8bffd6")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c.GetRawTransaction(hash)
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"800003000000316e851039019d39dfc2c37d6c3fee19fd58098700000000000000000000000000000000b004000000000170f3b507ea9eae15ebb7f10195c1239bbaa12ca996ff070e4a8501131045e033000001787cc0a786adfe829bc2dffc5637e6855c0a82e02deee97dedbc2aac3e0e5e1a00184a27db862300316e851039019d39dfc2c37d6c3fee19fd58098701420c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}`,
|
||||
result: func(c *Client) interface{} { return &transaction.Transaction{} },
|
||||
check: func(t *testing.T, c *Client, result interface{}) {
|
||||
res, ok := result.(*transaction.Transaction)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, uint8(0), res.Version)
|
||||
assert.Equal(t, "8185b0db7ed77190b93ac8bd44896822cd8f3cfcf702b3f50131e0efd200ef96", res.Hash().StringBE())
|
||||
assert.Equal(t, transaction.ContractType, res.Type)
|
||||
assert.Equal(t, false, res.Trimmed)
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexTxMoveNeo + `"}`,
|
||||
result: func(c *Client) interface{} {
|
||||
tx := getTxMoveNeo()
|
||||
return tx.Transaction
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -619,68 +462,9 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
}
|
||||
return c.GetRawTransactionVerbose(hash)
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"blockhash":"0xcbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86","confirmations":8,"blocktime":1589300496,"txid":"0x96ef00d2efe03101f5b302f7fc3c8fcd22688944bdc83ab99071d77edbb08581","size":254,"type":"ContractTransaction","version":0,"nonce":3,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0","valid_until_block":1200,"attributes":[],"cosigners":[],"vin":[{"txid":"0x33e045101301854a0e07ff96a92ca1ba9b23c19501f1b7eb15ae9eea07b5f370","vout":0}],"vout":[{"address":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","asset":"0x1a5e0e3eac2abced7de9ee2de0820a5c85e63756fcdfc29b82fead86a7c07c78","n":0,"value":"99999000"}],"scripts":[{"invocation":"0c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}}`,
|
||||
serverResponse: txMoveNeoVerbose,
|
||||
result: func(c *Client) interface{} {
|
||||
blockHash, err := util.Uint256DecodeStringLE("cbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sender, err := address.StringToUint160("ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
invocation, err := hex.DecodeString("0c402caebbee911a1f159aa05ab40093d086090a817e837f3f87e8b3e47f6b083649137770f6dda0349ddd611bc47402aca457a89b3b7b0076307ab6a47fd57048eb")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
verification, err := hex.DecodeString("0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vin, err := util.Uint256DecodeStringLE("33e045101301854a0e07ff96a92ca1ba9b23c19501f1b7eb15ae9eea07b5f370")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
outAddress, err := address.StringToUint160("ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tx := transaction.NewContractTX()
|
||||
tx.Nonce = 3
|
||||
tx.ValidUntilBlock = 1200
|
||||
tx.Sender = sender
|
||||
tx.Scripts = []transaction.Witness{
|
||||
{
|
||||
InvocationScript: invocation,
|
||||
VerificationScript: verification,
|
||||
},
|
||||
}
|
||||
tx.Inputs = []transaction.Input{
|
||||
{
|
||||
PrevHash: vin,
|
||||
PrevIndex: 0,
|
||||
},
|
||||
}
|
||||
tx.Outputs = []transaction.Output{
|
||||
{
|
||||
AssetID: core.GoverningTokenID(),
|
||||
Amount: util.Fixed8FromInt64(99999000),
|
||||
ScriptHash: outAddress,
|
||||
Position: 0,
|
||||
},
|
||||
}
|
||||
|
||||
// Update hashes for correct result comparison.
|
||||
_ = tx.Hash()
|
||||
|
||||
return &result.TransactionOutputRaw{
|
||||
Transaction: tx,
|
||||
TransactionMetadata: result.TransactionMetadata{
|
||||
Blockhash: blockHash,
|
||||
Confirmations: 8,
|
||||
Timestamp: uint64(1589300496),
|
||||
},
|
||||
}
|
||||
return getTxMoveNeo()
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -724,56 +508,15 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
},
|
||||
},
|
||||
},
|
||||
"gettxout": {
|
||||
"getunclaimedgas": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
hash, err := util.Uint256DecodeStringLE("f4250dab094c38d8265acc15c366dc508d2e14bf5699e12d9df26577ed74d657")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c.GetTxOut(hash, 0)
|
||||
return c.GetUnclaimedGas("AGofsxAUDwt52KjaB664GYsqVAkULYvKNt")
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"N":0,"Asset":"c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","Value":"2950","Address":"AHCNSDkh2Xs66SzmyKGdoDKY752uyeXDrt"}}`,
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"897299680935"}`,
|
||||
result: func(c *Client) interface{} {
|
||||
return &result.TransactionOutput{
|
||||
N: 0,
|
||||
Asset: "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
|
||||
Value: util.Fixed8FromInt64(2950),
|
||||
Address: "AHCNSDkh2Xs66SzmyKGdoDKY752uyeXDrt",
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"getunclaimed": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetUnclaimed("AGofsxAUDwt52KjaB664GYsqVAkULYvKNt")
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"available":750.032,"unavailable":2815.408,"unclaimed":3565.44}}`,
|
||||
result: func(c *Client) interface{} {
|
||||
return &result.Unclaimed{
|
||||
Available: util.Fixed8FromFloat(750.032),
|
||||
Unavailable: util.Fixed8FromFloat(2815.408),
|
||||
Unclaimed: util.Fixed8FromFloat(3565.44),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"getunspents": {
|
||||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetUnspents("AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y")
|
||||
},
|
||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"balance":[{"unspent":[{"txid":"0x83df8bd085fcb60b2789f7d0a9f876e5f3908567f7877fcba835e899b9dea0b5","n":0,"value":"100000000"}],"asset_hash":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","asset":"NEO","asset_symbol":"NEO","amount":"100000000"},{"unspent":[{"txid":"0x2ab085fa700dd0df4b73a94dc17a092ac3a85cbd965575ea1585d1668553b2f9","n":0,"value":"19351.99993"}],"asset_hash":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","asset":"GAS","asset_symbol":"GAS","amount":"19351.99993"}],"address":"AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"}}`,
|
||||
result: func(c *Client) interface{} { return &result.Unspents{} },
|
||||
check: func(t *testing.T, c *Client, uns interface{}) {
|
||||
res, ok := uns.(*result.Unspents)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y", res.Address)
|
||||
assert.Equal(t, 2, len(res.Balance))
|
||||
return util.Fixed8(897299680935)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -873,7 +616,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return nil, c.SendRawTransaction(transaction.NewContractTX())
|
||||
return nil, c.SendRawTransaction(transaction.New([]byte{byte(opcode.PUSH1)}, 0))
|
||||
},
|
||||
serverResponse: `{"jsonrpc":"2.0","id":1,"result":true}`,
|
||||
result: func(c *Client) interface{} {
|
||||
|
@ -994,7 +737,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
{
|
||||
name: "sendrawtransaction_bad_server_answer",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return nil, c.SendRawTransaction(transaction.NewContractTX())
|
||||
return nil, c.SendRawTransaction(transaction.New([]byte{byte(opcode.PUSH1)}, 0))
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1015,24 +758,12 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
},
|
||||
},
|
||||
`{"id":1,"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"}}`: {
|
||||
{
|
||||
name: "getaccountstate_invalid_params_error",
|
||||
invoke: func(c *Client) (i interface{}, err error) {
|
||||
return c.GetAccountState("")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getapplicationlog_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetApplicationLog(util.Uint256{})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getassetstate_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetAssetState(core.GoverningTokenID())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getbestblockhash_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
|
@ -1087,12 +818,6 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
return c.GetBlockSysFee(1)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getclaimable_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetClaimable("")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getconnectioncount_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
|
@ -1142,21 +867,9 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "gettxoutput_invalid_params_error",
|
||||
name: "getunclaimedgas_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetTxOut(util.Uint256{}, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getunclaimed_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetUnclaimed("")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getunspents_invalid_params_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetUnspents("")
|
||||
return c.GetUnclaimedGas("")
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1191,24 +904,12 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
},
|
||||
},
|
||||
`{}`: {
|
||||
{
|
||||
name: "getaccountstate_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetAccountState("")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getapplicationlog_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetApplicationLog(util.Uint256{})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getassetstate_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetAssetState(core.GoverningTokenID())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getbestblockhash_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
|
@ -1269,12 +970,6 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
return c.GetBlockSysFee(1)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getclaimable_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetClaimable("")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getconnectioncount_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
|
@ -1336,21 +1031,9 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "getxoutput_unmarshalling_error",
|
||||
name: "getunclaimedgas_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetTxOut(util.Uint256{}, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getunclaimed_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetUnclaimed("")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getunspents_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return c.GetUnspents("")
|
||||
return c.GetUnclaimedGas("")
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1380,7 +1063,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{
|
|||
{
|
||||
name: "sendrawtransaction_unmarshalling_error",
|
||||
invoke: func(c *Client) (interface{}, error) {
|
||||
return nil, c.SendRawTransaction(transaction.NewContractTX())
|
||||
return nil, c.SendRawTransaction(transaction.New([]byte{byte(opcode.PUSH1)}, 0))
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -117,8 +117,8 @@ func TestWSClientEvents(t *testing.T) {
|
|||
var events = []string{
|
||||
`{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","executions":[{"trigger":"Application","contract":"0x0000000000000000000000000000000000000000","vmstate":"HALT","gas_consumed":"2.291","stack":[],"notifications":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"636f6e74726163742063616c6c"},{"type":"ByteArray","value":"7472616e73666572"},{"type":"Array","value":[{"type":"ByteArray","value":"769162241eedf97c2481652adf1ba0f5bf57431b"},{"type":"ByteArray","value":"316e851039019d39dfc2c37d6c3fee19fd580987"},{"type":"Integer","value":"1000"}]}]}},{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"7472616e73666572"},{"type":"ByteArray","value":"769162241eedf97c2481652adf1ba0f5bf57431b"},{"type":"ByteArray","value":"316e851039019d39dfc2c37d6c3fee19fd580987"},{"type":"Integer","value":"1000"}]}}]}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"636f6e74726163742063616c6c"},{"type":"ByteArray","value":"7472616e73666572"},{"type":"Array","value":[{"type":"ByteArray","value":"769162241eedf97c2481652adf1ba0f5bf57431b"},{"type":"ByteArray","value":"316e851039019d39dfc2c37d6c3fee19fd580987"},{"type":"Integer","value":"1000"}]}]}}]}`,
|
||||
`{"jsonrpc":"2.0","method":"transaction_added","params":[{"txid":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","size":277,"type":"InvocationTransaction","version":1,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0037721","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"vin":[],"vout":[],"scripts":[{"invocation":"0c4027727296b84853c5d9e07fb8a40e885246ae25641383b16eefbe92027ecb1635b794aacf6bbfc3e828c73829b14791c483d19eb758b57638e3191393dbf2d288","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}],"script":"01e8030c14316e851039019d39dfc2c37d6c3fee19fd5809870c14769162241eedf97c2481652adf1ba0f5bf57431b13c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238"}]}`,
|
||||
`{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x239fea00c54c2f6812612874183b72bef4473fcdf68bf8da08d74fd5b6cab030","version":0,"previousblockhash":"0x04f7580b111ec75f0ce68d3a9fd70a0544b4521b4a98541694d8575c548b759e","merkleroot":"0xb2c7230ebee4cb83bc03afadbba413e6bca8fcdeaf9c077bea060918da0e52a1","time":1590006200,"index":207,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c4063429fca5ff75c964d9e38179c75978e33f8174d91a780c2e825265cf2447281594afdd5f3e216dcaf5ff0693aec83f415996cf224454495495f6bd0a4c5d08f0c4099680903a954278580d8533121c2cd3e53a089817b6a784901ec06178a60b5f1da6e70422bdcadc89029767e08d66ce4180b99334cb2d42f42e4216394af15920c4067d5e362189e48839a24e187c59d46f5d9db862c8a029777f1548b19632bfdc73ad373827ed02369f925e89c2303b64e6b9838dca229949b9b9d3bd4c0c3ed8f0c4021d4c00d4522805883f1db929554441bcbbee127c48f6b7feeeb69a72a78c7f0a75011663e239c0820ef903f36168f42936de10f0ef20681cb735a4b53d0390f","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0xf736cd91ab84062a21a09b424346b241987f6245ffe8c2b2db39d595c3c222f7","size":204,"type":"InvocationTransaction","version":1,"nonce":8,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0030421","valid_until_block":1200,"attributes":[],"cosigners":[],"vin":[],"vout":[],"scripts":[{"invocation":"0c4016e7a112742409cdfaad89dcdbcb52c94c5c1a69dfe5d8b999649eaaa787e31ca496d1734d6ea606c749ad36e9a88892240ae59e0efa7f544e0692124898d512","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}],"script":"10c00c04696e69740c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b52"},{"txid":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","size":277,"type":"InvocationTransaction","version":1,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0037721","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"vin":[],"vout":[],"scripts":[{"invocation":"0c4027727296b84853c5d9e07fb8a40e885246ae25641383b16eefbe92027ecb1635b794aacf6bbfc3e828c73829b14791c483d19eb758b57638e3191393dbf2d288","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}],"script":"01e8030c14316e851039019d39dfc2c37d6c3fee19fd5809870c14769162241eedf97c2481652adf1ba0f5bf57431b13c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238"}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"transaction_added","params":[{"txid":"0x1c615d4043c98fc0e285c2f40cc3601cf4ebe1cf9d2b404dfc67c9cd085444ec","size":265,"version":0,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"script":"007b0c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238","scripts":[{"invocation":"0c4034d02f3b97a220ffe79640e482b887ec0e44dcc95e719f5e2b43b29987f0c9822b9af0499d90094c6ad3ba191e434a3df5dd378d3b73318cf47c9f2d6d801cc8","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x765ea65b4de6addfee29b1c90ac922d1901c8d7ab7f2366da9a8ad3dd71ca703","version":0,"previousblockhash":"0xbdeed527a43ab72d5d8cecf1dc6ee142112ff8a8eaaaebc7206d3df3bf3c1169","merkleroot":"0xa1b321f59b127cddd23b0cd47fc9ec7920647d30d7ab23318a106597b9c9abad","time":1591366176006,"index":6,"nextconsensus":"AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL","witnesses":[{"invocation":"0c40da1f4b546a8a60e96596351234d7709391866bb3590a290133bc0c45837f1dac6351ee32506a7e0bbf6fcbcc3ec01222ccfe84bc1d4071221f4c432ebf569b620c40ee5906328012a8a4a411e7fa23aa8ba21fedb81b11581e5a287cad961fa36d2a20b2069549a5a14860d9e9ae3640ea20f9191d60ab7c2aeddf43edd6dabe558c0c40f5391e79e7d62f7ccaa900511d530f89de183fa51bc4af744bda81f763e14ddd7fb953e69b0901660d4752f240d5269344d0b64b50b124d1a316ad72486da15e0c40012f773faef2aee4af59e083b443ebe6cf404d12f49d32966c5f48f2c203e284429615aa2d34c827356d55c3be1612f67a5b725f6ff49b9b95b1f60306a72b71","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0x1c615d4043c98fc0e285c2f40cc3601cf4ebe1cf9d2b404dfc67c9cd085444ec","size":265,"version":0,"nonce":9,"sender":"ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":1}],"script":"007b0c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238","scripts":[{"invocation":"0c4034d02f3b97a220ffe79640e482b887ec0e44dcc95e719f5e2b43b29987f0c9822b9af0499d90094c6ad3ba191e434a3df5dd378d3b73318cf47c9f2d6d801cc8","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"}]}]}]}`,
|
||||
`{"jsonrpc":"2.0","method":"event_missed","params":[]}`,
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -5,77 +5,14 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
errs "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// CreateRawContractTransaction returns contract-type Transaction built from specified parameters.
|
||||
func CreateRawContractTransaction(params ContractTxParams) (*transaction.Transaction, error) {
|
||||
var (
|
||||
err error
|
||||
tx = transaction.NewContractTX()
|
||||
toAddressHash, fromAddressHash util.Uint160
|
||||
fromAddress string
|
||||
receiverOutput *transaction.Output
|
||||
|
||||
wif, assetID, toAddress, amount, balancer = params.WIF, params.AssetID, params.Address, params.Value, params.Balancer
|
||||
)
|
||||
|
||||
fromAddress = wif.PrivateKey.Address()
|
||||
|
||||
if fromAddressHash, err = address.StringToUint160(fromAddress); err != nil {
|
||||
return nil, errs.Wrapf(err, "Failed to take script hash from address: %v", fromAddress)
|
||||
}
|
||||
|
||||
if toAddressHash, err = address.StringToUint160(toAddress); err != nil {
|
||||
return nil, errs.Wrapf(err, "Failed to take script hash from address: %v", toAddress)
|
||||
}
|
||||
tx.Sender = fromAddressHash
|
||||
|
||||
if err = AddInputsAndUnspentsToTx(tx, fromAddress, assetID, amount, balancer); err != nil {
|
||||
return nil, errs.Wrap(err, "failed to add inputs and unspents to transaction")
|
||||
}
|
||||
receiverOutput = transaction.NewOutput(assetID, amount, toAddressHash)
|
||||
tx.AddOutput(receiverOutput)
|
||||
if acc, err := wallet.NewAccountFromWIF(wif.S); err != nil {
|
||||
return nil, err
|
||||
} else if err = acc.SignTx(tx); err != nil {
|
||||
return nil, errs.Wrap(err, "failed to sign tx")
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// AddInputsAndUnspentsToTx adds inputs needed to transaction and one output
|
||||
// with change.
|
||||
func AddInputsAndUnspentsToTx(tx *transaction.Transaction, addr string, assetID util.Uint256, amount util.Fixed8, balancer BalanceGetter) error {
|
||||
scriptHash, err := address.StringToUint160(addr)
|
||||
if err != nil {
|
||||
return errs.Wrapf(err, "failed to take script hash from address: %v", addr)
|
||||
}
|
||||
inputs, spent, err := balancer.CalculateInputs(addr, assetID, amount)
|
||||
if err != nil {
|
||||
return errs.Wrap(err, "failed to get inputs")
|
||||
}
|
||||
for _, input := range inputs {
|
||||
tx.AddInput(&input)
|
||||
}
|
||||
|
||||
if senderUnspent := spent - amount; senderUnspent > 0 {
|
||||
senderOutput := transaction.NewOutput(assetID, senderUnspent, scriptHash)
|
||||
tx.AddOutput(senderOutput)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetailsToSCProperties extract the fields needed from ContractDetails
|
||||
// and converts them to smartcontract.PropertyState.
|
||||
func DetailsToSCProperties(contract *smartcontract.ContractDetails) smartcontract.PropertyState {
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package request
|
||||
|
||||
/*
|
||||
Definition of types, interfaces and variables
|
||||
required for raw transaction composing.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
type (
|
||||
// ContractTxParams contains parameters for tx to transfer assets.
|
||||
// It includes (*Client).TransferAsset call params and utility data.
|
||||
ContractTxParams struct {
|
||||
AssetID util.Uint256
|
||||
Address string
|
||||
Value util.Fixed8
|
||||
WIF keys.WIF // a WIF to send the transaction
|
||||
// since there are many ways to provide unspents,
|
||||
// transaction composer stays agnostic to that how
|
||||
// unspents was got;
|
||||
Balancer BalanceGetter
|
||||
}
|
||||
|
||||
// BalanceGetter is an interface supporting CalculateInputs() method.
|
||||
BalanceGetter interface {
|
||||
// parameters
|
||||
// address: base58-encoded address assets would be transferred from
|
||||
// assetID: asset identifier
|
||||
// amount: an asset amount to spend
|
||||
// return values
|
||||
// inputs: UTXO's for the preparing transaction
|
||||
// total: summarized asset amount from all the `inputs`
|
||||
// error: error would be considered in the caller function
|
||||
CalculateInputs(address string, assetID util.Uint256, amount util.Fixed8) (inputs []transaction.Input, total util.Fixed8, err error)
|
||||
}
|
||||
)
|
|
@ -1,51 +0,0 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// AccountState wrapper used for the representation of
|
||||
// state.Account on the RPC Server.
|
||||
type AccountState struct {
|
||||
Version uint8 `json:"version"`
|
||||
ScriptHash util.Uint160 `json:"script_hash"`
|
||||
IsFrozen bool `json:"frozen"`
|
||||
Balances []Balance `json:"balances"`
|
||||
}
|
||||
|
||||
// Balances type for sorting balances in rpc response.
|
||||
type Balances []Balance
|
||||
|
||||
func (b Balances) Len() int { return len(b) }
|
||||
func (b Balances) Less(i, j int) bool { return bytes.Compare(b[i].Asset[:], b[j].Asset[:]) != -1 }
|
||||
func (b Balances) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
|
||||
// Balance response wrapper.
|
||||
type Balance struct {
|
||||
Asset util.Uint256 `json:"asset"`
|
||||
Value util.Fixed8 `json:"value"`
|
||||
}
|
||||
|
||||
// NewAccountState creates a new Account wrapper.
|
||||
func NewAccountState(a *state.Account) AccountState {
|
||||
balances := make(Balances, 0, len(a.Balances))
|
||||
for k, v := range a.GetBalanceValues() {
|
||||
balances = append(balances, Balance{
|
||||
Asset: k,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Sort(balances)
|
||||
|
||||
return AccountState{
|
||||
Version: a.Version,
|
||||
ScriptHash: a.ScriptHash,
|
||||
IsFrozen: a.IsFrozen,
|
||||
Balances: balances,
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// AssetState wrapper used for the representation of
|
||||
// state.Asset on the RPC Server.
|
||||
type AssetState struct {
|
||||
ID util.Uint256 `json:"id"`
|
||||
AssetType transaction.AssetType `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Amount util.Fixed8 `json:"amount"`
|
||||
Available util.Fixed8 `json:"available"`
|
||||
Precision uint8 `json:"precision"`
|
||||
Owner string `json:"owner"`
|
||||
Admin string `json:"admin"`
|
||||
Issuer string `json:"issuer"`
|
||||
Expiration uint32 `json:"expiration"`
|
||||
IsFrozen bool `json:"is_frozen"`
|
||||
}
|
||||
|
||||
// NewAssetState creates a new Asset wrapper.
|
||||
func NewAssetState(a *state.Asset) AssetState {
|
||||
return AssetState{
|
||||
ID: a.ID,
|
||||
AssetType: a.AssetType,
|
||||
Name: a.GetName(),
|
||||
Amount: a.Amount,
|
||||
Available: a.Available,
|
||||
Precision: a.Precision,
|
||||
Owner: a.Owner.String(),
|
||||
Admin: address.Uint160ToString(a.Admin),
|
||||
Issuer: address.Uint160ToString(a.Issuer),
|
||||
Expiration: a.Expiration,
|
||||
IsFrozen: a.IsFrozen,
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ func NewBlock(b *block.Block, chain blockchainer.Blockchainer) Block {
|
|||
Block: b,
|
||||
BlockMetadata: BlockMetadata{
|
||||
Size: io.GetVarSize(b),
|
||||
Confirmations: chain.BlockHeight() - b.Index - 1,
|
||||
Confirmations: chain.BlockHeight() - b.Index + 1,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package result
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
||||
// ClaimableInfo is a result of a getclaimable RPC call.
|
||||
type ClaimableInfo struct {
|
||||
Spents []Claimable `json:"claimable"`
|
||||
Address string `json:"address"`
|
||||
Unclaimed util.Fixed8 `json:"unclaimed"`
|
||||
}
|
||||
|
||||
// Claimable represents spent outputs which can be claimed.
|
||||
type Claimable struct {
|
||||
Tx util.Uint256 `json:"txid"`
|
||||
N int `json:"n"`
|
||||
Value util.Fixed8 `json:"value"`
|
||||
StartHeight uint32 `json:"start_height"`
|
||||
EndHeight uint32 `json:"end_height"`
|
||||
Generated util.Fixed8 `json:"generated"`
|
||||
SysFee util.Fixed8 `json:"sys_fee"`
|
||||
Unclaimed util.Fixed8 `json:"unclaimed"`
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// TransactionOutput is a wrapper to represent transaction's output.
|
||||
type TransactionOutput struct {
|
||||
N int `json:"n"`
|
||||
Asset string `json:"asset"`
|
||||
Value util.Fixed8 `json:"value"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// NewTxOutput converts out to a TransactionOutput.
|
||||
func NewTxOutput(out *transaction.Output) *TransactionOutput {
|
||||
addr := address.Uint160ToString(out.ScriptHash)
|
||||
|
||||
return &TransactionOutput{
|
||||
N: out.Position,
|
||||
Asset: "0x" + out.AssetID.String(),
|
||||
Value: out.Amount,
|
||||
Address: addr,
|
||||
}
|
||||
}
|
|
@ -28,10 +28,6 @@ type TransactionMetadata struct {
|
|||
func NewTransactionOutputRaw(tx *transaction.Transaction, header *block.Header, chain blockchainer.Blockchainer) TransactionOutputRaw {
|
||||
// confirmations formula
|
||||
confirmations := int(chain.BlockHeight() - header.Base.Index + 1)
|
||||
// set index position
|
||||
for i, o := range tx.Outputs {
|
||||
o.Position = i
|
||||
}
|
||||
return TransactionOutputRaw{
|
||||
Transaction: tx,
|
||||
TransactionMetadata: TransactionMetadata{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue