diff --git a/.docker/1600-privnet-blocks-single.acc.gz b/.docker/1600-privnet-blocks-single.acc.gz deleted file mode 100644 index e8be800fb..000000000 Binary files a/.docker/1600-privnet-blocks-single.acc.gz and /dev/null differ diff --git a/.docker/6000-privnet-blocks.acc.gz b/.docker/6000-privnet-blocks.acc.gz deleted file mode 100644 index b4d0213bb..000000000 Binary files a/.docker/6000-privnet-blocks.acc.gz and /dev/null differ diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml index 3094f2513..82b5e8f98 100644 --- a/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -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 diff --git a/Dockerfile b/Dockerfile index a9c4bdb4d..30d412517 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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/ diff --git a/cli/wallet/multisig.go b/cli/wallet/multisig.go index 883e3149e..639b77235 100644 --- a/cli/wallet/multisig.go +++ b/cli/wallet/multisig.go @@ -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)) - } } diff --git a/cli/wallet/nep5.go b/cli/wallet/nep5.go index d0e3e0d1f..cf61345ff 100644 --- a/cli/wallet/nep5.go +++ b/cli/wallet/nep5.go @@ -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 } diff --git a/cli/wallet/wallet.go b/cli/wallet/wallet.go index 6d6779ffd..0e349c64a 100644 --- a/cli/wallet/wallet.go +++ b/cli/wallet/wallet.go @@ -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 --from --to " + - " --amount --asset [NEO|GAS|] [--out ]", - 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 diff --git a/config/protocol.mainnet.yml b/config/protocol.mainnet.yml index 53070ff55..861041637 100644 --- a/config/protocol.mainnet.yml +++ b/config/protocol.mainnet.yml @@ -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 diff --git a/config/protocol.privnet.docker.four.yml b/config/protocol.privnet.docker.four.yml index e9bc5e8e1..e2fc0fefe 100644 --- a/config/protocol.privnet.docker.four.yml +++ b/config/protocol.privnet.docker.four.yml @@ -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 diff --git a/config/protocol.privnet.docker.one.yml b/config/protocol.privnet.docker.one.yml index 06bbad06e..f60ca55ba 100644 --- a/config/protocol.privnet.docker.one.yml +++ b/config/protocol.privnet.docker.one.yml @@ -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 diff --git a/config/protocol.privnet.docker.single.yml b/config/protocol.privnet.docker.single.yml index 5fdb643bc..f43ffaeed 100644 --- a/config/protocol.privnet.docker.single.yml +++ b/config/protocol.privnet.docker.single.yml @@ -8,9 +8,6 @@ ProtocolConfiguration: - 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2 SeedList: - 172.200.0.1:20333 - SystemFee: - IssueTransaction: 500 - RegisterTransaction: 10000 VerifyBlocks: true VerifyTransactions: true diff --git a/config/protocol.privnet.docker.three.yml b/config/protocol.privnet.docker.three.yml index a59daccbe..620ef90e9 100644 --- a/config/protocol.privnet.docker.three.yml +++ b/config/protocol.privnet.docker.three.yml @@ -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 diff --git a/config/protocol.privnet.docker.two.yml b/config/protocol.privnet.docker.two.yml index 082ba83b2..01f109416 100644 --- a/config/protocol.privnet.docker.two.yml +++ b/config/protocol.privnet.docker.two.yml @@ -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 diff --git a/config/protocol.privnet.yml b/config/protocol.privnet.yml index 1a24b933b..e8a1d7185 100644 --- a/config/protocol.privnet.yml +++ b/config/protocol.privnet.yml @@ -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 diff --git a/config/protocol.testnet.yml b/config/protocol.testnet.yml index 50b857115..785f3a429 100644 --- a/config/protocol.testnet.yml +++ b/config/protocol.testnet.yml @@ -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 diff --git a/config/protocol.unit_testnet.yml b/config/protocol.unit_testnet.yml index 37424cc3d..e786558a7 100644 --- a/config/protocol.unit_testnet.yml +++ b/config/protocol.unit_testnet.yml @@ -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 diff --git a/docs/cli.md b/docs/cli.md index 0c4f04567..f199cbc3a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -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" diff --git a/docs/rpc.md b/docs/rpc.md index ba025c07d..6d0b2f235 100644 --- a/docs/rpc.md +++ b/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 | diff --git a/integration/performance_test.go b/integration/performance_test.go index d5e80e03b..68592cc87 100644 --- a/integration/performance_test.go +++ b/integration/performance_test.go @@ -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, diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index c002d2bd7..ef1502cb8 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -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", diff --git a/pkg/config/config.go b/pkg/config/config.go index 999656ffb..f434191bd 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -40,9 +40,6 @@ func Load(path string, netMode NetMode) (Config, error) { } config := Config{ - ProtocolConfiguration: ProtocolConfiguration{ - SystemFee: SystemFee{}, - }, ApplicationConfiguration: ApplicationConfiguration{ PingInterval: 30, PingTimeout: 90, diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go index 421e3ff9b..94c5e452d 100644 --- a/pkg/config/protocol_config.go +++ b/pkg/config/protocol_config.go @@ -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) - } -} diff --git a/pkg/consensus/block_test.go b/pkg/consensus/block_test.go index 6e1d91f4e..312da1199 100644 --- a/pkg/consensus/block_test.go +++ b/pkg/consensus/block_test.go @@ -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()) } diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index 190f3577c..e0098f70e 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -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 diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 0b8d3dfc3..44ef14532 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -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() diff --git a/pkg/core/block/block.go b/pkg/core/block/block.go index 14c39ce51..54d88fa31 100644 --- a/pkg/core/block/block.go +++ b/pkg/core/block/block.go @@ -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 } diff --git a/pkg/core/block/block_test.go b/pkg/core/block/block_test.go index b9f6dc72d..01a8d7b73 100644 --- a/pkg/core/block/block_test.go +++ b/pkg/core/block/block_test.go @@ -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), }, } } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 7155b7734..1979b39d6 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -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 { diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 77a5d697e..1b50e88d9 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -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) diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index dacb0f2fd..5b84826b4 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -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) diff --git a/pkg/core/dao/cacheddao.go b/pkg/core/dao/cacheddao.go index 50d3b9514..9fc792308 100644 --- a/pkg/core/dao/cacheddao.go +++ b/pkg/core/dao/cacheddao.go @@ -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, } diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index f803078a4..59e6261f8 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -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) { diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 65e74b50e..640620a11 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -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) diff --git a/pkg/core/gas_price.go b/pkg/core/gas_price.go index 7d248d5ec..56094bc0b 100644 --- a/pkg/core/gas_price.go +++ b/pkg/core/gas_price.go @@ -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: diff --git a/pkg/core/gas_price_test.go b/pkg/core/gas_price_test.go index 661120696..87faae008 100644 --- a/pkg/core/gas_price_test.go +++ b/pkg/core/gas_price_test.go @@ -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), diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 9ff510f05..06e394ca0 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -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 { diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 74b5e364e..5997a8764 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -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) diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index e9ec0b2fe..90e1ce2ae 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -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 diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 3a6aba2ea..a781bcdb5 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -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 diff --git a/pkg/core/interops_test.go b/pkg/core/interops_test.go index c1ca1bdfa..ee78b1aa6 100644 --- a/pkg/core/interops_test.go +++ b/pkg/core/interops_test.go @@ -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, } diff --git a/pkg/core/mempool/mem_pool.go b/pkg/core/mempool/mem_pool.go index bd566936a..748c8b811 100644 --- a/pkg/core/mempool/mem_pool.go +++ b/pkg/core/mempool/mem_pool.go @@ -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() diff --git a/pkg/core/mempool/mem_pool_test.go b/pkg/core/mempool/mem_pool_test.go index c4dbfec75..969652c11 100644 --- a/pkg/core/mempool/mem_pool_test.go +++ b/pkg/core/mempool/mem_pool_test.go @@ -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{})) diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 8706f1ea1..109deceb3 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -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 { diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index aca59e0fb..0c9506e60 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -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 { diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index 7ea25e20f..eb8c0ef23 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -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) diff --git a/pkg/core/native_contract_test.go b/pkg/core/native_contract_test.go index fd2d10024..a95e8cd0b 100644 --- a/pkg/core/native_contract_test.go +++ b/pkg/core/native_contract_test.go @@ -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)) diff --git a/pkg/core/spent_coin.go b/pkg/core/spent_coin.go deleted file mode 100644 index 9d42cb8af..000000000 --- a/pkg/core/spent_coin.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/state/account.go b/pkg/core/state/account.go index 4e65ec27e..a0b82abd0 100644 --- a/pkg/core/state/account.go +++ b/pkg/core/state/account.go @@ -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 { diff --git a/pkg/core/state/account_test.go b/pkg/core/state/account_test.go index fab1049d7..32a7a6754 100644 --- a/pkg/core/state/account_test.go +++ b/pkg/core/state/account_test.go @@ -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)) diff --git a/pkg/core/state/asset.go b/pkg/core/state/asset.go deleted file mode 100644 index d59deba68..000000000 --- a/pkg/core/state/asset.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/state/asset_test.go b/pkg/core/state/asset_test.go deleted file mode 100644 index e3cbf2950..000000000 --- a/pkg/core/state/asset_test.go +++ /dev/null @@ -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()) -} diff --git a/pkg/core/state/unclaimed.go b/pkg/core/state/unclaimed.go deleted file mode 100644 index c0d3bf11c..000000000 --- a/pkg/core/state/unclaimed.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/state/unclaimed_test.go b/pkg/core/state/unclaimed_test.go deleted file mode 100644 index 5972688a6..000000000 --- a/pkg/core/state/unclaimed_test.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/state/unspent_coin.go b/pkg/core/state/unspent_coin.go deleted file mode 100644 index a0f1e635e..000000000 --- a/pkg/core/state/unspent_coin.go +++ /dev/null @@ -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()) -} diff --git a/pkg/core/state/unspent_coin_test.go b/pkg/core/state/unspent_coin_test.go deleted file mode 100644 index 51e778ab7..000000000 --- a/pkg/core/state/unspent_coin_test.go +++ /dev/null @@ -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)) -} diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index 12a46dd69..960f9ba89 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -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 diff --git a/pkg/core/storage/store_test.go b/pkg/core/storage/store_test.go index 9c9f6ef70..9b1e579e0 100644 --- a/pkg/core/storage/store_test.go +++ b/pkg/core/storage/store_test.go @@ -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, diff --git a/pkg/core/transaction/asset_type.go b/pkg/core/transaction/asset_type.go deleted file mode 100644 index 9d54eb28f..000000000 --- a/pkg/core/transaction/asset_type.go +++ /dev/null @@ -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 -) diff --git a/pkg/core/transaction/claim.go b/pkg/core/transaction/claim.go deleted file mode 100644 index 704d9035b..000000000 --- a/pkg/core/transaction/claim.go +++ /dev/null @@ -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) -} diff --git a/pkg/core/transaction/contract.go b/pkg/core/transaction/contract.go deleted file mode 100644 index c4a99a1cf..000000000 --- a/pkg/core/transaction/contract.go +++ /dev/null @@ -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) { -} diff --git a/pkg/core/transaction/contract_test.go b/pkg/core/transaction/contract_test.go deleted file mode 100644 index f535b899c..000000000 --- a/pkg/core/transaction/contract_test.go +++ /dev/null @@ -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)) -} -*/ diff --git a/pkg/core/transaction/helper_test.go b/pkg/core/transaction/helper_test.go index 1f1cae62f..6cbd5e61c 100644 --- a/pkg/core/transaction/helper_test.go +++ b/pkg/core/transaction/helper_test.go @@ -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" ) diff --git a/pkg/core/transaction/inout.go b/pkg/core/transaction/inout.go deleted file mode 100644 index 75bc026ac..000000000 --- a/pkg/core/transaction/inout.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/transaction/input.go b/pkg/core/transaction/input.go deleted file mode 100644 index 40462fba2..000000000 --- a/pkg/core/transaction/input.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/transaction/input_test.go b/pkg/core/transaction/input_test.go deleted file mode 100644 index c8b9fad06..000000000 --- a/pkg/core/transaction/input_test.go +++ /dev/null @@ -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)) -} diff --git a/pkg/core/transaction/invocation.go b/pkg/core/transaction/invocation.go deleted file mode 100644 index 9abb7b1a9..000000000 --- a/pkg/core/transaction/invocation.go +++ /dev/null @@ -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) - } -} diff --git a/pkg/core/transaction/invocation_test.go b/pkg/core/transaction/invocation_test.go deleted file mode 100644 index babe7d275..000000000 --- a/pkg/core/transaction/invocation_test.go +++ /dev/null @@ -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) -} diff --git a/pkg/core/transaction/issue.go b/pkg/core/transaction/issue.go deleted file mode 100644 index d2ab6034f..000000000 --- a/pkg/core/transaction/issue.go +++ /dev/null @@ -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) { -} diff --git a/pkg/core/transaction/output.go b/pkg/core/transaction/output.go deleted file mode 100644 index 11ad85b2e..000000000 --- a/pkg/core/transaction/output.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/transaction/register.go b/pkg/core/transaction/register.go deleted file mode 100644 index 9bff3efbe..000000000 --- a/pkg/core/transaction/register.go +++ /dev/null @@ -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"` -} diff --git a/pkg/core/transaction/register_test.go b/pkg/core/transaction/register_test.go deleted file mode 100644 index 23d52ab0e..000000000 --- a/pkg/core/transaction/register_test.go +++ /dev/null @@ -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)) -} -*/ diff --git a/pkg/core/transaction/result.go b/pkg/core/transaction/result.go deleted file mode 100644 index b5c639bca..000000000 --- a/pkg/core/transaction/result.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 6099ddaa2..f0d1f8763 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -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 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") diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go index 78e0a9d94..bbc082d18 100644 --- a/pkg/core/transaction/transaction_test.go +++ b/pkg/core/transaction/transaction_test.go @@ -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)) diff --git a/pkg/core/transaction/txer.go b/pkg/core/transaction/txer.go deleted file mode 100644 index f48077f4d..000000000 --- a/pkg/core/transaction/txer.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/transaction/type.go b/pkg/core/transaction/type.go deleted file mode 100644 index bf5d34420..000000000 --- a/pkg/core/transaction/type.go +++ /dev/null @@ -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") - } -} diff --git a/pkg/core/util.go b/pkg/core/util.go index 643e101e2..52362bc9d 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -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 { diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 090bfb8ac..da254c162 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -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()) -} diff --git a/pkg/interop/asset/asset.go b/pkg/interop/asset/asset.go deleted file mode 100644 index 01547f64c..000000000 --- a/pkg/interop/asset/asset.go +++ /dev/null @@ -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 -} diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index c38e4dd7d..bba276c7a 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -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{} -} diff --git a/pkg/interop/input/input.go b/pkg/interop/input/input.go deleted file mode 100644 index e577f2815..000000000 --- a/pkg/interop/input/input.go +++ /dev/null @@ -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 -} diff --git a/pkg/interop/output/output.go b/pkg/interop/output/output.go deleted file mode 100644 index 01ad0c828..000000000 --- a/pkg/interop/output/output.go +++ /dev/null @@ -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 -} diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go index e354d7baf..fc44f37c4 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -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. diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index b09f5e885..fe60dac6d 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -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") } diff --git a/pkg/rpc/client/client.go b/pkg/rpc/client/client.go index d5ad26e7b..1f2c1cad2 100644 --- a/pkg/rpc/client/client.go +++ b/pkg/rpc/client/client.go @@ -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, diff --git a/pkg/rpc/client/doc.go b/pkg/rpc/client/doc.go index c21d3ebf4..24c430d4e 100644 --- a/pkg/rpc/client/doc.go +++ b/pkg/rpc/client/doc.go @@ -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 diff --git a/pkg/rpc/client/doc_test.go b/pkg/rpc/client/doc_test.go index b7f3e5f00..496ca8d4d 100644 --- a/pkg/rpc/client/doc_test.go +++ b/pkg/rpc/client/doc_test.go @@ -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) } diff --git a/pkg/rpc/client/neoscan.go b/pkg/rpc/client/neoscan.go deleted file mode 100644 index 011eb9c67..000000000 --- a/pkg/rpc/client/neoscan.go +++ /dev/null @@ -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 -} diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 06302e8eb..95fcf83ec 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -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 { diff --git a/pkg/rpc/client/rpc.go b/pkg/rpc/client/rpc.go index e0b8326bd..d0d572e85 100644 --- a/pkg/rpc/client/rpc.go +++ b/pkg/rpc/client/rpc.go @@ -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") diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 31a7fe3cc..3600daf45 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -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)) }, }, { diff --git a/pkg/rpc/client/wsclient_test.go b/pkg/rpc/client/wsclient_test.go index a31a6beb0..6050c35f3 100644 --- a/pkg/rpc/client/wsclient_test.go +++ b/pkg/rpc/client/wsclient_test.go @@ -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) { diff --git a/pkg/rpc/request/txBuilder.go b/pkg/rpc/request/txBuilder.go index 0be028eb0..05def02a3 100644 --- a/pkg/rpc/request/txBuilder.go +++ b/pkg/rpc/request/txBuilder.go @@ -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 { diff --git a/pkg/rpc/request/txTypes.go b/pkg/rpc/request/txTypes.go deleted file mode 100644 index aebf8055f..000000000 --- a/pkg/rpc/request/txTypes.go +++ /dev/null @@ -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) - } -) diff --git a/pkg/rpc/response/result/account_state.go b/pkg/rpc/response/result/account_state.go deleted file mode 100644 index cbff2fff9..000000000 --- a/pkg/rpc/response/result/account_state.go +++ /dev/null @@ -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, - } -} diff --git a/pkg/rpc/response/result/asset_state.go b/pkg/rpc/response/result/asset_state.go deleted file mode 100644 index 3c3d5f38d..000000000 --- a/pkg/rpc/response/result/asset_state.go +++ /dev/null @@ -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, - } -} diff --git a/pkg/rpc/response/result/block.go b/pkg/rpc/response/result/block.go index d9517a757..bd754751f 100644 --- a/pkg/rpc/response/result/block.go +++ b/pkg/rpc/response/result/block.go @@ -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, }, } diff --git a/pkg/rpc/response/result/claimable.go b/pkg/rpc/response/result/claimable.go deleted file mode 100644 index 71dbc60b8..000000000 --- a/pkg/rpc/response/result/claimable.go +++ /dev/null @@ -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"` -} diff --git a/pkg/rpc/response/result/tx_output.go b/pkg/rpc/response/result/tx_output.go deleted file mode 100644 index 4078de755..000000000 --- a/pkg/rpc/response/result/tx_output.go +++ /dev/null @@ -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, - } -} diff --git a/pkg/rpc/response/result/tx_raw_output.go b/pkg/rpc/response/result/tx_raw_output.go index ae47dda50..e3372019d 100644 --- a/pkg/rpc/response/result/tx_raw_output.go +++ b/pkg/rpc/response/result/tx_raw_output.go @@ -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{ diff --git a/pkg/rpc/response/result/unclaimed.go b/pkg/rpc/response/result/unclaimed.go deleted file mode 100644 index f8cd8d1a9..000000000 --- a/pkg/rpc/response/result/unclaimed.go +++ /dev/null @@ -1,54 +0,0 @@ -package result - -import ( - "github.com/nspcc-dev/neo-go/pkg/core" - "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" - "github.com/nspcc-dev/neo-go/pkg/core/state" - "github.com/nspcc-dev/neo-go/pkg/util" -) - -// Unclaimed wrapper is used to represent getunclaimed return result. -type Unclaimed struct { - Available util.Fixed8 `json:"available"` - Unavailable util.Fixed8 `json:"unavailable"` - Unclaimed util.Fixed8 `json:"unclaimed"` -} - -// NewUnclaimed creates a new Unclaimed wrapper using given Blockchainer. -func NewUnclaimed(a *state.Account, chain blockchainer.Blockchainer) (*Unclaimed, error) { - var ( - available util.Fixed8 - unavailable util.Fixed8 - ) - - err := a.Unclaimed.ForEach(func(ucb *state.UnclaimedBalance) error { - gen, sys, err := chain.CalculateClaimable(ucb.Value, ucb.Start, ucb.End) - if err != nil { - return err - } - available += gen + sys - return nil - }) - if err != nil { - return nil, err - } - - blockHeight := chain.BlockHeight() - for _, usb := range a.Balances[core.GoverningTokenID()] { - _, txHeight, err := chain.GetTransaction(usb.Tx) - if err != nil { - return nil, err - } - gen, sys, err := chain.CalculateClaimable(usb.Value, txHeight, blockHeight) - if err != nil { - return nil, err - } - unavailable += gen + sys - } - - return &Unclaimed{ - Available: available, - Unavailable: unavailable, - Unclaimed: available + unavailable, - }, nil -} diff --git a/pkg/rpc/response/result/unspents.go b/pkg/rpc/response/result/unspents.go deleted file mode 100644 index 9a1613b86..000000000 --- a/pkg/rpc/response/result/unspents.go +++ /dev/null @@ -1,57 +0,0 @@ -package result - -import ( - "github.com/nspcc-dev/neo-go/pkg/core/blockchainer" - "github.com/nspcc-dev/neo-go/pkg/core/state" - "github.com/nspcc-dev/neo-go/pkg/util" -) - -// UnspentBalanceInfo wrapper is used to represent single unspent asset entry -// in `getunspents` output. -type UnspentBalanceInfo struct { - Unspents []state.UnspentBalance `json:"unspent"` - AssetHash util.Uint256 `json:"asset_hash"` - Asset string `json:"asset"` - AssetSymbol string `json:"asset_symbol"` - Amount util.Fixed8 `json:"amount"` -} - -// Unspents wrapper is used to represent getunspents return result. -type Unspents struct { - Balance []UnspentBalanceInfo `json:"balance"` - Address string `json:"address"` -} - -// GlobalAssets stores a map of asset IDs to user-friendly strings ("NEO"/"GAS"). -var GlobalAssets = map[string]string{ - "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b": "NEO", - "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7": "GAS", -} - -// NewUnspents creates a new Account wrapper using given Blockchainer. -func NewUnspents(a *state.Account, chain blockchainer.Blockchainer, addr string) Unspents { - res := Unspents{ - Address: addr, - Balance: make([]UnspentBalanceInfo, 0, len(a.Balances)), - } - balanceValues := a.GetBalanceValues() - for k, v := range a.Balances { - name, ok := GlobalAssets[k.StringLE()] - if !ok { - as := chain.GetAssetState(k) - if as != nil { - name = as.Name - } - } - - res.Balance = append(res.Balance, UnspentBalanceInfo{ - Unspents: v, - AssetHash: k, - Asset: name, - AssetSymbol: name, - Amount: balanceValues[k], - }) - } - - return res -} diff --git a/pkg/rpc/response/types.go b/pkg/rpc/response/types.go index ba23c7677..23e6368d2 100644 --- a/pkg/rpc/response/types.go +++ b/pkg/rpc/response/types.go @@ -26,12 +26,6 @@ type Raw struct { Result json.RawMessage `json:"result,omitempty"` } -// GetTxOut represents result of `gettxout` RPC call. -type GetTxOut struct { - HeaderAndError - Result *result.TransactionOutput -} - // GetRawTx represents verbose output of `getrawtransaction` RPC call. type GetRawTx struct { HeaderAndError diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index fad04857e..dbed9ab8b 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -79,16 +79,13 @@ const ( ) var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *response.Error){ - "getaccountstate": (*Server).getAccountState, "getapplicationlog": (*Server).getApplicationLog, - "getassetstate": (*Server).getAssetState, "getbestblockhash": (*Server).getBestBlockHash, "getblock": (*Server).getBlock, "getblockcount": (*Server).getBlockCount, "getblockhash": (*Server).getBlockHash, "getblockheader": (*Server).getBlockHeader, "getblocksysfee": (*Server).getBlockSysFee, - "getclaimable": (*Server).getClaimable, "getconnectioncount": (*Server).getConnectionCount, "getcontractstate": (*Server).getContractState, "getnep5balances": (*Server).getNEP5Balances, @@ -98,9 +95,7 @@ var rpcHandlers = map[string]func(*Server, request.Params) (interface{}, *respon "getrawtransaction": (*Server).getrawtransaction, "getstorage": (*Server).getStorage, "gettransactionheight": (*Server).getTransactionHeight, - "gettxout": (*Server).getTxOut, - "getunclaimed": (*Server).getUnclaimed, - "getunspents": (*Server).getUnspents, + "getunclaimedgas": (*Server).getUnclaimedGas, "getvalidators": (*Server).getValidators, "getversion": (*Server).getVersion, "invoke": (*Server).invoke, @@ -387,31 +382,39 @@ func (s *Server) getConnectionCount(_ request.Params) (interface{}, *response.Er return s.coreServer.PeerCount(), nil } -func (s *Server) getBlock(reqParams request.Params) (interface{}, *response.Error) { +func (s *Server) blockHashFromParam(param *request.Param) (util.Uint256, *response.Error) { var hash util.Uint256 - param, ok := reqParams.Value(0) - if !ok { - return nil, response.ErrInvalidParams - } - switch param.Type { case request.StringT: var err error hash, err = param.GetUint256() if err != nil { - return nil, response.ErrInvalidParams + return hash, response.ErrInvalidParams } case request.NumberT: num, err := s.blockHeightFromParam(param) if err != nil { - return nil, response.ErrInvalidParams + return hash, response.ErrInvalidParams } hash = s.chain.GetHeaderHash(num) default: + return hash, response.ErrInvalidParams + } + return hash, nil +} + +func (s *Server) getBlock(reqParams request.Params) (interface{}, *response.Error) { + param, ok := reqParams.Value(0) + if !ok { return nil, response.ErrInvalidParams } + hash, respErr := s.blockHashFromParam(param) + if respErr != nil { + return nil, respErr + } + block, err := s.chain.GetBlock(hash) if err != nil { return nil, response.NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err) @@ -458,7 +461,7 @@ func (s *Server) getRawMempool(_ request.Params) (interface{}, *response.Error) mp := s.chain.GetMemPool() hashList := make([]util.Uint256, 0) for _, item := range mp.GetVerifiedTransactions() { - hashList = append(hashList, item.Tx.Hash()) + hashList = append(hashList, item.Hash()) } return hashList, nil } @@ -471,24 +474,6 @@ func (s *Server) validateAddress(reqParams request.Params) (interface{}, *respon return validateAddress(param.Value), nil } -func (s *Server) getAssetState(reqParams request.Params) (interface{}, *response.Error) { - param, ok := reqParams.ValueWithType(0, request.StringT) - if !ok { - return nil, response.ErrInvalidParams - } - - paramAssetID, err := param.GetUint256() - if err != nil { - return nil, response.ErrInvalidParams - } - - as := s.chain.GetAssetState(paramAssetID) - if as != nil { - return result.NewAssetState(as), nil - } - return nil, response.NewRPCError("Unknown asset", "", nil) -} - // getApplicationLog returns the contract log based on the specified txid. func (s *Server) getApplicationLog(reqParams request.Params) (interface{}, *response.Error) { param, ok := reqParams.Value(0) @@ -511,69 +496,11 @@ func (s *Server) getApplicationLog(reqParams request.Params) (interface{}, *resp return nil, response.NewRPCError("Error while getting transaction", "", nil) } - var scriptHash util.Uint160 - switch t := tx.Data.(type) { - case *transaction.InvocationTX: - scriptHash = hash.Hash160(t.Script) - default: - return nil, response.NewRPCError("Invalid transaction type", "", nil) - } + scriptHash := hash.Hash160(tx.Script) return result.NewApplicationLog(appExecResult, scriptHash), nil } -func (s *Server) getClaimable(ps request.Params) (interface{}, *response.Error) { - p, ok := ps.ValueWithType(0, request.StringT) - if !ok { - return nil, response.ErrInvalidParams - } - u, err := p.GetUint160FromAddress() - if err != nil { - return nil, response.ErrInvalidParams - } - - var unclaimed []state.UnclaimedBalance - if acc := s.chain.GetAccountState(u); acc != nil { - err := acc.Unclaimed.ForEach(func(b *state.UnclaimedBalance) error { - unclaimed = append(unclaimed, *b) - return nil - }) - if err != nil { - return nil, response.NewInternalServerError("Unclaimed processing failure", err) - } - } - - var sum util.Fixed8 - claimable := make([]result.Claimable, 0, len(unclaimed)) - for _, ub := range unclaimed { - gen, sys, err := s.chain.CalculateClaimable(ub.Value, ub.Start, ub.End) - if err != nil { - s.log.Info("error while calculating claim bonus", zap.Error(err)) - continue - } - - uc := gen.Add(sys) - sum += uc - - claimable = append(claimable, result.Claimable{ - Tx: ub.Tx, - N: int(ub.Index), - Value: ub.Value, - StartHeight: ub.Start, - EndHeight: ub.End, - Generated: gen, - SysFee: sys, - Unclaimed: uc, - }) - } - - return result.ClaimableInfo{ - Spents: claimable, - Address: p.String(), - Unclaimed: sum, - }, nil -} - func (s *Server) getNEP5Balances(ps request.Params) (interface{}, *response.Error) { p, ok := ps.ValueWithType(0, request.StringT) if !ok { @@ -796,40 +723,6 @@ func (s *Server) getTransactionHeight(ps request.Params) (interface{}, *response return height, nil } -func (s *Server) getTxOut(ps request.Params) (interface{}, *response.Error) { - p, ok := ps.Value(0) - if !ok { - return nil, response.ErrInvalidParams - } - - h, err := p.GetUint256() - if err != nil { - return nil, response.ErrInvalidParams - } - - p, ok = ps.ValueWithType(1, request.NumberT) - if !ok { - return nil, response.ErrInvalidParams - } - - num, err := p.GetInt() - if err != nil || num < 0 { - return nil, response.ErrInvalidParams - } - - tx, _, err := s.chain.GetTransaction(h) - if err != nil { - return nil, response.NewInvalidParamsError(err.Error(), err) - } - - if num >= len(tx.Outputs) { - return nil, response.NewInvalidParamsError("invalid index", errors.New("too big index")) - } - - out := tx.Outputs[num] - return result.NewTxOutput(&out), nil -} - // getContractState returns contract state (contract information, according to the contract script hash). func (s *Server) getContractState(reqParams request.Params) (interface{}, *response.Error) { var results interface{} @@ -850,42 +743,6 @@ func (s *Server) getContractState(reqParams request.Params) (interface{}, *respo return results, nil } -func (s *Server) getAccountState(ps request.Params) (interface{}, *response.Error) { - return s.getAccountStateAux(ps, false) -} - -func (s *Server) getUnspents(ps request.Params) (interface{}, *response.Error) { - return s.getAccountStateAux(ps, true) -} - -// getAccountState returns account state either in short or full (unspents included) form. -func (s *Server) getAccountStateAux(reqParams request.Params, unspents bool) (interface{}, *response.Error) { - var resultsErr *response.Error - var results interface{} - - param, ok := reqParams.ValueWithType(0, request.StringT) - if !ok { - return nil, response.ErrInvalidParams - } else if scriptHash, err := param.GetUint160FromAddress(); err != nil { - return nil, response.ErrInvalidParams - } else { - as := s.chain.GetAccountState(scriptHash) - if as == nil { - as = state.NewAccount(scriptHash) - } - if unspents { - str, err := param.GetString() - if err != nil { - return nil, response.ErrInvalidParams - } - results = result.NewUnspents(as, s.chain, str) - } else { - results = result.NewAccountState(as) - } - } - return results, resultsErr -} - // getBlockSysFee returns the system fees of the block, based on the specified index. func (s *Server) getBlockSysFee(reqParams request.Params) (interface{}, *response.Error) { param, ok := reqParams.ValueWithType(0, request.NumberT) @@ -916,13 +773,14 @@ func (s *Server) getBlockSysFee(reqParams request.Params) (interface{}, *respons func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, *response.Error) { var verbose bool - param, ok := reqParams.ValueWithType(0, request.StringT) + param, ok := reqParams.Value(0) if !ok { return nil, response.ErrInvalidParams } - hash, err := param.GetUint256() - if err != nil { - return nil, response.ErrInvalidParams + + hash, respErr := s.blockHashFromParam(param) + if respErr != nil { + return nil, respErr } param, ok = reqParams.ValueWithType(1, request.NumberT) @@ -951,8 +809,8 @@ func (s *Server) getBlockHeader(reqParams request.Params) (interface{}, *respons return hex.EncodeToString(buf.Bytes()), nil } -// getUnclaimed returns unclaimed GAS amount of the specified address. -func (s *Server) getUnclaimed(ps request.Params) (interface{}, *response.Error) { +// getUnclaimedGas returns unclaimed GAS amount of the specified address. +func (s *Server) getUnclaimedGas(ps request.Params) (interface{}, *response.Error) { p, ok := ps.ValueWithType(0, request.StringT) if !ok { return nil, response.ErrInvalidParams @@ -962,15 +820,12 @@ func (s *Server) getUnclaimed(ps request.Params) (interface{}, *response.Error) return nil, response.ErrInvalidParams } - acc := s.chain.GetAccountState(u) - if acc == nil { - return nil, response.NewInternalServerError("unknown account", nil) + neo, neoHeight := s.chain.GetGoverningTokenBalance(u) + if neo == 0 { + return "0", nil } - res, errRes := result.NewUnclaimed(acc, s.chain) - if errRes != nil { - return nil, response.NewInternalServerError("can't create unclaimed response", errRes) - } - return res, nil + gas := s.chain.CalculateClaimable(int64(neo), neoHeight, s.chain.BlockHeight()+1) // +1 as in C#, for the next block. + return strconv.FormatInt(int64(gas), 10), nil // It's not represented as Fixed8 in C#. } // getValidators returns the current NEO consensus nodes information and voting status. diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 1bcb382b5..69af43432 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -18,13 +18,14 @@ import ( "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/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/internal/testchain" + "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/response" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -54,12 +55,12 @@ var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { { name: "positive", - params: `["0a0abf0188053113d0014e0cb9801d090a5d3e7640d76427fa1a3676e7cdf82e"]`, + params: `["5878052c7e9843786d64a9aeab16e74fabffd5abad9a0404aaf4f4bf2b6213e9"]`, result: func(e *executor) interface{} { return &result.ApplicationLog{} }, check: func(t *testing.T, e *executor, acc interface{}) { res, ok := acc.(*result.ApplicationLog) require.True(t, ok) - expectedTxHash, err := util.Uint256DecodeStringLE("0a0abf0188053113d0014e0cb9801d090a5d3e7640d76427fa1a3676e7cdf82e") + expectedTxHash, err := util.Uint256DecodeStringLE("5878052c7e9843786d64a9aeab16e74fabffd5abad9a0404aaf4f4bf2b6213e9") require.NoError(t, err) assert.Equal(t, expectedTxHash, res.TxHash) assert.Equal(t, 1, len(res.Executions)) @@ -82,45 +83,6 @@ var rpcTestCases = map[string][]rpcTestCase{ params: `["d24cc1d52b5c0216cbf3835bb5bac8ccf32639fa1ab6627ec4e2b9f33f7ec02f"]`, fail: true, }, - { - name: "invalid tx type", - params: `["f9adfde059810f37b3d0686d67f6b29034e0c669537df7e59b40c14a0508b9ed"]`, - fail: true, - }, - }, - "getaccountstate": { - { - name: "positive", - params: `["` + testchain.MultisigAddress() + `"]`, - result: func(e *executor) interface{} { return &result.AccountState{} }, - check: func(t *testing.T, e *executor, acc interface{}) { - res, ok := acc.(*result.AccountState) - require.True(t, ok) - assert.Equal(t, 1, len(res.Balances)) - assert.Equal(t, false, res.IsFrozen) - }, - }, - { - name: "positive null", - params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`, - result: func(e *executor) interface{} { return &result.AccountState{} }, - check: func(t *testing.T, e *executor, acc interface{}) { - res, ok := acc.(*result.AccountState) - require.True(t, ok) - assert.Equal(t, 0, len(res.Balances)) - assert.Equal(t, false, res.IsFrozen) - }, - }, - { - name: "no params", - params: `[]`, - fail: true, - }, - { - name: "invalid address", - params: `["notabase58"]`, - fail: true, - }, }, "getcontractstate": { { @@ -173,15 +135,21 @@ var rpcTestCases = map[string][]rpcTestCase{ rubles, err := util.Uint160DecodeStringLE(testContractHash) require.NoError(t, err) expected := result.NEP5Balances{ - Balances: []result.NEP5Balance{{ - Asset: rubles, - Amount: "8.77", - LastUpdated: 208, - }, + Balances: []result.NEP5Balance{ + { + Asset: rubles, + Amount: "8.77", + LastUpdated: 6, + }, + { + Asset: e.chain.GoverningTokenHash(), + Amount: "99998000", + LastUpdated: 4, + }, { Asset: e.chain.UtilityTokenHash(), - Amount: "10", - LastUpdated: 1, + Amount: "1023.99976000", + LastUpdated: 4, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), } @@ -210,47 +178,80 @@ var rpcTestCases = map[string][]rpcTestCase{ require.True(t, ok) rublesHash, err := util.Uint160DecodeStringLE(testContractHash) require.NoError(t, err) - blockSendRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(208)) + blockSendRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(6)) require.NoError(t, err) require.Equal(t, 1, len(blockSendRubles.Transactions)) txSendRublesHash := blockSendRubles.Transactions[0].Hash() - blockRecieveRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(207)) + blockReceiveRubles, err := e.chain.GetBlock(e.chain.GetHeaderHash(5)) require.NoError(t, err) - require.Equal(t, 2, len(blockRecieveRubles.Transactions)) - txRecieveRublesHash := blockRecieveRubles.Transactions[1].Hash() - blockRecieveGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(1)) + require.Equal(t, 2, len(blockReceiveRubles.Transactions)) + txReceiveRublesHash := blockReceiveRubles.Transactions[1].Hash() + blockReceiveGAS, err := e.chain.GetBlock(e.chain.GetHeaderHash(1)) require.NoError(t, err) - require.Equal(t, 1, len(blockRecieveGAS.Transactions)) - txRecieveGASHash := blockRecieveGAS.Transactions[0].Hash() + require.Equal(t, 2, len(blockReceiveGAS.Transactions)) + txReceiveNEOHash := blockReceiveGAS.Transactions[0].Hash() + txReceiveGASHash := blockReceiveGAS.Transactions[1].Hash() + blockSendNEO, err := e.chain.GetBlock(e.chain.GetHeaderHash(4)) require.NoError(t, err) + require.Equal(t, 1, len(blockSendNEO.Transactions)) + txSendNEOHash := blockSendNEO.Transactions[0].Hash() expected := result.NEP5Transfers{ - Sent: []result.NEP5Transfer{{ - Timestamp: blockSendRubles.Timestamp, - Asset: rublesHash, - Address: testchain.PrivateKeyByID(1).Address(), - Amount: "1.23", - Index: 208, - NotifyIndex: 0, - TxHash: txSendRublesHash, - }}, + Sent: []result.NEP5Transfer{ + { + Timestamp: blockSendRubles.Timestamp, + Asset: rublesHash, + Address: testchain.PrivateKeyByID(1).Address(), + Amount: "1.23", + Index: 6, + NotifyIndex: 0, + TxHash: txSendRublesHash, + }, + { + Timestamp: blockSendNEO.Timestamp, + Asset: e.chain.GoverningTokenHash(), + Address: testchain.PrivateKeyByID(1).Address(), + Amount: "1000", + Index: 4, + NotifyIndex: 0, + TxHash: txSendNEOHash, + }, + }, Received: []result.NEP5Transfer{ { - Timestamp: blockRecieveRubles.Timestamp, + Timestamp: blockReceiveRubles.Timestamp, Asset: rublesHash, Address: address.Uint160ToString(rublesHash), Amount: "10", - Index: 207, + Index: 5, NotifyIndex: 0, - TxHash: txRecieveRublesHash, + TxHash: txReceiveRublesHash, }, { - Timestamp: blockRecieveGAS.Timestamp, + Timestamp: blockSendNEO.Timestamp, + Asset: e.chain.UtilityTokenHash(), + Address: "", // Minted GAS. + Amount: "23.99976000", + Index: 4, + NotifyIndex: 0, + TxHash: txSendNEOHash, + }, + { + Timestamp: blockReceiveGAS.Timestamp, Asset: e.chain.UtilityTokenHash(), Address: testchain.MultisigAddress(), - Amount: "10", + Amount: "1000", Index: 1, NotifyIndex: 0, - TxHash: txRecieveGASHash, + TxHash: txReceiveGASHash, + }, + { + Timestamp: blockReceiveGAS.Timestamp, + Asset: e.chain.GoverningTokenHash(), + Address: testchain.MultisigAddress(), + Amount: "99999000", + Index: 1, + NotifyIndex: 0, + TxHash: txReceiveNEOHash, }, }, Address: testchain.PrivateKeyByID(0).Address(), @@ -299,34 +300,6 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, - "getassetstate": { - { - name: "positive", - params: `["f882fb865bab84b99623f21eedd902286af7da8d8a4609d7acefce04c851dc1c"]`, - result: func(e *executor) interface{} { return &result.AssetState{} }, - check: func(t *testing.T, e *executor, as interface{}) { - res, ok := as.(*result.AssetState) - require.True(t, ok) - assert.Equal(t, "00", res.Owner) - assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Admin) - }, - }, - { - name: "negative", - params: `["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de2"]`, - fail: true, - }, - { - name: "no params", - params: `[]`, - fail: true, - }, - { - name: "invalid hash", - params: `["notahex"]`, - fail: true, - }, - }, "getbestblockhash": { { params: "[]", @@ -340,38 +313,6 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, - "gettxout": { - { - name: "no params", - params: `[]`, - fail: true, - }, - { - name: "invalid hash", - params: `["notahex"]`, - fail: true, - }, - { - name: "missing hash", - params: `["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0]`, - fail: true, - }, - { - name: "invalid index", - params: `["7aadf91ca8ac1e2c323c025a7e492bee2dd90c783b86ebfc3b18db66b530a76d", "string"]`, - fail: true, - }, - { - name: "negative index", - params: `["7aadf91ca8ac1e2c323c025a7e492bee2dd90c783b86ebfc3b18db66b530a76d", -1]`, - fail: true, - }, - { - name: "too big index", - params: `["7aadf91ca8ac1e2c323c025a7e492bee2dd90c783b86ebfc3b18db66b530a76d", 100]`, - fail: true, - }, - }, "getblock": { { name: "positive", @@ -386,8 +327,6 @@ var rpcTestCases = map[string][]rpcTestCase{ assert.Equal(t, block.Hash(), res.Hash()) for i, tx := range res.Transactions { - require.Equal(t, transaction.ContractType, tx.Type) - actualTx := block.Transactions[i] require.True(t, ok) require.Equal(t, actualTx.Nonce, tx.Nonce) @@ -504,40 +443,6 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, - "getclaimable": { - { - name: "no params", - params: "[]", - fail: true, - }, - { - name: "invalid address", - params: `["invalid"]`, - fail: true, - }, - { - name: "normal address", - params: `["` + testchain.MultisigAddress() + `"]`, - result: func(*executor) interface{} { - // hash of the issueTx - h, _ := util.Uint256DecodeStringBE("d3a4f2249fe33b18bde73901c1ecc66200485f1c1dcd941b406a630b479090ae") - amount := util.Fixed8FromInt64(1 * 8) // (endHeight - startHeight) * genAmount[0] - return &result.ClaimableInfo{ - Spents: []result.Claimable{ - { - Tx: h, - Value: util.Fixed8FromInt64(100000000), - EndHeight: 1, - Generated: amount, - Unclaimed: amount, - }, - }, - Address: testchain.MultisigAddress(), - Unclaimed: amount, - } - }, - }, - }, "getconnectioncount": { { params: "[]", @@ -579,11 +484,16 @@ var rpcTestCases = map[string][]rpcTestCase{ "gettransactionheight": { { name: "positive", - params: `["0e873d5d565a03c6cd39efa3b446e1901b4636c448a22bc7e8c259c5a28a2eda"]`, + params: `["5878052c7e9843786d64a9aeab16e74fabffd5abad9a0404aaf4f4bf2b6213e9"]`, result: func(e *executor) interface{} { - h := 1 + h := 0 return &h }, + check: func(t *testing.T, e *executor, resp interface{}) { + h, ok := resp.(*int) + require.True(t, ok) + assert.Equal(t, 2, *h) + }, }, { name: "no params", @@ -601,7 +511,7 @@ var rpcTestCases = map[string][]rpcTestCase{ fail: true, }, }, - "getunclaimed": { + "getunclaimedgas": { { name: "no params", params: "[]", @@ -616,37 +526,14 @@ var rpcTestCases = map[string][]rpcTestCase{ name: "positive", params: `["` + testchain.MultisigAddress() + `"]`, result: func(*executor) interface{} { - return &result.Unclaimed{} + var s string + return &s }, - check: func(t *testing.T, e *executor, uncl interface{}) { - res, ok := uncl.(*result.Unclaimed) + check: func(t *testing.T, e *executor, resp interface{}) { + s, ok := resp.(*string) require.True(t, ok) - assert.Equal(t, res.Available, util.Fixed8FromInt64(8)) - assert.True(t, res.Unavailable > 0) - assert.Equal(t, res.Available+res.Unavailable, res.Unclaimed) - }, - }, - }, - "getunspents": { - { - name: "positive", - params: `["` + testchain.MultisigAddress() + `"]`, - result: func(e *executor) interface{} { return &result.Unspents{} }, - check: func(t *testing.T, e *executor, unsp interface{}) { - res, ok := unsp.(*result.Unspents) - require.True(t, ok) - require.Equal(t, 1, len(res.Balance)) - assert.Equal(t, 1, len(res.Balance[0].Unspents)) - }, - }, - { - name: "positive null", - params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`, - result: func(e *executor) interface{} { return &result.Unspents{} }, - check: func(t *testing.T, e *executor, unsp interface{}) { - res, ok := unsp.(*result.Unspents) - require.True(t, ok) - require.Equal(t, 0, len(res.Balance)) + // Incorrect, to be fixed later. + assert.Equal(t, "48000", *s) }, }, }, @@ -791,7 +678,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "sendrawtransaction": { { name: "positive", - params: `["80000b000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000a267050000000000b00400000000017a03a89832a347c4fb53af1f526d0d930b14ab6eb01629ce20ffbaeaeef58af3010001787cc0a786adfe829bc2dffc5637e6855c0a82e02deee97dedbc2aac3e0e5e1a0030d3dec3862300316e851039019d39dfc2c37d6c3fee19fd58098701420c40b6aeec1d2699194b842f399448b395d98bbb287dc89ea9e5ce3bb99a1c8c9bf933f55b69db6709b44e6a5c8b28b97018466479e5d500e414a0874c37abab262d290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`, + params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000f2ad050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c40df953141271169421cebab5d27a0163e294d7c7f2d0525b4498745344814fd3d6c5c591c9b1723d05d42856f409adb084cf67acc921cfafc629133a5eb5e7a7e290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4"]`, result: func(e *executor) interface{} { v := true return &v @@ -799,7 +686,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "negative", - params: `["0274d792072617720636f6e7472616374207472616e73616374696f6e206465736372697074696f6e01949354ea0a8b57dfee1e257a1aedd1e0eea2e5837de145e8da9c0f101bfccc8e0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500a3e11100000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5004f2418010000001cc9c05cefffe6cdd7b182816a9152ec218d2ec0014140dbd3cddac5cb2bd9bf6d93701f1a6f1c9dbe2d1b480c54628bbb2a4d536158c747a6af82698edf9f8af1cac3850bcb772bd9c8e4ac38f80704751cc4e0bd0e67232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]`, + params: `["000a000000316e851039019d39dfc2c37d6c3fee19fd5809870000000000000000f2ad050000000000b00400000001316e851039019d39dfc2c37d6c3fee19fd580987015d0300e87648170000000c1420728274afafc36f43a071d328cfa3e629d9cbb00c14316e851039019d39dfc2c37d6c3fee19fd58098713c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801420c40df953141271169421cebab5d27a0163e294d7c7f2d0525b4498745344814fd3d6c5c591c9b1723d05d42856f409adb084cf67acc921cfafc629133a5eb5e7a7e290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906aff"]`, fail: true, }, { @@ -929,7 +816,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] newTx := func() *transaction.Transaction { height := chain.BlockHeight() - tx := transaction.NewContractTX() + tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) tx.Nonce = height + 1 tx.ValidUntilBlock = height + 10 tx.Sender = acc0.PrivateKey().GetScriptHash() @@ -956,26 +843,32 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] t.Run("getrawtransaction", func(t *testing.T) { block, _ := chain.GetBlock(chain.GetHeaderHash(0)) - TXHash := block.Transactions[0].Hash() - rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, TXHash.StringLE()) + tx := block.Transactions[0] + rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, tx.Hash().StringLE()) body := doRPCCall(rpc, httpSrv.URL, t) result := checkErrGetResult(t, body, false) var res string err := json.Unmarshal(result, &res) require.NoErrorf(t, err, "could not parse response: %s", result) - assert.Equal(t, "400000000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b000000000000000000000000000000000000000000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000", res) + txBin, err := testserdes.EncodeBinary(tx) + require.NoError(t, err) + expected := hex.EncodeToString(txBin) + assert.Equal(t, expected, res) }) t.Run("getrawtransaction 2 arguments", func(t *testing.T) { block, _ := chain.GetBlock(chain.GetHeaderHash(0)) - TXHash := block.Transactions[0].Hash() - rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, TXHash.StringLE()) + tx := block.Transactions[0] + rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, tx.Hash().StringLE()) body := doRPCCall(rpc, httpSrv.URL, t) result := checkErrGetResult(t, body, false) var res string err := json.Unmarshal(result, &res) require.NoErrorf(t, err, "could not parse response: %s", result) - assert.Equal(t, "400000000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b000000000000000000000000000000000000000000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000", res) + txBin, err := testserdes.EncodeBinary(tx) + require.NoError(t, err) + expected := hex.EncodeToString(txBin) + assert.Equal(t, expected, res) }) t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) { @@ -987,19 +880,9 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] actual := result.TransactionOutputRaw{} err := json.Unmarshal(txOut, &actual) require.NoErrorf(t, err, "could not parse response: %s", txOut) - admin, err := util.Uint160DecodeStringBE("da1745e9b549bd0bfa1a569971c77eba30cd5a4b") - require.NoError(t, err) - assert.Equal(t, transaction.RegisterType, actual.Transaction.Type) - assert.Equal(t, &transaction.RegisterTX{ - AssetType: 0, - Name: `[{"lang":"zh-CN","name":"小蚁股"},{"lang":"en","name":"AntShare"}]`, - Amount: util.Fixed8FromInt64(100000000), - Precision: 0, - Owner: keys.PublicKey{}, - Admin: admin, - }, actual.Transaction.Data.(*transaction.RegisterTX)) - assert.Equal(t, 210, actual.Confirmations) + assert.Equal(t, block.Transactions[0], actual.Transaction) + assert.Equal(t, 8, actual.Confirmations) assert.Equal(t, TXHash, actual.Transaction.Hash()) }) @@ -1028,6 +911,10 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] t.Run("verbose=0", func(t *testing.T) { runCase(t, fmt.Sprintf(rpc, `["`+testHeaderHash+`", 0]`), &encoded, new(string)) }) + + t.Run("by number", func(t *testing.T) { + runCase(t, fmt.Sprintf(rpc, `[1]`), &encoded, new(string)) + }) }) t.Run("verbose != 0", func(t *testing.T) { @@ -1051,33 +938,15 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) [] }) }) - t.Run("gettxout", func(t *testing.T) { - block, _ := chain.GetBlock(chain.GetHeaderHash(0)) - require.Equal(t, 4, len(block.Transactions)) - tx := block.Transactions[2] - rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "gettxout", "params": [%s, %d]}"`, - `"`+tx.Hash().StringLE()+`"`, 0) - body := doRPCCall(rpc, httpSrv.URL, t) - res := checkErrGetResult(t, body, false) - - var txOut result.TransactionOutput - err := json.Unmarshal(res, &txOut) - require.NoErrorf(t, err, "could not parse response: %s", res) - assert.Equal(t, 0, txOut.N) - assert.Equal(t, "0x787cc0a786adfe829bc2dffc5637e6855c0a82e02deee97dedbc2aac3e0e5e1a", txOut.Asset) - assert.Equal(t, util.Fixed8FromInt64(100000000), txOut.Value) - assert.Equal(t, testchain.MultisigAddress(), txOut.Address) - }) - t.Run("getrawmempool", func(t *testing.T) { mp := chain.GetMemPool() // `expected` stores hashes of previously added txs expected := make([]util.Uint256, 0) for _, tx := range mp.GetVerifiedTransactions() { - expected = append(expected, tx.Tx.Hash()) + expected = append(expected, tx.Hash()) } for i := 0; i < 5; i++ { - tx := transaction.NewContractTX() + tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) assert.NoError(t, mp.Add(tx, &FeerStub{})) expected = append(expected, tx.Hash()) } diff --git a/pkg/rpc/server/subscription_test.go b/pkg/rpc/server/subscription_test.go index ef0ba8155..c11265cfe 100644 --- a/pkg/rpc/server/subscription_test.go +++ b/pkg/rpc/server/subscription_test.go @@ -9,7 +9,6 @@ import ( "github.com/gorilla/websocket" "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/internal/testchain" "github.com/nspcc-dev/neo-go/pkg/rpc/response" @@ -98,17 +97,12 @@ func TestSubscriptions(t *testing.T) { for _, b := range getTestBlocks(t) { require.NoError(t, chain.AddBlock(b)) - for _, tx := range b.Transactions { - var mayNotify bool - - if tx.Type == transaction.InvocationType { - resp := getNotification(t, respMsgs) - require.Equal(t, response.ExecutionEventID, resp.Event) - mayNotify = true - } + for range b.Transactions { + resp := getNotification(t, respMsgs) + require.Equal(t, response.ExecutionEventID, resp.Event) for { resp := getNotification(t, respMsgs) - if mayNotify && resp.Event == response.NotificationEventID { + if resp.Event == response.NotificationEventID { continue } require.Equal(t, response.TransactionEventID, resp.Event) diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index bae75785b..b24ef7a0e 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ diff --git a/pkg/smartcontract/context/context_test.go b/pkg/smartcontract/context/context_test.go index 6b9dc8ab5..ecd26e3e1 100644 --- a/pkg/smartcontract/context/context_test.go +++ b/pkg/smartcontract/context/context_test.go @@ -12,6 +12,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" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/stretchr/testify/require" ) @@ -164,17 +165,7 @@ func newParam(typ smartcontract.ParamType, name string) wallet.ContractParam { } func getContractTx() *transaction.Transaction { - tx := transaction.NewContractTX() - tx.AddInput(&transaction.Input{ - PrevHash: util.Uint256{1, 2, 3, 4}, - PrevIndex: 5, - }) - tx.AddOutput(&transaction.Output{ - AssetID: util.Uint256{7, 8, 9}, - Amount: 10, - ScriptHash: util.Uint160{11, 12}, - }) - tx.Data = new(transaction.ContractTX) + tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) tx.Attributes = make([]transaction.Attribute, 0) tx.Scripts = make([]transaction.Witness, 0) tx.Hash() diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index 5dad09926..54eb394a5 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -85,7 +85,7 @@ func (p *Parameter) MarshalJSON() ([]byte, error) { case MapType: ppair := p.Value.([]ParameterPair) resultRawValue, resultErr = json.Marshal(ppair) - case InteropInterfaceType: + case InteropInterfaceType, AnyType: resultRawValue = []byte("null") default: resultErr = errors.Errorf("Marshaller for type %s not implemented", p.Type) @@ -168,7 +168,7 @@ func (p *Parameter) UnmarshalJSON(data []byte) (err error) { return } p.Value = h - case InteropInterfaceType: + case InteropInterfaceType, AnyType: // stub, ignore value, it can only be null p.Value = nil default: diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 08c439ac4..1cecbc4f9 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -77,12 +77,6 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) { var numtoread int switch instr { - case opcode.OLDPUSH1: - // OLDPUSH1 is used during transition to NEO3 in verification scripts. - // FIXME remove #927 - if len(c.prog) == 1 { - return opcode.PUSH1, nil, nil - } case opcode.PUSHDATA1: if c.nextip >= len(c.prog) { err = errNoInstParam diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 4c35e5641..00eb106a2 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -135,12 +135,8 @@ func testFile(t *testing.T, filename string) { require.NoErrorf(t, json.Unmarshal(data, ut), "file: %s", filename) t.Run(ut.Category+":"+ut.Name, func(t *testing.T) { - isRot := strings.HasSuffix(filename, "ROT.json") for i := range ut.Tests { test := ut.Tests[i] - if isRot && test.Name == "Without push" { - return // FIXME #927 single ROT is interpreted as PUSH1 - } t.Run(ut.Tests[i].Name, func(t *testing.T) { prog := []byte(test.Script) vm := load(prog) diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index b8d56666e..76149a798 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -1,6 +1,6 @@ package opcode -//go:generate stringer -type=Opcode +//go:generate stringer -type=Opcode -linecomment // Opcode represents a single operation code for the NEO virtual machine. type Opcode byte @@ -87,7 +87,6 @@ const ( TUCK Opcode = 0x4E SWAP Opcode = 0x50 ROT Opcode = 0x51 - OLDPUSH1 Opcode = 0x51 // FIXME remove #927 ROLL Opcode = 0x52 REVERSE3 Opcode = 0x53 REVERSE4 Opcode = 0x54 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index d5ab10d51..6b44af438 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type Opcode -linecomment"; DO NOT EDIT. +// Code generated by "stringer -type=Opcode -linecomment"; DO NOT EDIT. package opcode @@ -77,7 +77,6 @@ func _() { _ = x[TUCK-78] _ = x[SWAP-80] _ = x[ROT-81] - _ = x[OLDPUSH1-81] _ = x[ROLL-82] _ = x[REVERSE3-83] _ = x[REVERSE4-84]