diff --git a/Makefile b/Makefile index 6ea18c5ee4..3e0dabf847 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ build-windows: build-lint: build lint +test-full: install + go test -tags $(BUILD_TAGS),runheavy ./... --timeout 60m --count 1 -failfast + test: install go test -tags $(BUILD_TAGS) ./... --timeout 30m --count 1 -failfast diff --git a/client/callview.go b/client/callview.go index de566a48ab..c64b5b45eb 100644 --- a/client/callview.go +++ b/client/callview.go @@ -34,7 +34,7 @@ func (c *WaspClient) CallView(chainID *iscp.ChainID, hContract iscp.Hname, funct return res, err case strings.Contains(err.Error(), "virtual state has been invalidated"): if time.Now().After(deadline) { - return nil, coreutil.ErrStateHasBeenInvalidated + return nil, coreutil.ErrorStateInvalidated } time.Sleep(retryTimeoutOnOptimisticReadFail) default: diff --git a/client/client.go b/client/client.go index 5aea07eb07..c28c0ea016 100644 --- a/client/client.go +++ b/client/client.go @@ -5,13 +5,11 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "strings" - "golang.org/x/xerrors" - "github.com/iotaledger/wasp/packages/webapi/model" + "golang.org/x/xerrors" ) // WaspClient allows to make requests to the Wasp web API. @@ -32,7 +30,7 @@ func NewWaspClient(baseURL string, httpClient ...http.Client) *WaspClient { } func processResponse(res *http.Response, decodeTo interface{}) error { - resBody, err := ioutil.ReadAll(res.Body) + resBody, err := io.ReadAll(res.Body) if err != nil { return xerrors.Errorf("unable to read response body: %w", err) } diff --git a/contracts/native/evm/README.md b/contracts/native/evm/README.md new file mode 100644 index 0000000000..dc7ef524e7 --- /dev/null +++ b/contracts/native/evm/README.md @@ -0,0 +1,110 @@ +# EVM support + +This package and subpackages contain the code for the `evmchain` and `evmlight` +smart contracts, which allow to execute Ethereum VM (EVM) code on top of the +ISCP chain, thus adding support for Ethereum smart contracts. + +## EVM flavors + +There are two EVM 'flavors', each one being a native ISCP smart contract. You +must decide which one to use when deploying a new EVM chain: + +### `evmchain` + +The `evmchain` implementation emulates a full Ethereum blockchain, as if it +were an Ethereum 'node', storing the full history of blocks and past states. + +Pros: +- More likely to be compatible with existing EVM tools (e.g.: Metamask, Remix, + Hardhat, etc.) that expect a 'real' Ethereum blockchain. + +Cons: +- Inefficient: spends time and space calculating the Merkle tree, verifying + blocks, storing past states, etc, when none of this is necessary in an ISCP + contract. + +### `evmlight` + +The `evmlight` implementation is a more efficient solution for EVM support. It +stores only the current EVM state in raw form, and after running an Ethereum +transaction, it: + +- Updates the EVM state +- Stores the transaction and receipt for future reference. Only the latest N + transactions/receipts are stored, to avoid using unlimited space. + +Pros: +- More space/time efficient than `evmchain` +- Potentially easier to integrate with ISCP (still in experimental phase) + +Cons: +- Less support for existing EVM tools that expect a 'real' Ethereum blockchain. + There is still partial support for some EVM tools. YMMV. + +## Enabling / disabling EVM + +EVM support is provided by the `evmchain` and `evmlight` native contracts, and +as such it needs to be enabled at compile time. **EVM support is enabled by +default, so no special action is needed.** + +EVM support inflates the `wasp` and `wasp-cli` binaries by several MB. If this +is a problem and you don't need EVM support, you can disable it at compile +time by providing the `-tags noevm` flag to the Go compiler. For example: + +``` +go install -tags noevm ./... +``` + +## Deploy + +You can use `wasp-cli` to deploy the `evmchain` or `evmlight` contract (given that you +already have access to an ISCP chain and have deposited some funds into your +on-chain account): + +``` +wasp-cli chain evm deploy --alloc 0x71562b71999873DB5b286dF957af199Ec94617F7:115792089237316195423570985008687907853269984665640564039457584007913129639927 +``` + +The `--alloc` parameter specifies the genesis allocation for the EVM chain, +with syntax `address:wei [address:wei ...]`. + +By default the `evmchain` contract will be deployed; you can change this with +`--evm-flavor evmlight`. + +## JSON-RPC + +Once your EVM chain is deployed, you can use the `wasp-cli chain evm jsonrpc` +command to start a JSON-RPC server. This will allow you to connect any standard +Ethereum tool, like Metamask. + +Note: If you are using `evmlight` you should run the JSON-RPC server with +`--name evmlight`. + +## Complete example using `wasp-cluster` + +In terminal #1, start a cluster: + +``` +wasp-cluster start -d +``` + +In terminal #2: + +``` +# initialize a private key and request some funds +wasp-cli init +wasp-cli request-funds + +# deploy an ISCP chain, deposit some funds to be used for fees +wasp-cli chain deploy --chain=mychain --committee=0,1,2,3 --quorum 3 +wasp-cli chain deposit IOTA:1000 + +# deploy an EVM chain +wasp-cli chain evm deploy --alloc 0x71562b71999873DB5b286dF957af199Ec94617F7:115792089237316195423570985008687907853269984665640564039457584007913129639927 +``` + +Finally we start the JSON-RPC server: + +``` +wasp-cli chain evm jsonrpc +``` diff --git a/packages/evm/db.go b/contracts/native/evm/evmchain/emulator/db.go similarity index 99% rename from packages/evm/db.go rename to contracts/native/evm/evmchain/emulator/db.go index 14a35a2bae..371142ebb5 100644 --- a/packages/evm/db.go +++ b/contracts/native/evm/evmchain/emulator/db.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evm +package emulator import ( "github.com/ethereum/go-ethereum/common" diff --git a/packages/evm/db_test.go b/contracts/native/evm/evmchain/emulator/db_test.go similarity index 95% rename from packages/evm/db_test.go rename to contracts/native/evm/evmchain/emulator/db_test.go index d4fa6fbc65..985014455e 100644 --- a/packages/evm/db_test.go +++ b/contracts/native/evm/evmchain/emulator/db_test.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evm +package emulator import ( "testing" diff --git a/packages/evm/emulator.go b/contracts/native/evm/evmchain/emulator/emulator.go similarity index 97% rename from packages/evm/emulator.go rename to contracts/native/evm/evmchain/emulator/emulator.go index 5292f376f3..f2dcc9e1b9 100644 --- a/packages/evm/emulator.go +++ b/contracts/native/evm/evmchain/emulator/emulator.go @@ -1,10 +1,10 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -// package evm provides tools to emulate Ethereum chains and contracts. +// package emulator provides tools to emulate Ethereum chains and contracts. // // Code adapted from go-ethereum/accounts/abi/bind/backends/simulated.go -package evm +package emulator import ( "context" @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/iotaledger/wasp/contracts/native/evm" "golang.org/x/xerrors" ) @@ -55,15 +56,11 @@ type EVMEmulator struct { } var ( - TxGas = uint64(21000) // gas cost of simple transfer (not contract creation / call) - GasLimitDefault = uint64(15000000) - GasPrice = big.NewInt(0) - vmConfig = vm.Config{} - timeDelta = uint64(1) // amount of seconds to add to timestamp by default for new blocks + TxGas = uint64(21000) // gas cost of simple transfer (not contract creation / call) + vmConfig = vm.Config{} + timeDelta = uint64(1) // amount of seconds to add to timestamp by default for new blocks ) -const DefaultChainID = 1074 // IOTA -- get it? - func MakeConfig(chainID int) *params.ChainConfig { return ¶ms.ChainConfig{ ChainID: big.NewInt(int64(chainID)), @@ -387,7 +384,7 @@ func (e *EVMEmulator) PendingNonceAt(account common.Address) (uint64, error) { // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated // chain doesn't have miners, we just return a gas price of 1 for any call. func (e *EVMEmulator) SuggestGasPrice() (*big.Int, error) { - return GasPrice, nil + return evm.GasPrice, nil } // EstimateGas executes the requested code against the currently pending block/state and diff --git a/packages/evm/emulator_test.go b/contracts/native/evm/evmchain/emulator/emulator_test.go similarity index 94% rename from packages/evm/emulator_test.go rename to contracts/native/evm/evmchain/emulator/emulator_test.go index 82b48768c1..a0bf039542 100644 --- a/packages/evm/emulator_test.go +++ b/contracts/native/evm/evmchain/emulator/emulator_test.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evm +package emulator import ( "crypto/ecdsa" @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/iotaledger/wasp/contracts/native/evm" "github.com/iotaledger/wasp/packages/evm/evmtest" "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/dict" @@ -32,7 +33,7 @@ func estimateGas(t testing.TB, emu *EVMEmulator, from common.Address, to *common }) if err != nil { t.Logf("%v", err) - return GasLimitDefault - 1 + return evm.GasLimitDefault - 1 } return gas } @@ -45,7 +46,7 @@ func sendTransaction(t testing.TB, emu *EVMEmulator, sender *ecdsa.PrivateKey, r gas := estimateGas(t, emu, senderAddress, &receiverAddress, amount, data) tx, err := types.SignTx( - types.NewTransaction(nonce, receiverAddress, amount, gas, GasPrice, data), + types.NewTransaction(nonce, receiverAddress, amount, gas, evm.GasPrice, data), emu.Signer(), sender, ) @@ -89,7 +90,7 @@ func testBlockchain(t testing.TB, db ethdb.Database) { faucetAddress: {Balance: faucetSupply}, } - InitGenesis(DefaultChainID, db, genesisAlloc, GasLimitDefault, 0) + InitGenesis(evm.DefaultChainID, db, genesisAlloc, evm.GasLimitDefault, 0) emu := NewEVMEmulator(db) defer emu.Close() @@ -110,7 +111,7 @@ func testBlockchain(t testing.TB, db ethdb.Database) { // assert that current block is genesis block := emu.Blockchain().CurrentBlock() require.NotNil(t, block) - require.EqualValues(t, GasLimitDefault, block.Header().GasLimit) + require.EqualValues(t, evm.GasLimitDefault, block.Header().GasLimit) genesisHash = block.Hash() } @@ -137,7 +138,7 @@ func testBlockchain(t testing.TB, db ethdb.Database) { sendTransaction(t, emu, faucet, receiverAddress, transferAmount, nil) require.EqualValues(t, 1, emu.Blockchain().CurrentBlock().NumberU64()) - require.EqualValues(t, GasLimitDefault, emu.Blockchain().CurrentBlock().Header().GasLimit) + require.EqualValues(t, evm.GasLimitDefault, emu.Blockchain().CurrentBlock().Header().GasLimit) } { @@ -178,7 +179,7 @@ func testBlockchainPersistence(t testing.TB, db ethdb.Database) { receiverAddress := crypto.PubkeyToAddress(receiver.PublicKey) transferAmount := big.NewInt(1000) - InitGenesis(DefaultChainID, db, genesisAlloc, GasLimitDefault, 0) + InitGenesis(evm.DefaultChainID, db, genesisAlloc, evm.GasLimitDefault, 0) // do a transfer using one instance of EVMEmulator func() { @@ -224,7 +225,7 @@ func deployEVMContract(t testing.TB, emu *EVMEmulator, creator *ecdsa.PrivateKey require.NoError(t, err) tx, err := types.SignTx( - types.NewContractCreation(nonce, txValue, gas, GasPrice, data), + types.NewContractCreation(nonce, txValue, gas, evm.GasPrice, data), emu.Signer(), creator, ) @@ -239,7 +240,7 @@ func deployEVMContract(t testing.TB, emu *EVMEmulator, creator *ecdsa.PrivateKey // assertions { require.EqualValues(t, 1, emu.Blockchain().CurrentBlock().NumberU64()) - require.EqualValues(t, GasLimitDefault, emu.Blockchain().CurrentBlock().Header().GasLimit) + require.EqualValues(t, evm.GasLimitDefault, emu.Blockchain().CurrentBlock().Header().GasLimit) // verify contract address { @@ -301,7 +302,7 @@ func testStorageContract(t testing.TB, db ethdb.Database) { faucetAddress: {Balance: faucetSupply}, } - InitGenesis(DefaultChainID, db, genesisAlloc, GasLimitDefault, 0) + InitGenesis(evm.DefaultChainID, db, genesisAlloc, evm.GasLimitDefault, 0) emu := NewEVMEmulator(db) defer emu.Close() @@ -374,7 +375,7 @@ func testERC20Contract(t testing.TB, db ethdb.Database) { faucetAddress: {Balance: faucetSupply}, } - InitGenesis(DefaultChainID, db, genesisAlloc, GasLimitDefault, 0) + InitGenesis(evm.DefaultChainID, db, genesisAlloc, evm.GasLimitDefault, 0) emu := NewEVMEmulator(db) defer emu.Close() @@ -467,7 +468,7 @@ func initBenchmark(b *testing.B) (*EVMEmulator, []*types.Transaction, dict.Dict) faucetAddress: {Balance: faucetSupply}, } - InitGenesis(DefaultChainID, db, genesisAlloc, GasLimitDefault, 0) + InitGenesis(evm.DefaultChainID, db, genesisAlloc, evm.GasLimitDefault, 0) emu := NewEVMEmulator(db) defer emu.Close() @@ -499,7 +500,7 @@ func initBenchmark(b *testing.B) (*EVMEmulator, []*types.Transaction, dict.Dict) gas := estimateGas(b, emu, senderAddress, &contractAddress, amount, callArguments) txs[i], err = types.SignTx( - types.NewTransaction(nonce, contractAddress, amount, gas, GasPrice, callArguments), + types.NewTransaction(nonce, contractAddress, amount, gas, evm.GasPrice, callArguments), emu.Signer(), sender, ) diff --git a/contracts/native/evm/evmchain/impl.go b/contracts/native/evm/evmchain/impl.go new file mode 100644 index 0000000000..588fac615c --- /dev/null +++ b/contracts/native/evm/evmchain/impl.go @@ -0,0 +1,231 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmchain + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evmchain/emulator" + "github.com/iotaledger/wasp/contracts/native/evm/evminternal" + "github.com/iotaledger/wasp/packages/evm/evmtypes" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/assert" + "github.com/iotaledger/wasp/packages/kv/codec" + "github.com/iotaledger/wasp/packages/kv/dict" +) + +var Processor = Contract.Processor(initialize, append( + evminternal.ManagementHandlers, + + evm.FuncSendTransaction.WithHandler(applyTransaction), + evm.FuncGetBalance.WithHandler(getBalance), + evm.FuncCallContract.WithHandler(callContract), + evm.FuncEstimateGas.WithHandler(estimateGas), + evm.FuncGetNonce.WithHandler(getNonce), + evm.FuncGetReceipt.WithHandler(getReceipt), + evm.FuncGetCode.WithHandler(getCode), + evm.FuncGetBlockNumber.WithHandler(getBlockNumber), + evm.FuncGetBlockByNumber.WithHandler(getBlockByNumber), + evm.FuncGetBlockByHash.WithHandler(getBlockByHash), + evm.FuncGetTransactionByHash.WithHandler(getTransactionByHash), + evm.FuncGetTransactionByBlockHashAndIndex.WithHandler(getTransactionByBlockHashAndIndex), + evm.FuncGetTransactionByBlockNumberAndIndex.WithHandler(getTransactionByBlockNumberAndIndex), + evm.FuncGetTransactionCountByBlockHash.WithHandler(getTransactionCountByBlockHash), + evm.FuncGetTransactionCountByBlockNumber.WithHandler(getTransactionCountByBlockNumber), + evm.FuncGetStorage.WithHandler(getStorage), + evm.FuncGetLogs.WithHandler(getLogs), +)...) + +func initialize(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + genesisAlloc, err := evmtypes.DecodeGenesisAlloc(ctx.Params().MustGet(evm.FieldGenesisAlloc)) + a.RequireNoError(err) + chainID, err := codec.DecodeUint16(ctx.Params().MustGet(evm.FieldChainID), evm.DefaultChainID) + a.RequireNoError(err) + emulator.InitGenesis( + int(chainID), + rawdb.NewDatabase(emulator.NewKVAdapter(ctx.State())), // TODO: use subrealm to avoid collisions with evm management + genesisAlloc, + evm.GasLimitDefault, + timestamp(ctx), + ) + evminternal.InitializeManagement(ctx) + return nil, nil +} + +func applyTransaction(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + + tx := &types.Transaction{} + err := tx.UnmarshalBinary(ctx.Params().MustGet(evm.FieldTransactionData)) + a.RequireNoError(err) + + return evminternal.RequireGasFee(ctx, tx.Gas(), func() uint64 { + emu := getOrCreateEmulator(ctx) + receipt, err := emu.SendTransaction(tx) + a.RequireNoError(err) + return receipt.GasUsed + }), nil +} + +func getBalance(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) + bal, err := emu.BalanceAt(addr, blockNumber) + a.RequireNoError(err) + return evminternal.Result(bal.Bytes()), nil + }) +} + +func getBlockNumber(ctx iscp.SandboxView) (dict.Dict, error) { + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + return evminternal.Result(emu.Blockchain().CurrentBlock().Number().Bytes()), nil + }) +} + +func getBlockByNumber(ctx iscp.SandboxView) (dict.Dict, error) { + return withBlockByNumber(ctx, func(emu *emulator.EVMEmulator, block *types.Block) (dict.Dict, error) { + if block == nil { + return nil, nil + } + return evminternal.Result(evmtypes.EncodeBlock(block)), nil + }) +} + +func getBlockByHash(ctx iscp.SandboxView) (dict.Dict, error) { + return withBlockByHash(ctx, func(emu *emulator.EVMEmulator, block *types.Block) (dict.Dict, error) { + if block == nil { + return nil, nil + } + return evminternal.Result(evmtypes.EncodeBlock(block)), nil + }) +} + +func getTransactionByHash(ctx iscp.SandboxView) (dict.Dict, error) { + return withTransactionByHash(ctx, func(emu *emulator.EVMEmulator, tx *types.Transaction) (dict.Dict, error) { + return txResult(ctx, emu, tx), nil + }) +} + +func getTransactionByBlockHashAndIndex(ctx iscp.SandboxView) (dict.Dict, error) { + return withTransactionByBlockHashAndIndex(ctx, func(emu *emulator.EVMEmulator, tx *types.Transaction) (dict.Dict, error) { + return txResult(ctx, emu, tx), nil + }) +} + +func getTransactionByBlockNumberAndIndex(ctx iscp.SandboxView) (dict.Dict, error) { + return withTransactionByBlockNumberAndIndex(ctx, func(emu *emulator.EVMEmulator, tx *types.Transaction) (dict.Dict, error) { + return txResult(ctx, emu, tx), nil + }) +} + +func getTransactionCountByBlockHash(ctx iscp.SandboxView) (dict.Dict, error) { + return withBlockByHash(ctx, func(emu *emulator.EVMEmulator, block *types.Block) (dict.Dict, error) { + if block == nil { + return nil, nil + } + return evminternal.Result(codec.EncodeUint64(uint64(len(block.Transactions())))), nil + }) +} + +func getTransactionCountByBlockNumber(ctx iscp.SandboxView) (dict.Dict, error) { + return withBlockByNumber(ctx, func(emu *emulator.EVMEmulator, block *types.Block) (dict.Dict, error) { + if block == nil { + return nil, nil + } + return evminternal.Result(codec.EncodeUint64(uint64(len(block.Transactions())))), nil + }) +} + +func getReceipt(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + return withTransactionByHash(ctx, func(emu *emulator.EVMEmulator, tx *types.Transaction) (dict.Dict, error) { + if tx == nil { + return nil, nil + } + receipt, err := emu.TransactionReceipt(tx.Hash()) + a.RequireNoError(err) + + return evminternal.Result(evmtypes.EncodeReceiptFull(receipt)), nil + }) +} + +func getNonce(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) + nonce, err := emu.NonceAt(addr, blockNumber) + a.RequireNoError(err) + return evminternal.Result(codec.EncodeUint64(nonce)), nil + }) +} + +func getCode(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) + code, err := emu.CodeAt(addr, blockNumber) + a.RequireNoError(err) + return evminternal.Result(code), nil + }) +} + +func getStorage(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + key := common.BytesToHash(ctx.Params().MustGet(evm.FieldKey)) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) + data, err := emu.StorageAt(addr, key, blockNumber) + a.RequireNoError(err) + return evminternal.Result(data), nil + }) +} + +func getLogs(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + q, err := evmtypes.DecodeFilterQuery(ctx.Params().MustGet(evm.FieldFilterQuery)) + a.RequireNoError(err) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + logs, err := emu.FilterLogs(q) + a.RequireNoError(err) + return evminternal.Result(evmtypes.EncodeLogs(logs)), nil + }) +} + +func callContract(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + callMsg, err := evmtypes.DecodeCallMsg(ctx.Params().MustGet(evm.FieldCallMsg)) + a.RequireNoError(err) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) + res, err := emu.CallContract(callMsg, blockNumber) + a.RequireNoError(err) + return evminternal.Result(res), nil + }) +} + +func estimateGas(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + callMsg, err := evmtypes.DecodeCallMsg(ctx.Params().MustGet(evm.FieldCallMsg)) + a.RequireNoError(err) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + gas, err := emu.EstimateGas(callMsg) + a.RequireNoError(err) + return evminternal.Result(codec.EncodeUint64(gas)), nil + }) +} diff --git a/contracts/native/evm/evmchain/interface.go b/contracts/native/evm/evmchain/interface.go new file mode 100644 index 0000000000..cf5991a4e6 --- /dev/null +++ b/contracts/native/evm/evmchain/interface.go @@ -0,0 +1,10 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// package evmchain provides the `evmchain` contract, which allows to emulate an +// Ethereum blockchain on top of ISCP. +package evmchain + +import "github.com/iotaledger/wasp/packages/iscp/coreutil" + +var Contract = coreutil.NewContract("evmchain", "EVM chain smart contract") diff --git a/contracts/native/evm/evmchain/internal.go b/contracts/native/evm/evmchain/internal.go new file mode 100644 index 0000000000..19eddbe21f --- /dev/null +++ b/contracts/native/evm/evmchain/internal.go @@ -0,0 +1,160 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmchain + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evmchain/emulator" + "github.com/iotaledger/wasp/packages/evm/evmtypes" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/assert" + "github.com/iotaledger/wasp/packages/kv/buffered" + "github.com/iotaledger/wasp/packages/kv/codec" + "github.com/iotaledger/wasp/packages/kv/dict" +) + +func isNotFound(err error) bool { + switch err { + case ethereum.NotFound: + return true + case emulator.ErrTransactionDoesNotExist: + return true + case emulator.ErrBlockDoesNotExist: + return true + } + return false +} + +// getOrCreateEmulator creates a new emulator instance if this is the first call to applyTransaction +// in the ISCP block; otherwise it returns the previously created instance. The purpose is to +// create a single Ethereum block for each ISCP block. +func getOrCreateEmulator(ctx iscp.Sandbox) *emulator.EVMEmulator { + bctx := ctx.BlockContext(createEmulator, commitEthereumBlock) + return bctx.(*emulator.EVMEmulator) +} + +func createEmulator(ctx iscp.Sandbox) interface{} { + return emulator.NewEVMEmulator(rawdb.NewDatabase(emulator.NewKVAdapter(ctx.State())), timestamp(ctx)) +} + +// timestamp returns the current timestamp in seconds since epoch +func timestamp(ctx iscp.SandboxBase) uint64 { + tsNano := time.Duration(ctx.GetTimestamp()) * time.Nanosecond + return uint64(tsNano / time.Second) +} + +func commitEthereumBlock(blockContext interface{}) { + emu := blockContext.(*emulator.EVMEmulator) + emu.Commit() + _ = emu.Close() +} + +func withEmulatorR(ctx iscp.SandboxView, f func(*emulator.EVMEmulator) (dict.Dict, error)) (dict.Dict, error) { + emu := emulator.NewEVMEmulator( + rawdb.NewDatabase(emulator.NewKVAdapter(buffered.NewBufferedKVStoreAccess(ctx.State()))), + timestamp(ctx), + ) + defer emu.Close() + return f(emu) +} + +func txResult(ctx iscp.SandboxView, emu *emulator.EVMEmulator, tx *types.Transaction) dict.Dict { + a := assert.NewAssert(ctx.Log()) + if tx == nil { + return nil + } + receipt, err := emu.TransactionReceipt(tx.Hash()) + a.RequireNoError(err) + return dict.Dict{ + evm.FieldTransaction: evmtypes.EncodeTransaction(tx), + evm.FieldBlockHash: receipt.BlockHash.Bytes(), + evm.FieldBlockNumber: codec.EncodeUint64(receipt.BlockNumber.Uint64()), + evm.FieldTransactionIndex: codec.EncodeUint64(uint64(receipt.TransactionIndex)), + } +} + +func withBlockByNumber(ctx iscp.SandboxView, f func(*emulator.EVMEmulator, *types.Block) (dict.Dict, error)) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + blockNumber := paramBlockNumber(ctx) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + block, err := emu.BlockByNumber(blockNumber) + if !isNotFound(err) { + a.RequireNoError(err) + } + + return f(emu, block) + }) +} + +func withBlockByHash(ctx iscp.SandboxView, f func(*emulator.EVMEmulator, *types.Block) (dict.Dict, error)) (dict.Dict, error) { + hash := common.BytesToHash(ctx.Params().MustGet(evm.FieldBlockHash)) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + block := emu.BlockByHash(hash) + return f(emu, block) + }) +} + +func withTransactionByHash(ctx iscp.SandboxView, f func(*emulator.EVMEmulator, *types.Transaction) (dict.Dict, error)) (dict.Dict, error) { + txHash := common.BytesToHash(ctx.Params().MustGet(evm.FieldTransactionHash)) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + tx := emu.TransactionByHash(txHash) + return f(emu, tx) + }) +} + +func withTransactionByBlockHashAndIndex(ctx iscp.SandboxView, f func(*emulator.EVMEmulator, *types.Transaction) (dict.Dict, error)) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + blockHash := common.BytesToHash(ctx.Params().MustGet(evm.FieldBlockHash)) + index, err := codec.DecodeUint64(ctx.Params().MustGet(evm.FieldTransactionIndex), 0) + a.RequireNoError(err) + + return withEmulatorR(ctx, func(emu *emulator.EVMEmulator) (dict.Dict, error) { + tx, err := emu.TransactionInBlock(blockHash, uint(index)) + if !isNotFound(err) { + a.RequireNoError(err) + } + return f(emu, tx) + }) +} + +func withTransactionByBlockNumberAndIndex(ctx iscp.SandboxView, f func(*emulator.EVMEmulator, *types.Transaction) (dict.Dict, error)) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + index, err := codec.DecodeUint64(ctx.Params().MustGet(evm.FieldTransactionIndex), 0) + a.RequireNoError(err) + return withBlockByNumber(ctx, func(emu *emulator.EVMEmulator, block *types.Block) (dict.Dict, error) { + if block == nil || index >= uint64(len(block.Transactions())) { + return f(emu, nil) + } + return f(emu, block.Transactions()[index]) + }) +} + +func paramBlockNumber(ctx iscp.SandboxView) *big.Int { + if ctx.Params().MustHas(evm.FieldBlockNumber) { + return new(big.Int).SetBytes(ctx.Params().MustGet(evm.FieldBlockNumber)) + } + return nil // latest block +} + +func paramBlockNumberOrHashAsNumber(ctx iscp.SandboxView, emu *emulator.EVMEmulator) *big.Int { + if ctx.Params().MustHas(evm.FieldBlockHash) { + a := assert.NewAssert(ctx.Log()) + blockHash := common.BytesToHash(ctx.Params().MustGet(evm.FieldBlockHash)) + header, err := emu.HeaderByHash(blockHash) + a.RequireNoError(err) + a.Require(header != nil, "block not found") + return header.Number + } + return paramBlockNumber(ctx) +} diff --git a/contracts/native/evm/evminternal/management.go b/contracts/native/evm/evminternal/management.go new file mode 100644 index 0000000000..8e3fd26a9b --- /dev/null +++ b/contracts/native/evm/evminternal/management.go @@ -0,0 +1,152 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evminternal + +import ( + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/assert" + "github.com/iotaledger/wasp/packages/iscp/colored" + "github.com/iotaledger/wasp/packages/iscp/coreutil" + "github.com/iotaledger/wasp/packages/kv/codec" + "github.com/iotaledger/wasp/packages/kv/dict" + "github.com/iotaledger/wasp/packages/kv/kvdecoder" + "github.com/iotaledger/wasp/packages/vm/core/accounts" + "github.com/iotaledger/wasp/packages/vm/core/governance" +) + +const ( + keyGasPerIota = "g" + keyEVMOwner = "o" + keyNextEVMOwner = "n" +) + +var ManagementHandlers = []coreutil.ProcessorEntryPoint{ + evm.FuncSetNextOwner.WithHandler(setNextOwner), + evm.FuncClaimOwnership.WithHandler(claimOwnership), + evm.FuncSetGasPerIota.WithHandler(setGasPerIota), + evm.FuncWithdrawGasFees.WithHandler(withdrawGasFees), + evm.FuncGetOwner.WithHandler(getOwner), + evm.FuncGetGasPerIota.WithHandler(getGasPerIota), +} + +func InitializeManagement(ctx iscp.Sandbox) { + ctx.State().Set(keyGasPerIota, codec.EncodeUint64(evm.DefaultGasPerIota)) + ctx.State().Set(keyEVMOwner, codec.EncodeAgentID(ctx.ContractCreator())) +} + +func requireOwner(ctx iscp.Sandbox) { + contractOwner, err := codec.DecodeAgentID(ctx.State().MustGet(keyEVMOwner)) + a := assert.NewAssert(ctx.Log()) + a.RequireNoError(err) + a.Require(contractOwner.Equals(ctx.Caller()), "can only be called by the contract owner") +} + +func setNextOwner(ctx iscp.Sandbox) (dict.Dict, error) { + requireOwner(ctx) + par := kvdecoder.New(ctx.Params(), ctx.Log()) + ctx.State().Set(keyNextEVMOwner, codec.EncodeAgentID(par.MustGetAgentID(evm.FieldNextEVMOwner))) + return nil, nil +} + +func claimOwnership(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + + nextOwner, err := codec.DecodeAgentID(ctx.State().MustGet(keyNextEVMOwner)) + a.RequireNoError(err) + a.Require(nextOwner.Equals(ctx.Caller()), "Can only be called by the contract owner") + + ctx.State().Set(keyEVMOwner, codec.EncodeAgentID(nextOwner)) + return nil, nil +} + +func getOwner(ctx iscp.SandboxView) (dict.Dict, error) { + return Result(ctx.State().MustGet(keyEVMOwner)), nil +} + +func setGasPerIota(ctx iscp.Sandbox) (dict.Dict, error) { + requireOwner(ctx) + par := kvdecoder.New(ctx.Params()) + gasPerIotaBin := codec.EncodeUint64(par.MustGetUint64(evm.FieldGasPerIota)) + ctx.State().Set(keyGasPerIota, gasPerIotaBin) + return nil, nil +} + +func getGasPerIota(ctx iscp.SandboxView) (dict.Dict, error) { + return Result(ctx.State().MustGet(keyGasPerIota)), nil +} + +func withdrawGasFees(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + requireOwner(ctx) + + paramsDecoder := kvdecoder.New(ctx.Params(), ctx.Log()) + targetAgentID := paramsDecoder.MustGetAgentID(evm.FieldAgentID, ctx.Caller()) + + isOnChain := targetAgentID.Address().Equals(ctx.ChainID().AsAddress()) + + if isOnChain { + params := codec.MakeDict(map[string]interface{}{ + accounts.ParamAgentID: targetAgentID, + }) + _, err := ctx.Call(accounts.Contract.Hname(), accounts.FuncDeposit.Hname(), params, ctx.Balances()) + a.RequireNoError(err) + return nil, nil + } + + a.Require(ctx.Send(targetAgentID.Address(), ctx.Balances(), &iscp.SendMetadata{ + TargetContract: targetAgentID.Hname(), + }), "withdraw.inconsistency: failed sending tokens ") + + return nil, nil +} + +func RequireGasFee(ctx iscp.Sandbox, txGasLimit uint64, f func() uint64) dict.Dict { + a := assert.NewAssert(ctx.Log()) + + transferredIotas := ctx.IncomingTransfer().Get(getFeeColor(ctx)) + gasPerIota, err := codec.DecodeUint64(ctx.State().MustGet(keyGasPerIota), 0) + a.RequireNoError(err) + + a.Require( + transferredIotas >= txGasLimit/gasPerIota, + "transferred tokens (%d) not enough to cover the gas limit set in the transaction (%d at %d gas per iota token)", transferredIotas, txGasLimit, gasPerIota, + ) + + gasUsed := f() + + iotasGasFee := gasUsed / gasPerIota + if transferredIotas > iotasGasFee { + // refund unspent gas fee to the sender's on-chain account + iotasGasRefund := transferredIotas - iotasGasFee + _, err = ctx.Call( + accounts.Contract.Hname(), + accounts.FuncDeposit.Hname(), + dict.Dict{accounts.ParamAgentID: codec.EncodeAgentID(ctx.Caller())}, + colored.NewBalancesForIotas(iotasGasRefund), + ) + a.RequireNoError(err) + } + + return dict.Dict{ + evm.FieldGasFee: codec.EncodeUint64(iotasGasFee), + evm.FieldGasUsed: codec.EncodeUint64(gasUsed), + } +} + +func getFeeColor(ctx iscp.Sandbox) colored.Color { + a := assert.NewAssert(ctx.Log()) + + // call root contract view to get the feecolor + feeInfo, err := ctx.Call( + governance.Contract.Hname(), + governance.FuncGetFeeInfo.Hname(), + dict.Dict{governance.ParamHname: ctx.Contract().Bytes()}, + nil, + ) + a.RequireNoError(err) + feeColor, err := codec.DecodeColor(feeInfo.MustGet(governance.ParamFeeColor)) + a.RequireNoError(err) + return feeColor +} diff --git a/contracts/native/evm/evminternal/util.go b/contracts/native/evm/evminternal/util.go new file mode 100644 index 0000000000..d2a8b06a62 --- /dev/null +++ b/contracts/native/evm/evminternal/util.go @@ -0,0 +1,16 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evminternal + +import ( + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/kv/dict" +) + +func Result(value []byte) dict.Dict { + if value == nil { + return nil + } + return dict.Dict{evm.FieldResult: value} +} diff --git a/contracts/native/evm/evmlight/emulator/blockchaindb.go b/contracts/native/evm/evmlight/emulator/blockchaindb.go new file mode 100644 index 0000000000..8f5ae71e21 --- /dev/null +++ b/contracts/native/evm/evmlight/emulator/blockchaindb.go @@ -0,0 +1,335 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package emulator + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/iotaledger/wasp/packages/evm/evmtypes" + "github.com/iotaledger/wasp/packages/kv" + "github.com/iotaledger/wasp/packages/kv/codec" +) + +const ( + keyChainID = "c" + keyGasLimit = "g" + keyNumber = "n" + keyTransactionByBlockNumber = "n:t" + keyReceiptByBlockNumber = "n:r" + keyTimestampByBlockNumber = "n:ts" + keyBlockHashByBlockNumber = "n:bh" + + // indexes: + + keyBlockNumberByBlockHash = "bh:n" + keyBlockNumberByTxHash = "th:n" +) + +// Amount of blocks to keep in DB. Older blocks will be pruned every time a transaction is added +const keepAmount = 100 + +type BlockchainDB struct { + kv kv.KVStore +} + +func NewBlockchainDB(store kv.KVStore) *BlockchainDB { + return &BlockchainDB{kv: store} +} + +func (bc *BlockchainDB) Initialized() bool { + return bc.kv.MustGet(keyChainID) != nil +} + +func (bc *BlockchainDB) Init(chainID uint16, gasLimit, timestamp uint64) { + bc.SetChainID(chainID) + bc.SetGasLimit(gasLimit) + bc.AddBlock(bc.makeHeader(nil, nil, timestamp)) +} + +func (bc *BlockchainDB) SetChainID(chainID uint16) { + bc.kv.Set(keyChainID, codec.EncodeUint16(chainID)) +} + +func (bc *BlockchainDB) GetChainID() uint16 { + chainID, err := codec.DecodeUint16(bc.kv.MustGet(keyChainID)) + if err != nil { + panic(err) + } + return chainID +} + +func (bc *BlockchainDB) SetGasLimit(gas uint64) { + bc.kv.Set(keyGasLimit, codec.EncodeUint64(gas)) +} + +func (bc *BlockchainDB) GetGasLimit() uint64 { + gas, err := codec.DecodeUint64(bc.kv.MustGet(keyGasLimit)) + if err != nil { + panic(err) + } + return gas +} + +func (bc *BlockchainDB) SetNumber(n *big.Int) { + bc.kv.Set(keyNumber, n.Bytes()) +} + +func (bc *BlockchainDB) GetNumber() *big.Int { + r := new(big.Int) + r.SetBytes(bc.kv.MustGet(keyNumber)) + return r +} + +func makeTransactionByBlockNumberKey(blockNumber *big.Int) kv.Key { + return keyTransactionByBlockNumber + kv.Key(blockNumber.Bytes()) +} + +func makeReceiptByBlockNumberKey(blockNumber *big.Int) kv.Key { + return keyReceiptByBlockNumber + kv.Key(blockNumber.Bytes()) +} + +func makeTimestampByBlockNumberKey(blockNumber *big.Int) kv.Key { + return keyTimestampByBlockNumber + kv.Key(blockNumber.Bytes()) +} + +func makeBlockHashByBlockNumberKey(blockNumber *big.Int) kv.Key { + return keyBlockHashByBlockNumber + kv.Key(blockNumber.Bytes()) +} + +func makeBlockNumberByBlockHashKey(hash common.Hash) kv.Key { + return keyBlockNumberByBlockHash + kv.Key(hash.Bytes()) +} + +func makeBlockNumberByTxHashKey(hash common.Hash) kv.Key { + return keyBlockNumberByTxHash + kv.Key(hash.Bytes()) +} + +func (bc *BlockchainDB) AddTransaction(tx *types.Transaction, receipt *types.Receipt, timestamp uint64) { + bc.kv.Set( + makeTransactionByBlockNumberKey(receipt.BlockNumber), + evmtypes.EncodeTransaction(tx), + ) + bc.kv.Set( + makeReceiptByBlockNumberKey(receipt.BlockNumber), + evmtypes.EncodeReceipt(receipt), + ) + bc.kv.Set( + makeBlockNumberByTxHashKey(tx.Hash()), + receipt.BlockNumber.Bytes(), + ) + header := bc.makeHeader(tx, receipt, timestamp) + bc.AddBlock(header) + + bc.prune(header.Number) +} + +func (bc *BlockchainDB) prune(currentNumber *big.Int) { + forget := new(big.Int).Sub(currentNumber, big.NewInt(int64(keepAmount))) + if forget.Cmp(common.Big1) >= 0 { + blockHash := bc.GetBlockHashByBlockNumber(forget) + txHash := bc.GetTransactionByBlockNumber(forget).Hash() + + bc.kv.Del(makeTransactionByBlockNumberKey(forget)) + bc.kv.Del(makeReceiptByBlockNumberKey(forget)) + bc.kv.Del(makeTimestampByBlockNumberKey(forget)) + bc.kv.Del(makeBlockHashByBlockNumberKey(forget)) + bc.kv.Del(makeBlockNumberByBlockHashKey(blockHash)) + bc.kv.Del(makeBlockNumberByTxHashKey(txHash)) + } +} + +func (bc *BlockchainDB) AddBlock(header *types.Header) { + bc.kv.Set( + makeTimestampByBlockNumberKey(header.Number), + codec.EncodeUint64(header.Time), + ) + bc.kv.Set( + makeBlockHashByBlockNumberKey(header.Number), + header.Hash().Bytes(), + ) + bc.kv.Set( + makeBlockNumberByBlockHashKey(header.Hash()), + header.Number.Bytes(), + ) + bc.SetNumber(header.Number) +} + +func (bc *BlockchainDB) GetReceiptByBlockNumber(blockNumber *big.Int) *types.Receipt { + b := bc.kv.MustGet(makeReceiptByBlockNumberKey(blockNumber)) + if b == nil { + return nil + } + r, err := evmtypes.DecodeReceipt(b) + if err != nil { + panic(err) + } + tx := bc.GetTransactionByBlockNumber(blockNumber) + r.TxHash = tx.Hash() + if tx.To() == nil { + from, _ := types.Sender(evmtypes.Signer(big.NewInt(int64(bc.GetChainID()))), tx) + r.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) + } + r.GasUsed = r.CumulativeGasUsed + r.BlockHash = bc.GetBlockHashByBlockNumber(blockNumber) + r.BlockNumber = blockNumber + return r +} + +func (bc *BlockchainDB) GetBlockNumberByTxHash(txHash common.Hash) *big.Int { + b := bc.kv.MustGet(makeBlockNumberByTxHashKey(txHash)) + if b == nil { + return nil + } + return new(big.Int).SetBytes(b) +} + +func (bc *BlockchainDB) GetReceiptByTxHash(txHash common.Hash) *types.Receipt { + blockNumber := bc.GetBlockNumberByTxHash(txHash) + if blockNumber == nil { + return nil + } + return bc.GetReceiptByBlockNumber(blockNumber) +} + +func (bc *BlockchainDB) GetTransactionByBlockNumber(blockNumber *big.Int) *types.Transaction { + b := bc.kv.MustGet(makeTransactionByBlockNumberKey(blockNumber)) + if b == nil { + return nil + } + tx, err := evmtypes.DecodeTransaction(b) + if err != nil { + panic(err) + } + return tx +} + +func (bc *BlockchainDB) GetTransactionByHash(txHash common.Hash) *types.Transaction { + blockNumber := bc.GetBlockNumberByTxHash(txHash) + if blockNumber == nil { + return nil + } + return bc.GetTransactionByBlockNumber(blockNumber) +} + +func (bc *BlockchainDB) GetBlockHashByBlockNumber(blockNumber *big.Int) common.Hash { + b := bc.kv.MustGet(makeBlockHashByBlockNumberKey(blockNumber)) + if b == nil { + return common.Hash{} + } + return common.BytesToHash(b) +} + +func (bc *BlockchainDB) GetBlockNumberByBlockHash(hash common.Hash) *big.Int { + b := bc.kv.MustGet(makeBlockNumberByBlockHashKey(hash)) + if b == nil { + return nil + } + return new(big.Int).SetBytes(b) +} + +func (bc *BlockchainDB) GetTimestampByBlockNumber(blockNumber *big.Int) uint64 { + n, err := codec.DecodeUint64(bc.kv.MustGet(makeTimestampByBlockNumberKey(blockNumber))) + if err != nil { + panic(err) + } + return n +} + +func (bc *BlockchainDB) makeHeader(tx *types.Transaction, receipt *types.Receipt, timestamp uint64) *types.Header { + if tx == nil { + // genesis block hash + blockNumber := common.Big0 + return &types.Header{ + Number: blockNumber, + GasLimit: bc.GetGasLimit(), + Time: timestamp, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + UncleHash: types.EmptyUncleHash, + } + } + blockNumber := receipt.BlockNumber + prevBlockNumber := new(big.Int).Sub(blockNumber, common.Big1) + return &types.Header{ + ParentHash: bc.GetBlockHashByBlockNumber(prevBlockNumber), + Number: blockNumber, + GasLimit: bc.GetGasLimit(), + GasUsed: receipt.GasUsed, + Time: timestamp, + TxHash: types.DeriveSha(types.Transactions{tx}, &fakeHasher{}), + ReceiptHash: types.DeriveSha(types.Receipts{receipt}, &fakeHasher{}), + Bloom: types.CreateBloom([]*types.Receipt{receipt}), + UncleHash: types.EmptyUncleHash, + } +} + +func (bc *BlockchainDB) GetHeaderByBlockNumber(blockNumber *big.Int) *types.Header { + if blockNumber.Cmp(bc.GetNumber()) > 0 { + return nil + } + return bc.makeHeader( + bc.GetTransactionByBlockNumber(blockNumber), + bc.GetReceiptByBlockNumber(blockNumber), + bc.GetTimestampByBlockNumber(blockNumber), + ) +} + +func (bc *BlockchainDB) GetHeaderByHash(hash common.Hash) *types.Header { + n := bc.GetBlockNumberByBlockHash(hash) + if n == nil { + return nil + } + return bc.GetHeaderByBlockNumber(n) +} + +func (bc *BlockchainDB) GetBlockByHash(hash common.Hash) *types.Block { + return bc.makeBlock(bc.GetHeaderByHash(hash)) +} + +func (bc *BlockchainDB) GetBlockByNumber(blockNumber *big.Int) *types.Block { + return bc.makeBlock(bc.GetHeaderByBlockNumber(blockNumber)) +} + +func (bc *BlockchainDB) GetCurrentBlock() *types.Block { + return bc.GetBlockByNumber(bc.GetNumber()) +} + +func (bc *BlockchainDB) makeBlock(header *types.Header) *types.Block { + if header == nil { + return nil + } + tx := bc.GetTransactionByBlockNumber(header.Number) + if tx == nil { + return types.NewBlock( + header, + []*types.Transaction{}, + []*types.Header{}, + []*types.Receipt{}, + &fakeHasher{}, + ) + } + receipt := bc.GetReceiptByBlockNumber(header.Number) + return types.NewBlock( + header, + []*types.Transaction{tx}, + []*types.Header{}, + []*types.Receipt{receipt}, + &fakeHasher{}, + ) +} + +type fakeHasher struct{} + +func (d *fakeHasher) Reset() { +} + +func (d *fakeHasher) Update(i1, i2 []byte) { +} + +func (d *fakeHasher) Hash() common.Hash { + return common.Hash{} +} diff --git a/contracts/native/evm/evmlight/emulator/emulator.go b/contracts/native/evm/evmlight/emulator/emulator.go new file mode 100644 index 0000000000..38c09b70e0 --- /dev/null +++ b/contracts/native/evm/evmlight/emulator/emulator.go @@ -0,0 +1,486 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package emulator + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/iotaledger/wasp/packages/evm/evmtypes" + "github.com/iotaledger/wasp/packages/kv" + "github.com/iotaledger/wasp/packages/kv/subrealm" + "golang.org/x/xerrors" +) + +type EVMEmulator struct { + timestamp uint64 + chainConfig *params.ChainConfig + kv kv.KVStore + IEVMBackend vm.ISCPBackend +} + +func makeConfig(chainID int) *params.ChainConfig { + return ¶ms.ChainConfig{ + ChainID: big.NewInt(int64(chainID)), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + Ethash: ¶ms.EthashConfig{}, + } +} + +const ( + keyStateDB = "s" + keyBlockchainDB = "b" +) + +func newStateDB(store kv.KVStore) *StateDB { + return NewStateDB(subrealm.New(store, keyStateDB)) +} + +func newBlockchainDB(store kv.KVStore) *BlockchainDB { + return NewBlockchainDB(subrealm.New(store, keyBlockchainDB)) +} + +// Init initializes the EVM state with the provided genesis allocation parameters +func Init(store kv.KVStore, chainID uint16, gasLimit, timestamp uint64, alloc core.GenesisAlloc) { + bdb := newBlockchainDB(store) + if bdb.Initialized() { + panic("evm state already initialized in kvstore") + } + bdb.Init(chainID, gasLimit, timestamp) + + statedb := newStateDB(store) + for addr, account := range alloc { + statedb.CreateAccount(addr) + if account.Balance != nil { + statedb.AddBalance(addr, account.Balance) + } + if account.Code != nil { + statedb.SetCode(addr, account.Code) + } + for k, v := range account.Storage { + statedb.SetState(addr, k, v) + } + statedb.SetNonce(addr, account.Nonce) + } +} + +func NewEVMEmulator(store kv.KVStore, timestamp uint64, backend vm.ISCPBackend) *EVMEmulator { + bdb := newBlockchainDB(store) + if !bdb.Initialized() { + panic("must initialize genesis block first") + } + return &EVMEmulator{ + timestamp: timestamp, + chainConfig: makeConfig(int(bdb.GetChainID())), + kv: store, + IEVMBackend: backend, + } +} + +func (e *EVMEmulator) StateDB() *StateDB { + return newStateDB(e.kv) +} + +func (e *EVMEmulator) BlockchainDB() *BlockchainDB { + return newBlockchainDB(e.kv) +} + +func (e *EVMEmulator) GasLimit() uint64 { + return e.BlockchainDB().GetGasLimit() +} + +func newRevertError(result *core.ExecutionResult) *revertError { + reason, errUnpack := abi.UnpackRevert(result.Revert()) + err := xerrors.New("execution reverted") + if errUnpack == nil { + err = xerrors.Errorf("execution reverted: %v", reason) + } + return &revertError{ + error: err, + reason: hexutil.Encode(result.Revert()), + } +} + +// revertError is an API error that encompasses an EVM revert with JSON error +// code and a binary data blob. +type revertError struct { + error + reason string // revert reason hex encoded +} + +// ErrorCode returns the JSON error code for a revert. +// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal +func (e *revertError) ErrorCode() int { + return 3 +} + +// ErrorData returns the hex encoded revert reason. +func (e *revertError) ErrorData() interface{} { + return e.reason +} + +// CallContract executes a contract call, without committing changes to the state +func (e *EVMEmulator) CallContract(call ethereum.CallMsg) ([]byte, error) { + res, err := e.callContract(call) + if err != nil { + return nil, err + } + // If the result contains a revert reason, try to unpack and return it. + if len(res.Revert()) > 0 { + return nil, newRevertError(res) + } + return res.Return(), res.Err +} + +// EstimateGas executes the requested code against the current state and +// returns the used amount of gas, discarding state changes +func (e *EVMEmulator) EstimateGas(call ethereum.CallMsg) (uint64, error) { + // Determine the lowest and highest possible gas limits to binary search in between + var ( + lo uint64 = params.TxGas - 1 + hi uint64 + max uint64 + ) + if call.Gas >= params.TxGas { + hi = call.Gas + } else { + hi = e.GasLimit() + } + max = hi + + // Create a helper to check if a gas allowance results in an executable transaction + executable := func(gas uint64) (bool, *core.ExecutionResult, error) { + call.Gas = gas + res, err := e.callContract(call) + if err != nil { + if errors.Is(err, core.ErrIntrinsicGas) { + return true, nil, nil // Special case, raise gas limit + } + return true, nil, fmt.Errorf("Bail out: %w", err) + } + return res.Failed(), res, nil //nolint:gocritic + } + + // Execute the binary search and hone in on an executable gas limit + for lo+1 < hi { + mid := (hi + lo) / 2 + failed, _, err := executable(mid) + // If the error is not nil(consensus error), it means the provided message + // call or transaction will never be accepted no matter how much gas it is + // assigned. Return the error directly, don't struggle any more + if err != nil { + return 0, fmt.Errorf("executable(mid): %w", err) + } + if failed { + lo = mid + } else { + hi = mid + } + } + + // Reject the transaction as invalid if it still fails at the highest allowance + if hi == max { + failed, result, err := executable(hi) + if err != nil { + return 0, fmt.Errorf("executable(hi): %w", err) + } + if failed { + if result != nil && !errors.Is(result.Err, vm.ErrOutOfGas) { + if len(result.Revert()) > 0 { + return 0, newRevertError(result) + } + return 0, fmt.Errorf("revert: %w", result.Err) + } + // Otherwise, the specified gas cap is too low + return 0, fmt.Errorf("gas required exceeds allowance (%d)", max) + } + } + return hi, nil +} + +func (e *EVMEmulator) PendingHeader() *types.Header { + return &types.Header{ + Difficulty: &big.Int{}, + Number: new(big.Int).Add(e.BlockchainDB().GetNumber(), common.Big1), + GasLimit: e.GasLimit(), + Time: e.timestamp, + } +} + +func (e *EVMEmulator) ChainContext() core.ChainContext { + return &chainContext{ + engine: ethash.NewFaker(), + } +} + +func (e *EVMEmulator) callContract(call ethereum.CallMsg) (*core.ExecutionResult, error) { + // Ensure message is initialized properly. + if call.GasPrice == nil { + call.GasPrice = big.NewInt(0) + } + if call.Gas == 0 { + call.Gas = e.GasLimit() + } + if call.Value == nil { + call.Value = new(big.Int) + } + + msg := callMsg{call} + + // run the EVM code on a buffered state (so that writes are not committed) + statedb := e.StateDB().Buffered().StateDB() + + return e.applyMessage(msg, statedb) +} + +func (e *EVMEmulator) applyMessage(msg core.Message, statedb vm.StateDB) (*core.ExecutionResult, error) { + pendingHeader := e.PendingHeader() + blockContext := core.NewEVMBlockContext(pendingHeader, e.ChainContext(), nil) + txContext := core.NewEVMTxContext(msg) + vmEnv := vm.NewEVM(blockContext, txContext, statedb, e.chainConfig, e.vmConfig()) + gasPool := core.GasPool(msg.Gas()) + vmEnv.Reset(txContext, statedb) + return core.ApplyMessage(vmEnv, msg, &gasPool) +} + +func (e *EVMEmulator) vmConfig() vm.Config { + return vm.Config{ + JumpTable: vm.NewISCPInstructionSet(e.GetIEVMBackend), + } +} + +func (e *EVMEmulator) GetIEVMBackend() vm.ISCPBackend { + return e.IEVMBackend +} + +// SendTransaction updates the pending block to include the given transaction. +// It returns an error if the transaction is invalid. +func (e *EVMEmulator) SendTransaction(tx *types.Transaction) (*types.Receipt, error) { + sender, err := types.Sender(e.Signer(), tx) + if err != nil { + return nil, xerrors.Errorf("invalid transaction: %w", err) + } + nonce := e.StateDB().GetNonce(sender) + if tx.Nonce() != nonce { + return nil, xerrors.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce) + } + + buf := e.StateDB().Buffered() + + pendingHeader := e.PendingHeader() + msg, err := tx.AsMessage(types.MakeSigner(e.chainConfig, pendingHeader.Number), pendingHeader.BaseFee) + if err != nil { + return nil, err + } + + statedb := buf.StateDB() + + result, err := e.applyMessage(msg, statedb) + if err != nil { + return nil, err + } + + receipt := &types.Receipt{ + Type: tx.Type(), + CumulativeGasUsed: result.UsedGas, + TxHash: tx.Hash(), + GasUsed: result.UsedGas, + Logs: statedb.GetLogs(tx.Hash()), + BlockNumber: e.PendingHeader().Number, + } + receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + if result.Failed() { + fmt.Printf("%s - gas limit %d - gas used %d - refund %d\n", result.Err, msg.Gas(), result.UsedGas, statedb.GetRefund()) + receipt.Status = types.ReceiptStatusFailed + } else { + receipt.Status = types.ReceiptStatusSuccessful + } + if msg.To() == nil { + receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce()) + } + + buf.Commit() + + e.BlockchainDB().AddTransaction(tx, receipt, e.timestamp) + + return receipt, nil +} + +// FilterLogs executes a log filter operation, blocking during execution and +// returning all the results in one batch. +func (e *EVMEmulator) FilterLogs(query *ethereum.FilterQuery) []*types.Log { + receipts := e.getReceiptsInFilterRange(query) + return e.filterLogs(query, receipts) +} + +func (e *EVMEmulator) getReceiptsInFilterRange(query *ethereum.FilterQuery) []*types.Receipt { + bc := e.BlockchainDB() + + if query.BlockHash != nil { + blockNumber := bc.GetBlockNumberByBlockHash(*query.BlockHash) + if blockNumber == nil { + return nil + } + receipt := bc.GetReceiptByBlockNumber(blockNumber) + if receipt == nil { + return nil + } + return []*types.Receipt{receipt} + } + + // Initialize unset filter boundaries to run from genesis to chain head + first := big.NewInt(1) // skip genesis since it has no logs + last := bc.GetNumber() + from := first + if query.FromBlock != nil && query.FromBlock.Cmp(first) >= 0 && query.FromBlock.Cmp(last) <= 0 { + from = query.FromBlock + } + to := last + if query.ToBlock != nil && query.ToBlock.Cmp(first) >= 0 && query.ToBlock.Cmp(last) <= 0 { + to = query.ToBlock + } + + var receipts []*types.Receipt + for i := new(big.Int).Set(from); i.Cmp(to) <= 0; i = i.Add(i, common.Big1) { + receipt := bc.GetReceiptByBlockNumber(i) + if receipt != nil { + receipts = append(receipts, receipt) + } + } + return receipts +} + +func (e *EVMEmulator) filterLogs(query *ethereum.FilterQuery, receipts []*types.Receipt) []*types.Log { + var logs []*types.Log + for _, r := range receipts { + if !bloomFilter(r.Bloom, query.Addresses, query.Topics) { + continue + } + for _, log := range r.Logs { + if !logMatches(log, query.Addresses, query.Topics) { + continue + } + logs = append(logs, log) + } + } + return logs +} + +func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]common.Hash) bool { + if len(addresses) > 0 { + var included bool + for _, addr := range addresses { + if types.BloomLookup(bloom, addr) { + included = true + break + } + } + if !included { + return false + } + } + + for _, sub := range topics { + included := len(sub) == 0 // empty rule set == wildcard + for _, topic := range sub { + if types.BloomLookup(bloom, topic) { + included = true + break + } + } + if !included { + return false + } + } + return true +} + +func logMatches(log *types.Log, addresses []common.Address, topics [][]common.Hash) bool { + if len(addresses) > 0 { + found := false + for _, a := range addresses { + if log.Address == a { + found = true + } + } + if !found { + return false + } + } + if len(topics) > 0 { + if len(topics) > len(log.Topics) { + return false + } + for i, sub := range topics { + match := len(sub) == 0 // empty rule set == wildcard + for _, topic := range sub { + if log.Topics[i] == topic { + match = true + break + } + } + if !match { + return false + } + } + } + return true +} + +func (e *EVMEmulator) Signer() types.Signer { + return evmtypes.Signer(e.chainConfig.ChainID) +} + +// callMsg implements core.Message to allow passing it as a transaction simulator. +type callMsg struct { + ethereum.CallMsg +} + +func (m callMsg) From() common.Address { return m.CallMsg.From } +func (m callMsg) Nonce() uint64 { return 0 } +func (m callMsg) IsFake() bool { return true } +func (m callMsg) To() *common.Address { return m.CallMsg.To } +func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap } +func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap } +func (m callMsg) Gas() uint64 { return m.CallMsg.Gas } +func (m callMsg) Value() *big.Int { return m.CallMsg.Value } +func (m callMsg) Data() []byte { return m.CallMsg.Data } +func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList } + +type chainContext struct { + engine consensus.Engine +} + +var _ core.ChainContext = &chainContext{} + +func (c *chainContext) Engine() consensus.Engine { + return c.engine +} + +func (c *chainContext) GetHeader(common.Hash, uint64) *types.Header { + panic("not implemented") +} diff --git a/contracts/native/evm/evmlight/emulator/emulator_test.go b/contracts/native/evm/evmlight/emulator/emulator_test.go new file mode 100644 index 0000000000..f45f0df61c --- /dev/null +++ b/contracts/native/evm/evmlight/emulator/emulator_test.go @@ -0,0 +1,495 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package emulator + +import ( + "crypto/ecdsa" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/evm/evmtest" + "github.com/iotaledger/wasp/packages/kv" + "github.com/iotaledger/wasp/packages/kv/dict" + "github.com/stretchr/testify/require" +) + +func estimateGas(t testing.TB, emu *EVMEmulator, from common.Address, to *common.Address, value *big.Int, data []byte) uint64 { + gas, err := emu.EstimateGas(ethereum.CallMsg{ + From: from, + To: to, + Value: value, + Data: data, + }) + if err != nil { + t.Logf("%v", err) + return evm.GasLimitDefault - 1 + } + return gas +} + +func sendTransaction(t testing.TB, emu *EVMEmulator, sender *ecdsa.PrivateKey, receiverAddress common.Address, amount *big.Int, data []byte) *types.Receipt { + senderAddress := crypto.PubkeyToAddress(sender.PublicKey) + + nonce := emu.StateDB().GetNonce(senderAddress) + gas := estimateGas(t, emu, senderAddress, &receiverAddress, amount, data) + + tx, err := types.SignTx( + types.NewTransaction(nonce, receiverAddress, amount, gas, evm.GasPrice, data), + emu.Signer(), + sender, + ) + require.NoError(t, err) + + receipt, err := emu.SendTransaction(tx) + require.NoError(t, err) + + return receipt +} + +func TestBlockchain(t *testing.T) { + // faucet address with initial supply + faucet, err := crypto.GenerateKey() + require.NoError(t, err) + faucetAddress := crypto.PubkeyToAddress(faucet.PublicKey) + faucetSupply := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)) + + // another account + receiver, err := crypto.GenerateKey() + require.NoError(t, err) + receiverAddress := crypto.PubkeyToAddress(receiver.PublicKey) + + genesisAlloc := map[common.Address]core.GenesisAccount{ + faucetAddress: {Balance: faucetSupply}, + } + + db := dict.Dict{} + Init(db, evm.DefaultChainID, evm.GasLimitDefault, 0, genesisAlloc) + emu := NewEVMEmulator(db, 1, &iscpBackend{}) + + // some assertions + { + require.EqualValues(t, evm.GasLimitDefault, emu.BlockchainDB().GetGasLimit()) + require.EqualValues(t, evm.DefaultChainID, emu.BlockchainDB().GetChainID()) + + genesis := emu.BlockchainDB().GetBlockByNumber(common.Big0) + require.NotNil(t, genesis) + + // the genesis block has block number 0 + require.EqualValues(t, 0, genesis.NumberU64()) + // the genesis block has 0 transactions + require.EqualValues(t, 0, genesis.Transactions().Len()) + + { + // assert that current block is genesis + block := emu.BlockchainDB().GetCurrentBlock() + require.NotNil(t, block) + require.EqualValues(t, evm.GasLimitDefault, block.Header().GasLimit) + } + + { + // same, getting the block by hash + genesis2 := emu.BlockchainDB().GetBlockByHash(genesis.Hash()) + require.NotNil(t, genesis2) + require.EqualValues(t, genesis.Hash(), genesis2.Hash()) + } + + { + state := emu.StateDB() + // check the balance of the faucet address + require.EqualValues(t, faucetSupply, state.GetBalance(faucetAddress)) + require.EqualValues(t, big.NewInt(0), state.GetBalance(receiverAddress)) + } + } + + transferAmount := big.NewInt(1000) + + // send a transaction transferring 1000 ETH to receiverAddress + { + receipt := sendTransaction(t, emu, faucet, receiverAddress, transferAmount, nil) + + require.EqualValues(t, 1, emu.BlockchainDB().GetNumber().Uint64()) + block := emu.BlockchainDB().GetCurrentBlock() + require.EqualValues(t, 1, block.Header().Number.Uint64()) + require.EqualValues(t, evm.GasLimitDefault, block.Header().GasLimit) + require.EqualValues(t, receipt.Bloom, block.Bloom()) + require.EqualValues(t, receipt.GasUsed, block.GasUsed()) + require.EqualValues(t, emu.BlockchainDB().GetBlockByNumber(common.Big0).Hash(), block.ParentHash()) + } + + { + state := emu.StateDB() + // check the new balances + require.EqualValues(t, (&big.Int{}).Sub(faucetSupply, transferAmount), state.GetBalance(faucetAddress)) + require.EqualValues(t, transferAmount, state.GetBalance(receiverAddress)) + } +} + +func TestBlockchainPersistence(t *testing.T) { + // faucet address with initial supply + faucet, err := crypto.GenerateKey() + require.NoError(t, err) + faucetAddress := crypto.PubkeyToAddress(faucet.PublicKey) + faucetSupply := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)) + + genesisAlloc := map[common.Address]core.GenesisAccount{ + faucetAddress: {Balance: faucetSupply}, + } + + // another account + receiver, err := crypto.GenerateKey() + require.NoError(t, err) + receiverAddress := crypto.PubkeyToAddress(receiver.PublicKey) + transferAmount := big.NewInt(1000) + + db := dict.Dict{} + Init(db, evm.DefaultChainID, evm.GasLimitDefault, 0, genesisAlloc) + + // do a transfer using one instance of EVMEmulator + func() { + emu := NewEVMEmulator(db, 1, &iscpBackend{}) + sendTransaction(t, emu, faucet, receiverAddress, transferAmount, nil) + }() + + // initialize a new EVMEmulator using the same DB and check the state + { + emu := NewEVMEmulator(db, 2, &iscpBackend{}) + state := emu.StateDB() + // check the new balances + require.EqualValues(t, (&big.Int{}).Sub(faucetSupply, transferAmount), state.GetBalance(faucetAddress)) + require.EqualValues(t, transferAmount, state.GetBalance(receiverAddress)) + } +} + +type contractFnCaller func(sender *ecdsa.PrivateKey, name string, args ...interface{}) *types.Receipt + +func deployEVMContract(t testing.TB, emu *EVMEmulator, creator *ecdsa.PrivateKey, contractABI abi.ABI, contractBytecode []byte, args ...interface{}) (common.Address, contractFnCaller) { + creatorAddress := crypto.PubkeyToAddress(creator.PublicKey) + + nonce := emu.StateDB().GetNonce(creatorAddress) + + txValue := big.NewInt(0) + + // initialize number as 42 + constructorArguments, err := contractABI.Pack("", args...) + require.NoError(t, err) + require.NotEmpty(t, constructorArguments) + + data := []byte{} + data = append(data, contractBytecode...) + data = append(data, constructorArguments...) + + gas := estimateGas(t, emu, creatorAddress, nil, txValue, data) + require.NoError(t, err) + + tx, err := types.SignTx( + types.NewContractCreation(nonce, txValue, gas, evm.GasPrice, data), + emu.Signer(), + creator, + ) + require.NoError(t, err) + + receipt, err := emu.SendTransaction(tx) + require.NoError(t, err) + + contractAddress := crypto.CreateAddress(creatorAddress, nonce) + + // assertions + { + require.EqualValues(t, 1, emu.BlockchainDB().GetNumber().Uint64()) + + // verify contract address + { + require.EqualValues(t, contractAddress, receipt.ContractAddress) + require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) + } + + // verify contract code + { + code := emu.StateDB().GetCode(contractAddress) + require.NotEmpty(t, code) + } + } + + callFn := func(sender *ecdsa.PrivateKey, name string, args ...interface{}) *types.Receipt { + callArguments, err := contractABI.Pack(name, args...) + require.NoError(t, err) + receipt := sendTransaction(t, emu, sender, contractAddress, big.NewInt(0), callArguments) + t.Logf("callFn %s Status: %d", name, receipt.Status) + t.Logf("Logs:") + for _, log := range receipt.Logs { + t.Logf(" - %s %x", log.Address, log.Data) + ev, err := contractABI.EventByID(log.Topics[0]) + if err != nil { + t.Logf(" - log is not an event: %+v", log.Topics) + } else { + t.Logf(" - %s %+v", ev, log.Topics[1:]) + } + } + return receipt + } + + return contractAddress, contractFnCaller(callFn) +} + +func TestStorageContract(t *testing.T) { + // faucet address with initial supply + faucet, err := crypto.GenerateKey() + require.NoError(t, err) + faucetAddress := crypto.PubkeyToAddress(faucet.PublicKey) + faucetSupply := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)) + + genesisAlloc := map[common.Address]core.GenesisAccount{ + faucetAddress: {Balance: faucetSupply}, + } + + db := dict.Dict{} + Init(db, evm.DefaultChainID, evm.GasLimitDefault, 0, genesisAlloc) + emu := NewEVMEmulator(db, 1, &iscpBackend{}) + + contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) + require.NoError(t, err) + + contractAddress, callFn := deployEVMContract( + t, + emu, + faucet, + contractABI, + evmtest.StorageContractBytecode, + uint32(42), + ) + + // call `retrieve` view, get 42 + { + callArguments, err := contractABI.Pack("retrieve") + require.NoError(t, err) + require.NotEmpty(t, callArguments) + + res, err := emu.CallContract(ethereum.CallMsg{To: &contractAddress, Data: callArguments}) + require.NoError(t, err) + require.NotEmpty(t, res) + + var v uint32 + err = contractABI.UnpackIntoInterface(&v, "retrieve", res) + require.NoError(t, err) + require.EqualValues(t, 42, v) + + // no state change + require.EqualValues(t, 1, emu.BlockchainDB().GetNumber().Uint64()) + } + + // send tx that calls `store(43)` + { + callFn(faucet, "store", uint32(43)) + require.EqualValues(t, 2, emu.BlockchainDB().GetNumber().Uint64()) + } + + // call `retrieve` view again, get 43 + { + callArguments, err := contractABI.Pack("retrieve") + require.NoError(t, err) + + res, err := emu.CallContract(ethereum.CallMsg{ + To: &contractAddress, + Data: callArguments, + }) + require.NoError(t, err) + require.NotEmpty(t, res) + + var v uint32 + err = contractABI.UnpackIntoInterface(&v, "retrieve", res) + require.NoError(t, err) + require.EqualValues(t, 43, v) + + // no state change + require.EqualValues(t, 2, emu.BlockchainDB().GetNumber().Uint64()) + } +} + +func TestERC20Contract(t *testing.T) { + // faucet address with initial supply + faucet, err := crypto.GenerateKey() + require.NoError(t, err) + faucetAddress := crypto.PubkeyToAddress(faucet.PublicKey) + faucetSupply := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)) + + genesisAlloc := map[common.Address]core.GenesisAccount{ + faucetAddress: {Balance: faucetSupply}, + } + + db := dict.Dict{} + Init(db, evm.DefaultChainID, evm.GasLimitDefault, 0, genesisAlloc) + emu := NewEVMEmulator(db, 1, &iscpBackend{}) + + contractABI, err := abi.JSON(strings.NewReader(evmtest.ERC20ContractABI)) + require.NoError(t, err) + + erc20Owner, err := crypto.GenerateKey() + require.NoError(t, err) + erc20OwnerAddress := crypto.PubkeyToAddress(erc20Owner.PublicKey) + + contractAddress, callFn := deployEVMContract( + t, + emu, + erc20Owner, + contractABI, + evmtest.ERC20ContractBytecode, + "TestCoin", + "TEST", + ) + + callIntViewFn := func(name string, args ...interface{}) *big.Int { + callArguments, err := contractABI.Pack(name, args...) + require.NoError(t, err) + + res, err := emu.CallContract(ethereum.CallMsg{To: &contractAddress, Data: callArguments}) + require.NoError(t, err) + + v := new(big.Int) + err = contractABI.UnpackIntoInterface(&v, name, res) + require.NoError(t, err) + return v + } + + // call `totalSupply` view + { + v := callIntViewFn("totalSupply") + // 100 * 10^18 + expected := new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) + require.Zero(t, v.Cmp(expected)) + } + + recipient, err := crypto.GenerateKey() + require.NoError(t, err) + recipientAddress := crypto.PubkeyToAddress(recipient.PublicKey) + + transferAmount := big.NewInt(1337) + + // call `transfer` => send 1337 TestCoin to recipientAddress + receipt := callFn(erc20Owner, "transfer", recipientAddress, transferAmount) + require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) + require.Equal(t, 1, len(receipt.Logs)) + + // call `balanceOf` view => check balance of recipient = 1337 TestCoin + require.Zero(t, callIntViewFn("balanceOf", recipientAddress).Cmp(transferAmount)) + + // call `transferFrom` as recipient without allowance => get error + receipt = callFn(recipient, "transferFrom", erc20OwnerAddress, recipientAddress, transferAmount) + require.Equal(t, types.ReceiptStatusFailed, receipt.Status) + require.Equal(t, 0, len(receipt.Logs)) + + // call `balanceOf` view => check balance of recipient = 1337 TestCoin + require.Zero(t, callIntViewFn("balanceOf", recipientAddress).Cmp(transferAmount)) + + // call `approve` as erc20Owner + receipt = callFn(erc20Owner, "approve", recipientAddress, transferAmount) + require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) + require.Equal(t, 1, len(receipt.Logs)) + + // call `transferFrom` as recipient with allowance => ok + receipt = callFn(recipient, "transferFrom", erc20OwnerAddress, recipientAddress, transferAmount) + require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) + require.Equal(t, 1, len(receipt.Logs)) + + // call `balanceOf` view => check balance of recipient = 2 * 1337 TestCoin + require.Zero(t, callIntViewFn("balanceOf", recipientAddress).Cmp(new(big.Int).Mul(transferAmount, big.NewInt(2)))) +} + +// TODO: test a contract calling selfdestruct + +func initBenchmark(b *testing.B) (*EVMEmulator, []*types.Transaction, dict.Dict) { + // faucet address with initial supply + faucet, err := crypto.GenerateKey() + require.NoError(b, err) + faucetAddress := crypto.PubkeyToAddress(faucet.PublicKey) + faucetSupply := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)) + + genesisAlloc := map[common.Address]core.GenesisAccount{ + faucetAddress: {Balance: faucetSupply}, + } + + db := dict.Dict{} + Init(db, evm.DefaultChainID, evm.GasLimitDefault, 0, genesisAlloc) + emu := NewEVMEmulator(db, 1, &iscpBackend{}) + + contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) + require.NoError(b, err) + + contractAddress, _ := deployEVMContract( + b, + emu, + faucet, + contractABI, + evmtest.StorageContractBytecode, + uint32(42), + ) + + txs := make([]*types.Transaction, b.N) + for i := 0; i < b.N; i++ { + sender, err := crypto.GenerateKey() // send from a new address so that nonce is always 0 + require.NoError(b, err) + + amount := big.NewInt(0) + nonce := uint64(0) + + callArguments, err := contractABI.Pack("store", uint32(i)) + require.NoError(b, err) + + gas := evm.GasLimitDefault + + txs[i], err = types.SignTx( + types.NewTransaction(nonce, contractAddress, amount, gas, evm.GasPrice, callArguments), + emu.Signer(), + sender, + ) + require.NoError(b, err) + } + + return emu, txs, db +} + +// BenchmarkEVMEmulator is a benchmark for the EVMEmulator that sends N EVM transactions. +// +// run with: go test -benchmem -cpu=1 -run=' ' -bench='Bench.*' +// +// To generate mem and cpu profiles, add -cpuprofile=cpu.out -memprofile=mem.out +// Then: go tool pprof -http :8080 {cpu,mem}.out +func BenchmarkEVMEmulator(b *testing.B) { + // setup: deploy the storage contract and prepare N transactions to send + emu, txs, db := initBenchmark(b) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + receipt, err := emu.SendTransaction(txs[i]) + require.NoError(b, err) + require.Equal(b, types.ReceiptStatusSuccessful, receipt.Status) + } + + b.ReportMetric(dbSize(db)/float64(b.N), "db:bytes/op") +} + +func dbSize(db kv.KVStore) float64 { + r := float64(0) + db.MustIterate("", func(key kv.Key, value []byte) bool { + r += float64(len(key) + len(value)) + return true + }) + return r +} + +type iscpBackend struct{} + +var _ vm.ISCPBackend = &iscpBackend{} + +func (i *iscpBackend) Event(s string) {} + +func (i *iscpBackend) Entropy() [32]byte { return [32]byte{} } diff --git a/contracts/native/evm/evmlight/emulator/statedb.go b/contracts/native/evm/evmlight/emulator/statedb.go new file mode 100644 index 0000000000..dd066c25bc --- /dev/null +++ b/contracts/native/evm/evmlight/emulator/statedb.go @@ -0,0 +1,246 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package emulator + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/iotaledger/wasp/packages/kv" + "github.com/iotaledger/wasp/packages/kv/buffered" + "github.com/iotaledger/wasp/packages/kv/codec" +) + +const ( + keyAccountBalance = "a" + keyAccountNonce = "n" + keyAccountCode = "c" + keyAccountState = "s" +) + +func accountKey(prefix kv.Key, addr common.Address) kv.Key { + return prefix + kv.Key(addr.Bytes()) +} + +func accountBalanceKey(addr common.Address) kv.Key { + return accountKey(keyAccountBalance, addr) +} + +func accountNonceKey(addr common.Address) kv.Key { + return accountKey(keyAccountNonce, addr) +} + +func accountCodeKey(addr common.Address) kv.Key { + return accountKey(keyAccountCode, addr) +} + +func accountStateKey(addr common.Address, hash common.Hash) kv.Key { + return accountKey(keyAccountState, addr) + kv.Key(hash[:]) +} + +// StateDB implements vm.StateDB with a kv.KVStore as backend +type StateDB struct { + kv kv.KVStore + logs []*types.Log + refund uint64 +} + +var _ vm.StateDB = &StateDB{} + +func NewStateDB(store kv.KVStore) *StateDB { + return &StateDB{kv: store} +} + +func (s *StateDB) CreateAccount(addr common.Address) { + s.setAccountBalance(addr, big.NewInt(0)) + s.SetNonce(addr, 0) +} + +func (s *StateDB) setAccountBalance(addr common.Address, amount *big.Int) { + s.kv.Set(accountBalanceKey(addr), amount.Bytes()) +} + +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { + if amount.Sign() == 0 { + return + } + s.setAccountBalance(addr, new(big.Int).Sub(s.GetBalance(addr), amount)) +} + +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { + if amount.Sign() == 0 { + return + } + s.setAccountBalance(addr, new(big.Int).Add(s.GetBalance(addr), amount)) +} + +func (s *StateDB) GetBalance(addr common.Address) *big.Int { + r := new(big.Int) + r.SetBytes(s.kv.MustGet(accountBalanceKey(addr))) + return r +} + +func (s *StateDB) GetNonce(addr common.Address) uint64 { + n, err := codec.DecodeUint64(s.kv.MustGet(accountNonceKey(addr)), 0) + if err != nil { + panic(err) + } + return n +} + +func (s *StateDB) SetNonce(addr common.Address, n uint64) { + s.kv.Set(accountNonceKey(addr), codec.EncodeUint64(n)) +} + +func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { + // TODO cache the code hash? + return crypto.Keccak256Hash(s.GetCode(addr)) +} + +func (s *StateDB) GetCode(addr common.Address) []byte { + return s.kv.MustGet(accountCodeKey(addr)) +} + +func (s *StateDB) SetCode(addr common.Address, code []byte) { + if code == nil { + s.kv.Del(accountCodeKey(addr)) + } else { + s.kv.Set(accountCodeKey(addr), code) + } +} + +func (s *StateDB) GetCodeSize(addr common.Address) int { + // TODO cache the code size? + return len(s.GetCode(addr)) +} + +func (s *StateDB) AddRefund(n uint64) { + s.refund += n +} + +func (s *StateDB) SubRefund(n uint64) { + if n > s.refund { + panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", n, s.refund)) + } + s.refund -= n +} + +func (s *StateDB) GetRefund() uint64 { + return s.refund +} + +func (s *StateDB) GetCommittedState(addr common.Address, key common.Hash) common.Hash { + return s.GetState(addr, key) +} + +func (s *StateDB) GetState(addr common.Address, key common.Hash) common.Hash { + return common.BytesToHash(s.kv.MustGet(accountStateKey(addr, key))) +} + +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { + s.kv.Set(accountStateKey(addr, key), value.Bytes()) +} + +func (s *StateDB) Suicide(addr common.Address) bool { + if !s.Exist(addr) { + return false + } + + s.kv.Del(accountBalanceKey(addr)) + s.kv.Del(accountNonceKey(addr)) + s.kv.Del(accountCodeKey(addr)) + + keys := make([]kv.Key, 0) + s.kv.MustIterateKeys(accountKey(keyAccountState, addr), func(key kv.Key) bool { + keys = append(keys, key) + return true + }) + for _, k := range keys { + s.kv.Del(k) + } + + return true +} + +func (s *StateDB) HasSuicided(common.Address) bool { return false } + +// Exist reports whether the given account exists in state. +// Notably this should also return true for suicided accounts. +func (s *StateDB) Exist(addr common.Address) bool { + return s.kv.MustHas(accountBalanceKey(addr)) +} + +// Empty returns whether the given account is empty. Empty +// is defined according to EIP161 (balance = nonce = code = 0). +func (s *StateDB) Empty(addr common.Address) bool { + return s.GetNonce(addr) == 0 && s.GetBalance(addr).Sign() == 0 && s.GetCodeSize(addr) == 0 +} + +func (s *StateDB) PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) { +} + +func (s *StateDB) AddressInAccessList(addr common.Address) bool { + return true +} + +func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk, slotOk bool) { + return true, true +} + +// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform +// even if the feature/fork is not active yet +func (s *StateDB) AddAddressToAccessList(addr common.Address) {} + +// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform +// even if the feature/fork is not active yet +func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { +} + +func (s *StateDB) RevertToSnapshot(int) {} +func (s *StateDB) Snapshot() int { return 0 } + +func (s *StateDB) AddLog(log *types.Log) { + log.Index = uint(len(s.logs)) + s.logs = append(s.logs, log) +} + +func (s *StateDB) GetLogs(hash common.Hash) []*types.Log { + return s.logs +} + +func (s *StateDB) AddPreimage(common.Hash, []byte) { panic("not implemented") } + +func (s *StateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error { + panic("not implemented") +} + +func (s *StateDB) Buffered() *BufferedStateDB { + return NewBufferedStateDB(s) +} + +// BufferedStateDB is a wrapper for StateDB that writes all mutations into an in-memory buffer, +// leaving the original state unmodified until the mutations are applied manually with Commit(). +type BufferedStateDB struct { + buf *buffered.BufferedKVStoreAccess + base kv.KVStore +} + +func NewBufferedStateDB(base *StateDB) *BufferedStateDB { + return &BufferedStateDB{ + buf: buffered.NewBufferedKVStoreAccess(base.kv), + base: base.kv, + } +} + +func (b *BufferedStateDB) StateDB() *StateDB { + return &StateDB{kv: b.buf} +} + +func (b *BufferedStateDB) Commit() { + b.buf.Mutations().ApplyTo(b.base) +} diff --git a/contracts/native/evm/evmlight/impl.go b/contracts/native/evm/evmlight/impl.go new file mode 100644 index 0000000000..087f6f460d --- /dev/null +++ b/contracts/native/evm/evmlight/impl.go @@ -0,0 +1,179 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmlight + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evminternal" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight/emulator" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight/iscpcontract" + "github.com/iotaledger/wasp/packages/evm/evmtypes" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/assert" + "github.com/iotaledger/wasp/packages/kv/codec" + "github.com/iotaledger/wasp/packages/kv/dict" +) + +var Processor = Contract.Processor(initialize, append( + evminternal.ManagementHandlers, + + evm.FuncSendTransaction.WithHandler(applyTransaction), + evm.FuncGetBalance.WithHandler(getBalance), + evm.FuncCallContract.WithHandler(callContract), + evm.FuncEstimateGas.WithHandler(estimateGas), + evm.FuncGetNonce.WithHandler(getNonce), + evm.FuncGetReceipt.WithHandler(getReceipt), + evm.FuncGetCode.WithHandler(getCode), + evm.FuncGetBlockNumber.WithHandler(getBlockNumber), + evm.FuncGetBlockByNumber.WithHandler(getBlockByNumber), + evm.FuncGetBlockByHash.WithHandler(getBlockByHash), + evm.FuncGetTransactionByHash.WithHandler(getTransactionByHash), + evm.FuncGetTransactionByBlockHashAndIndex.WithHandler(getTransactionByBlockHashAndIndex), + evm.FuncGetTransactionByBlockNumberAndIndex.WithHandler(getTransactionByBlockNumberAndIndex), + evm.FuncGetTransactionCountByBlockHash.WithHandler(getTransactionCountByBlockHash), + evm.FuncGetTransactionCountByBlockNumber.WithHandler(getTransactionCountByBlockNumber), + evm.FuncGetStorage.WithHandler(getStorage), + evm.FuncGetLogs.WithHandler(getLogs), +)...) + +func initialize(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + genesisAlloc, err := evmtypes.DecodeGenesisAlloc(ctx.Params().MustGet(evm.FieldGenesisAlloc)) + a.RequireNoError(err) + + // add the standard ISCP contract at arbitrary address 0x1074 + iscpcontract.DeployOnGenesis(genesisAlloc, ctx.ChainID()) + + chainID, err := codec.DecodeUint16(ctx.Params().MustGet(evm.FieldChainID), evm.DefaultChainID) + a.RequireNoError(err) + emulator.Init( + evmStateSubrealm(ctx.State()), + chainID, + evm.GasLimitDefault, // TODO: make gas limit configurable + timestamp(ctx), + genesisAlloc, + ) + evminternal.InitializeManagement(ctx) + return nil, nil +} + +func applyTransaction(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + + tx := &types.Transaction{} + err := tx.UnmarshalBinary(ctx.Params().MustGet(evm.FieldTransactionData)) + a.RequireNoError(err) + + return evminternal.RequireGasFee(ctx, tx.Gas(), func() uint64 { + emu := createEmulator(ctx) + receipt, err := emu.SendTransaction(tx) + a.RequireNoError(err) + return receipt.GasUsed + }), nil +} + +func getBalance(ctx iscp.SandboxView) (dict.Dict, error) { + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + emu := createEmulatorR(ctx) + _ = paramBlockNumberOrHashAsNumber(ctx, emu, false) + return evminternal.Result(emu.StateDB().GetBalance(addr).Bytes()), nil +} + +func getBlockNumber(ctx iscp.SandboxView) (dict.Dict, error) { + emu := createEmulatorR(ctx) + return evminternal.Result(emu.BlockchainDB().GetNumber().Bytes()), nil +} + +func getBlockByNumber(ctx iscp.SandboxView) (dict.Dict, error) { + return blockResult(blockByNumber(ctx)) +} + +func getBlockByHash(ctx iscp.SandboxView) (dict.Dict, error) { + return blockResult(blockByHash(ctx)) +} + +func getTransactionByHash(ctx iscp.SandboxView) (dict.Dict, error) { + return txResult(transactionByHash(ctx)) +} + +func getTransactionByBlockHashAndIndex(ctx iscp.SandboxView) (dict.Dict, error) { + return txResult(transactionByBlockHashAndIndex(ctx)) +} + +func getTransactionByBlockNumberAndIndex(ctx iscp.SandboxView) (dict.Dict, error) { + return txResult(transactionByBlockNumberAndIndex(ctx)) +} + +func getTransactionCountByBlockHash(ctx iscp.SandboxView) (dict.Dict, error) { + return txCountResult(blockByHash(ctx)) +} + +func getTransactionCountByBlockNumber(ctx iscp.SandboxView) (dict.Dict, error) { + return txCountResult(blockByNumber(ctx)) +} + +func getReceipt(ctx iscp.SandboxView) (dict.Dict, error) { + txHash := common.BytesToHash(ctx.Params().MustGet(evm.FieldTransactionHash)) + emu := createEmulatorR(ctx) + r := emu.BlockchainDB().GetReceiptByTxHash(txHash) + if r == nil { + return nil, nil + } + return evminternal.Result(evmtypes.EncodeReceiptFull(r)), nil +} + +func getNonce(ctx iscp.SandboxView) (dict.Dict, error) { + emu := createEmulatorR(ctx) + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + _ = paramBlockNumberOrHashAsNumber(ctx, emu, false) + return evminternal.Result(codec.EncodeUint64(emu.StateDB().GetNonce(addr))), nil +} + +func getCode(ctx iscp.SandboxView) (dict.Dict, error) { + emu := createEmulatorR(ctx) + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + _ = paramBlockNumberOrHashAsNumber(ctx, emu, false) + return evminternal.Result(emu.StateDB().GetCode(addr)), nil +} + +func getStorage(ctx iscp.SandboxView) (dict.Dict, error) { + emu := createEmulatorR(ctx) + addr := common.BytesToAddress(ctx.Params().MustGet(evm.FieldAddress)) + key := common.BytesToHash(ctx.Params().MustGet(evm.FieldKey)) + _ = paramBlockNumberOrHashAsNumber(ctx, emu, false) + data := emu.StateDB().GetState(addr, key) + return evminternal.Result(data[:]), nil +} + +func getLogs(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + q, err := evmtypes.DecodeFilterQuery(ctx.Params().MustGet(evm.FieldFilterQuery)) + a.RequireNoError(err) + emu := createEmulatorR(ctx) + logs := emu.FilterLogs(q) + return evminternal.Result(evmtypes.EncodeLogs(logs)), nil +} + +func callContract(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + callMsg, err := evmtypes.DecodeCallMsg(ctx.Params().MustGet(evm.FieldCallMsg)) + a.RequireNoError(err) + emu := createEmulatorR(ctx) + _ = paramBlockNumberOrHashAsNumber(ctx, emu, false) + res, err := emu.CallContract(callMsg) + a.RequireNoError(err) + return evminternal.Result(res), nil +} + +func estimateGas(ctx iscp.SandboxView) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + callMsg, err := evmtypes.DecodeCallMsg(ctx.Params().MustGet(evm.FieldCallMsg)) + a.RequireNoError(err) + emu := createEmulatorR(ctx) + gas, err := emu.EstimateGas(callMsg) + a.RequireNoError(err) + return evminternal.Result(codec.EncodeUint64(gas)), nil +} diff --git a/contracts/native/evm/evmlight/interface.go b/contracts/native/evm/evmlight/interface.go new file mode 100644 index 0000000000..3829f79296 --- /dev/null +++ b/contracts/native/evm/evmlight/interface.go @@ -0,0 +1,9 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// package evmlight provides the `evmlight` contract, which allows to run EVM code +package evmlight + +import "github.com/iotaledger/wasp/packages/iscp/coreutil" + +var Contract = coreutil.NewContract("evmlight", "evmlight smart contract") diff --git a/contracts/native/evm/evmlight/internal.go b/contracts/native/evm/evmlight/internal.go new file mode 100644 index 0000000000..67579d4e00 --- /dev/null +++ b/contracts/native/evm/evmlight/internal.go @@ -0,0 +1,181 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmlight + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evminternal" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight/emulator" + "github.com/iotaledger/wasp/packages/evm/evmtypes" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/assert" + "github.com/iotaledger/wasp/packages/kv" + "github.com/iotaledger/wasp/packages/kv/buffered" + "github.com/iotaledger/wasp/packages/kv/codec" + "github.com/iotaledger/wasp/packages/kv/dict" + "github.com/iotaledger/wasp/packages/kv/subrealm" +) + +// keyEVMState is the subrealm prefix for the EVM state +// If changed, make sure it does not collide with evm management keys +const keyEVMState = "s" + +func evmStateSubrealm(state kv.KVStore) kv.KVStore { + return subrealm.New(state, keyEVMState) +} + +func createEmulator(ctx iscp.Sandbox) *emulator.EVMEmulator { + return emulator.NewEVMEmulator(evmStateSubrealm(ctx.State()), timestamp(ctx), &iscpBackend{ctx}) +} + +func createEmulatorR(ctx iscp.SandboxView) *emulator.EVMEmulator { + return emulator.NewEVMEmulator(evmStateSubrealm(buffered.NewBufferedKVStoreAccess(ctx.State())), timestamp(ctx), &iscpBackendR{ctx}) +} + +// timestamp returns the current timestamp in seconds since epoch +func timestamp(ctx iscp.SandboxBase) uint64 { + tsNano := time.Duration(ctx.GetTimestamp()) * time.Nanosecond + return uint64(tsNano / time.Second) +} + +func blockResult(emu *emulator.EVMEmulator, block *types.Block) (dict.Dict, error) { + if block == nil { + return nil, nil + } + return evminternal.Result(evmtypes.EncodeBlock(block)), nil +} + +func txResult(emu *emulator.EVMEmulator, tx *types.Transaction) (dict.Dict, error) { + if tx == nil { + return nil, nil + } + bc := emu.BlockchainDB() + blockNumber := bc.GetBlockNumberByTxHash(tx.Hash()) + return dict.Dict{ + evm.FieldTransaction: evmtypes.EncodeTransaction(tx), + evm.FieldBlockHash: bc.GetBlockHashByBlockNumber(blockNumber).Bytes(), + evm.FieldBlockNumber: codec.EncodeUint64(blockNumber.Uint64()), + }, nil +} + +func txCountResult(emu *emulator.EVMEmulator, block *types.Block) (dict.Dict, error) { + if block == nil { + return nil, nil + } + n := uint64(0) + if block.NumberU64() != 0 { + n = 1 + } + return evminternal.Result(codec.EncodeUint64(n)), nil +} + +func blockByNumber(ctx iscp.SandboxView) (*emulator.EVMEmulator, *types.Block) { + emu := createEmulatorR(ctx) + blockNumber := paramBlockNumber(ctx, emu, true) + return emu, emu.BlockchainDB().GetBlockByNumber(blockNumber) +} + +func blockByHash(ctx iscp.SandboxView) (*emulator.EVMEmulator, *types.Block) { + emu := createEmulatorR(ctx) + hash := common.BytesToHash(ctx.Params().MustGet(evm.FieldBlockHash)) + return emu, emu.BlockchainDB().GetBlockByHash(hash) +} + +func transactionByHash(ctx iscp.SandboxView) (*emulator.EVMEmulator, *types.Transaction) { + emu := createEmulatorR(ctx) + txHash := common.BytesToHash(ctx.Params().MustGet(evm.FieldTransactionHash)) + return emu, emu.BlockchainDB().GetTransactionByHash(txHash) +} + +func transactionByBlockHashAndIndex(ctx iscp.SandboxView) (*emulator.EVMEmulator, *types.Transaction) { + emu := createEmulatorR(ctx) + blockHash := common.BytesToHash(ctx.Params().MustGet(evm.FieldBlockHash)) + + a := assert.NewAssert(ctx.Log()) + index, err := codec.DecodeUint64(ctx.Params().MustGet(evm.FieldTransactionIndex), 0) + a.RequireNoError(err) + // all blocks contain at most 1 tx + if index > 0 { + return emu, nil + } + + bc := emu.BlockchainDB() + blockNumber := bc.GetBlockNumberByBlockHash(blockHash) + if blockNumber == nil { + return emu, nil + } + return emu, bc.GetTransactionByBlockNumber(blockNumber) +} + +func transactionByBlockNumberAndIndex(ctx iscp.SandboxView) (*emulator.EVMEmulator, *types.Transaction) { + emu := createEmulatorR(ctx) + blockNumber := paramBlockNumber(ctx, emu, true) + + a := assert.NewAssert(ctx.Log()) + index, err := codec.DecodeUint64(ctx.Params().MustGet(evm.FieldTransactionIndex), 0) + a.RequireNoError(err) + // all blocks contain at most 1 tx + if index > 0 { + return emu, nil + } + + return emu, emu.BlockchainDB().GetTransactionByBlockNumber(blockNumber) +} + +func requireLatestBlock(ctx iscp.SandboxView, emu *emulator.EVMEmulator, allowPrevious bool, blockNumber *big.Int) *big.Int { + current := emu.BlockchainDB().GetNumber() + if blockNumber.Cmp(current) != 0 { + assert.NewAssert(ctx.Log()).Require(allowPrevious, "unsupported operation") + } + return blockNumber +} + +func paramBlockNumber(ctx iscp.SandboxView, emu *emulator.EVMEmulator, allowPrevious bool) *big.Int { + current := emu.BlockchainDB().GetNumber() + if ctx.Params().MustHas(evm.FieldBlockNumber) { + blockNumber := new(big.Int).SetBytes(ctx.Params().MustGet(evm.FieldBlockNumber)) + return requireLatestBlock(ctx, emu, allowPrevious, blockNumber) + } + return current +} + +func paramBlockNumberOrHashAsNumber(ctx iscp.SandboxView, emu *emulator.EVMEmulator, allowPrevious bool) *big.Int { // nolint:unparam + if ctx.Params().MustHas(evm.FieldBlockHash) { + a := assert.NewAssert(ctx.Log()) + blockHash := common.BytesToHash(ctx.Params().MustGet(evm.FieldBlockHash)) + header := emu.BlockchainDB().GetHeaderByHash(blockHash) + a.Require(header != nil, "block not found") + return requireLatestBlock(ctx, emu, allowPrevious, header.Number) + } + return paramBlockNumber(ctx, emu, allowPrevious) +} + +type iscpBackend struct { + ctx iscp.Sandbox +} + +var _ vm.ISCPBackend = &iscpBackend{} + +func (i *iscpBackend) Event(s string) { + i.ctx.Event(s) +} + +func (i *iscpBackend) Entropy() [32]byte { + return i.ctx.GetEntropy() +} + +type iscpBackendR struct { + ctx iscp.SandboxView +} + +var _ vm.ISCPBackend = &iscpBackendR{} + +func (i *iscpBackendR) Event(s string) { panic("should not happen") } +func (i *iscpBackendR) Entropy() [32]byte { panic("should not happen") } diff --git a/contracts/native/evm/evmlight/iscpcontract/ISCP.abi b/contracts/native/evm/evmlight/iscpcontract/ISCP.abi new file mode 100644 index 0000000000..bd11f70c6c --- /dev/null +++ b/contracts/native/evm/evmlight/iscpcontract/ISCP.abi @@ -0,0 +1 @@ +[{"inputs":[],"name":"getChainId","outputs":[{"components":[{"internalType":"bytes1","name":"typeId","type":"bytes1"},{"internalType":"bytes32","name":"digest","type":"bytes32"}],"internalType":"struct ISCPAddress","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/contracts/native/evm/evmlight/iscpcontract/ISCP.bin-runtime b/contracts/native/evm/evmlight/iscpcontract/ISCP.bin-runtime new file mode 100644 index 0000000000..2b552b6e68 --- /dev/null +++ b/contracts/native/evm/evmlight/iscpcontract/ISCP.bin-runtime @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506004361061002b5760003560e01c80633408e47014610030575b600080fd5b61003861004e565b604051610045919061018f565b60405180910390f35b6100566100cd565b60006040518060400160405290816000820160009054906101000a900460f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001600182015481525050905090565b604051806040016040528060007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001600080191681525090565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b6101418161010c565b82525050565b6000819050919050565b61015a81610147565b82525050565b6040820160008201516101766000850182610138565b5060208201516101896020850182610151565b50505050565b60006040820190506101a46000830184610160565b9291505056fea264697066735822122090941bc691f2aed05378d9828256be2567b2a441898dbfa39bcfd0d54ae7fe8964736f6c63430008090033 \ No newline at end of file diff --git a/contracts/native/evm/evmlight/iscpcontract/ISCP.sol b/contracts/native/evm/evmlight/iscpcontract/ISCP.sol new file mode 100644 index 0000000000..9be13d754a --- /dev/null +++ b/contracts/native/evm/evmlight/iscpcontract/ISCP.sol @@ -0,0 +1,36 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity >=0.8.5; + +// ISCP addresses are 33 bytes which is sadly larger than EVM's bytes32 type, so +// it will use two 32-byte slots. +struct ISCPAddress { + bytes1 typeId; + bytes32 digest; +} + +address constant ISCP_CONTRACT_ADDRESS = 0x0000000000000000000000000000000000001074; +address constant ISCP_YUL_ADDRESS = 0x0000000000000000000000000000000000001075; + +// The standard ISCP contract present in all EVM ISCP chains at ISCP_CONTRACT_ADDRESS +contract ISCP { + + // The ChainID of the underlying ISCP chain + ISCPAddress chainId; + + function getChainId() public view returns (ISCPAddress memory) { + return chainId; + } +} + +function iscpTriggerEvent(string memory s) { + (bool success, ) = ISCP_YUL_ADDRESS.delegatecall(abi.encodeWithSignature("triggerEvent(string)", s)); + assert(success); +} + +function iscpEntropy() returns (bytes32) { + (bool success, bytes memory result) = ISCP_YUL_ADDRESS.delegatecall(abi.encodeWithSignature("entropy()")); + assert(success); + return abi.decode(result, (bytes32)); +} diff --git a/contracts/native/evm/evmlight/iscpcontract/ISCP.yul b/contracts/native/evm/evmlight/iscpcontract/ISCP.yul new file mode 100644 index 0000000000..11983d92de --- /dev/null +++ b/contracts/native/evm/evmlight/iscpcontract/ISCP.yul @@ -0,0 +1,38 @@ +object "ISCPYul" { + code { + // Protection against sending Ether + require(iszero(callvalue())) + + switch selector() + + case 0xe6c75c6b /* "triggerEvent(string)" */ { + // triggerEvent("asd") -> + // 00 e6c75c6b first 4 bytes of keccak("triggerEvent(string)") + // 04 0000000000000000000000000000000000000000000000000000000000000020 location of data part + // 24 0000000000000000000000000000000000000000000000000000000000000003 len("asd") + // 44 6173640000000000000000000000000000000000000000000000000000000000 "asd" + let size := calldataload(0x24) + let offset := msize() + calldatacopy(offset, 0x44, size) + verbatim_2i_0o(hex"c0", offset, size) + } + + case 0x47ce07cc /* "entropy()" */ { + let e := verbatim_0i_1o(hex"c1") + mstore(0, e) + return(0, 0x20) + } + + default { + revert(0, 0) + } + + function selector() -> s { + s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) + } + + function require(condition) { + if iszero(condition) { revert(0, 0) } + } + } +} diff --git a/contracts/native/evm/evmlight/iscpcontract/ISCPYul.bin-runtime b/contracts/native/evm/evmlight/iscpcontract/ISCPYul.bin-runtime new file mode 100644 index 0000000000..f9ae42a4a7 --- /dev/null +++ b/contracts/native/evm/evmlight/iscpcontract/ISCPYul.bin-runtime @@ -0,0 +1 @@ +600734156071565b600d6048565b63e6c75c6b81146026576347ce07cc8114603857600080fd5b6024355981604482378181c050506043565bc18060005260206000f35b50607d565b60007c010000000000000000000000000000000000000000000000000000000060003504905090565b80607a57600080fd5b50565b diff --git a/contracts/native/evm/evmlight/iscpcontract/iscp.go b/contracts/native/evm/evmlight/iscpcontract/iscp.go new file mode 100644 index 0000000000..02ac34af45 --- /dev/null +++ b/contracts/native/evm/evmlight/iscpcontract/iscp.go @@ -0,0 +1,93 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package iscpcontract + +import ( + _ "embed" + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/wasp/packages/iscp" +) + +// If you change ISCP.sol or ISCP.yul, you must recompile them. You will need +// the `solc` binary installed in your system. Then, simply run `go generate` +// in this directory. + +//go:generate solc --abi --bin-runtime --overwrite ISCP.sol -o . +// +// To get the storage layout: solc --storage-layout ISCP.sol | tail -n +4 | jq . +var ( + // EVMAddress is the arbitrary address on which the standard + // ISCP EVM contract lives + EVMAddress = common.HexToAddress("0x1074") + //go:embed ISCP.abi + ABI string + //go:embed ISCP.bin-runtime + bytecodeHex string +) + +//go:generate sh -c "solc --strict-assembly ISCP.yul | awk '/Binary representation:/ { getline; print $0 }' > ISCPYul.bin-runtime" +var ( + evmYulAddress = common.HexToAddress("0x1075") + //go:embed ISCPYul.bin-runtime + yulBytecodeHex string +) + +// ISCPAddress maps to the equally-named struct in iscp.sol +type ISCPAddress struct { + TypeID [1]byte + Digest [32]byte +} + +func ChainIDToISCPAddress(chainID *iscp.ChainID) (ret ISCPAddress) { + ret.TypeID[0] = byte(chainID.AliasAddress.Type()) + copy(ret.Digest[:], chainID.AliasAddress.Digest()) + return ret +} + +func ChainIDFromISCPAddress(a ISCPAddress) *iscp.ChainID { + if a.TypeID[0] != byte(ledgerstate.AliasAddressType) { + panic(fmt.Sprintf("expected type id %d, got %d", ledgerstate.AliasAddressType, a.TypeID[0])) + } + var addressBytes []byte + addressBytes = append(addressBytes, a.TypeID[0]) + addressBytes = append(addressBytes, a.Digest[:]...) + chainID, err := iscp.ChainIDFromBytes(addressBytes) + if err != nil { + // should not happen + panic(err.Error()) + } + return chainID +} + +// DeployOnGenesis sets up the initial state of the ISCP EVM contract +// which will go into the EVM genesis block +func DeployOnGenesis(genesisAlloc core.GenesisAlloc, chainID *iscp.ChainID) { + chainIDAsISCPAddress := ChainIDToISCPAddress(chainID) + var typeIDHash common.Hash + typeIDHash[31] = chainIDAsISCPAddress.TypeID[0] + var digestHash common.Hash + copy(digestHash[:], chainIDAsISCPAddress.Digest[:]) + + genesisAlloc[EVMAddress] = core.GenesisAccount{ + Code: common.FromHex(strings.TrimSpace(bytecodeHex)), + Storage: map[common.Hash]common.Hash{ + // offset 0 / slot 0: chainID.typeId + common.HexToHash("00"): typeIDHash, + // offset 0 / slot 1: chainID.digest + common.HexToHash("01"): digestHash, + }, + Balance: &big.Int{}, + } + + genesisAlloc[evmYulAddress] = core.GenesisAccount{ + Code: common.FromHex(strings.TrimSpace(yulBytecodeHex)), + Balance: &big.Int{}, + } +} diff --git a/contracts/native/evm/evmlight/iscptest/ISCPTest.abi b/contracts/native/evm/evmlight/iscptest/ISCPTest.abi new file mode 100644 index 0000000000..7477d4004b --- /dev/null +++ b/contracts/native/evm/evmlight/iscptest/ISCPTest.abi @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"entropy","type":"bytes32"}],"name":"EntropyEvent","type":"event"},{"inputs":[],"name":"emitEntropy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getChainId","outputs":[{"components":[{"internalType":"bytes1","name":"typeId","type":"bytes1"},{"internalType":"bytes32","name":"digest","type":"bytes32"}],"internalType":"struct ISCPAddress","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"s","type":"string"}],"name":"triggerEvent","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/contracts/native/evm/evmlight/iscptest/ISCPTest.bin b/contracts/native/evm/evmlight/iscptest/ISCPTest.bin new file mode 100644 index 0000000000..9530ab1baa --- /dev/null +++ b/contracts/native/evm/evmlight/iscptest/ISCPTest.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506108b4806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80633408e470146100465780633772d53f14610064578063e6c75c6b1461006e575b600080fd5b61004e61008a565b60405161005b9190610458565b60405180910390f35b61006c61011c565b005b610088600480360381019061008391906105cd565b610162565b005b610092610396565b600061107473ffffffffffffffffffffffffffffffffffffffff16633408e4706040518163ffffffff1660e01b8152600401604080518083038186803b1580156100db57600080fd5b505afa1580156100ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061011391906106c3565b90508091505090565b600061012661016e565b90507f2778726dc1b9d6d2ee2628a18174907da485ba8765490e157ddf1202528ed5bc8160405161015791906106ff565b60405180910390a150565b61016b8161028a565b50565b600080600061107573ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f47ce07cc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161021c9190610794565b600060405180830381855af49150503d8060008114610257576040519150601f19603f3d011682016040523d82523d6000602084013e61025c565b606091505b50915091508161026f5761026e6107ab565b5b8080602001905181019061028391906107da565b9250505090565b600061107573ffffffffffffffffffffffffffffffffffffffff16826040516024016102b6919061085c565b6040516020818303038152906040527fe6c75c6b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516103409190610794565b600060405180830381855af49150503d806000811461037b576040519150601f19603f3d011682016040523d82523d6000602084013e610380565b606091505b5050905080610392576103916107ab565b5b5050565b604051806040016040528060007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001600080191681525090565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b61040a816103d5565b82525050565b6000819050919050565b61042381610410565b82525050565b60408201600082015161043f6000850182610401565b506020820151610452602085018261041a565b50505050565b600060408201905061046d6000830184610429565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6104da82610491565b810181811067ffffffffffffffff821117156104f9576104f86104a2565b5b80604052505050565b600061050c610473565b905061051882826104d1565b919050565b600067ffffffffffffffff821115610538576105376104a2565b5b61054182610491565b9050602081019050919050565b82818337600083830152505050565b600061057061056b8461051d565b610502565b90508281526020810184848401111561058c5761058b61048c565b5b61059784828561054e565b509392505050565b600082601f8301126105b4576105b3610487565b5b81356105c484826020860161055d565b91505092915050565b6000602082840312156105e3576105e261047d565b5b600082013567ffffffffffffffff81111561060157610600610482565b5b61060d8482850161059f565b91505092915050565b600080fd5b610624816103d5565b811461062f57600080fd5b50565b6000815190506106418161061b565b92915050565b61065081610410565b811461065b57600080fd5b50565b60008151905061066d81610647565b92915050565b60006040828403121561068957610688610616565b5b6106936040610502565b905060006106a384828501610632565b60008301525060206106b78482850161065e565b60208301525092915050565b6000604082840312156106d9576106d861047d565b5b60006106e784828501610673565b91505092915050565b6106f981610410565b82525050565b600060208201905061071460008301846106f0565b92915050565b600081519050919050565b600081905092915050565b60005b8381101561074e578082015181840152602081019050610733565b8381111561075d576000848401525b50505050565b600061076e8261071a565b6107788185610725565b9350610788818560208601610730565b80840191505092915050565b60006107a08284610763565b915081905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b6000602082840312156107f0576107ef61047d565b5b60006107fe8482850161065e565b91505092915050565b600081519050919050565b600082825260208201905092915050565b600061082e82610807565b6108388185610812565b9350610848818560208601610730565b61085181610491565b840191505092915050565b600060208201905081810360008301526108768184610823565b90509291505056fea2646970667358221220b0ba713bf0da1f6f49c8e4f20dbb6f97440bfd61a10185d3fd8b692aedaa9f3564736f6c63430008090033 \ No newline at end of file diff --git a/contracts/native/evm/evmlight/iscptest/ISCPTest.sol b/contracts/native/evm/evmlight/iscptest/ISCPTest.sol new file mode 100644 index 0000000000..583839c774 --- /dev/null +++ b/contracts/native/evm/evmlight/iscptest/ISCPTest.sol @@ -0,0 +1,26 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity >=0.8.5; + +import "@iscpcontract/ISCP.sol"; + +ISCP constant iscp = ISCP(ISCP_CONTRACT_ADDRESS); + +contract ISCPTest { + function getChainId() public view returns (ISCPAddress memory) { + ISCPAddress memory r = iscp.getChainId(); + return r; + } + + function triggerEvent(string memory s) public { + iscpTriggerEvent(s); + } + + event EntropyEvent(bytes32 entropy); + + function emitEntropy() public { + bytes32 e = iscpEntropy(); + emit EntropyEvent(e); + } +} diff --git a/contracts/native/evm/evmlight/iscptest/contracts.go b/contracts/native/evm/evmlight/iscptest/contracts.go new file mode 100644 index 0000000000..827ac276be --- /dev/null +++ b/contracts/native/evm/evmlight/iscptest/contracts.go @@ -0,0 +1,24 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package iscptest + +import ( + _ "embed" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// If you change ISCPTest.sol, you must recompile. You will need +// the `solc` binary installed in your system. Then, simply run `go generate` +// in this directory. + +//go:generate sh -c "solc --abi --bin --overwrite @iscpcontract=`realpath ../iscpcontract` ISCPTest.sol -o . && rm ISCP.*" +var ( + //go:embed ISCPTest.abi + ISCPTestContractABI string + //go:embed ISCPTest.bin + iscpTestContractBytecodeHex string + ISCPTestContractBytecode = common.FromHex(strings.TrimSpace(iscpTestContractBytecodeHex)) +) diff --git a/contracts/native/evm/evmtest/evm_test.go b/contracts/native/evm/evmtest/evm_test.go new file mode 100644 index 0000000000..3da67cd6ca --- /dev/null +++ b/contracts/native/evm/evmtest/evm_test.go @@ -0,0 +1,487 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmtest + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evmchain" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight" + "github.com/iotaledger/wasp/packages/evm/evmflavors" + "github.com/iotaledger/wasp/packages/evm/evmtest" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/colored" + "github.com/iotaledger/wasp/packages/iscp/coreutil" + "github.com/iotaledger/wasp/packages/solo" + "github.com/iotaledger/wasp/packages/solo/solobench" + "github.com/iotaledger/wasp/packages/testutil/testlogger" + "github.com/iotaledger/wasp/packages/vm/core/accounts" + "github.com/stretchr/testify/require" +) + +func TestDeploy(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + initEVMChain(t, evmFlavor) + }) +} + +func TestFaucetBalance(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + bal := evmChain.getBalance(evmChain.faucetAddress()) + require.Zero(t, evmChain.faucetSupply.Cmp(bal)) + }) +} + +func TestStorageContract(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + + // deploy solidity `storage` contract + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + // call FuncCallView to call EVM contract's `retrieve` view, get 42 + require.EqualValues(t, 42, storage.retrieve()) + + // call FuncSendTransaction with EVM tx that calls `store(43)` + res, err := storage.store(43) + require.NoError(t, err) + require.Equal(t, types.ReceiptStatusSuccessful, res.receipt.Status) + + // call `retrieve` view, get 43 + require.EqualValues(t, 43, storage.retrieve()) + }) +} + +func TestERC20Contract(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + + // deploy solidity `erc20` contract + erc20 := evmChain.deployERC20Contract(evmChain.faucetKey, "TestCoin", "TEST") + + // call `totalSupply` view + { + v := erc20.totalSupply() + // 100 * 10^18 + expected := new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) + require.Zero(t, v.Cmp(expected)) + } + + _, recipientAddress := generateEthereumKey(t) + transferAmount := big.NewInt(1337) + + // call `transfer` => send 1337 TestCoin to recipientAddress + res, err := erc20.transfer(recipientAddress, transferAmount) + require.NoError(t, err) + + require.Equal(t, types.ReceiptStatusSuccessful, res.receipt.Status) + require.Equal(t, 1, len(res.receipt.Logs)) + + // call `balanceOf` view => check balance of recipient = 1337 TestCoin + require.Zero(t, erc20.balanceOf(recipientAddress).Cmp(transferAmount)) + }) +} + +func TestGetCode(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + erc20 := evmChain.deployERC20Contract(evmChain.faucetKey, "TestCoin", "TEST") + + // get contract bytecode from EVM emulator + retrievedBytecode := evmChain.getCode(erc20.address) + + // ensure returned bytecode matches the expected runtime bytecode + require.True(t, bytes.Equal(retrievedBytecode, evmtest.ERC20ContractRuntimeBytecode), "bytecode retrieved from the chain must match the deployed bytecode") + }) +} + +func TestGasCharged(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + iotaWallet, iotaAddress := evmChain.solo.NewKeyPairWithFunds() + iotaAgentID := iscp.NewAgentID(iotaAddress, 0) + + initialBalance := evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA) + iotasSent := initialBalance - 1 + + // call `store(999)` with enough gas + res, err := storage.store(999, ethCallOptions{iota: iotaCallOptions{wallet: iotaWallet, transfer: iotasSent}}) + require.NoError(t, err) + require.Greater(t, res.iotaChargedFee, uint64(0)) + + // call `retrieve` view, get 42 + require.EqualValues(t, 999, storage.retrieve()) + + // user on-chain account is credited with excess iotas (iotasSent - gasUsed) + expectedUserBalance := iotasSent - res.iotaChargedFee + + evmChain.soloChain.AssertIotas(iotaAgentID, expectedUserBalance) + + // call `store(123)` without enough gas + _, err = storage.store(123, ethCallOptions{iota: iotaCallOptions{wallet: iotaWallet, transfer: 1}}) + require.Contains(t, err.Error(), "transferred tokens (1) not enough") + + // call `retrieve` view, get 999 - which means store(123) failed and the previous state is kept + require.EqualValues(t, 999, storage.retrieve()) + + // verify user on-chain account still has the same balance (no refund happened) + evmChain.soloChain.AssertIotas(iotaAgentID, expectedUserBalance) + }) +} + +func TestOwner(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + + // the default owner is correct + owner := evmChain.getOwner() + require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) + + // only the owner can call the setOwner endpoint + user1Wallet, user1Address := evmChain.solo.NewKeyPairWithFunds() + user1AgentID := iscp.NewAgentID(user1Address, 0) + _, err := evmChain.soloChain.PostRequestSync( + solo.NewCallParams(evmFlavor.Name, evm.FuncSetNextOwner.Name, evm.FieldNextEVMOwner, user1AgentID). + WithIotas(100000), + user1Wallet, + ) + require.Error(t, err) + + // ensure owner didn't change after a failed call + owner = evmChain.getOwner() + require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) + + // current owner is able to set a new "next owner" + _, err = evmChain.soloChain.PostRequestSync( + solo.NewCallParams(evmFlavor.Name, evm.FuncSetNextOwner.Name, evm.FieldNextEVMOwner, user1AgentID). + WithIotas(100000), + evmChain.soloChain.OriginatorKeyPair, + ) + require.NoError(t, err) + + // check that the owner didn't change yet (new owner needs to claim ownership) + owner = evmChain.getOwner() + require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) + + // check no other user can claim ownership + user2Wallet, _ := evmChain.solo.NewKeyPairWithFunds() + + _, err = evmChain.soloChain.PostRequestSync( + solo.NewCallParams(evmFlavor.Name, evm.FuncClaimOwnership.Name). + WithIotas(100000), + user2Wallet, + ) + require.Error(t, err) + + // owner still the same + owner = evmChain.getOwner() + require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) + + // claim ownership successfully + _, err = evmChain.soloChain.PostRequestSync( + solo.NewCallParams(evmFlavor.Name, evm.FuncClaimOwnership.Name). + WithIotas(100000), + user1Wallet, + ) + require.NoError(t, err) + owner = evmChain.getOwner() + require.True(t, owner.Equals(user1AgentID)) + }) +} + +func TestGasPerIotas(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + // the default value is correct + require.Equal(t, evm.DefaultGasPerIota, evmChain.getGasPerIotas()) + + res, err := storage.store(43) + require.NoError(t, err) + initialGasFee := res.iotaChargedFee + + // only the owner can call the setGasPerIotas endpoint + newGasPerIota := evm.DefaultGasPerIota * 1000 + newUserWallet, _ := evmChain.solo.NewKeyPairWithFunds() + err = evmChain.setGasPerIotas(newGasPerIota, iotaCallOptions{wallet: newUserWallet}) + require.Contains(t, err.Error(), "can only be called by the contract owner") + require.Equal(t, evm.DefaultGasPerIota, evmChain.getGasPerIotas()) + + // current owner is able to set a new gasPerIotas + err = evmChain.setGasPerIotas(newGasPerIota, iotaCallOptions{wallet: evmChain.soloChain.OriginatorKeyPair}) + require.NoError(t, err) + require.Equal(t, newGasPerIota, evmChain.getGasPerIotas()) + + // run an equivalent request and compare the gas fees + res, err = storage.store(44) + require.NoError(t, err) + require.Less(t, res.iotaChargedFee, initialGasFee) + }) +} + +func TestWithdrawalOwnerFees(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + // only the owner can call withdrawal + user1Wallet, user1Address := evmChain.solo.NewKeyPairWithFunds() + user1AgentID := iscp.NewAgentID(user1Address, 0) + + err := evmChain.withdrawGasFees(user1Wallet) + require.Contains(t, err.Error(), "can only be called by the contract owner") + + // change owner to user1 + err = evmChain.setNextOwner(user1AgentID) + require.NoError(t, err) + err = evmChain.claimOwnership(iotaCallOptions{wallet: user1Wallet}) + require.NoError(t, err) + + // collect fees from contract deployment + user1Balance0 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) + require.NoError(t, evmChain.withdrawGasFees(user1Wallet)) + user1Balance1 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) + require.Greater(t, user1Balance1, user1Balance0) + + // collect fees from a SC call, check that the collected fees matches the fees charged + user1Balance2 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) + res, err := storage.store(43) + require.NoError(t, err) + require.NoError(t, evmChain.withdrawGasFees(user1Wallet)) + user1Balance3 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) + require.Equal(t, user1Balance3, user1Balance2+res.iotaChargedFee) + + // try to withdraw a second time, it should succeed, but owner balance shouldnt not change (there are no fees to withdraw) + require.NoError(t, evmChain.withdrawGasFees(user1Wallet)) + user1Balance4 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) + require.Equal(t, user1Balance3, user1Balance4) + + // try to withdraw fees to another actor using using the FieldAgentId param + res, err = storage.store(44) + require.NoError(t, err) + _, user2Address := evmChain.solo.NewKeyPairWithFunds() + user2AgentID := iscp.NewAgentID(user2Address, 0) + user2Balance0 := evmChain.solo.GetAddressBalance(user2Address, colored.IOTA) + err = evmChain.withdrawGasFees(user1Wallet, user2AgentID) + require.NoError(t, err) + user2Balance1 := evmChain.solo.GetAddressBalance(user2Address, colored.IOTA) + require.Equal(t, user2Balance1, user2Balance0+res.iotaChargedFee+1) // 1 extra iota from the withdrawal request + }) +} + +// tests that the gas limits are correctly enforced based on the iotas sent +func TestGasLimit(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + gasPerIotas := evmChain.getGasPerIotas() + + // estimate gas by sending a valid tx + result, err := storage.store(123) + require.NoError(t, err) + gas := result.receipt.GasUsed + + // send again with same gas limit but not enough iotas + _, err = storage.store(123, ethCallOptions{gasLimit: gas, iota: iotaCallOptions{transfer: (gas+1)/gasPerIotas - 1}}) + require.Error(t, err) + require.Regexp(t, `transferred tokens \(\d+\) not enough`, err.Error()) + + // send again with gas limit not enough for transaction + _, err = storage.store(123, ethCallOptions{gasLimit: 1 * gasPerIotas, iota: iotaCallOptions{transfer: 1}}) + require.Error(t, err) + require.Regexp(t, `intrinsic gas too low: have \d+, want \d+`, err.Error()) + }) +} + +// ensure the amount of iotas sent impacts the amount of loop iterators (gas used) +func TestLoop(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + loop := evmChain.deployLoopContract(evmChain.faucetKey) + gasPerIotas := evmChain.getGasPerIotas() + + iotaWallet, iotaAddress := evmChain.solo.NewKeyPairWithFunds() + iotaAgentID := iscp.NewAgentID(iotaAddress, 0) + + initialBalance := evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA) + iotasSpent1 := uint64(100) + res, err := loop.loop(ethCallOptions{ + gasLimit: iotasSpent1 * gasPerIotas, + iota: iotaCallOptions{wallet: iotaWallet, transfer: iotasSpent1}, + }) + require.NoError(t, err) + require.Equal(t, res.iotaChargedFee, iotasSpent1) + gasUsed := res.receipt.GasUsed + + iotasSpent2 := uint64(1000) + res, err = loop.loop(ethCallOptions{ + gasLimit: iotasSpent2 * gasPerIotas, + iota: iotaCallOptions{wallet: iotaWallet, transfer: iotasSpent2}, + }) + require.NoError(t, err) + require.Equal(t, res.iotaChargedFee, iotasSpent2) + require.Greater(t, res.receipt.GasUsed, gasUsed) + + // ensure iotas sent are kept in the ISCP chain + require.Equal(t, evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA), initialBalance-iotasSpent1-iotasSpent2) + evmChain.soloChain.AssertIotas(iotaAgentID, 0) + }) +} + +func TestNonFaucetUsers(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + // call EVM contract with a new key (that doesn't own ether on the EVM evmChain.soloChain) + gasPerIotas := evmChain.getGasPerIotas() + iotas := uint64(10000) + // this should be successful because gasPrice is 0 + res, err := storage.store(123, ethCallOptions{gasLimit: iotas * gasPerIotas, iota: iotaCallOptions{transfer: iotas}}) + require.NoError(t, err) + require.Greater(t, res.iotaChargedFee, uint64(0)) + + require.EqualValues(t, 123, storage.retrieve()) + }) +} + +func TestPrePaidFees(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChain := initEVMChain(t, evmFlavor) + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + iotaWallet, iotaAddress := evmChain.solo.NewKeyPairWithFunds() + + // test sending off-ledger request without depositing funds first + txdata, _, _ := storage.buildEthTxData(nil, "store", uint32(999)) + offledgerRequest := evmChain.buildSoloRequest(evm.FuncSendTransaction.Name, 100, evm.FieldTransactionData, txdata) + evmChain.soloChain.PostRequestOffLedger(offledgerRequest, iotaWallet) + + // check that the tx has no effect + require.EqualValues(t, 42, storage.retrieve()) + + // deposit funds + initialBalance := evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA) + _, err := evmChain.soloChain.PostRequestSync( + solo.NewCallParams(accounts.Contract.Name, accounts.FuncDeposit.Name).WithIotas(initialBalance), + iotaWallet, + ) + require.NoError(t, err) + + // send offledger request again and check that is works + evmChain.soloChain.PostRequestOffLedger(offledgerRequest, iotaWallet) + require.EqualValues(t, 999, storage.retrieve()) + }) +} + +func TestISCPContract(t *testing.T) { + // deploy the evmlight contract, which starts an EVM chain and automatically + // deploys the iscp.sol EVM contract at address 0x1074 + evmChain := initEVMChain(t, evmlight.Contract) + + // deploy the iscp-test.sol EVM contract + iscpTest := evmChain.deployISCPTestContract(evmChain.faucetKey) + + // call the getChainId() view function of iscp-test.sol which in turn: + // calls the getChainId() view function of iscp.sol, which: + // returns the ChainID of the underlying ISCP chain + chainID := iscpTest.getChainID() + + require.Equal(t, evmChain.soloChain.ChainID.Array(), chainID.Array()) +} + +func TestISCPTriggerEvent(t *testing.T) { + evmChain := initEVMChain(t, evmlight.Contract) + iscpTest := evmChain.deployISCPTestContract(evmChain.faucetKey) + + // call the triggerEvent(string) function of iscp-test.sol which in turn: + // calls the iscpTriggerEvent(string) function of iscp.sol, which: + // executes a custom opcode, which: + // gets intercepted by the evmlight contract, which: + // triggers an ISCP event with the given string parameter + res, err := iscpTest.triggerEvent("Hi from EVM!") + require.NoError(t, err) + require.Equal(t, types.ReceiptStatusSuccessful, res.receipt.Status) + ev, err := evmChain.soloChain.GetEventsForBlock(evmChain.soloChain.GetLatestBlockInfo().BlockIndex) + require.NoError(t, err) + require.Len(t, ev, 1) + require.Contains(t, ev[0], "Hi from EVM!") +} + +func TestISCPEntropy(t *testing.T) { + evmChain := initEVMChain(t, evmlight.Contract) + iscpTest := evmChain.deployISCPTestContract(evmChain.faucetKey) + + // call the emitEntropy() function of iscp-test.sol which in turn: + // calls the iscpEntropy() function of iscp.sol, which: + // executes a custom opcode, which: + // gets intercepted by the evmlight contract, which: + // returns the entropy value from the sandbox + // emits an EVM event (aka log) with the entropy value + res, err := iscpTest.emitEntropy() + require.NoError(t, err) + require.Equal(t, types.ReceiptStatusSuccessful, res.receipt.Status) + require.Len(t, res.receipt.Logs, 1) + entropy := res.receipt.Logs[0].Data + require.Len(t, entropy, 32) + require.NotEqualValues(t, entropy, make([]byte, 32)) +} + +func initBenchmark(b *testing.B, evmFlavor *coreutil.ContractInfo) (*solo.Chain, []*solo.CallParams) { + // setup: deploy the EVM chain + log := testlogger.NewSilentLogger(b.Name(), true) + env := solo.NewWithLogger(b, log).WithNativeContract(evmflavors.Processors[evmFlavor.Name]) + evmChain := initEVMChainWithSolo(b, evmFlavor, env) + // setup: deploy the `storage` EVM contract + storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) + + // setup: prepare N requests that call FuncSendTransaction with an EVM tx + // that calls `storage.store()` + reqs := make([]*solo.CallParams, b.N) + for i := 0; i < b.N; i++ { + sender, err := crypto.GenerateKey() // send from a new address so that nonce is always 0 + require.NoError(b, err) + + // TODO: for some reason, estimating gas does not work here + opt := ethCallOptions{sender: sender, gasLimit: 50000} + txdata, _, opt := storage.buildEthTxData([]ethCallOptions{opt}, "store", uint32(i)) + iotaOpt := storage.chain.parseIotaCallOptions([]iotaCallOptions{opt.iota}) + reqs[i] = storage.chain.buildSoloRequest(evm.FuncSendTransaction.Name, iotaOpt.transfer, evm.FieldTransactionData, txdata) + } + + return evmChain.soloChain, reqs +} + +// run benchmarks with: go test -benchmem -cpu=1 -run=' ' -bench='Bench.*' + +func doBenchmark(b *testing.B, evmFlavor *coreutil.ContractInfo, f solobench.Func) { + chain, reqs := initBenchmark(b, evmFlavor) + f(b, chain, reqs, nil) +} + +func BenchmarkEVMChainSync(b *testing.B) { + doBenchmark(b, evmchain.Contract, solobench.RunBenchmarkSync) +} + +func BenchmarkEVMLightSync(b *testing.B) { + doBenchmark(b, evmlight.Contract, solobench.RunBenchmarkSync) +} + +func BenchmarkEVMChainAsync(b *testing.B) { + doBenchmark(b, evmchain.Contract, solobench.RunBenchmarkAsync) +} + +func BenchmarkEVMLightAsync(b *testing.B) { + doBenchmark(b, evmlight.Contract, solobench.RunBenchmarkAsync) +} diff --git a/contracts/native/evm/evmtest/evmmanagement_test.go b/contracts/native/evm/evmtest/evmmanagement_test.go new file mode 100644 index 0000000000..dcaa85343e --- /dev/null +++ b/contracts/native/evm/evmtest/evmmanagement_test.go @@ -0,0 +1,82 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmtest + +import ( + "testing" + + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/assert" + "github.com/iotaledger/wasp/packages/iscp/colored" + "github.com/iotaledger/wasp/packages/iscp/coreutil" + "github.com/iotaledger/wasp/packages/kv/dict" + "github.com/iotaledger/wasp/packages/solo" + "github.com/stretchr/testify/require" +) + +// TODO this SC could adjust gasPerIota based on some conditions + +var ( + evmChainMgmtContract = coreutil.NewContract("EVMChainManagement", "EVM chain management") + + mgmtFuncClaimOwnership = coreutil.Func("claimOwnership") + mgmtFuncWithdrawGasFees = coreutil.Func("withdrawGasFees") +) + +func TestRequestGasFees(t *testing.T) { + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + evmChainMgmtProcessor := evmChainMgmtContract.Processor(nil, + mgmtFuncClaimOwnership.WithHandler(func(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + _, err := ctx.Call(evmFlavor.Hname(), evm.FuncClaimOwnership.Hname(), nil, nil) + a.RequireNoError(err) + return nil, nil + }), + mgmtFuncWithdrawGasFees.WithHandler(func(ctx iscp.Sandbox) (dict.Dict, error) { + a := assert.NewAssert(ctx.Log()) + _, err := ctx.Call(evmFlavor.Hname(), evm.FuncWithdrawGasFees.Hname(), nil, nil) + a.RequireNoError(err) + return nil, nil + }), + ) + + evmChain := initEVMChain(t, evmFlavor, evmChainMgmtProcessor) + soloChain := evmChain.soloChain + + err := soloChain.DeployContract(nil, evmChainMgmtContract.Name, evmChainMgmtContract.ProgramHash) + require.NoError(t, err) + + // deploy solidity `storage` contract (just to produce some fees to be claimed) + evmChain.deployStorageContract(evmChain.faucetKey, 42) + + // change owner to evnchainmanagement SC + managerAgentID := iscp.NewAgentID(soloChain.ChainID.AsAddress(), iscp.Hn(evmChainMgmtContract.Name)) + _, err = soloChain.PostRequestSync( + solo.NewCallParams(evmFlavor.Name, evm.FuncSetNextOwner.Name, evm.FieldNextEVMOwner, managerAgentID). + WithIotas(1), + soloChain.OriginatorKeyPair, + ) + require.NoError(t, err) + + // claim ownership + _, err = soloChain.PostRequestSync( + solo.NewCallParams(evmChainMgmtContract.Name, mgmtFuncClaimOwnership.Name).WithIotas(1), + soloChain.OriginatorKeyPair, + ) + require.NoError(t, err) + + // call requestGasFees manually, so that the manager SC request funds from the evm chain, check funds are received by the manager SC + balance0 := soloChain.GetAccountBalance(managerAgentID).Get(colored.IOTA) + + _, err = soloChain.PostRequestSync( + solo.NewCallParams(evmChainMgmtContract.Name, mgmtFuncWithdrawGasFees.Name).WithIotas(1), + soloChain.OriginatorKeyPair, + ) + require.NoError(t, err) + balance1 := soloChain.GetAccountBalance(managerAgentID).Get(colored.IOTA) + + require.Greater(t, balance1, balance0) + }) +} diff --git a/contracts/native/evmchain/utils_test.go b/contracts/native/evm/evmtest/utils_test.go similarity index 71% rename from contracts/native/evmchain/utils_test.go rename to contracts/native/evm/evmtest/utils_test.go index aa87dbafbe..f079f775f1 100644 --- a/contracts/native/evmchain/utils_test.go +++ b/contracts/native/evm/evmtest/utils_test.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evmchain +package evmtest import ( "crypto/ecdsa" @@ -16,8 +16,12 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/iotaledger/hive.go/crypto/ed25519" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight/iscpcontract" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight/iscptest" + "github.com/iotaledger/wasp/packages/evm/evmflavors" "github.com/iotaledger/wasp/packages/evm/evmtest" + "github.com/iotaledger/wasp/packages/evm/evmtypes" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/iotaledger/wasp/packages/kv/codec" @@ -26,8 +30,15 @@ import ( "github.com/stretchr/testify/require" ) +func withEVMFlavors(t *testing.T, f func(*testing.T, *coreutil.ContractInfo)) { + for _, evmFlavor := range evmflavors.All { + t.Run(evmFlavor.Name, func(t *testing.T) { f(t, evmFlavor) }) + } +} + type evmChainInstance struct { t testing.TB + evmFlavor *coreutil.ContractInfo solo *solo.Solo soloChain *solo.Chain faucetKey *ecdsa.PrivateKey @@ -42,6 +53,10 @@ type evmContractInstance struct { abi abi.ABI } +type iscpTestContractInstance struct { + *evmContractInstance +} + type storageContractInstance struct { *evmContractInstance } @@ -66,28 +81,29 @@ type ethCallOptions struct { gasLimit uint64 } -func initEVMChain(t testing.TB, nativeContracts ...*coreutil.ContractProcessor) *evmChainInstance { - env := solo.New(t, false, false).WithNativeContract(Processor) +func initEVMChain(t testing.TB, evmFlavor *coreutil.ContractInfo, nativeContracts ...*coreutil.ContractProcessor) *evmChainInstance { + env := solo.New(t, true, true).WithNativeContract(evmflavors.Processors[evmFlavor.Name]) for _, c := range nativeContracts { env = env.WithNativeContract(c) } - return initEVMChainWithSolo(t, env) + return initEVMChainWithSolo(t, evmFlavor, env) } -func initEVMChainWithSolo(t testing.TB, env *solo.Solo) *evmChainInstance { +func initEVMChainWithSolo(t testing.TB, evmFlavor *coreutil.ContractInfo, env *solo.Solo) *evmChainInstance { faucetKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") chainID := evm.DefaultChainID e := &evmChainInstance{ t: t, + evmFlavor: evmFlavor, solo: env, soloChain: env.NewChain(nil, "ch1"), faucetKey: faucetKey, faucetSupply: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)), chainID: chainID, } - err := e.soloChain.DeployContract(nil, "evmchain", Contract.ProgramHash, - FieldChainID, codec.EncodeUint16(uint16(chainID)), - FieldGenesisAlloc, EncodeGenesisAlloc(map[common.Address]core.GenesisAccount{ + err := e.soloChain.DeployContract(nil, evmFlavor.Name, evmFlavor.ProgramHash, + evm.FieldChainID, codec.EncodeUint16(uint16(chainID)), + evm.FieldGenesisAlloc, evmtypes.EncodeGenesisAlloc(map[common.Address]core.GenesisAccount{ e.faucetAddress(): {Balance: e.faucetSupply}, }), ) @@ -110,7 +126,7 @@ func (e *evmChainInstance) parseIotaCallOptions(opts []iotaCallOptions) iotaCall } func (e *evmChainInstance) buildSoloRequest(funName string, transfer uint64, params ...interface{}) *solo.CallParams { - return solo.NewCallParams(Contract.Name, funName, params...).WithIotas(transfer) + return solo.NewCallParams(e.evmFlavor.Name, funName, params...).WithIotas(transfer) } func (e *evmChainInstance) postRequest(opts []iotaCallOptions, funName string, params ...interface{}) (dict.Dict, error) { @@ -122,44 +138,44 @@ func (e *evmChainInstance) postRequest(opts []iotaCallOptions, funName string, p } func (e *evmChainInstance) callView(funName string, params ...interface{}) (dict.Dict, error) { - return e.soloChain.CallView(Contract.Name, funName, params...) + return e.soloChain.CallView(e.evmFlavor.Name, funName, params...) } func (e *evmChainInstance) getCode(addr common.Address) []byte { - ret, err := e.callView(FuncGetCode.Name, FieldAddress, addr.Bytes()) + ret, err := e.callView(evm.FuncGetCode.Name, evm.FieldAddress, addr.Bytes()) require.NoError(e.t, err) - return ret.MustGet(FieldResult) + return ret.MustGet(evm.FieldResult) } func (e *evmChainInstance) getGasPerIotas() uint64 { - ret, err := e.callView(FuncGetGasPerIota.Name) + ret, err := e.callView(evm.FuncGetGasPerIota.Name) require.NoError(e.t, err) - gasPerIotas, err := codec.DecodeUint64(ret.MustGet(FieldResult)) + gasPerIotas, err := codec.DecodeUint64(ret.MustGet(evm.FieldResult)) require.NoError(e.t, err) return gasPerIotas } func (e *evmChainInstance) setGasPerIotas(newGasPerIota uint64, opts ...iotaCallOptions) error { - _, err := e.postRequest(opts, FuncSetGasPerIota.Name, FieldGasPerIota, newGasPerIota) + _, err := e.postRequest(opts, evm.FuncSetGasPerIota.Name, evm.FieldGasPerIota, newGasPerIota) return err } func (e *evmChainInstance) claimOwnership(opts ...iotaCallOptions) error { - _, err := e.postRequest(opts, FuncClaimOwnership.Name) + _, err := e.postRequest(opts, evm.FuncClaimOwnership.Name) return err } func (e *evmChainInstance) setNextOwner(nextOwner *iscp.AgentID, opts ...iotaCallOptions) error { - _, err := e.postRequest(opts, FuncSetNextOwner.Name, FieldNextEvmOwner, nextOwner) + _, err := e.postRequest(opts, evm.FuncSetNextOwner.Name, evm.FieldNextEVMOwner, nextOwner) return err } func (e *evmChainInstance) withdrawGasFees(wallet *ed25519.KeyPair, agentID ...*iscp.AgentID) error { var params []interface{} if len(agentID) > 0 { - params = append(params, FieldAgentID, agentID[0]) + params = append(params, evm.FieldAgentID, agentID[0]) } - _, err := e.postRequest([]iotaCallOptions{{wallet: wallet}}, FuncWithdrawGasFees.Name, params...) + _, err := e.postRequest([]iotaCallOptions{{wallet: wallet}}, evm.FuncWithdrawGasFees.Name, params...) return err } @@ -168,29 +184,33 @@ func (e *evmChainInstance) faucetAddress() common.Address { } func (e *evmChainInstance) getBalance(addr common.Address) *big.Int { - ret, err := e.callView(FuncGetBalance.Name, FieldAddress, addr.Bytes()) + ret, err := e.callView(evm.FuncGetBalance.Name, evm.FieldAddress, addr.Bytes()) require.NoError(e.t, err) bal := big.NewInt(0) - bal.SetBytes(ret.MustGet(FieldResult)) + bal.SetBytes(ret.MustGet(evm.FieldResult)) return bal } func (e *evmChainInstance) getNonce(addr common.Address) uint64 { - ret, err := e.callView(FuncGetNonce.Name, FieldAddress, addr.Bytes()) + ret, err := e.callView(evm.FuncGetNonce.Name, evm.FieldAddress, addr.Bytes()) require.NoError(e.t, err) - nonce, err := codec.DecodeUint64(ret.MustGet(FieldResult)) + nonce, err := codec.DecodeUint64(ret.MustGet(evm.FieldResult)) require.NoError(e.t, err) return nonce } func (e *evmChainInstance) getOwner() *iscp.AgentID { - ret, err := e.callView(FuncGetOwner.Name) + ret, err := e.callView(evm.FuncGetOwner.Name) require.NoError(e.t, err) - owner, err := codec.DecodeAgentID(ret.MustGet(FieldResult)) + owner, err := codec.DecodeAgentID(ret.MustGet(evm.FieldResult)) require.NoError(e.t, err) return owner } +func (e *evmChainInstance) deployISCPTestContract(creator *ecdsa.PrivateKey) *iscpTestContractInstance { + return &iscpTestContractInstance{e.deployContract(creator, iscptest.ISCPTestContractABI, iscptest.ISCPTestContractBytecode)} +} + func (e *evmChainInstance) deployStorageContract(creator *ecdsa.PrivateKey, n uint32) *storageContractInstance { // nolint:unparam return &storageContractInstance{e.deployContract(creator, evmtest.StorageContractABI, evmtest.StorageContractBytecode, n)} } @@ -204,7 +224,7 @@ func (e *evmChainInstance) deployLoopContract(creator *ecdsa.PrivateKey) *loopCo } func (e *evmChainInstance) signer() types.Signer { - return evm.Signer(big.NewInt(int64(e.chainID))) + return evmtypes.Signer(big.NewInt(int64(e.chainID))) } func (e *evmChainInstance) deployContract(creator *ecdsa.PrivateKey, abiJSON string, bytecode []byte, args ...interface{}) *evmContractInstance { @@ -240,8 +260,8 @@ func (e *evmChainInstance) deployContract(creator *ecdsa.PrivateKey, abiJSON str txdata, err := tx.MarshalBinary() require.NoError(e.t, err) - req, toUpload := solo.NewCallParamsOptimized(Contract.Name, FuncSendTransaction.Name, 1024, - FieldTransactionData, txdata, + req, toUpload := solo.NewCallParamsOptimized(e.evmFlavor.Name, evm.FuncSendTransaction.Name, 1024, + evm.FieldTransactionData, txdata, ) req.WithIotas(gas / e.getGasPerIotas()) for _, v := range toUpload { @@ -260,12 +280,12 @@ func (e *evmChainInstance) deployContract(creator *ecdsa.PrivateKey, abiJSON str } func (e *evmChainInstance) estimateGas(callMsg ethereum.CallMsg) uint64 { - ret, err := e.callView(FuncEstimateGas.Name, FieldCallMsg, EncodeCallMsg(callMsg)) + ret, err := e.callView(evm.FuncEstimateGas.Name, evm.FieldCallMsg, evmtypes.EncodeCallMsg(callMsg)) if err != nil { e.t.Logf("%v", err) return evm.GasLimitDefault - 1 } - gas, err := codec.DecodeUint64(ret.MustGet(FieldResult)) + gas, err := codec.DecodeUint64(ret.MustGet(evm.FieldResult)) require.NoError(e.t, err) return gas } @@ -322,7 +342,7 @@ func (e *evmContractInstance) buildEthTxData(opts []ethCallOptions, fnName strin type callFnResult struct { tx *types.Transaction - receipt *Receipt + receipt *types.Receipt iotaChargedFee uint64 } @@ -332,21 +352,21 @@ func (e *evmContractInstance) callFn(opts []ethCallOptions, fnName string, args txdata, tx, opt := e.buildEthTxData(opts, fnName, args...) res.tx = tx - result, err := e.chain.postRequest([]iotaCallOptions{opt.iota}, FuncSendTransaction.Name, FieldTransactionData, txdata) + result, err := e.chain.postRequest([]iotaCallOptions{opt.iota}, evm.FuncSendTransaction.Name, evm.FieldTransactionData, txdata) if err != nil { return } - res.iotaChargedFee, err = codec.DecodeUint64(result.MustGet(FieldGasFee)) + res.iotaChargedFee, err = codec.DecodeUint64(result.MustGet(evm.FieldGasFee)) require.NoError(e.chain.t, err) - gasUsed, err := codec.DecodeUint64(result.MustGet(FieldGasUsed)) + gasUsed, err := codec.DecodeUint64(result.MustGet(evm.FieldGasUsed)) require.NoError(e.chain.t, err) - receiptResult, err := e.chain.callView(FuncGetReceipt.Name, FieldTransactionHash, tx.Hash().Bytes()) + receiptResult, err := e.chain.callView(evm.FuncGetReceipt.Name, evm.FieldTransactionHash, tx.Hash().Bytes()) require.NoError(e.chain.t, err) - res.receipt, err = DecodeReceipt(receiptResult.MustGet(FieldResult)) + res.receipt, err = evmtypes.DecodeReceiptFull(receiptResult.MustGet(evm.FieldResult)) require.NoError(e.chain.t, err) require.LessOrEqual(e.chain.t, res.receipt.GasUsed, gasUsed) @@ -366,14 +386,31 @@ func (e *evmContractInstance) callView(opts []ethCallOptions, fnName string, arg Value: opt.value, Data: callArguments, }) - ret, err := e.chain.callView(FuncCallContract.Name, FieldCallMsg, EncodeCallMsg(callMsg)) + ret, err := e.chain.callView(evm.FuncCallContract.Name, evm.FieldCallMsg, evmtypes.EncodeCallMsg(callMsg)) require.NoError(e.chain.t, err) if v != nil { - err = e.abi.UnpackIntoInterface(v, fnName, ret.MustGet(FieldResult)) + err = e.abi.UnpackIntoInterface(v, fnName, ret.MustGet(evm.FieldResult)) require.NoError(e.chain.t, err) } } +func (i *iscpTestContractInstance) getChainID() *iscp.ChainID { + type r struct { + Result iscpcontract.ISCPAddress + } + var v r + i.callView(nil, "getChainId", nil, &v) + return iscpcontract.ChainIDFromISCPAddress(v.Result) +} + +func (i *iscpTestContractInstance) triggerEvent(s string) (res callFnResult, err error) { + return i.callFn(nil, "triggerEvent", s) +} + +func (i *iscpTestContractInstance) emitEntropy() (res callFnResult, err error) { + return i.callFn(nil, "emitEntropy") +} + func (s *storageContractInstance) retrieve() uint32 { var v uint32 s.callView(nil, "retrieve", nil, &v) diff --git a/contracts/native/evmchain/interface.go b/contracts/native/evm/interface.go similarity index 82% rename from contracts/native/evmchain/interface.go rename to contracts/native/evm/interface.go index a22adf1d4c..58ff731689 100644 --- a/contracts/native/evmchain/interface.go +++ b/contracts/native/evm/interface.go @@ -1,16 +1,14 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -// package evmchain provides the `evmchain` contract, which allows to emulate an -// Ethereum blockchain on top of ISCP. -package evmchain +package evm import ( + "math/big" + "github.com/iotaledger/wasp/packages/iscp/coreutil" ) -var Contract = coreutil.NewContract("evmchain", "EVM chain smart contract") - var ( // Ethereum blockchain FuncGetBalance = coreutil.ViewFunc("getBalance") @@ -26,8 +24,8 @@ var ( FuncGetTransactionByHash = coreutil.ViewFunc("getTransactionByHash") FuncGetTransactionByBlockHashAndIndex = coreutil.ViewFunc("getTransactionByBlockHashAndIndex") FuncGetTransactionByBlockNumberAndIndex = coreutil.ViewFunc("getTransactionByBlockNumberAndIndex") - FuncGetBlockTransactionCountByHash = coreutil.ViewFunc("getBlockTransactionCountByHash") - FuncGetBlockTransactionCountByNumber = coreutil.ViewFunc("getBlockTransactionCountByNumber") + FuncGetTransactionCountByBlockHash = coreutil.ViewFunc("getTransactionCountByBlockHash") + FuncGetTransactionCountByBlockNumber = coreutil.ViewFunc("getTransactionCountByBlockNumber") FuncGetStorage = coreutil.ViewFunc("getStorage") FuncGetLogs = coreutil.ViewFunc("getLogs") @@ -56,12 +54,18 @@ const ( FieldBlockNumber = "bn" FieldBlockHash = "bh" FieldCallMsg = "c" - FieldEvmOwner = "o" - FieldNextEvmOwner = "n" + FieldNextEVMOwner = "n" FieldGasPerIota = "w" FieldGasFee = "f" FieldGasUsed = "gu" FieldFilterQuery = "fq" ) -const DefaultGasPerIota uint64 = 1000 +const ( + DefaultChainID = 1074 // IOTA -- get it? + + DefaultGasPerIota uint64 = 1000 + GasLimitDefault = uint64(15000000) +) + +var GasPrice = big.NewInt(0) diff --git a/contracts/native/evmchain/README.md b/contracts/native/evmchain/README.md deleted file mode 100644 index a216677f5f..0000000000 --- a/contracts/native/evmchain/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# The `evmchain` smart contract - -The `evmchain` smart contract emulates an Ethereum blockchain on top of the -ISCP chain, allowing to run Ethereum smart contracts. - -## EVM support - -The `evmchain` contract is implemented as a native contract, and as such it -needs to be enabled at compile time. **EVM support is enabled by default, so -no special action is needed.** - -EVM support inflates the `wasp` and `wasp-cli` binaries by several MB. If this -is a problem and you don't need EVM support, you can disable it at compile -time by providing the `-tags noevm` flag to the Go compiler. For example: - -``` -go install -tags noevm ./... -``` - -## Deploy - -You can use `wasp-cli` to deploy the `evmchain` contract (given that you -already have access to an ISCP chain and have deposited some funds into your -on-chain account): - -``` -wasp-cli chain evm deploy --alloc 0x71562b71999873DB5b286dF957af199Ec94617F7:115792089237316195423570985008687907853269984665640564039457584007913129639927 -``` - -The `--alloc` parameter specifies the genesis allocation for the EVM chain, -with syntax `address:wei [address:wei ...]`. - -## JSON-RPC - -Once your EVM chain is deployed, you can use the `wasp-cli chain evm jsonrpc` -command to start a JSON-RPC server. This will allow you to connect any standard -Ethereum tool, like Metamask. - -## Complete example using `wasp-cluster` - -In terminal #1, start a cluster: - -``` -wasp-cluster start -d -``` - -In terminal #2: - -``` -# initialize a private key and request some funds -wasp-cli init -wasp-cli request-funds - -# deploy an ISCP chain, deposit some funds to be used for fees -wasp-cli chain deploy --chain=mychain --committee=0,1,2,3 --quorum 3 -wasp-cli chain deposit IOTA:1000 - -# deploy an EVM chain -wasp-cli chain evm deploy --alloc 0x71562b71999873DB5b286dF957af199Ec94617F7:115792089237316195423570985008687907853269984665640564039457584007913129639927 -``` - -Finally we start the JSON-RPC server: - -``` -wasp-cli chain evm jsonrpc -``` diff --git a/contracts/native/evmchain/evmchain_test.go b/contracts/native/evmchain/evmchain_test.go deleted file mode 100644 index be5c5362e1..0000000000 --- a/contracts/native/evmchain/evmchain_test.go +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package evmchain - -import ( - "bytes" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/iotaledger/wasp/packages/evm/evmtest" - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/iscp/colored" - "github.com/iotaledger/wasp/packages/solo" - "github.com/iotaledger/wasp/packages/solo/solobench" - "github.com/iotaledger/wasp/packages/testutil/testlogger" - "github.com/iotaledger/wasp/packages/vm/core/accounts" - "github.com/stretchr/testify/require" -) - -func TestDeploy(t *testing.T) { - initEVMChain(t) -} - -func TestFaucetBalance(t *testing.T) { - evmChain := initEVMChain(t) - bal := evmChain.getBalance(evmChain.faucetAddress()) - require.Zero(t, evmChain.faucetSupply.Cmp(bal)) -} - -func TestStorageContract(t *testing.T) { - evmChain := initEVMChain(t) - - // deploy solidity `storage` contract - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - // call evmchain's FuncCallView to call EVM contract's `retrieve` view, get 42 - require.EqualValues(t, 42, storage.retrieve()) - - // call FuncSendTransaction with EVM tx that calls `store(43)` - res, err := storage.store(43) - require.NoError(t, err) - require.Equal(t, types.ReceiptStatusSuccessful, res.receipt.Status) - - // call `retrieve` view, get 43 - require.EqualValues(t, 43, storage.retrieve()) -} - -func TestERC20Contract(t *testing.T) { - evmChain := initEVMChain(t) - - // deploy solidity `erc20` contract - erc20 := evmChain.deployERC20Contract(evmChain.faucetKey, "TestCoin", "TEST") - - // call `totalSupply` view - { - v := erc20.totalSupply() - // 100 * 10^18 - expected := new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) - require.Zero(t, v.Cmp(expected)) - } - - _, recipientAddress := generateEthereumKey(t) - transferAmount := big.NewInt(1337) - - // call `transfer` => send 1337 TestCoin to recipientAddress - res, err := erc20.transfer(recipientAddress, transferAmount) - require.NoError(t, err) - - require.Equal(t, types.ReceiptStatusSuccessful, res.receipt.Status) - require.Equal(t, 1, len(res.receipt.Logs)) - - // call `balanceOf` view => check balance of recipient = 1337 TestCoin - require.Zero(t, erc20.balanceOf(recipientAddress).Cmp(transferAmount)) -} - -func TestGetCode(t *testing.T) { - evmChain := initEVMChain(t) - erc20 := evmChain.deployERC20Contract(evmChain.faucetKey, "TestCoin", "TEST") - - // get contract bytecode from EVM emulator - retrievedBytecode := evmChain.getCode(erc20.address) - - // ensure returned bytecode matches the expected runtime bytecode - require.True(t, bytes.Equal(retrievedBytecode, evmtest.ERC20ContractRuntimeBytecode), "bytecode retrieved from the chain must match the deployed bytecode") -} - -func TestGasCharged(t *testing.T) { - evmChain := initEVMChain(t) - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - iotaWallet, iotaAddress := evmChain.solo.NewKeyPairWithFunds() - iotaAgentID := iscp.NewAgentID(iotaAddress, 0) - - initialBalance := evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA) - iotasSent := initialBalance - 1 - - // call `store(999)` with enough gas - res, err := storage.store(999, ethCallOptions{iota: iotaCallOptions{wallet: iotaWallet, transfer: iotasSent}}) - require.NoError(t, err) - require.Greater(t, res.iotaChargedFee, uint64(0)) - - // call `retrieve` view, get 42 - require.EqualValues(t, 999, storage.retrieve()) - - // user on-chain account is credited with excess iotas (iotasSent - gasUsed) - expectedUserBalance := iotasSent - res.iotaChargedFee - - evmChain.soloChain.AssertIotas(iotaAgentID, expectedUserBalance) - - // call `store(123)` without enough gas - _, err = storage.store(123, ethCallOptions{iota: iotaCallOptions{wallet: iotaWallet, transfer: 1}}) - require.Contains(t, err.Error(), "transferred tokens (1) not enough") - - // call `retrieve` view, get 999 - which means store(123) failed and the previous state is kept - require.EqualValues(t, 999, storage.retrieve()) - - // verify user on-chain account still has the same balance (no refund happened) - evmChain.soloChain.AssertIotas(iotaAgentID, expectedUserBalance) -} - -func TestOwner(t *testing.T) { - evmChain := initEVMChain(t) - - // the default owner is correct - owner := evmChain.getOwner() - require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) - - // only the owner can call the setOwner endpoint - user1Wallet, user1Address := evmChain.solo.NewKeyPairWithFunds() - user1AgentID := iscp.NewAgentID(user1Address, 0) - _, err := evmChain.soloChain.PostRequestSync( - solo.NewCallParams(Contract.Name, FuncSetNextOwner.Name, FieldNextEvmOwner, user1AgentID). - WithIotas(100000), - user1Wallet, - ) - require.Error(t, err) - - // ensure owner didn't change after a failed call - owner = evmChain.getOwner() - require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) - - // current owner is able to set a new "next owner" - _, err = evmChain.soloChain.PostRequestSync( - solo.NewCallParams(Contract.Name, FuncSetNextOwner.Name, FieldNextEvmOwner, user1AgentID). - WithIotas(100000), - evmChain.soloChain.OriginatorKeyPair, - ) - require.NoError(t, err) - - // check that the owner didn't change yet (new owner needs to claim ownership) - owner = evmChain.getOwner() - require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) - - // check no other user can claim ownership - user2Wallet, _ := evmChain.solo.NewKeyPairWithFunds() - - _, err = evmChain.soloChain.PostRequestSync( - solo.NewCallParams(Contract.Name, FuncClaimOwnership.Name). - WithIotas(100000), - user2Wallet, - ) - require.Error(t, err) - - // owner still the same - owner = evmChain.getOwner() - require.True(t, owner.Equals(evmChain.soloChain.OriginatorAgentID)) - - // claim ownership successfully - _, err = evmChain.soloChain.PostRequestSync( - solo.NewCallParams(Contract.Name, FuncClaimOwnership.Name). - WithIotas(100000), - user1Wallet, - ) - require.NoError(t, err) - owner = evmChain.getOwner() - require.True(t, owner.Equals(user1AgentID)) -} - -func TestGasPerIotas(t *testing.T) { - evmChain := initEVMChain(t) - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - // the default value is correct - require.Equal(t, DefaultGasPerIota, evmChain.getGasPerIotas()) - - res, err := storage.store(43) - require.NoError(t, err) - initialGasFee := res.iotaChargedFee - - // only the owner can call the setGasPerIotas endpoint - newGasPerIota := DefaultGasPerIota * 1000 - newUserWallet, _ := evmChain.solo.NewKeyPairWithFunds() - err = evmChain.setGasPerIotas(newGasPerIota, iotaCallOptions{wallet: newUserWallet}) - require.Contains(t, err.Error(), "can only be called by the contract owner") - require.Equal(t, DefaultGasPerIota, evmChain.getGasPerIotas()) - - // current owner is able to set a new gasPerIotas - err = evmChain.setGasPerIotas(newGasPerIota, iotaCallOptions{wallet: evmChain.soloChain.OriginatorKeyPair}) - require.NoError(t, err) - require.Equal(t, newGasPerIota, evmChain.getGasPerIotas()) - - // run an equivalent request and compare the gas fees - res, err = storage.store(44) - require.NoError(t, err) - require.Less(t, res.iotaChargedFee, initialGasFee) -} - -func TestWithdrawalOwnerFees(t *testing.T) { - evmChain := initEVMChain(t) - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - // only the owner can call withdrawal - user1Wallet, user1Address := evmChain.solo.NewKeyPairWithFunds() - user1AgentID := iscp.NewAgentID(user1Address, 0) - - err := evmChain.withdrawGasFees(user1Wallet) - require.Contains(t, err.Error(), "can only be called by the contract owner") - - // change owner to user1 - err = evmChain.setNextOwner(user1AgentID) - require.NoError(t, err) - err = evmChain.claimOwnership(iotaCallOptions{wallet: user1Wallet}) - require.NoError(t, err) - - // collect fees from contract deployment - user1Balance0 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) - require.NoError(t, evmChain.withdrawGasFees(user1Wallet)) - user1Balance1 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) - require.Greater(t, user1Balance1, user1Balance0) - - // collect fees from a SC call, check that the collected fees matches the fees charged - user1Balance2 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) - res, err := storage.store(43) - require.NoError(t, err) - require.NoError(t, evmChain.withdrawGasFees(user1Wallet)) - user1Balance3 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) - require.Equal(t, user1Balance3, user1Balance2+res.iotaChargedFee) - - // try to withdraw a second time, it should succeed, but owner balance shouldnt not change (there are no fees to withdraw) - require.NoError(t, evmChain.withdrawGasFees(user1Wallet)) - user1Balance4 := evmChain.solo.GetAddressBalance(user1Address, colored.IOTA) - require.Equal(t, user1Balance3, user1Balance4) - - // try to withdraw fees to another actor using using the FieldAgentId param - res, err = storage.store(44) - require.NoError(t, err) - _, user2Address := evmChain.solo.NewKeyPairWithFunds() - user2AgentID := iscp.NewAgentID(user2Address, 0) - user2Balance0 := evmChain.solo.GetAddressBalance(user2Address, colored.IOTA) - err = evmChain.withdrawGasFees(user1Wallet, user2AgentID) - require.NoError(t, err) - user2Balance1 := evmChain.solo.GetAddressBalance(user2Address, colored.IOTA) - require.Equal(t, user2Balance1, user2Balance0+res.iotaChargedFee+1) // 1 extra iota from the withdrawal request -} - -// tests that the gas limits are correctly enforced based on the iotas sent -func TestGasLimit(t *testing.T) { - evmChain := initEVMChain(t) - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - gasPerIotas := evmChain.getGasPerIotas() - - // estimate gas by sending a valid tx - result, err := storage.store(123) - require.NoError(t, err) - gas := result.receipt.GasUsed - - // send again with same gas limit but not enough iotas - _, err = storage.store(123, ethCallOptions{gasLimit: gas, iota: iotaCallOptions{transfer: (gas+1)/gasPerIotas - 1}}) - require.Error(t, err) - require.Regexp(t, `transferred tokens \(\d+\) not enough`, err.Error()) - - // send again with gas limit not enough for transaction - _, err = storage.store(123, ethCallOptions{gasLimit: 1 * gasPerIotas, iota: iotaCallOptions{transfer: 1}}) - require.Error(t, err) - require.Regexp(t, `intrinsic gas too low: have \d+, want \d+`, err.Error()) -} - -// ensure the amount of iotas sent impacts the amount of loop iterators (gas used) -func TestLoop(t *testing.T) { - evmChain := initEVMChain(t) - loop := evmChain.deployLoopContract(evmChain.faucetKey) - gasPerIotas := evmChain.getGasPerIotas() - - iotaWallet, iotaAddress := evmChain.solo.NewKeyPairWithFunds() - iotaAgentID := iscp.NewAgentID(iotaAddress, 0) - - initialBalance := evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA) - iotasSpent1 := uint64(100) - res, err := loop.loop(ethCallOptions{ - gasLimit: iotasSpent1 * gasPerIotas, - iota: iotaCallOptions{wallet: iotaWallet, transfer: iotasSpent1}, - }) - require.NoError(t, err) - require.Equal(t, res.iotaChargedFee, iotasSpent1) - gasUsed := res.receipt.GasUsed - - iotasSpent2 := uint64(1000) - res, err = loop.loop(ethCallOptions{ - gasLimit: iotasSpent2 * gasPerIotas, - iota: iotaCallOptions{wallet: iotaWallet, transfer: iotasSpent2}, - }) - require.NoError(t, err) - require.Equal(t, res.iotaChargedFee, iotasSpent2) - require.Greater(t, res.receipt.GasUsed, gasUsed) - - // ensure iotas sent are kept by the evmchain SC - require.Equal(t, evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA), initialBalance-iotasSpent1-iotasSpent2) - evmChain.soloChain.AssertIotas(iotaAgentID, 0) -} - -func TestNonFaucetUsers(t *testing.T) { - evmChain := initEVMChain(t) - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - // call EVM contract with a new key (that doesn't own ether on the EVM evmChain.soloChain) - gasPerIotas := evmChain.getGasPerIotas() - iotas := uint64(10000) - // this should be successful because gasPrice is 0 - res, err := storage.store(123, ethCallOptions{gasLimit: iotas * gasPerIotas, iota: iotaCallOptions{transfer: iotas}}) - require.NoError(t, err) - require.Greater(t, res.iotaChargedFee, uint64(0)) - - require.EqualValues(t, 123, storage.retrieve()) -} - -func TestPrePaidFees(t *testing.T) { - evmChain := initEVMChain(t) - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - iotaWallet, iotaAddress := evmChain.solo.NewKeyPairWithFunds() - - // test sending off-ledger request without depositing funds first - txdata, _, _ := storage.buildEthTxData(nil, "store", uint32(999)) - offledgerRequest := evmChain.buildSoloRequest(FuncSendTransaction.Name, 100, FieldTransactionData, txdata) - evmChain.soloChain.PostRequestOffLedger(offledgerRequest, iotaWallet) - - // check that the tx has no effect - require.EqualValues(t, 42, storage.retrieve()) - - // deposit funds - initialBalance := evmChain.solo.GetAddressBalance(iotaAddress, colored.IOTA) - _, err := evmChain.soloChain.PostRequestSync( - solo.NewCallParams(accounts.Contract.Name, accounts.FuncDeposit.Name).WithIotas(initialBalance), - iotaWallet, - ) - require.NoError(t, err) - - // send offledger request again and check that is works - evmChain.soloChain.PostRequestOffLedger(offledgerRequest, iotaWallet) - require.EqualValues(t, 999, storage.retrieve()) -} - -func initBenchmark(b *testing.B) (*solo.Chain, []*solo.CallParams) { - // setup: deploy the evmchain contract - log := testlogger.NewSilentLogger(b.Name(), true) - env := solo.NewWithLogger(b, log).WithNativeContract(Processor) - evmChain := initEVMChainWithSolo(b, env) - // setup: deploy the `storage` EVM contract - storage := evmChain.deployStorageContract(evmChain.faucetKey, 42) - - // setup: prepare N requests that call FuncSendTransaction with an EVM tx - // that calls `storage.store()` - reqs := make([]*solo.CallParams, b.N) - for i := 0; i < b.N; i++ { - sender, err := crypto.GenerateKey() // send from a new address so that nonce is always 0 - require.NoError(b, err) - - opt := ethCallOptions{sender: sender} - txdata, _, opt := storage.buildEthTxData([]ethCallOptions{opt}, "store", uint32(i)) - iotaOpt := storage.chain.parseIotaCallOptions([]iotaCallOptions{opt.iota}) - reqs[i] = storage.chain.buildSoloRequest(FuncSendTransaction.Name, iotaOpt.transfer, FieldTransactionData, txdata) - } - - return evmChain.soloChain, reqs -} - -// BenchmarkEVMStorageSync is a benchmark for the evmchain contract running under solo, -// processing requests synchronously, and producing 1 block per request. -// run with: go test -benchmem -cpu=1 -run=' ' -bench='Bench.*' -func BenchmarkEVMStorageSync(b *testing.B) { - chain, reqs := initBenchmark(b) - solobench.RunBenchmarkSync(b, chain, reqs, nil) -} - -// BenchmarkEVMStorageAsync is a benchmark for the evmchain contract running under solo, -// processing requests asynchronously, and producing 1 block per many requests. -// run with: go test -benchmem -cpu=1 -run=' ' -bench='Bench.*' -func BenchmarkEVMStorageAsync(b *testing.B) { - chain, reqs := initBenchmark(b) - solobench.RunBenchmarkAsync(b, chain, reqs, nil) -} diff --git a/contracts/native/evmchain/evmmanagement_test.go b/contracts/native/evmchain/evmmanagement_test.go deleted file mode 100644 index e17134ad45..0000000000 --- a/contracts/native/evmchain/evmmanagement_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package evmchain - -import ( - "testing" - - "github.com/iotaledger/wasp/packages/iscp/colored" - - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/iscp/assert" - "github.com/iotaledger/wasp/packages/iscp/coreutil" - "github.com/iotaledger/wasp/packages/kv/dict" - "github.com/iotaledger/wasp/packages/solo" - "github.com/stretchr/testify/require" -) - -// TODO this SC could adjust gasPerIota based on some conditions - -var ( - evmChainMgmtContract = coreutil.NewContract("EVMChainManagement", "EVM chain management") - - mgmtFuncClaimOwnership = coreutil.Func("claimOwnership") - mgmtFuncWithdrawGasFees = coreutil.Func("withdrawGasFees") - - evmChainMgmtProcessor = evmChainMgmtContract.Processor(nil, - mgmtFuncClaimOwnership.WithHandler(func(ctx iscp.Sandbox) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - _, err := ctx.Call(Contract.Hname(), FuncClaimOwnership.Hname(), nil, nil) - a.RequireNoError(err) - return nil, nil - }), - mgmtFuncWithdrawGasFees.WithHandler(func(ctx iscp.Sandbox) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - _, err := ctx.Call(Contract.Hname(), FuncWithdrawGasFees.Hname(), nil, nil) - a.RequireNoError(err) - return nil, nil - }), - ) -) - -func TestRequestGasFees(t *testing.T) { - evmChain := initEVMChain(t, evmChainMgmtProcessor) - soloChain := evmChain.soloChain - - err := soloChain.DeployContract(nil, evmChainMgmtContract.Name, evmChainMgmtContract.ProgramHash) - require.NoError(t, err) - - // deploy solidity `storage` contract (just to produce some fees to be claimed) - evmChain.deployStorageContract(evmChain.faucetKey, 42) - - // change owner to evnchainmanagement SC - managerAgentID := iscp.NewAgentID(soloChain.ChainID.AsAddress(), iscp.Hn(evmChainMgmtContract.Name)) - _, err = soloChain.PostRequestSync( - solo.NewCallParams(Contract.Name, FuncSetNextOwner.Name, FieldNextEvmOwner, managerAgentID). - WithIotas(1), - soloChain.OriginatorKeyPair, - ) - require.NoError(t, err) - - // claim ownership - _, err = soloChain.PostRequestSync( - solo.NewCallParams(evmChainMgmtContract.Name, mgmtFuncClaimOwnership.Name).WithIotas(1), - soloChain.OriginatorKeyPair, - ) - require.NoError(t, err) - - // call requestGasFees manually, so that the manager SC request funds from the evm chain, check funds are received by the manager SC - balance0 := soloChain.GetAccountBalance(managerAgentID).Get(colored.IOTA) - - _, err = soloChain.PostRequestSync( - solo.NewCallParams(evmChainMgmtContract.Name, mgmtFuncWithdrawGasFees.Name).WithIotas(1), - soloChain.OriginatorKeyPair, - ) - require.NoError(t, err) - balance1 := soloChain.GetAccountBalance(managerAgentID).Get(colored.IOTA) - - require.Greater(t, balance1, balance0) -} diff --git a/contracts/native/evmchain/impl.go b/contracts/native/evmchain/impl.go deleted file mode 100644 index d2ee4836b5..0000000000 --- a/contracts/native/evmchain/impl.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package evmchain - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/iotaledger/wasp/packages/evm" - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/iscp/assert" - "github.com/iotaledger/wasp/packages/iscp/colored" - "github.com/iotaledger/wasp/packages/kv/codec" - "github.com/iotaledger/wasp/packages/kv/dict" - "github.com/iotaledger/wasp/packages/kv/kvdecoder" - "github.com/iotaledger/wasp/packages/vm/core/accounts" -) - -var Processor = Contract.Processor(initialize, - // Ethereum blockchain - FuncSendTransaction.WithHandler(applyTransaction), - FuncGetBalance.WithHandler(getBalance), - FuncCallContract.WithHandler(callContract), - FuncEstimateGas.WithHandler(estimateGas), - FuncGetNonce.WithHandler(getNonce), - FuncGetReceipt.WithHandler(getReceipt), - FuncGetCode.WithHandler(getCode), - FuncGetBlockNumber.WithHandler(getBlockNumber), - FuncGetBlockByNumber.WithHandler(getBlockByNumber), - FuncGetBlockByHash.WithHandler(getBlockByHash), - FuncGetTransactionByHash.WithHandler(getTransactionByHash), - FuncGetTransactionByBlockHashAndIndex.WithHandler(getTransactionByBlockHashAndIndex), - FuncGetTransactionByBlockNumberAndIndex.WithHandler(getTransactionByBlockNumberAndIndex), - FuncGetBlockTransactionCountByHash.WithHandler(getBlockTransactionCountByHash), - FuncGetBlockTransactionCountByNumber.WithHandler(getBlockTransactionCountByNumber), - FuncGetStorage.WithHandler(getStorage), - FuncGetLogs.WithHandler(getLogs), - - // EVMchain SC management - FuncSetNextOwner.WithHandler(setNextOwner), - FuncClaimOwnership.WithHandler(claimOwnership), - FuncSetGasPerIota.WithHandler(setGasPerIota), - FuncWithdrawGasFees.WithHandler(withdrawGasFees), - FuncGetOwner.WithHandler(getOwner), - FuncGetGasPerIota.WithHandler(getGasPerIota), -) - -func initialize(ctx iscp.Sandbox) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - genesisAlloc, err := DecodeGenesisAlloc(ctx.Params().MustGet(FieldGenesisAlloc)) - a.RequireNoError(err) - chainID, err := codec.DecodeUint16(ctx.Params().MustGet(FieldChainID), evm.DefaultChainID) - a.RequireNoError(err) - evm.InitGenesis( - int(chainID), - rawdb.NewDatabase(evm.NewKVAdapter(ctx.State())), - genesisAlloc, - evm.GasLimitDefault, - timestamp(ctx), - ) - ctx.State().Set(FieldGasPerIota, codec.EncodeUint64(DefaultGasPerIota)) - ctx.State().Set(FieldEvmOwner, codec.EncodeAgentID(ctx.ContractCreator())) - return nil, nil -} - -func applyTransaction(ctx iscp.Sandbox) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - - tx := &types.Transaction{} - err := tx.UnmarshalBinary(ctx.Params().MustGet(FieldTransactionData)) - a.RequireNoError(err) - - transferredIotas := ctx.IncomingTransfer().Get(getFeeColor(ctx)) - gasPerIota, err := codec.DecodeUint64(ctx.State().MustGet(FieldGasPerIota), 0) - a.RequireNoError(err) - - a.Require( - transferredIotas >= tx.Gas()/gasPerIota, - "transferred tokens (%d) not enough to cover the gas limit set in the transaction (%d at %d gas per iota token)", transferredIotas, tx.Gas(), gasPerIota, - ) - - emu := getOrCreateEmulator(ctx) - receipt, err := emu.SendTransaction(tx) - a.RequireNoError(err) - - iotasGasFee := receipt.GasUsed / gasPerIota - if transferredIotas > iotasGasFee { - // refund unspent gas fee to the sender's on-chain account - iotasGasRefund := transferredIotas - iotasGasFee - _, err = ctx.Call( - accounts.Contract.Hname(), - accounts.FuncDeposit.Hname(), - dict.Dict{accounts.ParamAgentID: codec.EncodeAgentID(ctx.Caller())}, - colored.NewBalancesForIotas(iotasGasRefund), - ) - a.RequireNoError(err) - } - - return dict.Dict{ - FieldGasFee: codec.EncodeUint64(iotasGasFee), - FieldGasUsed: codec.EncodeUint64(receipt.GasUsed), - }, nil -} - -func getBalance(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - addr := common.BytesToAddress(ctx.Params().MustGet(FieldAddress)) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) - bal, err := emu.BalanceAt(addr, blockNumber) - a.RequireNoError(err) - return result(bal.Bytes()) - }) -} - -func getBlockNumber(ctx iscp.SandboxView) (dict.Dict, error) { - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - return result(emu.Blockchain().CurrentBlock().Number().Bytes()) - }) -} - -func getBlockByNumber(ctx iscp.SandboxView) (dict.Dict, error) { - return withBlockByNumber(ctx, func(emu *evm.EVMEmulator, block *types.Block) dict.Dict { - if block == nil { - return nil - } - return result(EncodeBlock(block)) - }) -} - -func getBlockByHash(ctx iscp.SandboxView) (dict.Dict, error) { - return withBlockByHash(ctx, func(emu *evm.EVMEmulator, block *types.Block) dict.Dict { - if block == nil { - return nil - } - return result(EncodeBlock(block)) - }) -} - -func getTransactionByHash(ctx iscp.SandboxView) (dict.Dict, error) { - return withTransactionByHash(ctx, func(emu *evm.EVMEmulator, tx *types.Transaction) dict.Dict { - return txResult(ctx, emu, tx) - }) -} - -func getTransactionByBlockHashAndIndex(ctx iscp.SandboxView) (dict.Dict, error) { - return withTransactionByBlockHashAndIndex(ctx, func(emu *evm.EVMEmulator, tx *types.Transaction) dict.Dict { - return txResult(ctx, emu, tx) - }) -} - -func getTransactionByBlockNumberAndIndex(ctx iscp.SandboxView) (dict.Dict, error) { - return withTransactionByBlockNumberAndIndex(ctx, func(emu *evm.EVMEmulator, tx *types.Transaction) dict.Dict { - return txResult(ctx, emu, tx) - }) -} - -func getBlockTransactionCountByHash(ctx iscp.SandboxView) (dict.Dict, error) { - return withBlockByHash(ctx, func(emu *evm.EVMEmulator, block *types.Block) dict.Dict { - if block == nil { - return nil - } - return result(codec.EncodeUint64(uint64(len(block.Transactions())))) - }) -} - -func getBlockTransactionCountByNumber(ctx iscp.SandboxView) (dict.Dict, error) { - return withBlockByNumber(ctx, func(emu *evm.EVMEmulator, block *types.Block) dict.Dict { - if block == nil { - return nil - } - return result(codec.EncodeUint64(uint64(len(block.Transactions())))) - }) -} - -func getReceipt(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - return withTransactionByHash(ctx, func(emu *evm.EVMEmulator, tx *types.Transaction) dict.Dict { - if tx == nil { - return nil - } - receipt, err := emu.TransactionReceipt(tx.Hash()) - a.RequireNoError(err) - - return result(NewReceipt(receipt, tx).Bytes()) - }) -} - -func getNonce(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - addr := common.BytesToAddress(ctx.Params().MustGet(FieldAddress)) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) - nonce, err := emu.NonceAt(addr, blockNumber) - a.RequireNoError(err) - return result(codec.EncodeUint64(nonce)) - }) -} - -func getCode(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - addr := common.BytesToAddress(ctx.Params().MustGet(FieldAddress)) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) - code, err := emu.CodeAt(addr, blockNumber) - a.RequireNoError(err) - return result(code) - }) -} - -func getStorage(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - addr := common.BytesToAddress(ctx.Params().MustGet(FieldAddress)) - key := common.BytesToHash(ctx.Params().MustGet(FieldKey)) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) - data, err := emu.StorageAt(addr, key, blockNumber) - a.RequireNoError(err) - return result(data) - }) -} - -func getLogs(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - q, err := DecodeFilterQuery(ctx.Params().MustGet(FieldFilterQuery)) - a.RequireNoError(err) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - logs, err := emu.FilterLogs(q) - a.RequireNoError(err) - return result(EncodeLogs(logs)) - }) -} - -func callContract(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - callMsg, err := DecodeCallMsg(ctx.Params().MustGet(FieldCallMsg)) - a.RequireNoError(err) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - blockNumber := paramBlockNumberOrHashAsNumber(ctx, emu) - res, err := emu.CallContract(callMsg, blockNumber) - a.RequireNoError(err) - return result(res) - }) -} - -func estimateGas(ctx iscp.SandboxView) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - callMsg, err := DecodeCallMsg(ctx.Params().MustGet(FieldCallMsg)) - a.RequireNoError(err) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - gas, err := emu.EstimateGas(callMsg) - a.RequireNoError(err) - return result(codec.EncodeUint64(gas)) - }) -} - -// EVM chain management functions /////////////////////////////////////////////////////////////////////////////////////// - -func requireOwner(ctx iscp.Sandbox) { - contractOwner, err := codec.DecodeAgentID(ctx.State().MustGet(FieldEvmOwner)) - a := assert.NewAssert(ctx.Log()) - a.RequireNoError(err) - a.Require(contractOwner.Equals(ctx.Caller()), "can only be called by the contract owner") -} - -func setNextOwner(ctx iscp.Sandbox) (dict.Dict, error) { - requireOwner(ctx) - par := kvdecoder.New(ctx.Params(), ctx.Log()) - ctx.State().Set(FieldNextEvmOwner, codec.EncodeAgentID(par.MustGetAgentID(FieldNextEvmOwner))) - return nil, nil -} - -func claimOwnership(ctx iscp.Sandbox) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - - nextOwner, err := codec.DecodeAgentID(ctx.State().MustGet(FieldNextEvmOwner)) - a.RequireNoError(err) - a.Require(nextOwner.Equals(ctx.Caller()), "Can only be called by the contract owner") - - ctx.State().Set(FieldEvmOwner, codec.EncodeAgentID(nextOwner)) - return nil, nil -} - -func getOwner(ctx iscp.SandboxView) (dict.Dict, error) { - return result(ctx.State().MustGet(FieldEvmOwner)), nil -} - -func setGasPerIota(ctx iscp.Sandbox) (dict.Dict, error) { - requireOwner(ctx) - par := kvdecoder.New(ctx.Params()) - gasPerIotaBin := codec.EncodeUint64(par.MustGetUint64(FieldGasPerIota)) - ctx.State().Set(FieldGasPerIota, gasPerIotaBin) - return nil, nil -} - -func getGasPerIota(ctx iscp.SandboxView) (dict.Dict, error) { - return result(ctx.State().MustGet(FieldGasPerIota)), nil -} - -func withdrawGasFees(ctx iscp.Sandbox) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - requireOwner(ctx) - - paramsDecoder := kvdecoder.New(ctx.Params(), ctx.Log()) - targetAgentID := paramsDecoder.MustGetAgentID(FieldAgentID, ctx.Caller()) - - isOnChain := targetAgentID.Address().Equals(ctx.ChainID().AsAddress()) - - if isOnChain { - params := codec.MakeDict(map[string]interface{}{ - accounts.ParamAgentID: targetAgentID, - }) - _, err := ctx.Call(accounts.Contract.Hname(), accounts.FuncDeposit.Hname(), params, ctx.Balances()) - a.RequireNoError(err) - return nil, nil - } - - a.Require(ctx.Send(targetAgentID.Address(), ctx.Balances(), &iscp.SendMetadata{ - TargetContract: targetAgentID.Hname(), - }), "withdraw.inconsistency: failed sending tokens ") - - return nil, nil -} diff --git a/contracts/native/evmchain/internal.go b/contracts/native/evmchain/internal.go deleted file mode 100644 index abf51520cd..0000000000 --- a/contracts/native/evmchain/internal.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package evmchain - -import ( - "math/big" - "time" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/iotaledger/wasp/packages/evm" - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/iscp/assert" - "github.com/iotaledger/wasp/packages/iscp/colored" - "github.com/iotaledger/wasp/packages/kv/buffered" - "github.com/iotaledger/wasp/packages/kv/codec" - "github.com/iotaledger/wasp/packages/kv/dict" - "github.com/iotaledger/wasp/packages/vm/core/governance" -) - -func isNotFound(err error) bool { - switch err { - case ethereum.NotFound: - return true - case evm.ErrTransactionDoesNotExist: - return true - case evm.ErrBlockDoesNotExist: - return true - } - return false -} - -// getOrCreateEmulator creates a new emulator instance if this is the first call to applyTransaction -// in the ISCP block; otherwise it returns the previously created instance. The purpose is to -// create a single Ethereum block for each ISCP block. -func getOrCreateEmulator(ctx iscp.Sandbox) *evm.EVMEmulator { - bctx := ctx.BlockContext(createEmulator, commitEthereumBlock) - return bctx.(*evm.EVMEmulator) -} - -func createEmulator(ctx iscp.Sandbox) interface{} { - return evm.NewEVMEmulator(rawdb.NewDatabase(evm.NewKVAdapter(ctx.State())), timestamp(ctx)) -} - -// timestamp returns the current timestamp in seconds since epoch -func timestamp(ctx iscp.SandboxBase) uint64 { - tsNano := time.Duration(ctx.GetTimestamp()) * time.Nanosecond - return uint64(tsNano / time.Second) -} - -func commitEthereumBlock(blockContext interface{}) { - emu := blockContext.(*evm.EVMEmulator) - emu.Commit() - _ = emu.Close() -} - -func withEmulatorR(ctx iscp.SandboxView, f func(*evm.EVMEmulator) dict.Dict) (dict.Dict, error) { - emu := evm.NewEVMEmulator( - rawdb.NewDatabase(evm.NewKVAdapter(buffered.NewBufferedKVStoreAccess(ctx.State()))), - timestamp(ctx), - ) - defer emu.Close() - return f(emu), nil -} - -func result(value []byte) dict.Dict { - if value == nil { - return nil - } - return dict.Dict{FieldResult: value} -} - -func txResult(ctx iscp.SandboxView, emu *evm.EVMEmulator, tx *types.Transaction) dict.Dict { - a := assert.NewAssert(ctx.Log()) - if tx == nil { - return nil - } - receipt, err := emu.TransactionReceipt(tx.Hash()) - a.RequireNoError(err) - return dict.Dict{ - FieldTransaction: EncodeTransaction(tx), - FieldBlockHash: receipt.BlockHash.Bytes(), - FieldBlockNumber: codec.EncodeUint64(receipt.BlockNumber.Uint64()), - FieldTransactionIndex: codec.EncodeUint64(uint64(receipt.TransactionIndex)), - } -} - -func withBlockByNumber(ctx iscp.SandboxView, f func(*evm.EVMEmulator, *types.Block) dict.Dict) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - blockNumber := paramBlockNumber(ctx) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - block, err := emu.BlockByNumber(blockNumber) - if !isNotFound(err) { - a.RequireNoError(err) - } - - return f(emu, block) - }) -} - -func withBlockByHash(ctx iscp.SandboxView, f func(*evm.EVMEmulator, *types.Block) dict.Dict) (dict.Dict, error) { - hash := common.BytesToHash(ctx.Params().MustGet(FieldBlockHash)) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - block := emu.BlockByHash(hash) - return f(emu, block) - }) -} - -func withTransactionByHash(ctx iscp.SandboxView, f func(*evm.EVMEmulator, *types.Transaction) dict.Dict) (dict.Dict, error) { - txHash := common.BytesToHash(ctx.Params().MustGet(FieldTransactionHash)) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - tx := emu.TransactionByHash(txHash) - return f(emu, tx) - }) -} - -func withTransactionByBlockHashAndIndex(ctx iscp.SandboxView, f func(*evm.EVMEmulator, *types.Transaction) dict.Dict) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - blockHash := common.BytesToHash(ctx.Params().MustGet(FieldBlockHash)) - index, err := codec.DecodeUint64(ctx.Params().MustGet(FieldTransactionIndex), 0) - a.RequireNoError(err) - - return withEmulatorR(ctx, func(emu *evm.EVMEmulator) dict.Dict { - tx, err := emu.TransactionInBlock(blockHash, uint(index)) - if !isNotFound(err) { - a.RequireNoError(err) - } - return f(emu, tx) - }) -} - -func withTransactionByBlockNumberAndIndex(ctx iscp.SandboxView, f func(*evm.EVMEmulator, *types.Transaction) dict.Dict) (dict.Dict, error) { - a := assert.NewAssert(ctx.Log()) - index, err := codec.DecodeUint64(ctx.Params().MustGet(FieldTransactionIndex), 0) - a.RequireNoError(err) - return withBlockByNumber(ctx, func(emu *evm.EVMEmulator, block *types.Block) dict.Dict { - if block == nil || index >= uint64(len(block.Transactions())) { - return f(emu, nil) - } - return f(emu, block.Transactions()[index]) - }) -} - -func paramBlockNumber(ctx iscp.SandboxView) *big.Int { - if ctx.Params().MustHas(FieldBlockNumber) { - return new(big.Int).SetBytes(ctx.Params().MustGet(FieldBlockNumber)) - } - return nil // latest block -} - -func paramBlockNumberOrHashAsNumber(ctx iscp.SandboxView, emu *evm.EVMEmulator) *big.Int { - if ctx.Params().MustHas(FieldBlockHash) { - a := assert.NewAssert(ctx.Log()) - blockHash := common.BytesToHash(ctx.Params().MustGet(FieldBlockHash)) - header, err := emu.HeaderByHash(blockHash) - a.RequireNoError(err) - a.Require(header != nil, "block not found") - return header.Number - } - return paramBlockNumber(ctx) -} - -func getFeeColor(ctx iscp.Sandbox) colored.Color { - a := assert.NewAssert(ctx.Log()) - - // call root contract view to get the feecolor - feeInfo, err := ctx.Call( - governance.Contract.Hname(), - governance.FuncGetFeeInfo.Hname(), - dict.Dict{governance.ParamHname: Contract.Hname().Bytes()}, - nil, - ) - a.RequireNoError(err) - feeColor, err := codec.DecodeColor(feeInfo.MustGet(governance.ParamFeeColor)) - a.RequireNoError(err) - return feeColor -} diff --git a/contracts/native/evmchain/receipt.go b/contracts/native/evmchain/receipt.go deleted file mode 100644 index 366c2b7df4..0000000000 --- a/contracts/native/evmchain/receipt.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package evmchain - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/iotaledger/hive.go/marshalutil" -) - -type Receipt struct { - TxHash common.Hash - TransactionIndex uint32 - BlockHash common.Hash - BlockNumber *big.Int - From common.Address - To *common.Address - CumulativeGasUsed uint64 - GasUsed uint64 - ContractAddress *common.Address - Logs []*types.Log - Bloom types.Bloom - Status uint64 -} - -func NewReceipt(r *types.Receipt, tx *types.Transaction) *Receipt { - ret := &Receipt{ - TxHash: r.TxHash, - TransactionIndex: uint32(r.TransactionIndex), - BlockHash: r.BlockHash, - BlockNumber: r.BlockNumber, - From: getSender(tx), - To: tx.To(), - CumulativeGasUsed: r.CumulativeGasUsed, - GasUsed: r.GasUsed, - Logs: r.Logs, - Bloom: r.Bloom, - Status: r.Status, - } - if r.ContractAddress != (common.Address{}) { - ret.ContractAddress = &common.Address{} - ret.ContractAddress.SetBytes(r.ContractAddress.Bytes()) - } - return ret -} - -func (r *Receipt) Bytes() []byte { - m := marshalutil.New() - m.WriteBytes(r.TxHash.Bytes()) - m.WriteUint32(r.TransactionIndex) - m.WriteBytes(r.BlockHash.Bytes()) - writeBytes(m, r.BlockNumber.Bytes()) - m.WriteBytes(r.From.Bytes()) - m.WriteBool(r.To != nil) - if r.To != nil { - m.WriteBytes(r.To.Bytes()) - } - m.WriteUint64(r.CumulativeGasUsed) - m.WriteUint64(r.GasUsed) - m.WriteBool(r.ContractAddress != nil) - if r.ContractAddress != nil { - m.WriteBytes(r.ContractAddress.Bytes()) - } - m.WriteUint32(uint32(len(r.Logs))) - for _, log := range r.Logs { - writeBytes(m, EncodeLog(log, false)) - } - m.WriteBytes(r.Bloom.Bytes()) - m.WriteUint64(r.Status) - return m.Bytes() -} - -func DecodeReceipt(receiptBytes []byte) (*Receipt, error) { //nolint:funlen - m := marshalutil.New(receiptBytes) - r := &Receipt{} - var err error - var b []byte - var exists bool - - if b, err = m.ReadBytes(common.HashLength); err != nil { - return nil, err - } - r.TxHash.SetBytes(b) - - if r.TransactionIndex, err = m.ReadUint32(); err != nil { - return nil, err - } - - if b, err = m.ReadBytes(common.HashLength); err != nil { - return nil, err - } - r.BlockHash.SetBytes(b) - - if b, err = readBytes(m); err != nil { - return nil, err - } - r.BlockNumber = new(big.Int) - r.BlockNumber.SetBytes(b) - - if b, err = m.ReadBytes(common.AddressLength); err != nil { - return nil, err - } - r.From.SetBytes(b) - - if exists, err = m.ReadBool(); err != nil { - return nil, err - } - if exists { - if b, err = m.ReadBytes(common.AddressLength); err != nil { - return nil, err - } - r.To = &common.Address{} - r.To.SetBytes(b) - } - - if r.CumulativeGasUsed, err = m.ReadUint64(); err != nil { - return nil, err - } - if r.GasUsed, err = m.ReadUint64(); err != nil { - return nil, err - } - - if exists, err = m.ReadBool(); err != nil { - return nil, err - } - if exists { - if b, err = m.ReadBytes(common.AddressLength); err != nil { - return nil, err - } - r.ContractAddress = &common.Address{} - r.ContractAddress.SetBytes(b) - } - - { - var n uint32 - if n, err = m.ReadUint32(); err != nil { - return nil, err - } - for i := 0; i < int(n); i++ { - if b, err = readBytes(m); err != nil { - return nil, err - } - var log *types.Log - if log, err = DecodeLog(b, false); err != nil { - return nil, err - } - log.BlockNumber = r.BlockNumber.Uint64() - log.TxHash = r.TxHash - log.TxIndex = uint(r.TransactionIndex) - log.BlockHash = r.BlockHash - log.Index = uint(i) - r.Logs = append(r.Logs, log) - } - } - - if b, err = m.ReadBytes(types.BloomByteLength); err != nil { - return nil, err - } - r.Bloom.SetBytes(b) - - if r.Status, err = m.ReadUint64(); err != nil { - return nil, err - } - return r, nil -} diff --git a/contracts/wasm/Cargo.lock b/contracts/wasm/Cargo.lock index 97ee2465ee..e8ccdfca85 100644 --- a/contracts/wasm/Cargo.lock +++ b/contracts/wasm/Cargo.lock @@ -301,8 +301,6 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen", - "wasm-bindgen-test", - "wee_alloc", ] [[package]] diff --git a/contracts/wasm/cleanup.cmd b/contracts/wasm/cleanup.cmd new file mode 100644 index 0000000000..60650bbb4d --- /dev/null +++ b/contracts/wasm/cleanup.cmd @@ -0,0 +1,17 @@ +del /s consts.* +del /s contract.* +del /s keys.* +del /s lib.* +del /s params.* +del /s results.* +del /s state.* +del /s typedefs.* +del /s types.* +del /s main.go +del /s index.ts +del /s tsconfig.json +del /s /q *.wasm +for /d %%f in (*.) do del /s /q %%f\go\pkg\*.* +for /d %%f in (*.) do del /s /q %%f\pkg\*.* +for /d %%f in (*.) do del /s /q %%f\ts\pkg\*.* +del /s /q target\*.* diff --git a/contracts/wasm/core_build.cmd b/contracts/wasm/core_build.cmd new file mode 100644 index 0000000000..79d5f2e936 --- /dev/null +++ b/contracts/wasm/core_build.cmd @@ -0,0 +1,6 @@ +@echo off +cd ..\..\packages\vm\wasmlib +schema -core -go -rust -ts -force +del /s /q d:\work\node_modules\wasmlib\*.* >nul: +xcopy /s /q d:\Work\go\github.com\iotaledger\wasp\packages\vm\wasmlib\ts\wasmlib d:\work\node_modules\wasmlib +cd ..\..\..\contracts\wasm diff --git a/contracts/wasm/dividend/Cargo.toml b/contracts/wasm/dividend/Cargo.toml index e9bec18aad..5a603fb952 100644 --- a/contracts/wasm/dividend/Cargo.toml +++ b/contracts/wasm/dividend/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/dividend/consts.go b/contracts/wasm/dividend/go/dividend/consts.go similarity index 94% rename from contracts/wasm/dividend/consts.go rename to contracts/wasm/dividend/go/dividend/consts.go index ee65dc068a..c6ef2283ad 100644 --- a/contracts/wasm/dividend/consts.go +++ b/contracts/wasm/dividend/go/dividend/consts.go @@ -7,7 +7,7 @@ package dividend -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "dividend" diff --git a/contracts/wasm/dividend/contract.go b/contracts/wasm/dividend/go/dividend/contract.go similarity index 96% rename from contracts/wasm/dividend/contract.go rename to contracts/wasm/dividend/go/dividend/contract.go index 80332d4696..603f113e47 100644 --- a/contracts/wasm/dividend/contract.go +++ b/contracts/wasm/dividend/go/dividend/contract.go @@ -7,7 +7,7 @@ package dividend -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type DivideCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/dividend/dividend.go b/contracts/wasm/dividend/go/dividend/dividend.go similarity index 99% rename from contracts/wasm/dividend/dividend.go rename to contracts/wasm/dividend/go/dividend/dividend.go index 9582374462..7752295c0d 100644 --- a/contracts/wasm/dividend/dividend.go +++ b/contracts/wasm/dividend/go/dividend/dividend.go @@ -15,7 +15,7 @@ package dividend import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // 'init' is used as a way to initialize a smart contract. It is an optional diff --git a/contracts/wasm/dividend/keys.go b/contracts/wasm/dividend/go/dividend/keys.go similarity index 90% rename from contracts/wasm/dividend/keys.go rename to contracts/wasm/dividend/go/dividend/keys.go index 06beeede02..aa3708f338 100644 --- a/contracts/wasm/dividend/keys.go +++ b/contracts/wasm/dividend/go/dividend/keys.go @@ -7,7 +7,7 @@ package dividend -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamAddress = 0 diff --git a/contracts/wasm/dividend/lib.go b/contracts/wasm/dividend/go/dividend/lib.go similarity index 98% rename from contracts/wasm/dividend/lib.go rename to contracts/wasm/dividend/go/dividend/lib.go index 39e246e919..141fd7ad4c 100644 --- a/contracts/wasm/dividend/lib.go +++ b/contracts/wasm/dividend/go/dividend/lib.go @@ -7,7 +7,7 @@ package dividend -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/dividend/params.go b/contracts/wasm/dividend/go/dividend/params.go similarity index 96% rename from contracts/wasm/dividend/params.go rename to contracts/wasm/dividend/go/dividend/params.go index cc860cedec..4306dec638 100644 --- a/contracts/wasm/dividend/params.go +++ b/contracts/wasm/dividend/go/dividend/params.go @@ -7,7 +7,7 @@ package dividend -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableInitParams struct { id int32 diff --git a/contracts/wasm/dividend/results.go b/contracts/wasm/dividend/go/dividend/results.go similarity index 93% rename from contracts/wasm/dividend/results.go rename to contracts/wasm/dividend/go/dividend/results.go index a2f3a2a9c1..1e4f12e3ed 100644 --- a/contracts/wasm/dividend/results.go +++ b/contracts/wasm/dividend/go/dividend/results.go @@ -7,7 +7,7 @@ package dividend -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableGetFactorResults struct { id int32 diff --git a/contracts/wasm/dividend/state.go b/contracts/wasm/dividend/go/dividend/state.go similarity index 97% rename from contracts/wasm/dividend/state.go rename to contracts/wasm/dividend/go/dividend/state.go index 8cfb4accb9..91571d4443 100644 --- a/contracts/wasm/dividend/state.go +++ b/contracts/wasm/dividend/go/dividend/state.go @@ -7,7 +7,7 @@ package dividend -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayOfImmutableAddress struct { objID int32 diff --git a/contracts/wasm/dividend/wasmmain/main.go b/contracts/wasm/dividend/go/main.go similarity index 80% rename from contracts/wasm/dividend/wasmmain/main.go rename to contracts/wasm/dividend/go/main.go index 887a36f174..13ca6f38f9 100644 --- a/contracts/wasm/dividend/wasmmain/main.go +++ b/contracts/wasm/dividend/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/dividend" + +import "github.com/iotaledger/wasp/contracts/wasm/dividend/go/dividend" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() dividend.OnLoad() diff --git a/contracts/wasm/dividend/src/lib.rs b/contracts/wasm/dividend/src/lib.rs index 1b5e1baafe..ef70c94781 100644 --- a/contracts/wasm/dividend/src/lib.rs +++ b/contracts/wasm/dividend/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use dividend::*; diff --git a/contracts/wasm/dividend/test/dividend_bg.wasm b/contracts/wasm/dividend/test/dividend_bg.wasm index d7938e1f08..5680a3756c 100644 Binary files a/contracts/wasm/dividend/test/dividend_bg.wasm and b/contracts/wasm/dividend/test/dividend_bg.wasm differ diff --git a/contracts/wasm/dividend/test/dividend_test.go b/contracts/wasm/dividend/test/dividend_test.go index b356fd20e9..3432ccc528 100644 --- a/contracts/wasm/dividend/test/dividend_test.go +++ b/contracts/wasm/dividend/test/dividend_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/iotaledger/wasp/contracts/wasm/dividend" + "github.com/iotaledger/wasp/contracts/wasm/dividend/go/dividend" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" diff --git a/contracts/wasm/dividend/ts/dividend/consts.ts b/contracts/wasm/dividend/ts/dividend/consts.ts new file mode 100644 index 0000000000..3972e9b7ca --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/consts.ts @@ -0,0 +1,38 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "dividend"; +export const ScDescription = "Simple dividend smart contract"; +export const HScName = new wasmlib.ScHname(0xcce2e239); + +export const ParamAddress = "address"; +export const ParamFactor = "factor"; +export const ParamOwner = "owner"; + +export const ResultFactor = "factor"; +export const ResultOwner = "owner"; + +export const StateMemberList = "memberList"; +export const StateMembers = "members"; +export const StateOwner = "owner"; +export const StateTotalFactor = "totalFactor"; + +export const FuncDivide = "divide"; +export const FuncInit = "init"; +export const FuncMember = "member"; +export const FuncSetOwner = "setOwner"; +export const ViewGetFactor = "getFactor"; +export const ViewGetOwner = "getOwner"; + +export const HFuncDivide = new wasmlib.ScHname(0xc7878107); +export const HFuncInit = new wasmlib.ScHname(0x1f44d644); +export const HFuncMember = new wasmlib.ScHname(0xc07da2cb); +export const HFuncSetOwner = new wasmlib.ScHname(0x2a15fe7b); +export const HViewGetFactor = new wasmlib.ScHname(0x0ee668fe); +export const HViewGetOwner = new wasmlib.ScHname(0x137107a6); diff --git a/contracts/wasm/dividend/ts/dividend/contract.ts b/contracts/wasm/dividend/ts/dividend/contract.ts new file mode 100644 index 0000000000..e53fae2a2c --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/contract.ts @@ -0,0 +1,107 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class DivideCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncDivide); +} + +export class DivideContext { + state: sc.MutableDividendState = new sc.MutableDividendState(); +} + +export class InitCall { + func: wasmlib.ScInitFunc = new wasmlib.ScInitFunc(sc.HScName, sc.HFuncInit); + params: sc.MutableInitParams = new sc.MutableInitParams(); +} + +export class InitContext { + params: sc.ImmutableInitParams = new sc.ImmutableInitParams(); + state: sc.MutableDividendState = new sc.MutableDividendState(); +} + +export class MemberCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncMember); + params: sc.MutableMemberParams = new sc.MutableMemberParams(); +} + +export class MemberContext { + params: sc.ImmutableMemberParams = new sc.ImmutableMemberParams(); + state: sc.MutableDividendState = new sc.MutableDividendState(); +} + +export class SetOwnerCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSetOwner); + params: sc.MutableSetOwnerParams = new sc.MutableSetOwnerParams(); +} + +export class SetOwnerContext { + params: sc.ImmutableSetOwnerParams = new sc.ImmutableSetOwnerParams(); + state: sc.MutableDividendState = new sc.MutableDividendState(); +} + +export class GetFactorCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetFactor); + params: sc.MutableGetFactorParams = new sc.MutableGetFactorParams(); + results: sc.ImmutableGetFactorResults = new sc.ImmutableGetFactorResults(); +} + +export class GetFactorContext { + params: sc.ImmutableGetFactorParams = new sc.ImmutableGetFactorParams(); + results: sc.MutableGetFactorResults = new sc.MutableGetFactorResults(); + state: sc.ImmutableDividendState = new sc.ImmutableDividendState(); +} + +export class GetOwnerCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetOwner); + results: sc.ImmutableGetOwnerResults = new sc.ImmutableGetOwnerResults(); +} + +export class GetOwnerContext { + results: sc.MutableGetOwnerResults = new sc.MutableGetOwnerResults(); + state: sc.ImmutableDividendState = new sc.ImmutableDividendState(); +} + +export class ScFuncs { + + static divide(ctx: wasmlib.ScFuncCallContext): DivideCall { + let f = new DivideCall(); + return f; + } + + static init(ctx: wasmlib.ScFuncCallContext): InitCall { + let f = new InitCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static member(ctx: wasmlib.ScFuncCallContext): MemberCall { + let f = new MemberCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static setOwner(ctx: wasmlib.ScFuncCallContext): SetOwnerCall { + let f = new SetOwnerCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static getFactor(ctx: wasmlib.ScViewCallContext): GetFactorCall { + let f = new GetFactorCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getOwner(ctx: wasmlib.ScViewCallContext): GetOwnerCall { + let f = new GetOwnerCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/dividend/ts/dividend/dividend.ts b/contracts/wasm/dividend/ts/dividend/dividend.ts new file mode 100644 index 0000000000..62cd68dbca --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/dividend.ts @@ -0,0 +1,237 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// This example implements 'dividend', a simple smart contract that will +// automatically disperse iota tokens which are sent to the contract to a group +// of member addresses according to predefined division factors. The intent is +// to showcase basic functionality of WasmLib through a minimal implementation +// and not to come up with a complete robust real-world solution. +// Note that we have drawn sometimes out constructs that could have been done +// in a single line over multiple statements to be able to properly document +// step by step what is happening in the code. We also unnecessarily annotate +// all 'let' statements with their assignment type to improve understanding. + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +// 'init' is used as a way to initialize a smart contract. It is an optional +// function that will automatically be called upon contract deployment. In this +// case we use it to initialize the 'owner' state variable so that we can later +// use this information to prevent non-owners from calling certain functions. +// The 'init' function takes a single optional parameter: +// - 'owner', which is the agent id of the entity owning the contract. +// When this parameter is omitted the owner will default to the contract creator. +export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { + // The schema tool has already created a proper InitContext for this function that + // allows us to access call parameters and state storage in a type-safe manner. + + // First we set up a default value for the owner in case the optional + // 'owner' parameter was omitted. + let owner: wasmlib.ScAgentID = ctx.contractCreator(); + + // Now we check if the optional 'owner' parameter is present in the params map. + if (f.params.owner().exists()) { + // Yes, it was present, so now we overwrite the default owner with + // the one specified by the 'owner' parameter. + owner = f.params.owner().value(); + } + + // Now that we have sorted out which agent will be the owner of this contract + // we will save this value in the 'owner' variable in state storage on the host. + // Read the documentation on schema.json to understand why this state variable is + // supported at compile-time by code generated from schema.json by the schema tool. + f.state.owner().setValue(owner); +} + +// 'member' is a function that can only be used by the entity that owns the +// 'dividend' smart contract. It can be used to define the group of member +// addresses and dispersal factors one by one prior to sending tokens to the +// smart contract's 'divide' function. The 'member' function takes 2 parameters, +// which are both required: +// - 'address', which is an Address to use as member in the group, and +// - 'factor', which is an Int64 relative dispersal factor associated with +// that address +// The 'member' function will save the address/factor combination in state storage +// and also calculate and store a running sum of all factors so that the 'divide' +// function can simply start using these precalculated values when called. +export function funcMember(ctx: wasmlib.ScFuncContext, f: sc.MemberContext): void { + // Note that the schema tool has already dealt with making sure that this function + // can only be called by the owner and that the required parameters are present. + // So once we get to this point in the code we can take that as a given. + + // Since we are sure that the 'factor' parameter actually exists we can + // retrieve its actual value into an i64. Note that we use Rust's built-in + // data types when manipulating Int64, String, or Bytes value objects. + let factor: i64 = f.params.factor().value(); + + // As an extra requirement we check that the 'factor' parameter value is not + // negative. If it is, we panic out with an error message. + // Note how we avoid an if expression like this one here: + // if (factor < 0) { + // ctx.panic("negative factor"); + // } + // Using the require() method instead reduces typing and enhances readability. + ctx.require(factor >= 0, "negative factor"); + + // Since we are sure that the 'address' parameter actually exists we can + // retrieve its actual value into an ScAddress value type. + let address: wasmlib.ScAddress = f.params.address().value(); + + // We will store the address/factor combinations in a key/value sub-map of the + // state storage named 'members'. The schema tool has generated an appropriately + // type-checked proxy map for us from the schema.json state storage definition. + // If there is no 'members' map present yet in state storage an empty map will + // automatically be created on the host. + let members: sc.MapAddressToMutableInt64 = f.state.members(); + + // Now we create an ScMutableInt64 proxy for the value stored in the 'members' + // map under the key defined by the 'address' parameter we retrieved earlier. + let currentFactor: wasmlib.ScMutableInt64 = members.getInt64(address); + + // We check to see if this key/value combination exists in the 'members' map. + if (!currentFactor.exists()) { + // If it does not exist yet then we have to add this new address to the + // 'memberList' array that keeps track of all address keys used in the + // 'members' map. The schema tool has again created the appropriate type + // for us already. Here too, if the address array was not present yet it + // will automatically be created on the host. + let memberList: sc.ArrayOfMutableAddress = f.state.memberList(); + + // Now we will append the new address to the memberList array. + // First we determine the current length of the array. + let length: i32 = memberList.length(); + + // Next we create an ScMutableAddress proxy to the address value that lives + // at that index in the memberList array (no value, since we're appending). + let newAddress: wasmlib.ScMutableAddress = memberList.getAddress(length); + + // And finally we append the new address to the array by telling the proxy + // to update the value it refers to with the 'address' parameter. + newAddress.setValue(address); + + // Note that we could have achieved the last 3 lines of code in a single line: + // memberList.getAddress(memberList.length()).setValue(address); + } + + // Create an ScMutableInt64 proxy named 'totalFactor' for an Int64 value in + // state storage. Note that we don't care whether this value exists or not, + // because WasmLib will treat it as if it has the default value of zero. + let totalFactor: wasmlib.ScMutableInt64 = f.state.totalFactor(); + + // Now we calculate the new running total sum of factors by first getting the + // current value of 'totalFactor' from the state storage, then subtracting the + // current value of the factor associated with the 'address' parameter, if any + // exists. Again, if the associated value doesn't exist, WasmLib will assume it + // to be zero. Finally we add the factor retrieved from the parameters, + // resulting in the new totalFactor. + let newTotalFactor: i64 = totalFactor.value() - currentFactor.value() + factor; + + // Now we store the new totalFactor in the state storage. + totalFactor.setValue(newTotalFactor); + + // And we also store the factor from the parameters under the address from the + // parameters in the state storage that the proxy refers to. + currentFactor.setValue(factor); +} + +// 'divide' is a function that will take any iotas it receives and properly +// disperse them to the addresses in the member list according to the dispersion +// factors associated with these addresses. +// Anyone can send iota tokens to this function and they will automatically be +// divided over the member list. Note that this function does not deal with +// fractions. It simply truncates the calculated amount to the nearest lower +// integer and keeps any remaining iotas in its own account. They will be added +// to any next round of tokens received prior to calculation of the new +// dividend amounts. +export function funcDivide(ctx: wasmlib.ScFuncContext, f: sc.DivideContext): void { + + // Create an ScBalances map proxy to the account balances for this + // smart contract. Note that ScBalances wraps an ScImmutableMap of + // token color/amount combinations in a simpler to use interface. + let balances: wasmlib.ScBalances = ctx.balances(); + + // Retrieve the amount of plain iota tokens from the account balance. + let amount: i64 = balances.balance(wasmlib.ScColor.IOTA); + + // Retrieve the pre-calculated totalFactor value from the state storage. + let totalFactor: i64 = f.state.totalFactor().value(); + + // Get the proxy to the 'members' map in the state storage. + let members: sc.MapAddressToMutableInt64 = f.state.members(); + + // Get the proxy to the 'memberList' array in the state storage. + let memberList: sc.ArrayOfMutableAddress = f.state.memberList(); + + // Determine the current length of the memberList array. + let size: i32 = memberList.length(); + + // Loop through all indexes of the memberList array. + for (let i = 0; i < size; i++) { + // Retrieve the next indexed address from the memberList array. + let address: wasmlib.ScAddress = memberList.getAddress(i).value(); + + // Retrieve the factor associated with the address from the members map. + let factor: i64 = members.getInt64(address).value(); + + // Calculate the fair share of iotas to disperse to this member based on the + // factor we just retrieved. Note that the result will be truncated. + let share: i64 = amount * factor / totalFactor; + + // Is there anything to disperse to this member? + if (share > 0) { + // Yes, so let's set up an ScTransfers map proxy that transfers the + // calculated amount of iotas. Note that ScTransfers wraps an + // ScMutableMap of token color/amount combinations in a simpler to use + // interface. The constructor we use here creates and initializes a + // single token color transfer in a single statement. The actual color + // and amount values passed in will be stored in a new map on the host. + let transfers: wasmlib.ScTransfers = wasmlib.ScTransfers.iotas(share); + + // Perform the actual transfer of tokens from the smart contract to the + // member address. The transferToAddress() method receives the address + // value and the proxy to the new transfers map on the host, and will + // call the corresponding host sandbox function with these values. + ctx.transferToAddress(address, transfers); + } + } +} + +// 'setOwner' is used to change the owner of the smart contract. +// It updates the 'owner' state variable with the provided agent id. +// The 'setOwner' function takes a single mandatory parameter: +// - 'owner', which is the agent id of the entity that will own the contract. +// Only the current owner can change the owner. +export function funcSetOwner(ctx: wasmlib.ScFuncContext, f: sc.SetOwnerContext): void { + // Note that the schema tool has already dealt with making sure that this function + // can only be called by the owner and that the required parameter is present. + // So once we get to this point in the code we can take that as a given. + + // Save the new owner parameter value in the 'owner' variable in state storage. + f.state.owner().setValue(f.params.owner().value()); +} + +// 'getFactor' is a simple View function. It will retrieve the factor +// associated with the (mandatory) address parameter it was provided with. +export function viewGetFactor(ctx: wasmlib.ScViewContext, f: sc.GetFactorContext): void { + + // Since we are sure that the 'address' parameter actually exists we can + // retrieve its actual value into an ScAddress value type. + let address: wasmlib.ScAddress = f.params.address().value(); + + // Create an ScImmutableMap proxy to the 'members' map in the state storage. + // Note that for views this is an *immutable* map as opposed to the *mutable* + // map we can access from the *mutable* state that gets passed to funcs. + let members: sc.MapAddressToImmutableInt64 = f.state.members(); + + // Retrieve the factor associated with the address parameter. + let factor: i64 = members.getInt64(address).value(); + + // Set the factor in the results map of the function context. + // The contents of this results map is returned to the caller of the function. + f.results.factor().setValue(factor); +} + +// 'getOwner' can be used to retrieve the current owner of the dividend contract +export function viewGetOwner(ctx: wasmlib.ScViewContext, f: sc.GetOwnerContext): void { + f.results.owner().setValue(f.state.owner().value()); +} diff --git a/contracts/wasm/dividend/ts/dividend/index.ts b/contracts/wasm/dividend/ts/dividend/index.ts new file mode 100644 index 0000000000..cf9a259fc4 --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/index.ts @@ -0,0 +1,16 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./dividend"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; diff --git a/contracts/wasm/dividend/ts/dividend/keys.ts b/contracts/wasm/dividend/ts/dividend/keys.ts new file mode 100644 index 0000000000..d63c0823de --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/keys.ts @@ -0,0 +1,33 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamAddress = 0; +export const IdxParamFactor = 1; +export const IdxParamOwner = 2; +export const IdxResultFactor = 3; +export const IdxResultOwner = 4; +export const IdxStateMemberList = 5; +export const IdxStateMembers = 6; +export const IdxStateOwner = 7; +export const IdxStateTotalFactor = 8; + +export let keyMap: string[] = [ + sc.ParamAddress, + sc.ParamFactor, + sc.ParamOwner, + sc.ResultFactor, + sc.ResultOwner, + sc.StateMemberList, + sc.StateMembers, + sc.StateOwner, + sc.StateTotalFactor, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/dividend/ts/dividend/lib.ts b/contracts/wasm/dividend/ts/dividend/lib.ts new file mode 100644 index 0000000000..a34a6d8586 --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/lib.ts @@ -0,0 +1,95 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncDivide, funcDivideThunk); + exports.addFunc(sc.FuncInit, funcInitThunk); + exports.addFunc(sc.FuncMember, funcMemberThunk); + exports.addFunc(sc.FuncSetOwner, funcSetOwnerThunk); + exports.addView(sc.ViewGetFactor, viewGetFactorThunk); + exports.addView(sc.ViewGetOwner, viewGetOwnerThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcDivideThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("dividend.funcDivide"); + let f = new sc.DivideContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcDivide(ctx, f); + ctx.log("dividend.funcDivide ok"); +} + +function funcInitThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("dividend.funcInit"); + let f = new sc.InitContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcInit(ctx, f); + ctx.log("dividend.funcInit ok"); +} + +function funcMemberThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("dividend.funcMember"); + // only defined owner of contract can add members + let access = ctx.state().getAgentID(wasmlib.Key32.fromString("owner")); + ctx.require(access.exists(), "access not set: owner"); + ctx.require(ctx.caller().equals(access.value()), "no permission"); + + let f = new sc.MemberContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.address().exists(), "missing mandatory address") + ctx.require(f.params.factor().exists(), "missing mandatory factor") + sc.funcMember(ctx, f); + ctx.log("dividend.funcMember ok"); +} + +function funcSetOwnerThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("dividend.funcSetOwner"); + // only defined owner of contract can change owner + let access = ctx.state().getAgentID(wasmlib.Key32.fromString("owner")); + ctx.require(access.exists(), "access not set: owner"); + ctx.require(ctx.caller().equals(access.value()), "no permission"); + + let f = new sc.SetOwnerContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.owner().exists(), "missing mandatory owner") + sc.funcSetOwner(ctx, f); + ctx.log("dividend.funcSetOwner ok"); +} + +function viewGetFactorThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("dividend.viewGetFactor"); + let f = new sc.GetFactorContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.address().exists(), "missing mandatory address") + sc.viewGetFactor(ctx, f); + ctx.log("dividend.viewGetFactor ok"); +} + +function viewGetOwnerThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("dividend.viewGetOwner"); + let f = new sc.GetOwnerContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewGetOwner(ctx, f); + ctx.log("dividend.viewGetOwner ok"); +} diff --git a/contracts/wasm/dividend/ts/dividend/params.ts b/contracts/wasm/dividend/ts/dividend/params.ts new file mode 100644 index 0000000000..e02167e7a0 --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/params.ts @@ -0,0 +1,73 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableInitParams extends wasmlib.ScMapID { + + owner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + } +} + +export class MutableInitParams extends wasmlib.ScMapID { + + owner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + } +} + +export class ImmutableMemberParams extends wasmlib.ScMapID { + + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + factor(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamFactor]); + } +} + +export class MutableMemberParams extends wasmlib.ScMapID { + + address(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + factor(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamFactor]); + } +} + +export class ImmutableSetOwnerParams extends wasmlib.ScMapID { + + owner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + } +} + +export class MutableSetOwnerParams extends wasmlib.ScMapID { + + owner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + } +} + +export class ImmutableGetFactorParams extends wasmlib.ScMapID { + + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } +} + +export class MutableGetFactorParams extends wasmlib.ScMapID { + + address(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } +} diff --git a/contracts/wasm/dividend/ts/dividend/results.ts b/contracts/wasm/dividend/ts/dividend/results.ts new file mode 100644 index 0000000000..0a0ac5af3d --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/results.ts @@ -0,0 +1,37 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableGetFactorResults extends wasmlib.ScMapID { + + factor(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultFactor]); + } +} + +export class MutableGetFactorResults extends wasmlib.ScMapID { + + factor(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultFactor]); + } +} + +export class ImmutableGetOwnerResults extends wasmlib.ScMapID { + + owner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultOwner]); + } +} + +export class MutableGetOwnerResults extends wasmlib.ScMapID { + + owner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultOwner]); + } +} diff --git a/contracts/wasm/dividend/ts/dividend/state.ts b/contracts/wasm/dividend/ts/dividend/state.ts new file mode 100644 index 0000000000..60be5bad30 --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/state.ts @@ -0,0 +1,115 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableAddress { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getAddress(index: i32): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.objID, new wasmlib.Key32(index)); + } +} + +export class MapAddressToImmutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getInt64(key: wasmlib.ScAddress): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.objID, key.getKeyID()); + } +} + +export class ImmutableDividendState extends wasmlib.ScMapID { + + memberList(): sc.ArrayOfImmutableAddress { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS); + return new sc.ArrayOfImmutableAddress(arrID) + } + + members(): sc.MapAddressToImmutableInt64 { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateMembers], wasmlib.TYPE_MAP); + return new sc.MapAddressToImmutableInt64(mapID); + } + + owner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxStateOwner]); + } + + totalFactor(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateTotalFactor]); + } +} + +export class ArrayOfMutableAddress { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getAddress(index: i32): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.objID, new wasmlib.Key32(index)); + } +} + +export class MapAddressToMutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getInt64(key: wasmlib.ScAddress): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.objID, key.getKeyID()); + } +} + +export class MutableDividendState extends wasmlib.ScMapID { + + memberList(): sc.ArrayOfMutableAddress { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS); + return new sc.ArrayOfMutableAddress(arrID) + } + + members(): sc.MapAddressToMutableInt64 { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateMembers], wasmlib.TYPE_MAP); + return new sc.MapAddressToMutableInt64(mapID); + } + + owner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxStateOwner]); + } + + totalFactor(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateTotalFactor]); + } +} diff --git a/contracts/wasm/dividend/ts/dividend/tsconfig.json b/contracts/wasm/dividend/ts/dividend/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/dividend/ts/dividend/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/donatewithfeedback/Cargo.toml b/contracts/wasm/donatewithfeedback/Cargo.toml index b746d1a480..4dc25d4110 100644 --- a/contracts/wasm/donatewithfeedback/Cargo.toml +++ b/contracts/wasm/donatewithfeedback/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/donatewithfeedback/consts.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/consts.go similarity index 95% rename from contracts/wasm/donatewithfeedback/consts.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/consts.go index 1cb2fb8cdd..e15c8bd7a1 100644 --- a/contracts/wasm/donatewithfeedback/consts.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/consts.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "donatewithfeedback" diff --git a/contracts/wasm/donatewithfeedback/contract.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/contract.go similarity index 95% rename from contracts/wasm/donatewithfeedback/contract.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/contract.go index 2e08fcaf27..11d419e9a6 100644 --- a/contracts/wasm/donatewithfeedback/contract.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/contract.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type DonateCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/donatewithfeedback/donatewithfeedback.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/donatewithfeedback.go similarity index 97% rename from contracts/wasm/donatewithfeedback/donatewithfeedback.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/donatewithfeedback.go index e989370c31..4df9cda20d 100644 --- a/contracts/wasm/donatewithfeedback/donatewithfeedback.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/donatewithfeedback.go @@ -4,7 +4,7 @@ package donatewithfeedback import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) func funcDonate(ctx wasmlib.ScFuncContext, f *DonateContext) { diff --git a/contracts/wasm/donatewithfeedback/keys.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/keys.go similarity index 93% rename from contracts/wasm/donatewithfeedback/keys.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/keys.go index bfaa962501..3d99eb879e 100644 --- a/contracts/wasm/donatewithfeedback/keys.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/keys.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamAmount = 0 diff --git a/contracts/wasm/donatewithfeedback/lib.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/lib.go similarity index 97% rename from contracts/wasm/donatewithfeedback/lib.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/lib.go index cb779f674d..1e9d2da3ce 100644 --- a/contracts/wasm/donatewithfeedback/lib.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/lib.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/donatewithfeedback/params.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/params.go similarity index 95% rename from contracts/wasm/donatewithfeedback/params.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/params.go index 7f094bafa4..299c6dc2d4 100644 --- a/contracts/wasm/donatewithfeedback/params.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/params.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableDonateParams struct { id int32 diff --git a/contracts/wasm/donatewithfeedback/results.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/results.go similarity index 97% rename from contracts/wasm/donatewithfeedback/results.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/results.go index e3791c4848..89abd2c897 100644 --- a/contracts/wasm/donatewithfeedback/results.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/results.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableDonationResults struct { id int32 diff --git a/contracts/wasm/donatewithfeedback/state.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/state.go similarity index 96% rename from contracts/wasm/donatewithfeedback/state.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/state.go index 23169d263b..9cb723cdac 100644 --- a/contracts/wasm/donatewithfeedback/state.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/state.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayOfImmutableDonation struct { objID int32 diff --git a/contracts/wasm/donatewithfeedback/types.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/structs.go similarity index 96% rename from contracts/wasm/donatewithfeedback/types.go rename to contracts/wasm/donatewithfeedback/go/donatewithfeedback/structs.go index ff5571aa48..ba28b32d44 100644 --- a/contracts/wasm/donatewithfeedback/types.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/structs.go @@ -7,7 +7,7 @@ package donatewithfeedback -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type Donation struct { Amount int64 // amount donated diff --git a/contracts/wasm/donatewithfeedback/wasmmain/main.go b/contracts/wasm/donatewithfeedback/go/main.go similarity index 90% rename from contracts/wasm/donatewithfeedback/wasmmain/main.go rename to contracts/wasm/donatewithfeedback/go/main.go index 41730c7888..a2260be5b6 100644 --- a/contracts/wasm/donatewithfeedback/wasmmain/main.go +++ b/contracts/wasm/donatewithfeedback/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/donatewithfeedback" + +import "github.com/iotaledger/wasp/contracts/wasm/donatewithfeedback/go/donatewithfeedback" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() donatewithfeedback.OnLoad() diff --git a/contracts/wasm/donatewithfeedback/src/donatewithfeedback.rs b/contracts/wasm/donatewithfeedback/src/donatewithfeedback.rs index 20633ae61c..66a15acc75 100644 --- a/contracts/wasm/donatewithfeedback/src/donatewithfeedback.rs +++ b/contracts/wasm/donatewithfeedback/src/donatewithfeedback.rs @@ -4,7 +4,7 @@ use wasmlib::*; use crate::*; -use crate::types::*; +use crate::structs::*; pub fn func_donate(ctx: &ScFuncContext, f: &DonateContext) { let mut donation = Donation { diff --git a/contracts/wasm/donatewithfeedback/src/lib.rs b/contracts/wasm/donatewithfeedback/src/lib.rs index b6e3502035..bad98e5096 100644 --- a/contracts/wasm/donatewithfeedback/src/lib.rs +++ b/contracts/wasm/donatewithfeedback/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use donatewithfeedback::*; @@ -27,7 +26,7 @@ mod keys; mod params; mod results; mod state; -mod types; +mod structs; mod donatewithfeedback; #[no_mangle] diff --git a/contracts/wasm/donatewithfeedback/src/results.rs b/contracts/wasm/donatewithfeedback/src/results.rs index 9d404a1bc0..2f65fcdb4b 100644 --- a/contracts/wasm/donatewithfeedback/src/results.rs +++ b/contracts/wasm/donatewithfeedback/src/results.rs @@ -13,7 +13,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; -use crate::types::*; +use crate::structs::*; #[derive(Clone, Copy)] pub struct ImmutableDonationResults { diff --git a/contracts/wasm/donatewithfeedback/src/state.rs b/contracts/wasm/donatewithfeedback/src/state.rs index 0eeec74348..f424d24c95 100644 --- a/contracts/wasm/donatewithfeedback/src/state.rs +++ b/contracts/wasm/donatewithfeedback/src/state.rs @@ -13,7 +13,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; -use crate::types::*; +use crate::structs::*; pub struct ArrayOfImmutableDonation { pub(crate) obj_id: i32, diff --git a/contracts/wasm/donatewithfeedback/src/types.rs b/contracts/wasm/donatewithfeedback/src/structs.rs similarity index 100% rename from contracts/wasm/donatewithfeedback/src/types.rs rename to contracts/wasm/donatewithfeedback/src/structs.rs diff --git a/contracts/wasm/donatewithfeedback/test/donatewithfeedback_bg.wasm b/contracts/wasm/donatewithfeedback/test/donatewithfeedback_bg.wasm index 9fdf3014bf..909efc2899 100644 Binary files a/contracts/wasm/donatewithfeedback/test/donatewithfeedback_bg.wasm and b/contracts/wasm/donatewithfeedback/test/donatewithfeedback_bg.wasm differ diff --git a/contracts/wasm/donatewithfeedback/test/donatewithfeedback_test.go b/contracts/wasm/donatewithfeedback/test/donatewithfeedback_test.go index 0ebb0673b5..8c3edbf8cc 100644 --- a/contracts/wasm/donatewithfeedback/test/donatewithfeedback_test.go +++ b/contracts/wasm/donatewithfeedback/test/donatewithfeedback_test.go @@ -6,7 +6,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/donatewithfeedback" + "github.com/iotaledger/wasp/contracts/wasm/donatewithfeedback/go/donatewithfeedback" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/consts.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/consts.ts new file mode 100644 index 0000000000..0fd579aa81 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/consts.ts @@ -0,0 +1,38 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "donatewithfeedback"; +export const HScName = new wasmlib.ScHname(0x696d7f66); + +export const ParamAmount = "amount"; +export const ParamFeedback = "feedback"; +export const ParamNr = "nr"; + +export const ResultAmount = "amount"; +export const ResultCount = "count"; +export const ResultDonator = "donator"; +export const ResultError = "error"; +export const ResultFeedback = "feedback"; +export const ResultMaxDonation = "maxDonation"; +export const ResultTimestamp = "timestamp"; +export const ResultTotalDonation = "totalDonation"; + +export const StateLog = "log"; +export const StateMaxDonation = "maxDonation"; +export const StateTotalDonation = "totalDonation"; + +export const FuncDonate = "donate"; +export const FuncWithdraw = "withdraw"; +export const ViewDonation = "donation"; +export const ViewDonationInfo = "donationInfo"; + +export const HFuncDonate = new wasmlib.ScHname(0xdc9b133a); +export const HFuncWithdraw = new wasmlib.ScHname(0x9dcc0f41); +export const HViewDonation = new wasmlib.ScHname(0xbdb245ba); +export const HViewDonationInfo = new wasmlib.ScHname(0xc8f7c726); diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/contract.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/contract.ts new file mode 100644 index 0000000000..10b82740b5 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/contract.ts @@ -0,0 +1,78 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class DonateCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncDonate); + params: sc.MutableDonateParams = new sc.MutableDonateParams(); +} + +export class DonateContext { + params: sc.ImmutableDonateParams = new sc.ImmutableDonateParams(); + state: sc.MutableDonateWithFeedbackState = new sc.MutableDonateWithFeedbackState(); +} + +export class WithdrawCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncWithdraw); + params: sc.MutableWithdrawParams = new sc.MutableWithdrawParams(); +} + +export class WithdrawContext { + params: sc.ImmutableWithdrawParams = new sc.ImmutableWithdrawParams(); + state: sc.MutableDonateWithFeedbackState = new sc.MutableDonateWithFeedbackState(); +} + +export class DonationCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewDonation); + params: sc.MutableDonationParams = new sc.MutableDonationParams(); + results: sc.ImmutableDonationResults = new sc.ImmutableDonationResults(); +} + +export class DonationContext { + params: sc.ImmutableDonationParams = new sc.ImmutableDonationParams(); + results: sc.MutableDonationResults = new sc.MutableDonationResults(); + state: sc.ImmutableDonateWithFeedbackState = new sc.ImmutableDonateWithFeedbackState(); +} + +export class DonationInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewDonationInfo); + results: sc.ImmutableDonationInfoResults = new sc.ImmutableDonationInfoResults(); +} + +export class DonationInfoContext { + results: sc.MutableDonationInfoResults = new sc.MutableDonationInfoResults(); + state: sc.ImmutableDonateWithFeedbackState = new sc.ImmutableDonateWithFeedbackState(); +} + +export class ScFuncs { + + static donate(ctx: wasmlib.ScFuncCallContext): DonateCall { + let f = new DonateCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static withdraw(ctx: wasmlib.ScFuncCallContext): WithdrawCall { + let f = new WithdrawCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static donation(ctx: wasmlib.ScViewCallContext): DonationCall { + let f = new DonationCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static donationInfo(ctx: wasmlib.ScViewCallContext): DonationInfoCall { + let f = new DonationInfoCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/donatewithfeedback.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/donatewithfeedback.ts new file mode 100644 index 0000000000..c15e3bda21 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/donatewithfeedback.ts @@ -0,0 +1,61 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function funcDonate(ctx: wasmlib.ScFuncContext, f: sc.DonateContext): void { + let donation = new sc.Donation(); + donation.amount = ctx.incoming().balance(wasmlib.ScColor.IOTA); + donation.donator = ctx.caller(); + donation.error = ""; + donation.feedback = f.params.feedback().value(); + donation.timestamp = ctx.timestamp(); + if (donation.amount == 0 || donation.feedback.length == 0) { + donation.error = "error: empty feedback or donated amount = 0".toString(); + if (donation.amount > 0) { + ctx.transferToAddress(donation.donator.address(), wasmlib.ScTransfers.iotas(donation.amount)); + donation.amount = 0; + } + } + let log = f.state.log(); + log.getDonation(log.length()).setValue(donation); + + let largestDonation = f.state.maxDonation(); + let totalDonated = f.state.totalDonation(); + if (donation.amount > largestDonation.value()) { + largestDonation.setValue(donation.amount); + } + totalDonated.setValue(totalDonated.value() + donation.amount); +} + +export function funcWithdraw(ctx: wasmlib.ScFuncContext, f: sc.WithdrawContext): void { + let balance = ctx.balances().balance(wasmlib.ScColor.IOTA); + let amount = f.params.amount().value(); + if (amount == 0 || amount > balance) { + amount = balance; + } + if (amount == 0) { + ctx.log("dwf.withdraw: nothing to withdraw"); + return; + } + + let scCreator = ctx.contractCreator().address(); + ctx.transferToAddress(scCreator, wasmlib.ScTransfers.iotas(amount)); +} + +export function viewDonation(ctx: wasmlib.ScViewContext, f: sc.DonationContext): void { + let nr = (f.params.nr().value()) as i32; + let donation = f.state.log().getDonation(nr).value(); + f.results.amount().setValue(donation.amount); + f.results.donator().setValue(donation.donator); + f.results.error().setValue(donation.error); + f.results.feedback().setValue(donation.feedback); + f.results.timestamp().setValue(donation.timestamp); +} + +export function viewDonationInfo(ctx: wasmlib.ScViewContext, f: sc.DonationInfoContext): void { + f.results.maxDonation().setValue(f.state.maxDonation().value()); + f.results.totalDonation().setValue(f.state.totalDonation().value()); + f.results.count().setValue(f.state.log().length() as i64); +} diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/index.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/index.ts new file mode 100644 index 0000000000..e5bfe68faa --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/index.ts @@ -0,0 +1,17 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./donatewithfeedback"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; +export * from "./structs"; diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/keys.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/keys.ts new file mode 100644 index 0000000000..897c2d1de2 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/keys.ts @@ -0,0 +1,43 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamAmount = 0; +export const IdxParamFeedback = 1; +export const IdxParamNr = 2; +export const IdxResultAmount = 3; +export const IdxResultCount = 4; +export const IdxResultDonator = 5; +export const IdxResultError = 6; +export const IdxResultFeedback = 7; +export const IdxResultMaxDonation = 8; +export const IdxResultTimestamp = 9; +export const IdxResultTotalDonation = 10; +export const IdxStateLog = 11; +export const IdxStateMaxDonation = 12; +export const IdxStateTotalDonation = 13; + +export let keyMap: string[] = [ + sc.ParamAmount, + sc.ParamFeedback, + sc.ParamNr, + sc.ResultAmount, + sc.ResultCount, + sc.ResultDonator, + sc.ResultError, + sc.ResultFeedback, + sc.ResultMaxDonation, + sc.ResultTimestamp, + sc.ResultTotalDonation, + sc.StateLog, + sc.StateMaxDonation, + sc.StateTotalDonation, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/lib.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/lib.ts new file mode 100644 index 0000000000..28dca4c343 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/lib.ts @@ -0,0 +1,66 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncDonate, funcDonateThunk); + exports.addFunc(sc.FuncWithdraw, funcWithdrawThunk); + exports.addView(sc.ViewDonation, viewDonationThunk); + exports.addView(sc.ViewDonationInfo, viewDonationInfoThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcDonateThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("donatewithfeedback.funcDonate"); + let f = new sc.DonateContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcDonate(ctx, f); + ctx.log("donatewithfeedback.funcDonate ok"); +} + +function funcWithdrawThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("donatewithfeedback.funcWithdraw"); + // only SC creator can withdraw donated funds + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.WithdrawContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcWithdraw(ctx, f); + ctx.log("donatewithfeedback.funcWithdraw ok"); +} + +function viewDonationThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("donatewithfeedback.viewDonation"); + let f = new sc.DonationContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.nr().exists(), "missing mandatory nr") + sc.viewDonation(ctx, f); + ctx.log("donatewithfeedback.viewDonation ok"); +} + +function viewDonationInfoThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("donatewithfeedback.viewDonationInfo"); + let f = new sc.DonationInfoContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewDonationInfo(ctx, f); + ctx.log("donatewithfeedback.viewDonationInfo ok"); +} diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/params.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/params.ts new file mode 100644 index 0000000000..355aa8dca4 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/params.ts @@ -0,0 +1,51 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableDonateParams extends wasmlib.ScMapID { + + feedback(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamFeedback]); + } +} + +export class MutableDonateParams extends wasmlib.ScMapID { + + feedback(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamFeedback]); + } +} + +export class ImmutableWithdrawParams extends wasmlib.ScMapID { + + amount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } +} + +export class MutableWithdrawParams extends wasmlib.ScMapID { + + amount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } +} + +export class ImmutableDonationParams extends wasmlib.ScMapID { + + nr(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamNr]); + } +} + +export class MutableDonationParams extends wasmlib.ScMapID { + + nr(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamNr]); + } +} diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/results.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/results.ts new file mode 100644 index 0000000000..e85ff09046 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/results.ts @@ -0,0 +1,85 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableDonationResults extends wasmlib.ScMapID { + + amount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + } + + donator(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultDonator]); + } + + error(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultError]); + } + + feedback(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultFeedback]); + } + + timestamp(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultTimestamp]); + } +} + +export class MutableDonationResults extends wasmlib.ScMapID { + + amount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + } + + donator(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultDonator]); + } + + error(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultError]); + } + + feedback(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultFeedback]); + } + + timestamp(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultTimestamp]); + } +} + +export class ImmutableDonationInfoResults extends wasmlib.ScMapID { + + count(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultCount]); + } + + maxDonation(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultMaxDonation]); + } + + totalDonation(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultTotalDonation]); + } +} + +export class MutableDonationInfoResults extends wasmlib.ScMapID { + + count(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultCount]); + } + + maxDonation(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultMaxDonation]); + } + + totalDonation(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultTotalDonation]); + } +} diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/state.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/state.ts new file mode 100644 index 0000000000..aac74b4133 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/state.ts @@ -0,0 +1,77 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableDonation { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getDonation(index: i32): sc.ImmutableDonation { + return new sc.ImmutableDonation(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableDonateWithFeedbackState extends wasmlib.ScMapID { + + log(): sc.ArrayOfImmutableDonation { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateLog], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableDonation(arrID) + } + + maxDonation(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateMaxDonation]); + } + + totalDonation(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateTotalDonation]); + } +} + +export class ArrayOfMutableDonation { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getDonation(index: i32): sc.MutableDonation { + return new sc.MutableDonation(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableDonateWithFeedbackState extends wasmlib.ScMapID { + + log(): sc.ArrayOfMutableDonation { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateLog], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableDonation(arrID) + } + + maxDonation(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateMaxDonation]); + } + + totalDonation(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateTotalDonation]); + } +} diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/structs.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/structs.ts new file mode 100644 index 0000000000..76419ec3df --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/structs.ts @@ -0,0 +1,78 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export class Donation { + amount : i64 = 0; // amount donated + donator : wasmlib.ScAgentID = new wasmlib.ScAgentID(); // who donated + error : string = ""; // error to be reported to donator if anything goes wrong + feedback : string = ""; // the feedback for the person donated to + timestamp: i64 = 0; // when the donation took place + + static fromBytes(bytes: u8[]): Donation { + let decode = new wasmlib.BytesDecoder(bytes); + let data = new Donation(); + data.amount = decode.int64(); + data.donator = decode.agentID(); + data.error = decode.string(); + data.feedback = decode.string(); + data.timestamp = decode.int64(); + decode.close(); + return data; + } + + bytes(): u8[] { + return new wasmlib.BytesEncoder(). + int64(this.amount). + agentID(this.donator). + string(this.error). + string(this.feedback). + int64(this.timestamp). + data(); + } +} + +export class ImmutableDonation { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + value(): Donation { + return Donation.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} + +export class MutableDonation { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + setValue(value: Donation): void { + wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes()); + } + + value(): Donation { + return Donation.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/tsconfig.json b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/erc20/Cargo.toml b/contracts/wasm/erc20/Cargo.toml index 6ffb483304..41b760cbfc 100644 --- a/contracts/wasm/erc20/Cargo.toml +++ b/contracts/wasm/erc20/Cargo.toml @@ -6,7 +6,7 @@ name = "erc20" description = "ERC-20 PoC for IOTA Smart Contracts" license = "Apache-2.0" version = "0.1.0" -authors = ["lunfardo314 "] +authors = ["lunfardo314 ", "Eric Hop "] edition = "2018" repository = "https://github.com/iotaledger/wasp" @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/erc20/consts.go b/contracts/wasm/erc20/go/erc20/consts.go similarity index 95% rename from contracts/wasm/erc20/consts.go rename to contracts/wasm/erc20/go/erc20/consts.go index ccf5420200..21ffa48e92 100644 --- a/contracts/wasm/erc20/consts.go +++ b/contracts/wasm/erc20/go/erc20/consts.go @@ -7,7 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "erc20" diff --git a/contracts/wasm/erc20/contract.go b/contracts/wasm/erc20/go/erc20/contract.go similarity index 97% rename from contracts/wasm/erc20/contract.go rename to contracts/wasm/erc20/go/erc20/contract.go index d31d36ac96..7a085bfe48 100644 --- a/contracts/wasm/erc20/contract.go +++ b/contracts/wasm/erc20/go/erc20/contract.go @@ -7,7 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ApproveCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/erc20/erc20.go b/contracts/wasm/erc20/go/erc20/erc20.go similarity index 98% rename from contracts/wasm/erc20/erc20.go rename to contracts/wasm/erc20/go/erc20/erc20.go index 46ff2db299..cb17321793 100644 --- a/contracts/wasm/erc20/erc20.go +++ b/contracts/wasm/erc20/go/erc20/erc20.go @@ -7,7 +7,7 @@ package erc20 import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // Sets the allowance value for delegated account diff --git a/contracts/wasm/erc20/keys.go b/contracts/wasm/erc20/go/erc20/keys.go similarity index 92% rename from contracts/wasm/erc20/keys.go rename to contracts/wasm/erc20/go/erc20/keys.go index d873439099..5ab67393c5 100644 --- a/contracts/wasm/erc20/keys.go +++ b/contracts/wasm/erc20/go/erc20/keys.go @@ -7,7 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamAccount = 0 diff --git a/contracts/wasm/erc20/lib.go b/contracts/wasm/erc20/go/erc20/lib.go similarity index 98% rename from contracts/wasm/erc20/lib.go rename to contracts/wasm/erc20/go/erc20/lib.go index 7e64272766..d4948bdbf6 100644 --- a/contracts/wasm/erc20/lib.go +++ b/contracts/wasm/erc20/go/erc20/lib.go @@ -7,7 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/erc20/params.go b/contracts/wasm/erc20/go/erc20/params.go similarity index 98% rename from contracts/wasm/erc20/params.go rename to contracts/wasm/erc20/go/erc20/params.go index 26a9895c33..b91f061931 100644 --- a/contracts/wasm/erc20/params.go +++ b/contracts/wasm/erc20/go/erc20/params.go @@ -7,7 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableApproveParams struct { id int32 diff --git a/contracts/wasm/erc20/results.go b/contracts/wasm/erc20/go/erc20/results.go similarity index 95% rename from contracts/wasm/erc20/results.go rename to contracts/wasm/erc20/go/erc20/results.go index a9ca2fb7d4..627016ca28 100644 --- a/contracts/wasm/erc20/results.go +++ b/contracts/wasm/erc20/go/erc20/results.go @@ -7,7 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableAllowanceResults struct { id int32 diff --git a/contracts/wasm/erc20/state.go b/contracts/wasm/erc20/go/erc20/state.go similarity index 96% rename from contracts/wasm/erc20/state.go rename to contracts/wasm/erc20/go/erc20/state.go index 9ead00bfa7..8736cc54c3 100644 --- a/contracts/wasm/erc20/state.go +++ b/contracts/wasm/erc20/go/erc20/state.go @@ -7,7 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type MapAgentIDToImmutableAllowancesForAgent struct { objID int32 diff --git a/contracts/wasm/erc20/typedefs.go b/contracts/wasm/erc20/go/erc20/typedefs.go similarity index 92% rename from contracts/wasm/erc20/typedefs.go rename to contracts/wasm/erc20/go/erc20/typedefs.go index dd488e73a9..bdbfe132a8 100644 --- a/contracts/wasm/erc20/typedefs.go +++ b/contracts/wasm/erc20/go/erc20/typedefs.go @@ -7,9 +7,7 @@ package erc20 -import "github.com/iotaledger/wasp/packages/vm/wasmlib" - -type ImmutableAllowancesForAgent = MapAgentIDToImmutableInt64 +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type MapAgentIDToImmutableInt64 struct { objID int32 @@ -19,7 +17,7 @@ func (m MapAgentIDToImmutableInt64) GetInt64(key wasmlib.ScAgentID) wasmlib.ScIm return wasmlib.NewScImmutableInt64(m.objID, key.KeyID()) } -type MutableAllowancesForAgent = MapAgentIDToMutableInt64 +type ImmutableAllowancesForAgent = MapAgentIDToImmutableInt64 type MapAgentIDToMutableInt64 struct { objID int32 @@ -32,3 +30,5 @@ func (m MapAgentIDToMutableInt64) Clear() { func (m MapAgentIDToMutableInt64) GetInt64(key wasmlib.ScAgentID) wasmlib.ScMutableInt64 { return wasmlib.NewScMutableInt64(m.objID, key.KeyID()) } + +type MutableAllowancesForAgent = MapAgentIDToMutableInt64 diff --git a/contracts/wasm/erc20/wasmmain/main.go b/contracts/wasm/erc20/go/main.go similarity index 81% rename from contracts/wasm/erc20/wasmmain/main.go rename to contracts/wasm/erc20/go/main.go index 9c5dbecbe2..876c78787d 100644 --- a/contracts/wasm/erc20/wasmmain/main.go +++ b/contracts/wasm/erc20/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/erc20" + +import "github.com/iotaledger/wasp/contracts/wasm/erc20/go/erc20" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() erc20.OnLoad() diff --git a/contracts/wasm/erc20/src/lib.rs b/contracts/wasm/erc20/src/lib.rs index ee5f3edf8d..6aacad2b4a 100644 --- a/contracts/wasm/erc20/src/lib.rs +++ b/contracts/wasm/erc20/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use erc20::*; diff --git a/contracts/wasm/erc20/src/typedefs.rs b/contracts/wasm/erc20/src/typedefs.rs index b4e5d3fcf1..406956ec5e 100644 --- a/contracts/wasm/erc20/src/typedefs.rs +++ b/contracts/wasm/erc20/src/typedefs.rs @@ -12,8 +12,6 @@ use wasmlib::*; use wasmlib::host::*; -pub type ImmutableAllowancesForAgent = MapAgentIDToImmutableInt64; - pub struct MapAgentIDToImmutableInt64 { pub(crate) obj_id: i32, } @@ -24,7 +22,7 @@ impl MapAgentIDToImmutableInt64 { } } -pub type MutableAllowancesForAgent = MapAgentIDToMutableInt64; +pub type ImmutableAllowancesForAgent = MapAgentIDToImmutableInt64; pub struct MapAgentIDToMutableInt64 { pub(crate) obj_id: i32, @@ -40,4 +38,6 @@ impl MapAgentIDToMutableInt64 { } } +pub type MutableAllowancesForAgent = MapAgentIDToMutableInt64; + // @formatter:on diff --git a/contracts/wasm/erc20/test/erc20_bg.wasm b/contracts/wasm/erc20/test/erc20_bg.wasm index a4fac81df0..68e2d7e73c 100644 Binary files a/contracts/wasm/erc20/test/erc20_bg.wasm and b/contracts/wasm/erc20/test/erc20_bg.wasm differ diff --git a/contracts/wasm/erc20/test/erc20_test.go b/contracts/wasm/erc20/test/erc20_test.go index 8a663b254e..56a5f82437 100644 --- a/contracts/wasm/erc20/test/erc20_test.go +++ b/contracts/wasm/erc20/test/erc20_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/erc20" + "github.com/iotaledger/wasp/contracts/wasm/erc20/go/erc20" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/core" "github.com/iotaledger/wasp/packages/vm/wasmsolo" diff --git a/contracts/wasm/erc20/test/init_test.go b/contracts/wasm/erc20/test/init_test.go index c69c64c44b..5bac03f74d 100644 --- a/contracts/wasm/erc20/test/init_test.go +++ b/contracts/wasm/erc20/test/init_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/erc20" + "github.com/iotaledger/wasp/contracts/wasm/erc20/go/erc20" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/core" "github.com/iotaledger/wasp/packages/vm/wasmsolo" diff --git a/contracts/wasm/erc20/ts/erc20/consts.ts b/contracts/wasm/erc20/ts/erc20/consts.ts new file mode 100644 index 0000000000..46fac336fb --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/consts.ts @@ -0,0 +1,42 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "erc20"; +export const ScDescription = "ERC-20 PoC for IOTA Smart Contracts"; +export const HScName = new wasmlib.ScHname(0x200e3733); + +export const ParamAccount = "ac"; +export const ParamAmount = "am"; +export const ParamCreator = "c"; +export const ParamDelegation = "d"; +export const ParamRecipient = "r"; +export const ParamSupply = "s"; + +export const ResultAmount = "am"; +export const ResultSupply = "s"; + +export const StateAllAllowances = "a"; +export const StateBalances = "b"; +export const StateSupply = "s"; + +export const FuncApprove = "approve"; +export const FuncInit = "init"; +export const FuncTransfer = "transfer"; +export const FuncTransferFrom = "transferFrom"; +export const ViewAllowance = "allowance"; +export const ViewBalanceOf = "balanceOf"; +export const ViewTotalSupply = "totalSupply"; + +export const HFuncApprove = new wasmlib.ScHname(0xa0661268); +export const HFuncInit = new wasmlib.ScHname(0x1f44d644); +export const HFuncTransfer = new wasmlib.ScHname(0xa15da184); +export const HFuncTransferFrom = new wasmlib.ScHname(0xd5e0a602); +export const HViewAllowance = new wasmlib.ScHname(0x5e16006a); +export const HViewBalanceOf = new wasmlib.ScHname(0x67ef8df4); +export const HViewTotalSupply = new wasmlib.ScHname(0x9505e6ca); diff --git a/contracts/wasm/erc20/ts/erc20/contract.ts b/contracts/wasm/erc20/ts/erc20/contract.ts new file mode 100644 index 0000000000..edfb4700c2 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/contract.ts @@ -0,0 +1,128 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ApproveCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncApprove); + params: sc.MutableApproveParams = new sc.MutableApproveParams(); +} + +export class ApproveContext { + params: sc.ImmutableApproveParams = new sc.ImmutableApproveParams(); + state: sc.MutableErc20State = new sc.MutableErc20State(); +} + +export class InitCall { + func: wasmlib.ScInitFunc = new wasmlib.ScInitFunc(sc.HScName, sc.HFuncInit); + params: sc.MutableInitParams = new sc.MutableInitParams(); +} + +export class InitContext { + params: sc.ImmutableInitParams = new sc.ImmutableInitParams(); + state: sc.MutableErc20State = new sc.MutableErc20State(); +} + +export class TransferCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTransfer); + params: sc.MutableTransferParams = new sc.MutableTransferParams(); +} + +export class TransferContext { + params: sc.ImmutableTransferParams = new sc.ImmutableTransferParams(); + state: sc.MutableErc20State = new sc.MutableErc20State(); +} + +export class TransferFromCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTransferFrom); + params: sc.MutableTransferFromParams = new sc.MutableTransferFromParams(); +} + +export class TransferFromContext { + params: sc.ImmutableTransferFromParams = new sc.ImmutableTransferFromParams(); + state: sc.MutableErc20State = new sc.MutableErc20State(); +} + +export class AllowanceCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewAllowance); + params: sc.MutableAllowanceParams = new sc.MutableAllowanceParams(); + results: sc.ImmutableAllowanceResults = new sc.ImmutableAllowanceResults(); +} + +export class AllowanceContext { + params: sc.ImmutableAllowanceParams = new sc.ImmutableAllowanceParams(); + results: sc.MutableAllowanceResults = new sc.MutableAllowanceResults(); + state: sc.ImmutableErc20State = new sc.ImmutableErc20State(); +} + +export class BalanceOfCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewBalanceOf); + params: sc.MutableBalanceOfParams = new sc.MutableBalanceOfParams(); + results: sc.ImmutableBalanceOfResults = new sc.ImmutableBalanceOfResults(); +} + +export class BalanceOfContext { + params: sc.ImmutableBalanceOfParams = new sc.ImmutableBalanceOfParams(); + results: sc.MutableBalanceOfResults = new sc.MutableBalanceOfResults(); + state: sc.ImmutableErc20State = new sc.ImmutableErc20State(); +} + +export class TotalSupplyCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewTotalSupply); + results: sc.ImmutableTotalSupplyResults = new sc.ImmutableTotalSupplyResults(); +} + +export class TotalSupplyContext { + results: sc.MutableTotalSupplyResults = new sc.MutableTotalSupplyResults(); + state: sc.ImmutableErc20State = new sc.ImmutableErc20State(); +} + +export class ScFuncs { + + static approve(ctx: wasmlib.ScFuncCallContext): ApproveCall { + let f = new ApproveCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static init(ctx: wasmlib.ScFuncCallContext): InitCall { + let f = new InitCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static transfer(ctx: wasmlib.ScFuncCallContext): TransferCall { + let f = new TransferCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static transferFrom(ctx: wasmlib.ScFuncCallContext): TransferFromCall { + let f = new TransferFromCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static allowance(ctx: wasmlib.ScViewCallContext): AllowanceCall { + let f = new AllowanceCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static balanceOf(ctx: wasmlib.ScViewCallContext): BalanceOfCall { + let f = new BalanceOfCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static totalSupply(ctx: wasmlib.ScViewCallContext): TotalSupplyCall { + let f = new TotalSupplyCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/erc20/ts/erc20/erc20.ts b/contracts/wasm/erc20/ts/erc20/erc20.ts new file mode 100644 index 0000000000..ce0b7f6f2e --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/erc20.ts @@ -0,0 +1,125 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// implementation of ERC-20 smart contract for ISCP +// following https://ethereum.org/en/developers/tutorials/understand-the-erc-20-token-smart-contract/ + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +// Sets the allowance value for delegated account +// inputs: +// - PARAM_DELEGATION: agentID +// - PARAM_AMOUNT: i64 +export function funcApprove(ctx: wasmlib.ScFuncContext, f: sc.ApproveContext): void { + let delegation = f.params.delegation().value(); + let amount = f.params.amount().value(); + ctx.require(amount > 0, "erc20.approve.fail: wrong 'amount' parameter"); + + // all allowances are in the map under the name of he owner + let allowances = f.state.allAllowances().getAllowancesForAgent(ctx.caller()); + allowances.getInt64(delegation).setValue(amount); +} + +// onInit is a constructor entry point. It initializes the smart contract with the +// initial value of the token supply and the owner of that supply +// - input: +// -- PARAM_SUPPLY must be nonzero positive integer. Mandatory +// -- PARAM_CREATOR is the AgentID where initial supply is placed. Mandatory +export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { + let supply = f.params.supply().value(); + ctx.require(supply > 0, "erc20.onInit.fail: wrong 'supply' parameter"); + f.state.supply().setValue(supply); + + // we cannot use 'caller' here because onInit is always called from the 'root' + // so, owner of the initial supply must be provided as a parameter PARAM_CREATOR to constructor (onInit) + // assign the whole supply to creator + let creator = f.params.creator().value(); + f.state.balances().getInt64(creator).setValue(supply); + + let t = "erc20.onInit.success. Supply: " + supply.toString() + + ", creator:" + creator.toString(); + ctx.log(t); +} + +// transfer moves tokens from caller's account to target account +// Input: +// - PARAM_ACCOUNT: agentID +// - PARAM_AMOUNT: i64 +export function funcTransfer(ctx: wasmlib.ScFuncContext, f: sc.TransferContext): void { + let amount = f.params.amount().value(); + ctx.require(amount > 0, "erc20.transfer.fail: wrong 'amount' parameter"); + + let balances = f.state.balances(); + let sourceBalance = balances.getInt64(ctx.caller()); + ctx.require(sourceBalance.value() >= amount, "erc20.transfer.fail: not enough funds"); + + let targetAddr = f.params.account().value(); + let targetBalance = balances.getInt64(targetAddr); + let result = targetBalance.value() + amount; + ctx.require(result > 0, "erc20.transfer.fail: overflow"); + + sourceBalance.setValue(sourceBalance.value() - amount); + targetBalance.setValue(targetBalance.value() + amount); +} + +// Moves the amount of tokens from sender to recipient using the allowance mechanism. +// Amount is then deducted from the caller’s allowance. This function emits the Transfer event. +// Input: +// - PARAM_ACCOUNT: agentID the spender +// - PARAM_RECIPIENT: agentID the target +// - PARAM_AMOUNT: i64 +export function funcTransferFrom(ctx: wasmlib.ScFuncContext, f: sc.TransferFromContext): void { + // validate parameters + let account = f.params.account().value(); + let recipient = f.params.recipient().value(); + let amount = f.params.amount().value(); + ctx.require(amount > 0, "erc20.transferFrom.fail: wrong 'amount' parameter"); + + // allowances are in the map under the name of the account + let allowances = f.state.allAllowances().getAllowancesForAgent(account); + let allowance = allowances.getInt64(recipient); + ctx.require(allowance.value() >= amount, "erc20.transferFrom.fail: not enough allowance"); + + let balances = f.state.balances(); + let sourceBalance = balances.getInt64(account); + ctx.require(sourceBalance.value() >= amount, "erc20.transferFrom.fail: not enough funds"); + + let recipientBalance = balances.getInt64(recipient); + let result = recipientBalance.value() + amount; + ctx.require(result > 0, "erc20.transferFrom.fail: overflow"); + + sourceBalance.setValue(sourceBalance.value() - amount); + recipientBalance.setValue(recipientBalance.value() + amount); + allowance.setValue(allowance.value() - amount); +} + +// the view returns max number of tokens the owner PARAM_ACCOUNT of the account +// allowed to retrieve to another party PARAM_DELEGATION +// Input: +// - PARAM_ACCOUNT: agentID +// - PARAM_DELEGATION: agentID +// Output: +// - PARAM_AMOUNT: i64 +export function viewAllowance(ctx: wasmlib.ScViewContext, f: sc.AllowanceContext): void { + // all allowances of the address 'owner' are stored in the map of the same name + let allowances = f.state.allAllowances().getAllowancesForAgent(f.params.account().value()); + let allow = allowances.getInt64(f.params.delegation().value()).value(); + f.results.amount().setValue(allow); +} + +// the view returns balance of the token held in the account +// Input: +// - PARAM_ACCOUNT: agentID +export function viewBalanceOf(ctx: wasmlib.ScViewContext, f: sc.BalanceOfContext): void { + let balances = f.state.balances(); + let balance = balances.getInt64(f.params.account().value()); + f.results.amount().setValue(balance.value()); +} + +// the view returns total supply set when creating the contract (a constant). +// Output: +// - PARAM_SUPPLY: i64 +export function viewTotalSupply(ctx: wasmlib.ScViewContext, f: sc.TotalSupplyContext): void { + f.results.supply().setValue(f.state.supply().value()); +} diff --git a/contracts/wasm/erc20/ts/erc20/index.ts b/contracts/wasm/erc20/ts/erc20/index.ts new file mode 100644 index 0000000000..602bcc4fe7 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/index.ts @@ -0,0 +1,17 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./erc20"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; +export * from "./typedefs"; diff --git a/contracts/wasm/erc20/ts/erc20/keys.ts b/contracts/wasm/erc20/ts/erc20/keys.ts new file mode 100644 index 0000000000..94bbb87373 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/keys.ts @@ -0,0 +1,37 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamAccount = 0; +export const IdxParamAmount = 1; +export const IdxParamCreator = 2; +export const IdxParamDelegation = 3; +export const IdxParamRecipient = 4; +export const IdxParamSupply = 5; +export const IdxResultAmount = 6; +export const IdxResultSupply = 7; +export const IdxStateAllAllowances = 8; +export const IdxStateBalances = 9; +export const IdxStateSupply = 10; + +export let keyMap: string[] = [ + sc.ParamAccount, + sc.ParamAmount, + sc.ParamCreator, + sc.ParamDelegation, + sc.ParamRecipient, + sc.ParamSupply, + sc.ResultAmount, + sc.ResultSupply, + sc.StateAllAllowances, + sc.StateBalances, + sc.StateSupply, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/erc20/ts/erc20/lib.ts b/contracts/wasm/erc20/ts/erc20/lib.ts new file mode 100644 index 0000000000..02027a3e21 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/lib.ts @@ -0,0 +1,105 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncApprove, funcApproveThunk); + exports.addFunc(sc.FuncInit, funcInitThunk); + exports.addFunc(sc.FuncTransfer, funcTransferThunk); + exports.addFunc(sc.FuncTransferFrom, funcTransferFromThunk); + exports.addView(sc.ViewAllowance, viewAllowanceThunk); + exports.addView(sc.ViewBalanceOf, viewBalanceOfThunk); + exports.addView(sc.ViewTotalSupply, viewTotalSupplyThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcApproveThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("erc20.funcApprove"); + let f = new sc.ApproveContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.amount().exists(), "missing mandatory amount") + ctx.require(f.params.delegation().exists(), "missing mandatory delegation") + sc.funcApprove(ctx, f); + ctx.log("erc20.funcApprove ok"); +} + +function funcInitThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("erc20.funcInit"); + let f = new sc.InitContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.creator().exists(), "missing mandatory creator") + ctx.require(f.params.supply().exists(), "missing mandatory supply") + sc.funcInit(ctx, f); + ctx.log("erc20.funcInit ok"); +} + +function funcTransferThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("erc20.funcTransfer"); + let f = new sc.TransferContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.account().exists(), "missing mandatory account") + ctx.require(f.params.amount().exists(), "missing mandatory amount") + sc.funcTransfer(ctx, f); + ctx.log("erc20.funcTransfer ok"); +} + +function funcTransferFromThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("erc20.funcTransferFrom"); + let f = new sc.TransferFromContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.account().exists(), "missing mandatory account") + ctx.require(f.params.amount().exists(), "missing mandatory amount") + ctx.require(f.params.recipient().exists(), "missing mandatory recipient") + sc.funcTransferFrom(ctx, f); + ctx.log("erc20.funcTransferFrom ok"); +} + +function viewAllowanceThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("erc20.viewAllowance"); + let f = new sc.AllowanceContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.account().exists(), "missing mandatory account") + ctx.require(f.params.delegation().exists(), "missing mandatory delegation") + sc.viewAllowance(ctx, f); + ctx.log("erc20.viewAllowance ok"); +} + +function viewBalanceOfThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("erc20.viewBalanceOf"); + let f = new sc.BalanceOfContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.account().exists(), "missing mandatory account") + sc.viewBalanceOf(ctx, f); + ctx.log("erc20.viewBalanceOf ok"); +} + +function viewTotalSupplyThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("erc20.viewTotalSupply"); + let f = new sc.TotalSupplyContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewTotalSupply(ctx, f); + ctx.log("erc20.viewTotalSupply ok"); +} diff --git a/contracts/wasm/erc20/ts/erc20/params.ts b/contracts/wasm/erc20/ts/erc20/params.ts new file mode 100644 index 0000000000..23c35d10da --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/params.ts @@ -0,0 +1,141 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableApproveParams extends wasmlib.ScMapID { + + amount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } + + delegation(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamDelegation]); + } +} + +export class MutableApproveParams extends wasmlib.ScMapID { + + amount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } + + delegation(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamDelegation]); + } +} + +export class ImmutableInitParams extends wasmlib.ScMapID { + + creator(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamCreator]); + } + + supply(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamSupply]); + } +} + +export class MutableInitParams extends wasmlib.ScMapID { + + creator(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamCreator]); + } + + supply(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamSupply]); + } +} + +export class ImmutableTransferParams extends wasmlib.ScMapID { + + account(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } + + amount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } +} + +export class MutableTransferParams extends wasmlib.ScMapID { + + account(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } + + amount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } +} + +export class ImmutableTransferFromParams extends wasmlib.ScMapID { + + account(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } + + amount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } + + recipient(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamRecipient]); + } +} + +export class MutableTransferFromParams extends wasmlib.ScMapID { + + account(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } + + amount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamAmount]); + } + + recipient(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamRecipient]); + } +} + +export class ImmutableAllowanceParams extends wasmlib.ScMapID { + + account(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } + + delegation(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamDelegation]); + } +} + +export class MutableAllowanceParams extends wasmlib.ScMapID { + + account(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } + + delegation(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamDelegation]); + } +} + +export class ImmutableBalanceOfParams extends wasmlib.ScMapID { + + account(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } +} + +export class MutableBalanceOfParams extends wasmlib.ScMapID { + + account(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAccount]); + } +} diff --git a/contracts/wasm/erc20/ts/erc20/results.ts b/contracts/wasm/erc20/ts/erc20/results.ts new file mode 100644 index 0000000000..26435174d4 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/results.ts @@ -0,0 +1,51 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableAllowanceResults extends wasmlib.ScMapID { + + amount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + } +} + +export class MutableAllowanceResults extends wasmlib.ScMapID { + + amount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + } +} + +export class ImmutableBalanceOfResults extends wasmlib.ScMapID { + + amount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + } +} + +export class MutableBalanceOfResults extends wasmlib.ScMapID { + + amount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + } +} + +export class ImmutableTotalSupplyResults extends wasmlib.ScMapID { + + supply(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultSupply]); + } +} + +export class MutableTotalSupplyResults extends wasmlib.ScMapID { + + supply(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultSupply]); + } +} diff --git a/contracts/wasm/erc20/ts/erc20/state.ts b/contracts/wasm/erc20/ts/erc20/state.ts new file mode 100644 index 0000000000..abca138678 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/state.ts @@ -0,0 +1,73 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class MapAgentIDToImmutableAllowancesForAgent { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getAllowancesForAgent(key: wasmlib.ScAgentID): sc.ImmutableAllowancesForAgent { + let subID = wasmlib.getObjectID(this.objID, key.getKeyID(), wasmlib.TYPE_MAP); + return new sc.ImmutableAllowancesForAgent(subID); + } +} + +export class ImmutableErc20State extends wasmlib.ScMapID { + + allAllowances(): sc.MapAgentIDToImmutableAllowancesForAgent { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAllAllowances], wasmlib.TYPE_MAP); + return new sc.MapAgentIDToImmutableAllowancesForAgent(mapID); + } + + balances(): sc.MapAgentIDToImmutableInt64 { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBalances], wasmlib.TYPE_MAP); + return new sc.MapAgentIDToImmutableInt64(mapID); + } + + supply(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateSupply]); + } +} + +export class MapAgentIDToMutableAllowancesForAgent { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getAllowancesForAgent(key: wasmlib.ScAgentID): sc.MutableAllowancesForAgent { + let subID = wasmlib.getObjectID(this.objID, key.getKeyID(), wasmlib.TYPE_MAP); + return new sc.MutableAllowancesForAgent(subID); + } +} + +export class MutableErc20State extends wasmlib.ScMapID { + + allAllowances(): sc.MapAgentIDToMutableAllowancesForAgent { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAllAllowances], wasmlib.TYPE_MAP); + return new sc.MapAgentIDToMutableAllowancesForAgent(mapID); + } + + balances(): sc.MapAgentIDToMutableInt64 { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBalances], wasmlib.TYPE_MAP); + return new sc.MapAgentIDToMutableInt64(mapID); + } + + supply(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateSupply]); + } +} diff --git a/contracts/wasm/erc20/ts/erc20/tsconfig.json b/contracts/wasm/erc20/ts/erc20/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/erc20/ts/erc20/typedefs.ts b/contracts/wasm/erc20/ts/erc20/typedefs.ts new file mode 100644 index 0000000000..2d2c7ef115 --- /dev/null +++ b/contracts/wasm/erc20/ts/erc20/typedefs.ts @@ -0,0 +1,43 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class MapAgentIDToImmutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getInt64(key: wasmlib.ScAgentID): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.objID, key.getKeyID()); + } +} + +export class ImmutableAllowancesForAgent extends MapAgentIDToImmutableInt64 { +}; + +export class MapAgentIDToMutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getInt64(key: wasmlib.ScAgentID): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.objID, key.getKeyID()); + } +} + +export class MutableAllowancesForAgent extends MapAgentIDToMutableInt64 { +}; diff --git a/contracts/wasm/fairauction/Cargo.toml b/contracts/wasm/fairauction/Cargo.toml index 6832001901..b9fd6aa045 100644 --- a/contracts/wasm/fairauction/Cargo.toml +++ b/contracts/wasm/fairauction/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/fairauction/consts.go b/contracts/wasm/fairauction/go/fairauction/consts.go similarity index 96% rename from contracts/wasm/fairauction/consts.go rename to contracts/wasm/fairauction/go/fairauction/consts.go index 2aae9484fa..225392daec 100644 --- a/contracts/wasm/fairauction/consts.go +++ b/contracts/wasm/fairauction/go/fairauction/consts.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "fairauction" diff --git a/contracts/wasm/fairauction/contract.go b/contracts/wasm/fairauction/go/fairauction/contract.go similarity index 96% rename from contracts/wasm/fairauction/contract.go rename to contracts/wasm/fairauction/go/fairauction/contract.go index 784bdbb601..2d5922d91b 100644 --- a/contracts/wasm/fairauction/contract.go +++ b/contracts/wasm/fairauction/go/fairauction/contract.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type FinalizeAuctionCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/fairauction/fairauction.go b/contracts/wasm/fairauction/go/fairauction/fairauction.go similarity index 99% rename from contracts/wasm/fairauction/fairauction.go rename to contracts/wasm/fairauction/go/fairauction/fairauction.go index 8bc2c09b54..c987fcc0a0 100644 --- a/contracts/wasm/fairauction/fairauction.go +++ b/contracts/wasm/fairauction/go/fairauction/fairauction.go @@ -4,7 +4,7 @@ package fairauction import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) const ( diff --git a/contracts/wasm/fairauction/keys.go b/contracts/wasm/fairauction/go/fairauction/keys.go similarity index 95% rename from contracts/wasm/fairauction/keys.go rename to contracts/wasm/fairauction/go/fairauction/keys.go index e61a25063c..24584b37df 100644 --- a/contracts/wasm/fairauction/keys.go +++ b/contracts/wasm/fairauction/go/fairauction/keys.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamColor = 0 diff --git a/contracts/wasm/fairauction/lib.go b/contracts/wasm/fairauction/go/fairauction/lib.go similarity index 98% rename from contracts/wasm/fairauction/lib.go rename to contracts/wasm/fairauction/go/fairauction/lib.go index 5e5737d621..c59c2a1b0a 100644 --- a/contracts/wasm/fairauction/lib.go +++ b/contracts/wasm/fairauction/go/fairauction/lib.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/fairauction/params.go b/contracts/wasm/fairauction/go/fairauction/params.go similarity index 97% rename from contracts/wasm/fairauction/params.go rename to contracts/wasm/fairauction/go/fairauction/params.go index 769f3aaf5f..7e85c051fb 100644 --- a/contracts/wasm/fairauction/params.go +++ b/contracts/wasm/fairauction/go/fairauction/params.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableFinalizeAuctionParams struct { id int32 diff --git a/contracts/wasm/fairauction/results.go b/contracts/wasm/fairauction/go/fairauction/results.go similarity index 98% rename from contracts/wasm/fairauction/results.go rename to contracts/wasm/fairauction/go/fairauction/results.go index 6befa9526b..3e10283f12 100644 --- a/contracts/wasm/fairauction/results.go +++ b/contracts/wasm/fairauction/go/fairauction/results.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableGetInfoResults struct { id int32 diff --git a/contracts/wasm/fairauction/state.go b/contracts/wasm/fairauction/go/fairauction/state.go similarity index 98% rename from contracts/wasm/fairauction/state.go rename to contracts/wasm/fairauction/go/fairauction/state.go index 0e706bd064..aa6ae8655b 100644 --- a/contracts/wasm/fairauction/state.go +++ b/contracts/wasm/fairauction/go/fairauction/state.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type MapColorToImmutableAuction struct { objID int32 diff --git a/contracts/wasm/fairauction/types.go b/contracts/wasm/fairauction/go/fairauction/structs.go similarity index 98% rename from contracts/wasm/fairauction/types.go rename to contracts/wasm/fairauction/go/fairauction/structs.go index 6ee5973a36..681e9506d2 100644 --- a/contracts/wasm/fairauction/types.go +++ b/contracts/wasm/fairauction/go/fairauction/structs.go @@ -7,7 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type Auction struct { Color wasmlib.ScColor // color of tokens for sale diff --git a/contracts/wasm/fairauction/typedefs.go b/contracts/wasm/fairauction/go/fairauction/typedefs.go similarity index 95% rename from contracts/wasm/fairauction/typedefs.go rename to contracts/wasm/fairauction/go/fairauction/typedefs.go index 1c0cce4148..719567b083 100644 --- a/contracts/wasm/fairauction/typedefs.go +++ b/contracts/wasm/fairauction/go/fairauction/typedefs.go @@ -7,9 +7,7 @@ package fairauction -import "github.com/iotaledger/wasp/packages/vm/wasmlib" - -type ImmutableBidderList = ArrayOfImmutableAgentID +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayOfImmutableAgentID struct { objID int32 @@ -23,7 +21,7 @@ func (a ArrayOfImmutableAgentID) GetAgentID(index int32) wasmlib.ScImmutableAgen return wasmlib.NewScImmutableAgentID(a.objID, wasmlib.Key32(index)) } -type MutableBidderList = ArrayOfMutableAgentID +type ImmutableBidderList = ArrayOfImmutableAgentID type ArrayOfMutableAgentID struct { objID int32 @@ -41,7 +39,7 @@ func (a ArrayOfMutableAgentID) GetAgentID(index int32) wasmlib.ScMutableAgentID return wasmlib.NewScMutableAgentID(a.objID, wasmlib.Key32(index)) } -type ImmutableBids = MapAgentIDToImmutableBid +type MutableBidderList = ArrayOfMutableAgentID type MapAgentIDToImmutableBid struct { objID int32 @@ -51,7 +49,7 @@ func (m MapAgentIDToImmutableBid) GetBid(key wasmlib.ScAgentID) ImmutableBid { return ImmutableBid{objID: m.objID, keyID: key.KeyID()} } -type MutableBids = MapAgentIDToMutableBid +type ImmutableBids = MapAgentIDToImmutableBid type MapAgentIDToMutableBid struct { objID int32 @@ -64,3 +62,5 @@ func (m MapAgentIDToMutableBid) Clear() { func (m MapAgentIDToMutableBid) GetBid(key wasmlib.ScAgentID) MutableBid { return MutableBid{objID: m.objID, keyID: key.KeyID()} } + +type MutableBids = MapAgentIDToMutableBid diff --git a/contracts/wasm/fairauction/wasmmain/main.go b/contracts/wasm/fairauction/go/main.go similarity index 79% rename from contracts/wasm/fairauction/wasmmain/main.go rename to contracts/wasm/fairauction/go/main.go index d524dc7d75..be0f4b3c1b 100644 --- a/contracts/wasm/fairauction/wasmmain/main.go +++ b/contracts/wasm/fairauction/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/fairauction" + +import "github.com/iotaledger/wasp/contracts/wasm/fairauction/go/fairauction" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() fairauction.OnLoad() diff --git a/contracts/wasm/fairauction/src/fairauction.rs b/contracts/wasm/fairauction/src/fairauction.rs index f53d01e6f5..822eed784b 100644 --- a/contracts/wasm/fairauction/src/fairauction.rs +++ b/contracts/wasm/fairauction/src/fairauction.rs @@ -1,11 +1,11 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use types::*; use wasmlib::*; use crate::*; use crate::contract::*; +use crate::structs::*; const DURATION_DEFAULT: i32 = 60; const DURATION_MIN: i32 = 1; @@ -205,10 +205,10 @@ pub fn view_get_info(ctx: &ScViewContext, f: &GetInfoContext) { fn transfer_tokens(ctx: &ScFuncContext, agent: &ScAgentID, color: &ScColor, amount: i64) { if agent.is_address() { // send back to original Tangle address - ctx.transfer_to_address(&agent.address(), ScTransfers::new(color, amount)); + ctx.transfer_to_address(&agent.address(), ScTransfers::transfer(color, amount)); return; } // TODO not an address, deposit into account on chain - ctx.transfer_to_address(&agent.address(), ScTransfers::new(color, amount)); + ctx.transfer_to_address(&agent.address(), ScTransfers::transfer(color, amount)); } diff --git a/contracts/wasm/fairauction/src/lib.rs b/contracts/wasm/fairauction/src/lib.rs index 6cfda57814..d9b6366b4b 100644 --- a/contracts/wasm/fairauction/src/lib.rs +++ b/contracts/wasm/fairauction/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use fairauction::*; @@ -27,8 +26,8 @@ mod keys; mod params; mod results; mod state; +mod structs; mod typedefs; -mod types; mod fairauction; #[no_mangle] diff --git a/contracts/wasm/fairauction/src/results.rs b/contracts/wasm/fairauction/src/results.rs index d2591ad20d..2edace8c43 100644 --- a/contracts/wasm/fairauction/src/results.rs +++ b/contracts/wasm/fairauction/src/results.rs @@ -13,7 +13,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; -use crate::types::*; +use crate::structs::*; #[derive(Clone, Copy)] pub struct ImmutableGetInfoResults { diff --git a/contracts/wasm/fairauction/src/state.rs b/contracts/wasm/fairauction/src/state.rs index d94177bb98..6201943eb4 100644 --- a/contracts/wasm/fairauction/src/state.rs +++ b/contracts/wasm/fairauction/src/state.rs @@ -13,8 +13,8 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; +use crate::structs::*; use crate::typedefs::*; -use crate::types::*; pub struct MapColorToImmutableAuction { pub(crate) obj_id: i32, diff --git a/contracts/wasm/fairauction/src/types.rs b/contracts/wasm/fairauction/src/structs.rs similarity index 100% rename from contracts/wasm/fairauction/src/types.rs rename to contracts/wasm/fairauction/src/structs.rs diff --git a/contracts/wasm/fairauction/src/typedefs.rs b/contracts/wasm/fairauction/src/typedefs.rs index bf624de501..260aaebe7c 100644 --- a/contracts/wasm/fairauction/src/typedefs.rs +++ b/contracts/wasm/fairauction/src/typedefs.rs @@ -12,9 +12,7 @@ use wasmlib::*; use wasmlib::host::*; -use crate::types::*; - -pub type ImmutableBidderList = ArrayOfImmutableAgentID; +use crate::structs::*; pub struct ArrayOfImmutableAgentID { pub(crate) obj_id: i32, @@ -30,7 +28,7 @@ impl ArrayOfImmutableAgentID { } } -pub type MutableBidderList = ArrayOfMutableAgentID; +pub type ImmutableBidderList = ArrayOfImmutableAgentID; pub struct ArrayOfMutableAgentID { pub(crate) obj_id: i32, @@ -50,7 +48,7 @@ impl ArrayOfMutableAgentID { } } -pub type ImmutableBids = MapAgentIDToImmutableBid; +pub type MutableBidderList = ArrayOfMutableAgentID; pub struct MapAgentIDToImmutableBid { pub(crate) obj_id: i32, @@ -62,7 +60,7 @@ impl MapAgentIDToImmutableBid { } } -pub type MutableBids = MapAgentIDToMutableBid; +pub type ImmutableBids = MapAgentIDToImmutableBid; pub struct MapAgentIDToMutableBid { pub(crate) obj_id: i32, @@ -78,4 +76,6 @@ impl MapAgentIDToMutableBid { } } +pub type MutableBids = MapAgentIDToMutableBid; + // @formatter:on diff --git a/contracts/wasm/fairauction/test/fairauction_bg.wasm b/contracts/wasm/fairauction/test/fairauction_bg.wasm index 7480ffa3d9..960c862ae3 100644 Binary files a/contracts/wasm/fairauction/test/fairauction_bg.wasm and b/contracts/wasm/fairauction/test/fairauction_bg.wasm differ diff --git a/contracts/wasm/fairauction/test/fairauction_test.go b/contracts/wasm/fairauction/test/fairauction_test.go index e880ef4c04..389ea409ad 100644 --- a/contracts/wasm/fairauction/test/fairauction_test.go +++ b/contracts/wasm/fairauction/test/fairauction_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/iotaledger/wasp/contracts/wasm/fairauction" + "github.com/iotaledger/wasp/contracts/wasm/fairauction/go/fairauction" "github.com/iotaledger/wasp/packages/solo" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/fairauction/ts/fairauction/consts.ts b/contracts/wasm/fairauction/ts/fairauction/consts.ts new file mode 100644 index 0000000000..9fb063c8f5 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/consts.ts @@ -0,0 +1,48 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "fairauction"; +export const ScDescription = "Decentralized auction to securely sell tokens to the highest bidder"; +export const HScName = new wasmlib.ScHname(0x1b5c43b1); + +export const ParamColor = "color"; +export const ParamDescription = "description"; +export const ParamDuration = "duration"; +export const ParamMinimumBid = "minimumBid"; +export const ParamOwnerMargin = "ownerMargin"; + +export const ResultBidders = "bidders"; +export const ResultColor = "color"; +export const ResultCreator = "creator"; +export const ResultDeposit = "deposit"; +export const ResultDescription = "description"; +export const ResultDuration = "duration"; +export const ResultHighestBid = "highestBid"; +export const ResultHighestBidder = "highestBidder"; +export const ResultMinimumBid = "minimumBid"; +export const ResultNumTokens = "numTokens"; +export const ResultOwnerMargin = "ownerMargin"; +export const ResultWhenStarted = "whenStarted"; + +export const StateAuctions = "auctions"; +export const StateBidderList = "bidderList"; +export const StateBids = "bids"; +export const StateOwnerMargin = "ownerMargin"; + +export const FuncFinalizeAuction = "finalizeAuction"; +export const FuncPlaceBid = "placeBid"; +export const FuncSetOwnerMargin = "setOwnerMargin"; +export const FuncStartAuction = "startAuction"; +export const ViewGetInfo = "getInfo"; + +export const HFuncFinalizeAuction = new wasmlib.ScHname(0x8d534ddc); +export const HFuncPlaceBid = new wasmlib.ScHname(0x9bd72fa9); +export const HFuncSetOwnerMargin = new wasmlib.ScHname(0x1774461a); +export const HFuncStartAuction = new wasmlib.ScHname(0xd5b7bacb); +export const HViewGetInfo = new wasmlib.ScHname(0xcfedba5f); diff --git a/contracts/wasm/fairauction/ts/fairauction/contract.ts b/contracts/wasm/fairauction/ts/fairauction/contract.ts new file mode 100644 index 0000000000..8c70e07bb8 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/contract.ts @@ -0,0 +1,94 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class FinalizeAuctionCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncFinalizeAuction); + params: sc.MutableFinalizeAuctionParams = new sc.MutableFinalizeAuctionParams(); +} + +export class FinalizeAuctionContext { + params: sc.ImmutableFinalizeAuctionParams = new sc.ImmutableFinalizeAuctionParams(); + state: sc.MutableFairAuctionState = new sc.MutableFairAuctionState(); +} + +export class PlaceBidCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncPlaceBid); + params: sc.MutablePlaceBidParams = new sc.MutablePlaceBidParams(); +} + +export class PlaceBidContext { + params: sc.ImmutablePlaceBidParams = new sc.ImmutablePlaceBidParams(); + state: sc.MutableFairAuctionState = new sc.MutableFairAuctionState(); +} + +export class SetOwnerMarginCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSetOwnerMargin); + params: sc.MutableSetOwnerMarginParams = new sc.MutableSetOwnerMarginParams(); +} + +export class SetOwnerMarginContext { + params: sc.ImmutableSetOwnerMarginParams = new sc.ImmutableSetOwnerMarginParams(); + state: sc.MutableFairAuctionState = new sc.MutableFairAuctionState(); +} + +export class StartAuctionCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncStartAuction); + params: sc.MutableStartAuctionParams = new sc.MutableStartAuctionParams(); +} + +export class StartAuctionContext { + params: sc.ImmutableStartAuctionParams = new sc.ImmutableStartAuctionParams(); + state: sc.MutableFairAuctionState = new sc.MutableFairAuctionState(); +} + +export class GetInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetInfo); + params: sc.MutableGetInfoParams = new sc.MutableGetInfoParams(); + results: sc.ImmutableGetInfoResults = new sc.ImmutableGetInfoResults(); +} + +export class GetInfoContext { + params: sc.ImmutableGetInfoParams = new sc.ImmutableGetInfoParams(); + results: sc.MutableGetInfoResults = new sc.MutableGetInfoResults(); + state: sc.ImmutableFairAuctionState = new sc.ImmutableFairAuctionState(); +} + +export class ScFuncs { + + static finalizeAuction(ctx: wasmlib.ScFuncCallContext): FinalizeAuctionCall { + let f = new FinalizeAuctionCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static placeBid(ctx: wasmlib.ScFuncCallContext): PlaceBidCall { + let f = new PlaceBidCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static setOwnerMargin(ctx: wasmlib.ScFuncCallContext): SetOwnerMarginCall { + let f = new SetOwnerMarginCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static startAuction(ctx: wasmlib.ScFuncCallContext): StartAuctionCall { + let f = new StartAuctionCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static getInfo(ctx: wasmlib.ScViewCallContext): GetInfoCall { + let f = new GetInfoCall(); + f.func.setPtrs(f.params, f.results); + return f; + } +} diff --git a/contracts/wasm/fairauction/ts/fairauction/fairauction.ts b/contracts/wasm/fairauction/ts/fairauction/fairauction.ts new file mode 100644 index 0000000000..936a50dddc --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/fairauction.ts @@ -0,0 +1,208 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +const DURATION_DEFAULT: i32 = 60; +const DURATION_MIN: i32 = 1; +const DURATION_MAX: i32 = 120; +const MAX_DESCRIPTION_LENGTH: i32 = 150; +const OWNER_MARGIN_DEFAULT: i64 = 50; +const OWNER_MARGIN_MIN: i64 = 5; +const OWNER_MARGIN_MAX: i64 = 100; + +export function funcFinalizeAuction(ctx: wasmlib.ScFuncContext, f: sc.FinalizeAuctionContext): void { + let color = f.params.color().value(); + let currentAuction = f.state.auctions().getAuction(color); + ctx.require(currentAuction.exists(), "Missing auction info"); + let auction = currentAuction.value(); + if (auction.highestBid < 0) { + ctx.log("No one bid on " + color.toString()); + let ownerFee = auction.minimumBid * auction.ownerMargin / 1000; + if (ownerFee == 0) { + ownerFee = 1; + } + // finalizeAuction request token was probably not confirmed yet + transferTokens(ctx, ctx.contractCreator(), wasmlib.ScColor.IOTA, ownerFee - 1); + transferTokens(ctx, auction.creator, auction.color, auction.numTokens); + transferTokens(ctx, auction.creator, wasmlib.ScColor.IOTA, auction.deposit - ownerFee); + return; + } + + let ownerFee = auction.highestBid * auction.ownerMargin / 1000; + if (ownerFee == 0) { + ownerFee = 1; + } + + // return staked bids to losers + let bids = f.state.bids().getBids(color); + let bidderList = f.state.bidderList().getBidderList(color); + let size = bidderList.length(); + for (let i = 0; i < size; i++) { + let loser = bidderList.getAgentID(i).value(); + if (loser != auction.highestBidder) { + let bid = bids.getBid(loser).value(); + transferTokens(ctx, loser, wasmlib.ScColor.IOTA, bid.amount); + } + } + + // finalizeAuction request token was probably not confirmed yet + transferTokens(ctx, ctx.contractCreator(), wasmlib.ScColor.IOTA, ownerFee - 1); + transferTokens(ctx, auction.highestBidder, auction.color, auction.numTokens); + transferTokens(ctx, auction.creator, wasmlib.ScColor.IOTA, auction.deposit + auction.highestBid - ownerFee); +} + +export function funcPlaceBid(ctx: wasmlib.ScFuncContext, f: sc.PlaceBidContext): void { + let bidAmount = ctx.incoming().balance(wasmlib.ScColor.IOTA); + ctx.require(bidAmount > 0, "Missing bid amount"); + + let color = f.params.color().value(); + let currentAuction = f.state.auctions().getAuction(color); + ctx.require(currentAuction.exists(), "Missing auction info"); + + let auction = currentAuction.value(); + let bids = f.state.bids().getBids(color); + let bidderList = f.state.bidderList().getBidderList(color); + let caller = ctx.caller(); + let currentBid = bids.getBid(caller); + if (currentBid.exists()) { + ctx.log("Upped bid from: " + caller.toString()); + let bid = currentBid.value(); + bidAmount += bid.amount; + bid.amount = bidAmount; + bid.timestamp = ctx.timestamp(); + currentBid.setValue(bid); + } else { + ctx.require(bidAmount >= auction.minimumBid, "Insufficient bid amount"); + ctx.log("New bid from: " + caller.toString()); + let index = bidderList.length(); + bidderList.getAgentID(index).setValue(caller); + let bid = new sc.Bid(); + bid.index = index; + bid.amount = bidAmount; + bid.timestamp = ctx.timestamp(); + currentBid.setValue(bid); + } + if (bidAmount > auction.highestBid) { + ctx.log("New highest bidder"); + auction.highestBid = bidAmount; + auction.highestBidder = caller; + currentAuction.setValue(auction); + } +} + +export function funcSetOwnerMargin(ctx: wasmlib.ScFuncContext, f: sc.SetOwnerMarginContext): void { + let ownerMargin = f.params.ownerMargin().value(); + if (ownerMargin < OWNER_MARGIN_MIN) { + ownerMargin = OWNER_MARGIN_MIN; + } + if (ownerMargin > OWNER_MARGIN_MAX) { + ownerMargin = OWNER_MARGIN_MAX; + } + f.state.ownerMargin().setValue(ownerMargin); +} + +export function funcStartAuction(ctx: wasmlib.ScFuncContext, f: sc.StartAuctionContext): void { + let color = f.params.color().value(); + if (color == wasmlib.ScColor.IOTA || color == wasmlib.ScColor.MINT) { + ctx.panic("Reserved auction token color"); + } + let numTokens = ctx.incoming().balance(color); + if (numTokens == 0) { + ctx.panic("Missing auction tokens"); + } + + let minimumBid = f.params.minimumBid().value(); + + // duration in minutes + let duration = f.params.duration().value(); + if (duration == 0) { + duration = DURATION_DEFAULT; + } + if (duration < DURATION_MIN) { + duration = DURATION_MIN; + } + if (duration > DURATION_MAX) { + duration = DURATION_MAX; + } + + let description = f.params.description().value(); + if (description == "") { + description = "N/A".toString(); + } + if (description.length > MAX_DESCRIPTION_LENGTH) { + description = description.slice(0,MAX_DESCRIPTION_LENGTH) + "[...]"; + } + + let ownerMargin = f.state.ownerMargin().value(); + if (ownerMargin == 0) { + ownerMargin = OWNER_MARGIN_DEFAULT; + } + + // need at least 1 iota to run SC + let margin = minimumBid * ownerMargin / 1000; + if (margin == 0) { + margin = 1; + } + let deposit = ctx.incoming().balance(wasmlib.ScColor.IOTA); + if (deposit < margin) { + ctx.panic("Insufficient deposit"); + } + + let currentAuction = f.state.auctions().getAuction(color); + if (currentAuction.exists()) { + ctx.panic("Auction for this token color already exists"); + } + + let auction = new sc.Auction(); + auction.creator = ctx.caller(); + auction.color = color; + auction.deposit = deposit; + auction.description = description; + auction.duration = duration; + auction.highestBid = -1; + auction.highestBidder = new wasmlib.ScAgentID(); + auction.minimumBid = minimumBid; + auction.numTokens = numTokens; + auction.ownerMargin = ownerMargin; + auction.whenStarted = ctx.timestamp(); + currentAuction.setValue(auction); + + let fa = sc.ScFuncs.finalizeAuction(ctx); + fa.params.color().setValue(auction.color); + fa.func.delay(duration * 60).transferIotas(1).post(); +} + +export function viewGetInfo(ctx: wasmlib.ScViewContext, f: sc.GetInfoContext): void { + let color = f.params.color().value(); + let currentAuction = f.state.auctions().getAuction(color); + ctx.require(currentAuction.exists(), "Missing auction info"); + + let auction = currentAuction.value(); + f.results.color().setValue(auction.color); + f.results.creator().setValue(auction.creator); + f.results.deposit().setValue(auction.deposit); + f.results.description().setValue(auction.description); + f.results.duration().setValue(auction.duration); + f.results.highestBid().setValue(auction.highestBid); + f.results.highestBidder().setValue(auction.highestBidder); + f.results.minimumBid().setValue(auction.minimumBid); + f.results.numTokens().setValue(auction.numTokens); + f.results.ownerMargin().setValue(auction.ownerMargin); + f.results.whenStarted().setValue(auction.whenStarted); + + let bidderList = f.state.bidderList().getBidderList(color); + f.results.bidders().setValue(bidderList.length()); +} + +function transferTokens(ctx: wasmlib.ScFuncContext, agent: wasmlib.ScAgentID, color: wasmlib.ScColor, amount: i64): void { + if (agent.isAddress()) { + // send back to original Tangle address + ctx.transferToAddress(agent.address(), wasmlib.ScTransfers.transfer(color, amount)); + return; + } + + // TODO not an address, deposit into account on chain + ctx.transferToAddress(agent.address(), wasmlib.ScTransfers.transfer(color, amount)); +} diff --git a/contracts/wasm/fairauction/ts/fairauction/index.ts b/contracts/wasm/fairauction/ts/fairauction/index.ts new file mode 100644 index 0000000000..e8011fbe09 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/index.ts @@ -0,0 +1,18 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./fairauction"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; +export * from "./structs"; +export * from "./typedefs"; diff --git a/contracts/wasm/fairauction/ts/fairauction/keys.ts b/contracts/wasm/fairauction/ts/fairauction/keys.ts new file mode 100644 index 0000000000..9bb253e5d8 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/keys.ts @@ -0,0 +1,57 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamColor = 0; +export const IdxParamDescription = 1; +export const IdxParamDuration = 2; +export const IdxParamMinimumBid = 3; +export const IdxParamOwnerMargin = 4; +export const IdxResultBidders = 5; +export const IdxResultColor = 6; +export const IdxResultCreator = 7; +export const IdxResultDeposit = 8; +export const IdxResultDescription = 9; +export const IdxResultDuration = 10; +export const IdxResultHighestBid = 11; +export const IdxResultHighestBidder = 12; +export const IdxResultMinimumBid = 13; +export const IdxResultNumTokens = 14; +export const IdxResultOwnerMargin = 15; +export const IdxResultWhenStarted = 16; +export const IdxStateAuctions = 17; +export const IdxStateBidderList = 18; +export const IdxStateBids = 19; +export const IdxStateOwnerMargin = 20; + +export let keyMap: string[] = [ + sc.ParamColor, + sc.ParamDescription, + sc.ParamDuration, + sc.ParamMinimumBid, + sc.ParamOwnerMargin, + sc.ResultBidders, + sc.ResultColor, + sc.ResultCreator, + sc.ResultDeposit, + sc.ResultDescription, + sc.ResultDuration, + sc.ResultHighestBid, + sc.ResultHighestBidder, + sc.ResultMinimumBid, + sc.ResultNumTokens, + sc.ResultOwnerMargin, + sc.ResultWhenStarted, + sc.StateAuctions, + sc.StateBidderList, + sc.StateBids, + sc.StateOwnerMargin, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/fairauction/ts/fairauction/lib.ts b/contracts/wasm/fairauction/ts/fairauction/lib.ts new file mode 100644 index 0000000000..9b46528318 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/lib.ts @@ -0,0 +1,84 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncFinalizeAuction, funcFinalizeAuctionThunk); + exports.addFunc(sc.FuncPlaceBid, funcPlaceBidThunk); + exports.addFunc(sc.FuncSetOwnerMargin, funcSetOwnerMarginThunk); + exports.addFunc(sc.FuncStartAuction, funcStartAuctionThunk); + exports.addView(sc.ViewGetInfo, viewGetInfoThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcFinalizeAuctionThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairauction.funcFinalizeAuction"); + // only SC itself can invoke this function + ctx.require(ctx.caller().equals(ctx.accountID()), "no permission"); + + let f = new sc.FinalizeAuctionContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.color().exists(), "missing mandatory color") + sc.funcFinalizeAuction(ctx, f); + ctx.log("fairauction.funcFinalizeAuction ok"); +} + +function funcPlaceBidThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairauction.funcPlaceBid"); + let f = new sc.PlaceBidContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.color().exists(), "missing mandatory color") + sc.funcPlaceBid(ctx, f); + ctx.log("fairauction.funcPlaceBid ok"); +} + +function funcSetOwnerMarginThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairauction.funcSetOwnerMargin"); + // only SC creator can set owner margin + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.SetOwnerMarginContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.ownerMargin().exists(), "missing mandatory ownerMargin") + sc.funcSetOwnerMargin(ctx, f); + ctx.log("fairauction.funcSetOwnerMargin ok"); +} + +function funcStartAuctionThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairauction.funcStartAuction"); + let f = new sc.StartAuctionContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.color().exists(), "missing mandatory color") + ctx.require(f.params.minimumBid().exists(), "missing mandatory minimumBid") + sc.funcStartAuction(ctx, f); + ctx.log("fairauction.funcStartAuction ok"); +} + +function viewGetInfoThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("fairauction.viewGetInfo"); + let f = new sc.GetInfoContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.color().exists(), "missing mandatory color") + sc.viewGetInfo(ctx, f); + ctx.log("fairauction.viewGetInfo ok"); +} diff --git a/contracts/wasm/fairauction/ts/fairauction/params.ts b/contracts/wasm/fairauction/ts/fairauction/params.ts new file mode 100644 index 0000000000..493722760d --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/params.ts @@ -0,0 +1,103 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableFinalizeAuctionParams extends wasmlib.ScMapID { + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class MutableFinalizeAuctionParams extends wasmlib.ScMapID { + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class ImmutablePlaceBidParams extends wasmlib.ScMapID { + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class MutablePlaceBidParams extends wasmlib.ScMapID { + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class ImmutableSetOwnerMarginParams extends wasmlib.ScMapID { + + ownerMargin(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamOwnerMargin]); + } +} + +export class MutableSetOwnerMarginParams extends wasmlib.ScMapID { + + ownerMargin(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamOwnerMargin]); + } +} + +export class ImmutableStartAuctionParams extends wasmlib.ScMapID { + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } + + description(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamDescription]); + } + + duration(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamDuration]); + } + + minimumBid(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamMinimumBid]); + } +} + +export class MutableStartAuctionParams extends wasmlib.ScMapID { + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } + + description(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamDescription]); + } + + duration(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamDuration]); + } + + minimumBid(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamMinimumBid]); + } +} + +export class ImmutableGetInfoParams extends wasmlib.ScMapID { + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class MutableGetInfoParams extends wasmlib.ScMapID { + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} diff --git a/contracts/wasm/fairauction/ts/fairauction/results.ts b/contracts/wasm/fairauction/ts/fairauction/results.ts new file mode 100644 index 0000000000..59dd2a1465 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/results.ts @@ -0,0 +1,111 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableGetInfoResults extends wasmlib.ScMapID { + + bidders(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxResultBidders]); + } + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxResultColor]); + } + + creator(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultCreator]); + } + + deposit(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultDeposit]); + } + + description(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultDescription]); + } + + duration(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxResultDuration]); + } + + highestBid(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultHighestBid]); + } + + highestBidder(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultHighestBidder]); + } + + minimumBid(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultMinimumBid]); + } + + numTokens(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultNumTokens]); + } + + ownerMargin(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultOwnerMargin]); + } + + whenStarted(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultWhenStarted]); + } +} + +export class MutableGetInfoResults extends wasmlib.ScMapID { + + bidders(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxResultBidders]); + } + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxResultColor]); + } + + creator(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultCreator]); + } + + deposit(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultDeposit]); + } + + description(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultDescription]); + } + + duration(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxResultDuration]); + } + + highestBid(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultHighestBid]); + } + + highestBidder(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultHighestBidder]); + } + + minimumBid(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultMinimumBid]); + } + + numTokens(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultNumTokens]); + } + + ownerMargin(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultOwnerMargin]); + } + + whenStarted(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultWhenStarted]); + } +} diff --git a/contracts/wasm/fairauction/ts/fairauction/state.ts b/contracts/wasm/fairauction/ts/fairauction/state.ts new file mode 100644 index 0000000000..3de34106d4 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/state.ts @@ -0,0 +1,141 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class MapColorToImmutableAuction { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getAuction(key: wasmlib.ScColor): sc.ImmutableAuction { + return new sc.ImmutableAuction(this.objID, key.getKeyID()); + } +} + +export class MapColorToImmutableBidderList { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getBidderList(key: wasmlib.ScColor): sc.ImmutableBidderList { + let subID = wasmlib.getObjectID(this.objID, key.getKeyID(), wasmlib.TYPE_ARRAY|wasmlib.TYPE_AGENT_ID); + return new sc.ImmutableBidderList(subID); + } +} + +export class MapColorToImmutableBids { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getBids(key: wasmlib.ScColor): sc.ImmutableBids { + let subID = wasmlib.getObjectID(this.objID, key.getKeyID(), wasmlib.TYPE_MAP); + return new sc.ImmutableBids(subID); + } +} + +export class ImmutableFairAuctionState extends wasmlib.ScMapID { + + auctions(): sc.MapColorToImmutableAuction { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAuctions], wasmlib.TYPE_MAP); + return new sc.MapColorToImmutableAuction(mapID); + } + + bidderList(): sc.MapColorToImmutableBidderList { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBidderList], wasmlib.TYPE_MAP); + return new sc.MapColorToImmutableBidderList(mapID); + } + + bids(): sc.MapColorToImmutableBids { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBids], wasmlib.TYPE_MAP); + return new sc.MapColorToImmutableBids(mapID); + } + + ownerMargin(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateOwnerMargin]); + } +} + +export class MapColorToMutableAuction { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getAuction(key: wasmlib.ScColor): sc.MutableAuction { + return new sc.MutableAuction(this.objID, key.getKeyID()); + } +} + +export class MapColorToMutableBidderList { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getBidderList(key: wasmlib.ScColor): sc.MutableBidderList { + let subID = wasmlib.getObjectID(this.objID, key.getKeyID(), wasmlib.TYPE_ARRAY|wasmlib.TYPE_AGENT_ID); + return new sc.MutableBidderList(subID); + } +} + +export class MapColorToMutableBids { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getBids(key: wasmlib.ScColor): sc.MutableBids { + let subID = wasmlib.getObjectID(this.objID, key.getKeyID(), wasmlib.TYPE_MAP); + return new sc.MutableBids(subID); + } +} + +export class MutableFairAuctionState extends wasmlib.ScMapID { + + auctions(): sc.MapColorToMutableAuction { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAuctions], wasmlib.TYPE_MAP); + return new sc.MapColorToMutableAuction(mapID); + } + + bidderList(): sc.MapColorToMutableBidderList { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBidderList], wasmlib.TYPE_MAP); + return new sc.MapColorToMutableBidderList(mapID); + } + + bids(): sc.MapColorToMutableBids { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBids], wasmlib.TYPE_MAP); + return new sc.MapColorToMutableBids(mapID); + } + + ownerMargin(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateOwnerMargin]); + } +} diff --git a/contracts/wasm/fairauction/ts/fairauction/structs.ts b/contracts/wasm/fairauction/ts/fairauction/structs.ts new file mode 100644 index 0000000000..9a53456c19 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/structs.ts @@ -0,0 +1,160 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export class Auction { + color : wasmlib.ScColor = new wasmlib.ScColor(0); // color of tokens for sale + creator : wasmlib.ScAgentID = new wasmlib.ScAgentID(); // issuer of start_auction transaction + deposit : i64 = 0; // deposit by auction owner to cover the SC fees + description : string = ""; // auction description + duration : i32 = 0; // auction duration in minutes + highestBid : i64 = 0; // the current highest bid amount + highestBidder: wasmlib.ScAgentID = new wasmlib.ScAgentID(); // the current highest bidder + minimumBid : i64 = 0; // minimum bid amount + numTokens : i64 = 0; // number of tokens for sale + ownerMargin : i64 = 0; // auction owner's margin in promilles + whenStarted : i64 = 0; // timestamp when auction started + + static fromBytes(bytes: u8[]): Auction { + let decode = new wasmlib.BytesDecoder(bytes); + let data = new Auction(); + data.color = decode.color(); + data.creator = decode.agentID(); + data.deposit = decode.int64(); + data.description = decode.string(); + data.duration = decode.int32(); + data.highestBid = decode.int64(); + data.highestBidder = decode.agentID(); + data.minimumBid = decode.int64(); + data.numTokens = decode.int64(); + data.ownerMargin = decode.int64(); + data.whenStarted = decode.int64(); + decode.close(); + return data; + } + + bytes(): u8[] { + return new wasmlib.BytesEncoder(). + color(this.color). + agentID(this.creator). + int64(this.deposit). + string(this.description). + int32(this.duration). + int64(this.highestBid). + agentID(this.highestBidder). + int64(this.minimumBid). + int64(this.numTokens). + int64(this.ownerMargin). + int64(this.whenStarted). + data(); + } +} + +export class ImmutableAuction { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + value(): Auction { + return Auction.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} + +export class MutableAuction { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + setValue(value: Auction): void { + wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes()); + } + + value(): Auction { + return Auction.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} + +export class Bid { + amount : i64 = 0; // cumulative amount of bids from same bidder + index : i32 = 0; // index of bidder in bidder list + timestamp: i64 = 0; // timestamp of most recent bid + + static fromBytes(bytes: u8[]): Bid { + let decode = new wasmlib.BytesDecoder(bytes); + let data = new Bid(); + data.amount = decode.int64(); + data.index = decode.int32(); + data.timestamp = decode.int64(); + decode.close(); + return data; + } + + bytes(): u8[] { + return new wasmlib.BytesEncoder(). + int64(this.amount). + int32(this.index). + int64(this.timestamp). + data(); + } +} + +export class ImmutableBid { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + value(): Bid { + return Bid.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} + +export class MutableBid { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + setValue(value: Bid): void { + wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes()); + } + + value(): Bid { + return Bid.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} diff --git a/contracts/wasm/fairauction/ts/fairauction/tsconfig.json b/contracts/wasm/fairauction/ts/fairauction/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/fairauction/ts/fairauction/typedefs.ts b/contracts/wasm/fairauction/ts/fairauction/typedefs.ts new file mode 100644 index 0000000000..30d6a27bac --- /dev/null +++ b/contracts/wasm/fairauction/ts/fairauction/typedefs.ts @@ -0,0 +1,85 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableAgentID { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getAgentID(index: i32): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableBidderList extends ArrayOfImmutableAgentID { +}; + +export class ArrayOfMutableAgentID { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getAgentID(index: i32): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableBidderList extends ArrayOfMutableAgentID { +}; + +export class MapAgentIDToImmutableBid { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getBid(key: wasmlib.ScAgentID): sc.ImmutableBid { + return new sc.ImmutableBid(this.objID, key.getKeyID()); + } +} + +export class ImmutableBids extends MapAgentIDToImmutableBid { +}; + +export class MapAgentIDToMutableBid { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getBid(key: wasmlib.ScAgentID): sc.MutableBid { + return new sc.MutableBid(this.objID, key.getKeyID()); + } +} + +export class MutableBids extends MapAgentIDToMutableBid { +}; diff --git a/contracts/wasm/fairroulette/Cargo.toml b/contracts/wasm/fairroulette/Cargo.toml index 58d004131b..101c6afcdc 100644 --- a/contracts/wasm/fairroulette/Cargo.toml +++ b/contracts/wasm/fairroulette/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/fairroulette/consts.go b/contracts/wasm/fairroulette/go/fairroulette/consts.go similarity index 96% rename from contracts/wasm/fairroulette/consts.go rename to contracts/wasm/fairroulette/go/fairroulette/consts.go index b185d33590..c1909a2672 100644 --- a/contracts/wasm/fairroulette/consts.go +++ b/contracts/wasm/fairroulette/go/fairroulette/consts.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "fairroulette" diff --git a/contracts/wasm/fairroulette/contract.go b/contracts/wasm/fairroulette/go/fairroulette/contract.go similarity index 97% rename from contracts/wasm/fairroulette/contract.go rename to contracts/wasm/fairroulette/go/fairroulette/contract.go index 3cf132423b..6f324490b1 100644 --- a/contracts/wasm/fairroulette/contract.go +++ b/contracts/wasm/fairroulette/go/fairroulette/contract.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ForcePayoutCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/fairroulette/fairroulette.go b/contracts/wasm/fairroulette/go/fairroulette/fairroulette.go similarity index 99% rename from contracts/wasm/fairroulette/fairroulette.go rename to contracts/wasm/fairroulette/go/fairroulette/fairroulette.go index c3dff4360c..a26a299a70 100644 --- a/contracts/wasm/fairroulette/fairroulette.go +++ b/contracts/wasm/fairroulette/go/fairroulette/fairroulette.go @@ -10,7 +10,7 @@ package fairroulette import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // Define some default configuration parameters. @@ -36,6 +36,7 @@ const NanoTimeDivider = 1000_000_000 // The 'member' function will save the number together with the address of the better and // the amount of incoming iotas as the bet amount in its state. func funcPlaceBet(ctx wasmlib.ScFuncContext, f *PlaceBetContext) { + // Get the array of current bets from state storage. bets := f.State.Bets() for i := int32(0); i < bets.Length(); i++ { diff --git a/contracts/wasm/fairroulette/keys.go b/contracts/wasm/fairroulette/go/fairroulette/keys.go similarity index 93% rename from contracts/wasm/fairroulette/keys.go rename to contracts/wasm/fairroulette/go/fairroulette/keys.go index 976b2bf95d..2837c356f4 100644 --- a/contracts/wasm/fairroulette/keys.go +++ b/contracts/wasm/fairroulette/go/fairroulette/keys.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamNumber = 0 diff --git a/contracts/wasm/fairroulette/lib.go b/contracts/wasm/fairroulette/go/fairroulette/lib.go similarity index 98% rename from contracts/wasm/fairroulette/lib.go rename to contracts/wasm/fairroulette/go/fairroulette/lib.go index abefde04e9..a6b2d30b11 100644 --- a/contracts/wasm/fairroulette/lib.go +++ b/contracts/wasm/fairroulette/go/fairroulette/lib.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/fairroulette/params.go b/contracts/wasm/fairroulette/go/fairroulette/params.go similarity index 93% rename from contracts/wasm/fairroulette/params.go rename to contracts/wasm/fairroulette/go/fairroulette/params.go index f864ca6c33..f049718b87 100644 --- a/contracts/wasm/fairroulette/params.go +++ b/contracts/wasm/fairroulette/go/fairroulette/params.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutablePlaceBetParams struct { id int32 diff --git a/contracts/wasm/fairroulette/results.go b/contracts/wasm/fairroulette/go/fairroulette/results.go similarity index 96% rename from contracts/wasm/fairroulette/results.go rename to contracts/wasm/fairroulette/go/fairroulette/results.go index 4ac7a68088..284847a184 100644 --- a/contracts/wasm/fairroulette/results.go +++ b/contracts/wasm/fairroulette/go/fairroulette/results.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableLastWinningNumberResults struct { id int32 diff --git a/contracts/wasm/fairroulette/state.go b/contracts/wasm/fairroulette/go/fairroulette/state.go similarity index 97% rename from contracts/wasm/fairroulette/state.go rename to contracts/wasm/fairroulette/go/fairroulette/state.go index 0ea532bb86..f69f330f7e 100644 --- a/contracts/wasm/fairroulette/state.go +++ b/contracts/wasm/fairroulette/go/fairroulette/state.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayOfImmutableBet struct { objID int32 diff --git a/contracts/wasm/fairroulette/types.go b/contracts/wasm/fairroulette/go/fairroulette/structs.go similarity index 95% rename from contracts/wasm/fairroulette/types.go rename to contracts/wasm/fairroulette/go/fairroulette/structs.go index e1b1ff5a2c..34b0d66237 100644 --- a/contracts/wasm/fairroulette/types.go +++ b/contracts/wasm/fairroulette/go/fairroulette/structs.go @@ -7,7 +7,7 @@ package fairroulette -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type Bet struct { Amount int64 diff --git a/contracts/wasm/fairroulette/wasmmain/main.go b/contracts/wasm/fairroulette/go/main.go similarity index 79% rename from contracts/wasm/fairroulette/wasmmain/main.go rename to contracts/wasm/fairroulette/go/main.go index 0753c18b8f..17d9e08530 100644 --- a/contracts/wasm/fairroulette/wasmmain/main.go +++ b/contracts/wasm/fairroulette/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/fairroulette" + +import "github.com/iotaledger/wasp/contracts/wasm/fairroulette/go/fairroulette" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() fairroulette.OnLoad() diff --git a/contracts/wasm/fairroulette/src/fairroulette.rs b/contracts/wasm/fairroulette/src/fairroulette.rs index e5fdbe1445..f955a59d1c 100644 --- a/contracts/wasm/fairroulette/src/fairroulette.rs +++ b/contracts/wasm/fairroulette/src/fairroulette.rs @@ -8,10 +8,10 @@ // through a minimal implementation and not to come up with a complete real-world solution. use wasmlib::*; -use crate::contract::ScFuncs; -use crate::types::*; use crate::*; +use crate::contract::*; +use crate::structs::*; // Define some default configuration parameters. @@ -36,7 +36,8 @@ const NANO_TIME_DIVIDER: i64 = 1000000000; // The 'member' function will save the number together with the address of the better and // the amount of incoming iotas as the bet amount in its state. pub fn func_place_bet(ctx: &ScFuncContext, f: &PlaceBetContext) { - let bets = f.state.bets(); + // Get the array of current bets from state storage. + let bets: ArrayOfMutableBet = f.state.bets(); for i in 0..bets.length() { let bet: Bet = bets.get_bet(i).value(); @@ -73,9 +74,6 @@ pub fn func_place_bet(ctx: &ScFuncContext, f: &PlaceBetContext) { number: number, }; - // Get the array of current bets from state storage. - let bets: ArrayOfMutableBet = f.state.bets(); - // Determine what the next bet number is by retrieving the length of the bets array. let bet_nr: i32 = bets.length(); diff --git a/contracts/wasm/fairroulette/src/lib.rs b/contracts/wasm/fairroulette/src/lib.rs index 4726227780..86a275e671 100644 --- a/contracts/wasm/fairroulette/src/lib.rs +++ b/contracts/wasm/fairroulette/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use fairroulette::*; @@ -27,7 +26,7 @@ mod keys; mod params; mod results; mod state; -mod types; +mod structs; mod fairroulette; #[no_mangle] diff --git a/contracts/wasm/fairroulette/src/results.rs b/contracts/wasm/fairroulette/src/results.rs index 7a3c35914d..ac407245b1 100644 --- a/contracts/wasm/fairroulette/src/results.rs +++ b/contracts/wasm/fairroulette/src/results.rs @@ -13,7 +13,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; -use crate::types::*; +use crate::structs::*; #[derive(Clone, Copy)] pub struct ImmutableLastWinningNumberResults { diff --git a/contracts/wasm/fairroulette/src/state.rs b/contracts/wasm/fairroulette/src/state.rs index 3a52c34129..f67c00ce16 100644 --- a/contracts/wasm/fairroulette/src/state.rs +++ b/contracts/wasm/fairroulette/src/state.rs @@ -13,7 +13,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; -use crate::types::*; +use crate::structs::*; pub struct ArrayOfImmutableBet { pub(crate) obj_id: i32, diff --git a/contracts/wasm/fairroulette/src/types.rs b/contracts/wasm/fairroulette/src/structs.rs similarity index 100% rename from contracts/wasm/fairroulette/src/types.rs rename to contracts/wasm/fairroulette/src/structs.rs diff --git a/contracts/wasm/fairroulette/test/fairroulette_bg.wasm b/contracts/wasm/fairroulette/test/fairroulette_bg.wasm index e25353d109..40dc5968cc 100644 Binary files a/contracts/wasm/fairroulette/test/fairroulette_bg.wasm and b/contracts/wasm/fairroulette/test/fairroulette_bg.wasm differ diff --git a/contracts/wasm/fairroulette/test/fairroulette_test.go b/contracts/wasm/fairroulette/test/fairroulette_test.go index 26a685e817..09b45e53d5 100644 --- a/contracts/wasm/fairroulette/test/fairroulette_test.go +++ b/contracts/wasm/fairroulette/test/fairroulette_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/iotaledger/wasp/contracts/wasm/fairroulette" + "github.com/iotaledger/wasp/contracts/wasm/fairroulette/go/fairroulette" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/fairroulette/ts/fairroulette/consts.ts b/contracts/wasm/fairroulette/ts/fairroulette/consts.ts new file mode 100644 index 0000000000..45f2a3828f --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/consts.ts @@ -0,0 +1,46 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "fairroulette"; +export const HScName = new wasmlib.ScHname(0xdf79d138); + +export const ParamNumber = "number"; +export const ParamPlayPeriod = "playPeriod"; + +export const ResultLastWinningNumber = "lastWinningNumber"; +export const ResultRoundNumber = "roundNumber"; +export const ResultRoundStartedAt = "roundStartedAt"; +export const ResultRoundStatus = "roundStatus"; + +export const StateBets = "bets"; +export const StateLastWinningNumber = "lastWinningNumber"; +export const StatePlayPeriod = "playPeriod"; +export const StateRoundNumber = "roundNumber"; +export const StateRoundStartedAt = "roundStartedAt"; +export const StateRoundStatus = "roundStatus"; + +export const FuncForcePayout = "forcePayout"; +export const FuncForceReset = "forceReset"; +export const FuncPayWinners = "payWinners"; +export const FuncPlaceBet = "placeBet"; +export const FuncPlayPeriod = "playPeriod"; +export const ViewLastWinningNumber = "lastWinningNumber"; +export const ViewRoundNumber = "roundNumber"; +export const ViewRoundStartedAt = "roundStartedAt"; +export const ViewRoundStatus = "roundStatus"; + +export const HFuncForcePayout = new wasmlib.ScHname(0x555a4c4f); +export const HFuncForceReset = new wasmlib.ScHname(0xa331951e); +export const HFuncPayWinners = new wasmlib.ScHname(0xfb2b0144); +export const HFuncPlaceBet = new wasmlib.ScHname(0xdfba7d1b); +export const HFuncPlayPeriod = new wasmlib.ScHname(0xcb94b293); +export const HViewLastWinningNumber = new wasmlib.ScHname(0x2f5f09fe); +export const HViewRoundNumber = new wasmlib.ScHname(0x0dcfe520); +export const HViewRoundStartedAt = new wasmlib.ScHname(0x725de8b4); +export const HViewRoundStatus = new wasmlib.ScHname(0x145053b5); diff --git a/contracts/wasm/fairroulette/ts/fairroulette/contract.ts b/contracts/wasm/fairroulette/ts/fairroulette/contract.ts new file mode 100644 index 0000000000..601fcc7be7 --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/contract.ts @@ -0,0 +1,147 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ForcePayoutCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncForcePayout); +} + +export class ForcePayoutContext { + state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); +} + +export class ForceResetCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncForceReset); +} + +export class ForceResetContext { + state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); +} + +export class PayWinnersCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncPayWinners); +} + +export class PayWinnersContext { + state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); +} + +export class PlaceBetCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncPlaceBet); + params: sc.MutablePlaceBetParams = new sc.MutablePlaceBetParams(); +} + +export class PlaceBetContext { + params: sc.ImmutablePlaceBetParams = new sc.ImmutablePlaceBetParams(); + state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); +} + +export class PlayPeriodCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncPlayPeriod); + params: sc.MutablePlayPeriodParams = new sc.MutablePlayPeriodParams(); +} + +export class PlayPeriodContext { + params: sc.ImmutablePlayPeriodParams = new sc.ImmutablePlayPeriodParams(); + state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); +} + +export class LastWinningNumberCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewLastWinningNumber); + results: sc.ImmutableLastWinningNumberResults = new sc.ImmutableLastWinningNumberResults(); +} + +export class LastWinningNumberContext { + results: sc.MutableLastWinningNumberResults = new sc.MutableLastWinningNumberResults(); + state: sc.ImmutableFairRouletteState = new sc.ImmutableFairRouletteState(); +} + +export class RoundNumberCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewRoundNumber); + results: sc.ImmutableRoundNumberResults = new sc.ImmutableRoundNumberResults(); +} + +export class RoundNumberContext { + results: sc.MutableRoundNumberResults = new sc.MutableRoundNumberResults(); + state: sc.ImmutableFairRouletteState = new sc.ImmutableFairRouletteState(); +} + +export class RoundStartedAtCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewRoundStartedAt); + results: sc.ImmutableRoundStartedAtResults = new sc.ImmutableRoundStartedAtResults(); +} + +export class RoundStartedAtContext { + results: sc.MutableRoundStartedAtResults = new sc.MutableRoundStartedAtResults(); + state: sc.ImmutableFairRouletteState = new sc.ImmutableFairRouletteState(); +} + +export class RoundStatusCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewRoundStatus); + results: sc.ImmutableRoundStatusResults = new sc.ImmutableRoundStatusResults(); +} + +export class RoundStatusContext { + results: sc.MutableRoundStatusResults = new sc.MutableRoundStatusResults(); + state: sc.ImmutableFairRouletteState = new sc.ImmutableFairRouletteState(); +} + +export class ScFuncs { + + static forcePayout(ctx: wasmlib.ScFuncCallContext): ForcePayoutCall { + let f = new ForcePayoutCall(); + return f; + } + + static forceReset(ctx: wasmlib.ScFuncCallContext): ForceResetCall { + let f = new ForceResetCall(); + return f; + } + + static payWinners(ctx: wasmlib.ScFuncCallContext): PayWinnersCall { + let f = new PayWinnersCall(); + return f; + } + + static placeBet(ctx: wasmlib.ScFuncCallContext): PlaceBetCall { + let f = new PlaceBetCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static playPeriod(ctx: wasmlib.ScFuncCallContext): PlayPeriodCall { + let f = new PlayPeriodCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static lastWinningNumber(ctx: wasmlib.ScViewCallContext): LastWinningNumberCall { + let f = new LastWinningNumberCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static roundNumber(ctx: wasmlib.ScViewCallContext): RoundNumberCall { + let f = new RoundNumberCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static roundStartedAt(ctx: wasmlib.ScViewCallContext): RoundStartedAtCall { + let f = new RoundStartedAtCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static roundStatus(ctx: wasmlib.ScViewCallContext): RoundStatusCall { + let f = new RoundStatusCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/fairroulette/ts/fairroulette/fairroulette.ts b/contracts/wasm/fairroulette/ts/fairroulette/fairroulette.ts new file mode 100644 index 0000000000..83db2978ca --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/fairroulette.ts @@ -0,0 +1,313 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// This example implements 'fairroulette', a simple smart contract that can automatically handle +// an unlimited amount of bets on a number during a timed betting round. Once a betting round +// is over the contract will automatically pay out the winners proportionally to their bet amount. +// The intent is to showcase basic functionality of WasmLib and timed calling of functions +// through a minimal implementation and not to come up with a complete real-world solution. + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +// Define some default configuration parameters. + +// The maximum number one can bet on. The range of numbers starts at 1. +const MAX_NUMBER: i64 = 8; + +// The default playing period of one betting round in seconds. +const DEFAULT_PLAY_PERIOD: i32 = 60; + +// Enable this if you deploy the contract to an actual node. It will pay out the prize after a certain timeout. +const ENABLE_SELF_POST: boolean = true; + +// The number to divide nano seconds to seconds. +const NANO_TIME_DIVIDER: i64 = 1000000000; + +// 'placeBet' is used by betters to place a bet on a number from 1 to MAX_NUMBER. The first +// incoming bet triggers a betting round of configurable duration. After the playing period +// expires the smart contract will automatically pay out any winners and start a new betting +// round upon arrival of a new bet. +// The 'placeBet' function takes 1 mandatory parameter: +// - 'number', which must be an Int64 number from 1 to MAX_NUMBER +// The 'member' function will save the number together with the address of the better and +// the amount of incoming iotas as the bet amount in its state. +export function funcPlaceBet(ctx: wasmlib.ScFuncContext, f: sc.PlaceBetContext): void { + // Get the array of current bets from state storage. + let bets: sc.ArrayOfMutableBet = f.state.bets(); + + for (let i = 0; i < bets.length(); i++) { + let bet: sc.Bet = bets.getBet(i).value(); + + if (bet.better.address() == ctx.caller().address()) { + ctx.panic("Bet already placed for this round"); + } + } + + // Since we are sure that the 'number' parameter actually exists we can + // retrieve its actual value into an i64. + let number: i64 = f.params.number().value(); + + // Require that the number is a valid number to bet on, otherwise panic out. + ctx.require(number >= 1 && number <= MAX_NUMBER, "invalid number"); + + // Create ScBalances proxy to the incoming balances for this request. + // Note that ScBalances wraps an ScImmutableMap of token color/amount combinations + // in a simpler to use interface. + let incoming: wasmlib.ScBalances = ctx.incoming(); + + // Retrieve the amount of plain iota tokens that are part of the incoming balance. + let amount: i64 = incoming.balance(wasmlib.ScColor.IOTA); + + // Require that there are actually some plain iotas there + ctx.require(amount > 0, "empty bet"); + + // Now we gather all information together into a single serializable struct + // Note that we use the caller() method of the function context to determine + // the agent id of the better. This is where a potential pay-out will be sent. + let bet = new sc.Bet(); + bet.better = ctx.caller(); + bet.amount = amount; + bet.number = number; + + // Determine what the next bet number is by retrieving the length of the bets array. + let betNr: i32 = bets.length(); + + // Append the bet data to the bets array. The bet array will automatically take care + // of serializing the bet struct into a bytes representation. + bets.getBet(betNr).setValue(bet); + + ctx.event("fairroulette.bet.placed " + + bet.better.address().toString() + " " + + bet.amount.toString() + " " + + bet.number.toString() + ); + + // Was this the first bet of this round? + if (betNr == 0) { + // Yes it was, query the state for the length of the playing period in seconds by + // retrieving the playPeriod value from state storage + let playPeriod: i32 = f.state.playPeriod().value(); + + // if the play period is less than 10 seconds we override it with the default duration. + // Note that this will also happen when the play period was not set yet because in that + // case a zero value was returned. + if (playPeriod < 10) { + playPeriod = DEFAULT_PLAY_PERIOD; + f.state.playPeriod().setValue(playPeriod); + } + + if (ENABLE_SELF_POST) { + f.state.roundStatus().setValue(1); + + // timestamp is nanotime, divide by NANO_TIME_DIVIDER to get seconds => common unix timestamp + let timestamp = (ctx.timestamp() / NANO_TIME_DIVIDER) as i32; + f.state.roundStartedAt().setValue(timestamp); + + ctx.event("fairroulette.round.state " + + f.state.roundStatus().toString() + " " + + timestamp.toString() + ); + + let roundNumber = f.state.roundNumber(); + roundNumber.setValue(roundNumber.value() + 1); + + ctx.event("fairroulette.round.number " + roundNumber.toString()); + + // And now for our next trick we post a delayed request to ourselves on the Tangle. + // We are requesting to call the 'payWinners' function, but delay it for the playPeriod + // amount of seconds. This will lock in the playing period, during which more bets can + // be placed. Once the 'payWinners' function gets triggered by the ISCP it will gather all + // bets up to that moment as the ones to consider for determining the winner. + sc.ScFuncs.payWinners(ctx).func + .delay(playPeriod) + .transferIotas(1) + .post(); + } + } +} + +// 'payWinners' is a function whose execution gets initiated by the 'placeBet' function. +// It collects a list of all bets, generates a random number, sorts out the winners and transfers +// the calculated winning sum to each attendee. +export function funcPayWinners(ctx: wasmlib.ScFuncContext, f: sc.PayWinnersContext): void { + // Use the built-in random number generator which has been automatically initialized by + // using the transaction hash as initial entropy data. Note that the pseudo-random number + // generator will use the next 8 bytes from the hash as its random Int64 number and once + // it runs out of data it simply hashes the previous hash for a next pseudo-random sequence. + // Here we determine the winning number for this round in the range of 1 thru MAX_NUMBER. + let winningNumber: i64 = ctx.utility().random(MAX_NUMBER - 1) + 1; + + // Save the last winning number in state storage under 'lastWinningNumber' so that there + // is (limited) time for people to call the 'getLastWinningNumber' View to verify the last + // winning number if they wish. Note that this is just a silly example. We could log much + // more extensive statistics information about each playing round in state storage and + // make that data available through views for anyone to see. + f.state.lastWinningNumber().setValue(winningNumber); + + // Gather all winners and calculate some totals at the same time. + // Keep track of the total bet amount, the total win amount, and all the winners. + // Note how we decided to keep the winners in a local vector instead of creating + // yet another array in state storage or having to go through lockedBets again. + let totalBetAmount: i64 = 0; + let totalWinAmount: i64 = 0; + let winners: sc.Bet[] = []; + + // Get the 'bets' array in state storage. + let bets: sc.ArrayOfMutableBet = f.state.bets(); + + // Determine the amount of bets in the 'bets' array. + let nrBets: i32 = bets.length(); + + // Loop through all indexes of the 'bets' array. + for (let i = 0; i < nrBets; i++) { + // Retrieve the bet stored at the next index + let bet: sc.Bet = bets.getBet(i).value(); + + // Add this bet's amount to the running total bet amount + totalBetAmount += bet.amount; + + // Did this better bet on the winning number? + if (bet.number == winningNumber) { + // Yes, add this bet amount to the running total win amount. + totalWinAmount += bet.amount; + + // And save this bet in the winners vector. + winners.push(bet); + } + } + + // Now that we preprocessed all bets we can get rid of the data in state storage + // so that the 'bets' array becomes available for when the next betting round ends. + bets.clear(); + + ctx.event("fairroulette.round.winningNumber " + winningNumber.toString()); + + // Did we have any winners at all? + if (winners.length == 0) { + // No winners, log this fact to the log on the host. + ctx.log("Nobody wins!"); + } + + // Pay out the winners proportionally to their bet amount. Note that we could configure + // a small percentage that would go to the owner of the smart contract as hosting payment. + + // Keep track of the total payout so we can calculate the remainder after truncation. + let totalPayout: i64 = 0; + + // Loop through all winners. + let size = winners.length; + for (let i = 0; i < size; i++) { + // Get the next winner. + let bet: sc.Bet = winners[i]; + + // Determine the proportional win amount (we could take our percentage here) + let payout: i64 = totalBetAmount * bet.amount / totalWinAmount; + + // Anything to pay to the winner? + if (payout != 0) { + // Yep, keep track of the running total payout + totalPayout += payout; + + // Set up an ScTransfers proxy that transfers the correct amount of iotas. + // Note that ScTransfers wraps an ScMutableMap of token color/amount combinations + // in a simpler to use interface. The constructor we use here creates and initializes + // a single token color transfer in a single statement. The actual color and amount + // values passed in will be stored in a new map on the host. + let transfers: wasmlib.ScTransfers = wasmlib.ScTransfers.iotas(payout); + + // Perform the actual transfer of tokens from the smart contract to the address + // of the winner. The transferToAddress() method receives the address value and + // the proxy to the new transfers map on the host, and will call the corresponding + // host sandbox function with these values. + ctx.transferToAddress(bet.better.address(), transfers); + } + + // Announce who got sent what as event. + ctx.event("fairroulette.payout " + + bet.better.address().toString() + " " + + payout.toString() + ); + } + + // This is where we transfer the remainder after payout to the creator of the smart contract. + // The bank always wins :-P + let remainder: i64 = totalBetAmount - totalPayout; + if (remainder != 0) { + // We have a remainder. First create a transfer for the remainder. + let transfers: wasmlib.ScTransfers = wasmlib.ScTransfers.iotas(remainder); + + // Send the remainder to the contract creator. + ctx.transferToAddress(ctx.contractCreator().address(), transfers); + } + + // Set round status to 0, send out event to notify that the round has ended + f.state.roundStatus().setValue(0); + ctx.event("fairroulette.round.state " + f.state.roundStatus().toString()); +} + +export function funcForceReset(ctx: wasmlib.ScFuncContext, f: sc.ForceResetContext): void { + + // Get the 'bets' array in state storage. + let bets: sc.ArrayOfMutableBet = f.state.bets(); + + // Clear all bets. + bets.clear(); + + // Set round status to 0, send out event to notify that the round has ended + f.state.roundStatus().setValue(0); + ctx.event("fairroulette.round.state +" + f.state.roundStatus().toString()); +} + +// 'playPeriod' can be used by the contract creator to set the length of a betting round +// to a different value than the default value, which is 120 seconds. +export function funcPlayPeriod(ctx: wasmlib.ScFuncContext, f: sc.PlayPeriodContext): void { + // Since we are sure that the 'playPeriod' parameter actually exists we can + // retrieve its actual value into an i32 value. + let playPeriod: i32 = f.params.playPeriod().value(); + + // Require that the play period (in seconds) is not ridiculously low. + // Otherwise, panic out with an error message. + ctx.require(playPeriod >= 10, "invalid play period"); + + // Now we set the corresponding variable 'playPeriod' in state storage. + f.state.playPeriod().setValue(playPeriod); +} + +export function viewLastWinningNumber(ctx: wasmlib.ScViewContext, f: sc.LastWinningNumberContext): void { + // Get the 'lastWinningNumber' int64 value from state storage. + let lastWinningNumber = f.state.lastWinningNumber().value(); + + // Set the 'lastWinningNumber' in results to the value from state storage. + f.results + .lastWinningNumber() + .setValue(lastWinningNumber); +} + +export function viewRoundNumber(ctx: wasmlib.ScViewContext, f: sc.RoundNumberContext): void { + // Get the 'roundNumber' int64 value from state storage. + let roundNumber = f.state.roundNumber().value(); + + // Set the 'roundNumber' in results to the value from state storage. + f.results.roundNumber().setValue(roundNumber); +} + +export function viewRoundStatus(ctx: wasmlib.ScViewContext, f: sc.RoundStatusContext): void { + // Get the 'roundStatus' int16 value from state storage. + let roundStatus = f.state.roundStatus().value(); + + // Set the 'roundStatus' in results to the value from state storage. + f.results.roundStatus().setValue(roundStatus); +} + +export function viewRoundStartedAt(ctx: wasmlib.ScViewContext, f: sc.RoundStartedAtContext): void { + // Get the 'roundStartedAt' int32 value from state storage. + let roundStartedAt = f.state.roundStartedAt().value(); + + // Set the 'roundStartedAt' in results to the value from state storage. + f.results.roundStartedAt().setValue(roundStartedAt); +} + +export function funcForcePayout(ctx: wasmlib.ScFuncContext, f: sc.ForcePayoutContext): void { + sc.ScFuncs.payWinners(ctx).func.call(); +} diff --git a/contracts/wasm/fairroulette/ts/fairroulette/index.ts b/contracts/wasm/fairroulette/ts/fairroulette/index.ts new file mode 100644 index 0000000000..d6151e8562 --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/index.ts @@ -0,0 +1,17 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./fairroulette"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; +export * from "./structs"; diff --git a/contracts/wasm/fairroulette/ts/fairroulette/keys.ts b/contracts/wasm/fairroulette/ts/fairroulette/keys.ts new file mode 100644 index 0000000000..c458464b29 --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/keys.ts @@ -0,0 +1,39 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamNumber = 0; +export const IdxParamPlayPeriod = 1; +export const IdxResultLastWinningNumber = 2; +export const IdxResultRoundNumber = 3; +export const IdxResultRoundStartedAt = 4; +export const IdxResultRoundStatus = 5; +export const IdxStateBets = 6; +export const IdxStateLastWinningNumber = 7; +export const IdxStatePlayPeriod = 8; +export const IdxStateRoundNumber = 9; +export const IdxStateRoundStartedAt = 10; +export const IdxStateRoundStatus = 11; + +export let keyMap: string[] = [ + sc.ParamNumber, + sc.ParamPlayPeriod, + sc.ResultLastWinningNumber, + sc.ResultRoundNumber, + sc.ResultRoundStartedAt, + sc.ResultRoundStatus, + sc.StateBets, + sc.StateLastWinningNumber, + sc.StatePlayPeriod, + sc.StateRoundNumber, + sc.StateRoundStartedAt, + sc.StateRoundStatus, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/fairroulette/ts/fairroulette/lib.ts b/contracts/wasm/fairroulette/ts/fairroulette/lib.ts new file mode 100644 index 0000000000..46d913ab04 --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/lib.ts @@ -0,0 +1,122 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncForcePayout, funcForcePayoutThunk); + exports.addFunc(sc.FuncForceReset, funcForceResetThunk); + exports.addFunc(sc.FuncPayWinners, funcPayWinnersThunk); + exports.addFunc(sc.FuncPlaceBet, funcPlaceBetThunk); + exports.addFunc(sc.FuncPlayPeriod, funcPlayPeriodThunk); + exports.addView(sc.ViewLastWinningNumber, viewLastWinningNumberThunk); + exports.addView(sc.ViewRoundNumber, viewRoundNumberThunk); + exports.addView(sc.ViewRoundStartedAt, viewRoundStartedAtThunk); + exports.addView(sc.ViewRoundStatus, viewRoundStatusThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcForcePayoutThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairroulette.funcForcePayout"); + // only SC creator can restart the round forcefully + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.ForcePayoutContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcForcePayout(ctx, f); + ctx.log("fairroulette.funcForcePayout ok"); +} + +function funcForceResetThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairroulette.funcForceReset"); + // only SC creator can restart the round forcefully + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.ForceResetContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcForceReset(ctx, f); + ctx.log("fairroulette.funcForceReset ok"); +} + +function funcPayWinnersThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairroulette.funcPayWinners"); + // only SC itself can invoke this function + ctx.require(ctx.caller().equals(ctx.accountID()), "no permission"); + + let f = new sc.PayWinnersContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcPayWinners(ctx, f); + ctx.log("fairroulette.funcPayWinners ok"); +} + +function funcPlaceBetThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairroulette.funcPlaceBet"); + let f = new sc.PlaceBetContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.number().exists(), "missing mandatory number") + sc.funcPlaceBet(ctx, f); + ctx.log("fairroulette.funcPlaceBet ok"); +} + +function funcPlayPeriodThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("fairroulette.funcPlayPeriod"); + // only SC creator can update the play period + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.PlayPeriodContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.playPeriod().exists(), "missing mandatory playPeriod") + sc.funcPlayPeriod(ctx, f); + ctx.log("fairroulette.funcPlayPeriod ok"); +} + +function viewLastWinningNumberThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("fairroulette.viewLastWinningNumber"); + let f = new sc.LastWinningNumberContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewLastWinningNumber(ctx, f); + ctx.log("fairroulette.viewLastWinningNumber ok"); +} + +function viewRoundNumberThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("fairroulette.viewRoundNumber"); + let f = new sc.RoundNumberContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewRoundNumber(ctx, f); + ctx.log("fairroulette.viewRoundNumber ok"); +} + +function viewRoundStartedAtThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("fairroulette.viewRoundStartedAt"); + let f = new sc.RoundStartedAtContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewRoundStartedAt(ctx, f); + ctx.log("fairroulette.viewRoundStartedAt ok"); +} + +function viewRoundStatusThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("fairroulette.viewRoundStatus"); + let f = new sc.RoundStatusContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewRoundStatus(ctx, f); + ctx.log("fairroulette.viewRoundStatus ok"); +} diff --git a/contracts/wasm/fairroulette/ts/fairroulette/params.ts b/contracts/wasm/fairroulette/ts/fairroulette/params.ts new file mode 100644 index 0000000000..a79831a801 --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/params.ts @@ -0,0 +1,37 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutablePlaceBetParams extends wasmlib.ScMapID { + + number(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamNumber]); + } +} + +export class MutablePlaceBetParams extends wasmlib.ScMapID { + + number(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamNumber]); + } +} + +export class ImmutablePlayPeriodParams extends wasmlib.ScMapID { + + playPeriod(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamPlayPeriod]); + } +} + +export class MutablePlayPeriodParams extends wasmlib.ScMapID { + + playPeriod(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamPlayPeriod]); + } +} diff --git a/contracts/wasm/fairroulette/ts/fairroulette/results.ts b/contracts/wasm/fairroulette/ts/fairroulette/results.ts new file mode 100644 index 0000000000..6ac963982b --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/results.ts @@ -0,0 +1,65 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableLastWinningNumberResults extends wasmlib.ScMapID { + + lastWinningNumber(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultLastWinningNumber]); + } +} + +export class MutableLastWinningNumberResults extends wasmlib.ScMapID { + + lastWinningNumber(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultLastWinningNumber]); + } +} + +export class ImmutableRoundNumberResults extends wasmlib.ScMapID { + + roundNumber(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultRoundNumber]); + } +} + +export class MutableRoundNumberResults extends wasmlib.ScMapID { + + roundNumber(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultRoundNumber]); + } +} + +export class ImmutableRoundStartedAtResults extends wasmlib.ScMapID { + + roundStartedAt(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxResultRoundStartedAt]); + } +} + +export class MutableRoundStartedAtResults extends wasmlib.ScMapID { + + roundStartedAt(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxResultRoundStartedAt]); + } +} + +export class ImmutableRoundStatusResults extends wasmlib.ScMapID { + + roundStatus(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, sc.idxMap[sc.IdxResultRoundStatus]); + } +} + +export class MutableRoundStatusResults extends wasmlib.ScMapID { + + roundStatus(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, sc.idxMap[sc.IdxResultRoundStatus]); + } +} diff --git a/contracts/wasm/fairroulette/ts/fairroulette/state.ts b/contracts/wasm/fairroulette/ts/fairroulette/state.ts new file mode 100644 index 0000000000..622f56cf8a --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/state.ts @@ -0,0 +1,101 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableBet { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBet(index: i32): sc.ImmutableBet { + return new sc.ImmutableBet(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableFairRouletteState extends wasmlib.ScMapID { + + bets(): sc.ArrayOfImmutableBet { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBet(arrID) + } + + lastWinningNumber(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateLastWinningNumber]); + } + + playPeriod(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxStatePlayPeriod]); + } + + roundNumber(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateRoundNumber]); + } + + roundStartedAt(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxStateRoundStartedAt]); + } + + roundStatus(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, sc.idxMap[sc.IdxStateRoundStatus]); + } +} + +export class ArrayOfMutableBet { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBet(index: i32): sc.MutableBet { + return new sc.MutableBet(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableFairRouletteState extends wasmlib.ScMapID { + + bets(): sc.ArrayOfMutableBet { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBet(arrID) + } + + lastWinningNumber(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateLastWinningNumber]); + } + + playPeriod(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxStatePlayPeriod]); + } + + roundNumber(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateRoundNumber]); + } + + roundStartedAt(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxStateRoundStartedAt]); + } + + roundStatus(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, sc.idxMap[sc.IdxStateRoundStatus]); + } +} diff --git a/contracts/wasm/fairroulette/ts/fairroulette/structs.ts b/contracts/wasm/fairroulette/ts/fairroulette/structs.ts new file mode 100644 index 0000000000..4f5e2d41ed --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/structs.ts @@ -0,0 +1,72 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export class Bet { + amount: i64 = 0; + better: wasmlib.ScAgentID = new wasmlib.ScAgentID(); + number: i64 = 0; + + static fromBytes(bytes: u8[]): Bet { + let decode = new wasmlib.BytesDecoder(bytes); + let data = new Bet(); + data.amount = decode.int64(); + data.better = decode.agentID(); + data.number = decode.int64(); + decode.close(); + return data; + } + + bytes(): u8[] { + return new wasmlib.BytesEncoder(). + int64(this.amount). + agentID(this.better). + int64(this.number). + data(); + } +} + +export class ImmutableBet { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + value(): Bet { + return Bet.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} + +export class MutableBet { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + setValue(value: Bet): void { + wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes()); + } + + value(): Bet { + return Bet.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} diff --git a/contracts/wasm/fairroulette/ts/fairroulette/tsconfig.json b/contracts/wasm/fairroulette/ts/fairroulette/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/fairroulette/ts/fairroulette/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/go_all.cmd b/contracts/wasm/go_all.cmd new file mode 100644 index 0000000000..d8d1725710 --- /dev/null +++ b/contracts/wasm/go_all.cmd @@ -0,0 +1,2 @@ +@echo off +for /d %%f in (*.) do call go_build.cmd %%f %1 diff --git a/contracts/wasm/go_build.cmd b/contracts/wasm/go_build.cmd new file mode 100644 index 0000000000..feeee21a1b --- /dev/null +++ b/contracts/wasm/go_build.cmd @@ -0,0 +1,10 @@ +@echo off +cd %1 +if not exist schema.yaml goto :xit +echo Building %1 +schema -go %2 +echo compiling %1_go.wasm +if not exist go\pkg mkdir go\pkg +tinygo build -o go/pkg/%1_go.wasm -target wasm go/main.go +:xit +cd .. diff --git a/contracts/wasm/helloworld/Cargo.toml b/contracts/wasm/helloworld/Cargo.toml index b2d6d2c698..5969d1d7b0 100644 --- a/contracts/wasm/helloworld/Cargo.toml +++ b/contracts/wasm/helloworld/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/helloworld/consts.go b/contracts/wasm/helloworld/go/helloworld/consts.go similarity index 89% rename from contracts/wasm/helloworld/consts.go rename to contracts/wasm/helloworld/go/helloworld/consts.go index 9269a45142..8a2c2473ee 100644 --- a/contracts/wasm/helloworld/consts.go +++ b/contracts/wasm/helloworld/go/helloworld/consts.go @@ -7,7 +7,7 @@ package helloworld -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "helloworld" diff --git a/contracts/wasm/helloworld/contract.go b/contracts/wasm/helloworld/go/helloworld/contract.go similarity index 91% rename from contracts/wasm/helloworld/contract.go rename to contracts/wasm/helloworld/go/helloworld/contract.go index 83326df1c2..3423976513 100644 --- a/contracts/wasm/helloworld/contract.go +++ b/contracts/wasm/helloworld/go/helloworld/contract.go @@ -7,7 +7,7 @@ package helloworld -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type HelloWorldCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/helloworld/helloworld.go b/contracts/wasm/helloworld/go/helloworld/helloworld.go similarity index 85% rename from contracts/wasm/helloworld/helloworld.go rename to contracts/wasm/helloworld/go/helloworld/helloworld.go index 56bcd84496..8556be3915 100644 --- a/contracts/wasm/helloworld/helloworld.go +++ b/contracts/wasm/helloworld/go/helloworld/helloworld.go @@ -4,7 +4,7 @@ package helloworld import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) //nolint:unparam diff --git a/contracts/wasm/helloworld/keys.go b/contracts/wasm/helloworld/go/helloworld/keys.go similarity index 83% rename from contracts/wasm/helloworld/keys.go rename to contracts/wasm/helloworld/go/helloworld/keys.go index c3c77f9762..cfb0975574 100644 --- a/contracts/wasm/helloworld/keys.go +++ b/contracts/wasm/helloworld/go/helloworld/keys.go @@ -7,7 +7,7 @@ package helloworld -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const IdxResultHelloWorld = 0 diff --git a/contracts/wasm/helloworld/lib.go b/contracts/wasm/helloworld/go/helloworld/lib.go similarity index 94% rename from contracts/wasm/helloworld/lib.go rename to contracts/wasm/helloworld/go/helloworld/lib.go index f2958690ba..9292b24c0a 100644 --- a/contracts/wasm/helloworld/lib.go +++ b/contracts/wasm/helloworld/go/helloworld/lib.go @@ -7,7 +7,7 @@ package helloworld -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/helloworld/results.go b/contracts/wasm/helloworld/go/helloworld/results.go similarity index 90% rename from contracts/wasm/helloworld/results.go rename to contracts/wasm/helloworld/go/helloworld/results.go index b7e026a867..4f5c596589 100644 --- a/contracts/wasm/helloworld/results.go +++ b/contracts/wasm/helloworld/go/helloworld/results.go @@ -7,7 +7,7 @@ package helloworld -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableGetHelloWorldResults struct { id int32 diff --git a/contracts/wasm/helloworld/state.go b/contracts/wasm/helloworld/go/helloworld/state.go similarity index 100% rename from contracts/wasm/helloworld/state.go rename to contracts/wasm/helloworld/go/helloworld/state.go diff --git a/contracts/wasm/helloworld/wasmmain/main.go b/contracts/wasm/helloworld/go/main.go similarity index 80% rename from contracts/wasm/helloworld/wasmmain/main.go rename to contracts/wasm/helloworld/go/main.go index e729578cc7..e91b27bf55 100644 --- a/contracts/wasm/helloworld/wasmmain/main.go +++ b/contracts/wasm/helloworld/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/helloworld" + +import "github.com/iotaledger/wasp/contracts/wasm/helloworld/go/helloworld" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() helloworld.OnLoad() diff --git a/contracts/wasm/helloworld/src/contract.rs b/contracts/wasm/helloworld/src/contract.rs index f1703ceb59..1a1534a38f 100644 --- a/contracts/wasm/helloworld/src/contract.rs +++ b/contracts/wasm/helloworld/src/contract.rs @@ -14,7 +14,6 @@ use std::ptr; use wasmlib::*; use crate::consts::*; -use crate::params::*; use crate::results::*; pub struct HelloWorldCall { diff --git a/contracts/wasm/helloworld/src/lib.rs b/contracts/wasm/helloworld/src/lib.rs index 5c5b51ae3b..931b312fdf 100644 --- a/contracts/wasm/helloworld/src/lib.rs +++ b/contracts/wasm/helloworld/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use helloworld::*; @@ -17,14 +16,12 @@ use wasmlib::host::*; use crate::consts::*; use crate::keys::*; -use crate::params::*; use crate::results::*; use crate::state::*; mod consts; mod contract; mod keys; -mod params; mod results; mod state; mod helloworld; diff --git a/contracts/wasm/helloworld/test/helloworld_bg.wasm b/contracts/wasm/helloworld/test/helloworld_bg.wasm index 91f2cea118..8af77e238c 100644 Binary files a/contracts/wasm/helloworld/test/helloworld_bg.wasm and b/contracts/wasm/helloworld/test/helloworld_bg.wasm differ diff --git a/contracts/wasm/helloworld/test/helloworld_test.go b/contracts/wasm/helloworld/test/helloworld_test.go index 8c41f36220..c18c9ed42c 100644 --- a/contracts/wasm/helloworld/test/helloworld_test.go +++ b/contracts/wasm/helloworld/test/helloworld_test.go @@ -6,7 +6,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/helloworld" + "github.com/iotaledger/wasp/contracts/wasm/helloworld/go/helloworld" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/helloworld/ts/helloworld/consts.ts b/contracts/wasm/helloworld/ts/helloworld/consts.ts new file mode 100644 index 0000000000..d93bd75a35 --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/consts.ts @@ -0,0 +1,20 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "helloworld"; +export const ScDescription = "The ubiquitous hello world demo"; +export const HScName = new wasmlib.ScHname(0x0683223c); + +export const ResultHelloWorld = "helloWorld"; + +export const FuncHelloWorld = "helloWorld"; +export const ViewGetHelloWorld = "getHelloWorld"; + +export const HFuncHelloWorld = new wasmlib.ScHname(0x9d042e65); +export const HViewGetHelloWorld = new wasmlib.ScHname(0x210439ce); diff --git a/contracts/wasm/helloworld/ts/helloworld/contract.ts b/contracts/wasm/helloworld/ts/helloworld/contract.ts new file mode 100644 index 0000000000..ac9276f39a --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/contract.ts @@ -0,0 +1,41 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class HelloWorldCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncHelloWorld); +} + +export class HelloWorldContext { + state: sc.MutableHelloWorldState = new sc.MutableHelloWorldState(); +} + +export class GetHelloWorldCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetHelloWorld); + results: sc.ImmutableGetHelloWorldResults = new sc.ImmutableGetHelloWorldResults(); +} + +export class GetHelloWorldContext { + results: sc.MutableGetHelloWorldResults = new sc.MutableGetHelloWorldResults(); + state: sc.ImmutableHelloWorldState = new sc.ImmutableHelloWorldState(); +} + +export class ScFuncs { + + static helloWorld(ctx: wasmlib.ScFuncCallContext): HelloWorldCall { + let f = new HelloWorldCall(); + return f; + } + + static getHelloWorld(ctx: wasmlib.ScViewCallContext): GetHelloWorldCall { + let f = new GetHelloWorldCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/helloworld/ts/helloworld/helloworld.ts b/contracts/wasm/helloworld/ts/helloworld/helloworld.ts new file mode 100644 index 0000000000..1c1accb316 --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/helloworld.ts @@ -0,0 +1,13 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function funcHelloWorld(ctx: wasmlib.ScFuncContext, f: sc.HelloWorldContext): void { + ctx.log("Hello, world!"); +} + +export function viewGetHelloWorld(ctx: wasmlib.ScViewContext, f: sc.GetHelloWorldContext): void { + f.results.helloWorld().setValue("Hello, world!"); +} diff --git a/contracts/wasm/helloworld/ts/helloworld/index.ts b/contracts/wasm/helloworld/ts/helloworld/index.ts new file mode 100644 index 0000000000..d88f5510f4 --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/index.ts @@ -0,0 +1,15 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./helloworld"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./results"; +export * from "./state"; diff --git a/contracts/wasm/helloworld/ts/helloworld/keys.ts b/contracts/wasm/helloworld/ts/helloworld/keys.ts new file mode 100644 index 0000000000..b8d49c07ea --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/keys.ts @@ -0,0 +1,17 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxResultHelloWorld = 0; + +export let keyMap: string[] = [ + sc.ResultHelloWorld, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/helloworld/ts/helloworld/lib.ts b/contracts/wasm/helloworld/ts/helloworld/lib.ts new file mode 100644 index 0000000000..be19e8e7dc --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/lib.ts @@ -0,0 +1,40 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncHelloWorld, funcHelloWorldThunk); + exports.addView(sc.ViewGetHelloWorld, viewGetHelloWorldThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcHelloWorldThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("helloworld.funcHelloWorld"); + let f = new sc.HelloWorldContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcHelloWorld(ctx, f); + ctx.log("helloworld.funcHelloWorld ok"); +} + +function viewGetHelloWorldThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("helloworld.viewGetHelloWorld"); + let f = new sc.GetHelloWorldContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewGetHelloWorld(ctx, f); + ctx.log("helloworld.viewGetHelloWorld ok"); +} diff --git a/contracts/wasm/helloworld/ts/helloworld/results.ts b/contracts/wasm/helloworld/ts/helloworld/results.ts new file mode 100644 index 0000000000..2497774190 --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/results.ts @@ -0,0 +1,23 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableGetHelloWorldResults extends wasmlib.ScMapID { + + helloWorld(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultHelloWorld]); + } +} + +export class MutableGetHelloWorldResults extends wasmlib.ScMapID { + + helloWorld(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultHelloWorld]); + } +} diff --git a/contracts/wasm/helloworld/ts/helloworld/state.ts b/contracts/wasm/helloworld/ts/helloworld/state.ts new file mode 100644 index 0000000000..2fc69e22f8 --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/state.ts @@ -0,0 +1,15 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableHelloWorldState extends wasmlib.ScMapID { +} + +export class MutableHelloWorldState extends wasmlib.ScMapID { +} diff --git a/contracts/wasm/helloworld/ts/helloworld/tsconfig.json b/contracts/wasm/helloworld/ts/helloworld/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/helloworld/ts/helloworld/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/inccounter/Cargo.toml b/contracts/wasm/inccounter/Cargo.toml index 012ba86e93..85cf6dbe20 100644 --- a/contracts/wasm/inccounter/Cargo.toml +++ b/contracts/wasm/inccounter/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/inccounter/consts.go b/contracts/wasm/inccounter/go/inccounter/consts.go similarity index 96% rename from contracts/wasm/inccounter/consts.go rename to contracts/wasm/inccounter/go/inccounter/consts.go index 77f307495c..9b4c883fa5 100644 --- a/contracts/wasm/inccounter/consts.go +++ b/contracts/wasm/inccounter/go/inccounter/consts.go @@ -7,7 +7,7 @@ package inccounter -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "inccounter" diff --git a/contracts/wasm/inccounter/contract.go b/contracts/wasm/inccounter/go/inccounter/contract.go similarity index 98% rename from contracts/wasm/inccounter/contract.go rename to contracts/wasm/inccounter/go/inccounter/contract.go index f487f33d94..ce3aa3e852 100644 --- a/contracts/wasm/inccounter/contract.go +++ b/contracts/wasm/inccounter/go/inccounter/contract.go @@ -7,7 +7,7 @@ package inccounter -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type CallIncrementCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/inccounter/inccounter.go b/contracts/wasm/inccounter/go/inccounter/inccounter.go similarity index 98% rename from contracts/wasm/inccounter/inccounter.go rename to contracts/wasm/inccounter/go/inccounter/inccounter.go index ef06dc3fab..23e1dd665e 100644 --- a/contracts/wasm/inccounter/inccounter.go +++ b/contracts/wasm/inccounter/go/inccounter/inccounter.go @@ -4,7 +4,7 @@ package inccounter import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) var LocalStateMustIncrement = false diff --git a/contracts/wasm/inccounter/keys.go b/contracts/wasm/inccounter/go/inccounter/keys.go similarity index 89% rename from contracts/wasm/inccounter/keys.go rename to contracts/wasm/inccounter/go/inccounter/keys.go index f0fbf71ac0..47800369d6 100644 --- a/contracts/wasm/inccounter/keys.go +++ b/contracts/wasm/inccounter/go/inccounter/keys.go @@ -7,7 +7,7 @@ package inccounter -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamCounter = 0 diff --git a/contracts/wasm/inccounter/lib.go b/contracts/wasm/inccounter/go/inccounter/lib.go similarity index 98% rename from contracts/wasm/inccounter/lib.go rename to contracts/wasm/inccounter/go/inccounter/lib.go index 41e4a0630b..944556592f 100644 --- a/contracts/wasm/inccounter/lib.go +++ b/contracts/wasm/inccounter/go/inccounter/lib.go @@ -7,7 +7,7 @@ package inccounter -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/inccounter/params.go b/contracts/wasm/inccounter/go/inccounter/params.go similarity index 96% rename from contracts/wasm/inccounter/params.go rename to contracts/wasm/inccounter/go/inccounter/params.go index 572135bc68..e3dc8887dc 100644 --- a/contracts/wasm/inccounter/params.go +++ b/contracts/wasm/inccounter/go/inccounter/params.go @@ -7,7 +7,7 @@ package inccounter -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableIncrementWithDelayParams struct { id int32 diff --git a/contracts/wasm/inccounter/results.go b/contracts/wasm/inccounter/go/inccounter/results.go similarity index 89% rename from contracts/wasm/inccounter/results.go rename to contracts/wasm/inccounter/go/inccounter/results.go index 43a1a1cea7..b197174349 100644 --- a/contracts/wasm/inccounter/results.go +++ b/contracts/wasm/inccounter/go/inccounter/results.go @@ -7,7 +7,7 @@ package inccounter -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableGetCounterResults struct { id int32 diff --git a/contracts/wasm/inccounter/state.go b/contracts/wasm/inccounter/go/inccounter/state.go similarity index 92% rename from contracts/wasm/inccounter/state.go rename to contracts/wasm/inccounter/go/inccounter/state.go index fb0993d972..0795d48060 100644 --- a/contracts/wasm/inccounter/state.go +++ b/contracts/wasm/inccounter/go/inccounter/state.go @@ -7,7 +7,7 @@ package inccounter -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableIncCounterState struct { id int32 diff --git a/contracts/wasm/inccounter/wasmmain/main.go b/contracts/wasm/inccounter/go/main.go similarity index 80% rename from contracts/wasm/inccounter/wasmmain/main.go rename to contracts/wasm/inccounter/go/main.go index a4c7b3bd51..7a5a3f98bd 100644 --- a/contracts/wasm/inccounter/wasmmain/main.go +++ b/contracts/wasm/inccounter/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/inccounter" + +import "github.com/iotaledger/wasp/contracts/wasm/inccounter/go/inccounter" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() inccounter.OnLoad() diff --git a/contracts/wasm/inccounter/src/inccounter.rs b/contracts/wasm/inccounter/src/inccounter.rs index ed2a765a1d..a7fef760ca 100644 --- a/contracts/wasm/inccounter/src/inccounter.rs +++ b/contracts/wasm/inccounter/src/inccounter.rs @@ -8,13 +8,6 @@ use crate::contract::*; static mut LOCAL_STATE_MUST_INCREMENT: bool = false; -pub fn func_init(_ctx: &ScFuncContext, f: &InitContext) { - if f.params.counter().exists() { - let counter = f.params.counter().value(); - f.state.counter().set_value(counter); - } -} - pub fn func_call_increment(ctx: &ScFuncContext, f: &CallIncrementContext) { let counter = f.state.counter(); let value = counter.value(); @@ -42,6 +35,19 @@ pub fn func_increment(_ctx: &ScFuncContext, f: &IncrementContext) { counter.set_value(counter.value() + 1); } +pub fn func_increment_with_delay(ctx: &ScFuncContext, f: &IncrementWithDelayContext) { + let delay = f.params.delay().value(); + let inc = ScFuncs::call_increment(ctx); + inc.func.delay(delay).transfer_iotas(1).post(); +} + +pub fn func_init(_ctx: &ScFuncContext, f: &InitContext) { + if f.params.counter().exists() { + let counter = f.params.counter().value(); + f.state.counter().set_value(counter); + } +} + pub fn func_local_state_internal_call(ctx: &ScFuncContext, f: &LocalStateInternalCallContext) { unsafe { LOCAL_STATE_MUST_INCREMENT = false; @@ -168,9 +174,3 @@ fn when_must_increment_state(ctx: &ScFuncContext, state: &MutableIncCounterState let counter = state.counter(); counter.set_value(counter.value() + 1); } - -pub fn func_increment_with_delay(ctx: &ScFuncContext, f: &IncrementWithDelayContext) { - let delay = f.params.delay().value(); - let inc = inccounter::ScFuncs::call_increment(ctx); - inc.func.delay(delay).transfer_iotas(1).post(); -} diff --git a/contracts/wasm/inccounter/src/lib.rs b/contracts/wasm/inccounter/src/lib.rs index 3e1e8275cc..9b7e552473 100644 --- a/contracts/wasm/inccounter/src/lib.rs +++ b/contracts/wasm/inccounter/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use inccounter::*; diff --git a/contracts/wasm/inccounter/test/inccounter_bg.wasm b/contracts/wasm/inccounter/test/inccounter_bg.wasm index 37f836ce3c..bb828e396a 100644 Binary files a/contracts/wasm/inccounter/test/inccounter_bg.wasm and b/contracts/wasm/inccounter/test/inccounter_bg.wasm differ diff --git a/contracts/wasm/inccounter/test/inccounter_test.go b/contracts/wasm/inccounter/test/inccounter_test.go index 6229f2aa23..fc44b3f16a 100644 --- a/contracts/wasm/inccounter/test/inccounter_test.go +++ b/contracts/wasm/inccounter/test/inccounter_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/iotaledger/wasp/contracts/wasm/inccounter" + "github.com/iotaledger/wasp/contracts/wasm/inccounter/go/inccounter" "github.com/iotaledger/wasp/packages/vm/wasmhost" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" @@ -108,6 +108,11 @@ func TestIncrementLocalStateInternalCall(t *testing.T) { } func TestIncrementLocalStateSandboxCall(t *testing.T) { + // TODO need to save globals for TypeScript Wasm for this to succeed + if *wasmsolo.TsWasm { + t.SkipNow() + } + ctx := setupTest(t) localStateSandboxCall := inccounter.ScFuncs.LocalStateSandboxCall(ctx) @@ -126,6 +131,11 @@ func TestIncrementLocalStateSandboxCall(t *testing.T) { } func TestIncrementLocalStatePost(t *testing.T) { + // TODO need to save globals for TypeScript Wasm for this to succeed + if *wasmsolo.TsWasm { + t.SkipNow() + } + ctx := setupTest(t) localStatePost := inccounter.ScFuncs.LocalStatePost(ctx) @@ -146,7 +156,9 @@ func TestIncrementLocalStatePost(t *testing.T) { } func TestLeb128(t *testing.T) { + wasmhost.DisableWasmTimeout = true ctx := setupTest(t) + wasmhost.DisableWasmTimeout = false testLeb128 := inccounter.ScFuncs.TestLeb128(ctx) testLeb128.Func.TransferIotas(1).Post() diff --git a/contracts/wasm/inccounter/ts/inccounter/consts.ts b/contracts/wasm/inccounter/ts/inccounter/consts.ts new file mode 100644 index 0000000000..ef54d18fec --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/consts.ts @@ -0,0 +1,51 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "inccounter"; +export const HScName = new wasmlib.ScHname(0xaf2438e9); + +export const ParamCounter = "counter"; +export const ParamDelay = "delay"; +export const ParamDummy = "dummy"; +export const ParamNumRepeats = "numRepeats"; + +export const ResultCounter = "counter"; + +export const StateCounter = "counter"; +export const StateNumRepeats = "numRepeats"; + +export const FuncCallIncrement = "callIncrement"; +export const FuncCallIncrementRecurse5x = "callIncrementRecurse5x"; +export const FuncEndlessLoop = "endlessLoop"; +export const FuncIncrement = "increment"; +export const FuncIncrementWithDelay = "incrementWithDelay"; +export const FuncInit = "init"; +export const FuncLocalStateInternalCall = "localStateInternalCall"; +export const FuncLocalStatePost = "localStatePost"; +export const FuncLocalStateSandboxCall = "localStateSandboxCall"; +export const FuncPostIncrement = "postIncrement"; +export const FuncRepeatMany = "repeatMany"; +export const FuncTestLeb128 = "testLeb128"; +export const FuncWhenMustIncrement = "whenMustIncrement"; +export const ViewGetCounter = "getCounter"; + +export const HFuncCallIncrement = new wasmlib.ScHname(0xeb5dcacd); +export const HFuncCallIncrementRecurse5x = new wasmlib.ScHname(0x8749fbff); +export const HFuncEndlessLoop = new wasmlib.ScHname(0x365f0929); +export const HFuncIncrement = new wasmlib.ScHname(0xd351bd12); +export const HFuncIncrementWithDelay = new wasmlib.ScHname(0xa235bba7); +export const HFuncInit = new wasmlib.ScHname(0x1f44d644); +export const HFuncLocalStateInternalCall = new wasmlib.ScHname(0xecfc5d33); +export const HFuncLocalStatePost = new wasmlib.ScHname(0x3fd54d13); +export const HFuncLocalStateSandboxCall = new wasmlib.ScHname(0x7bd22c53); +export const HFuncPostIncrement = new wasmlib.ScHname(0x81c772f5); +export const HFuncRepeatMany = new wasmlib.ScHname(0x4ff450d3); +export const HFuncTestLeb128 = new wasmlib.ScHname(0xd8364cb9); +export const HFuncWhenMustIncrement = new wasmlib.ScHname(0xb4c3e7a6); +export const HViewGetCounter = new wasmlib.ScHname(0xb423e607); diff --git a/contracts/wasm/inccounter/ts/inccounter/contract.ts b/contracts/wasm/inccounter/ts/inccounter/contract.ts new file mode 100644 index 0000000000..99b1df9558 --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/contract.ts @@ -0,0 +1,209 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class CallIncrementCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncCallIncrement); +} + +export class CallIncrementContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class CallIncrementRecurse5xCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncCallIncrementRecurse5x); +} + +export class CallIncrementRecurse5xContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class EndlessLoopCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncEndlessLoop); +} + +export class EndlessLoopContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class IncrementCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncIncrement); +} + +export class IncrementContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class IncrementWithDelayCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncIncrementWithDelay); + params: sc.MutableIncrementWithDelayParams = new sc.MutableIncrementWithDelayParams(); +} + +export class IncrementWithDelayContext { + params: sc.ImmutableIncrementWithDelayParams = new sc.ImmutableIncrementWithDelayParams(); + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class InitCall { + func: wasmlib.ScInitFunc = new wasmlib.ScInitFunc(sc.HScName, sc.HFuncInit); + params: sc.MutableInitParams = new sc.MutableInitParams(); +} + +export class InitContext { + params: sc.ImmutableInitParams = new sc.ImmutableInitParams(); + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class LocalStateInternalCallCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncLocalStateInternalCall); +} + +export class LocalStateInternalCallContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class LocalStatePostCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncLocalStatePost); +} + +export class LocalStatePostContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class LocalStateSandboxCallCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncLocalStateSandboxCall); +} + +export class LocalStateSandboxCallContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class PostIncrementCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncPostIncrement); +} + +export class PostIncrementContext { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class RepeatManyCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncRepeatMany); + params: sc.MutableRepeatManyParams = new sc.MutableRepeatManyParams(); +} + +export class RepeatManyContext { + params: sc.ImmutableRepeatManyParams = new sc.ImmutableRepeatManyParams(); + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class TestLeb128Call { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestLeb128); +} + +export class TestLeb128Context { + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class WhenMustIncrementCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncWhenMustIncrement); + params: sc.MutableWhenMustIncrementParams = new sc.MutableWhenMustIncrementParams(); +} + +export class WhenMustIncrementContext { + params: sc.ImmutableWhenMustIncrementParams = new sc.ImmutableWhenMustIncrementParams(); + state: sc.MutableIncCounterState = new sc.MutableIncCounterState(); +} + +export class GetCounterCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetCounter); + results: sc.ImmutableGetCounterResults = new sc.ImmutableGetCounterResults(); +} + +export class GetCounterContext { + results: sc.MutableGetCounterResults = new sc.MutableGetCounterResults(); + state: sc.ImmutableIncCounterState = new sc.ImmutableIncCounterState(); +} + +export class ScFuncs { + + static callIncrement(ctx: wasmlib.ScFuncCallContext): CallIncrementCall { + let f = new CallIncrementCall(); + return f; + } + + static callIncrementRecurse5x(ctx: wasmlib.ScFuncCallContext): CallIncrementRecurse5xCall { + let f = new CallIncrementRecurse5xCall(); + return f; + } + + static endlessLoop(ctx: wasmlib.ScFuncCallContext): EndlessLoopCall { + let f = new EndlessLoopCall(); + return f; + } + + static increment(ctx: wasmlib.ScFuncCallContext): IncrementCall { + let f = new IncrementCall(); + return f; + } + + static incrementWithDelay(ctx: wasmlib.ScFuncCallContext): IncrementWithDelayCall { + let f = new IncrementWithDelayCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static init(ctx: wasmlib.ScFuncCallContext): InitCall { + let f = new InitCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static localStateInternalCall(ctx: wasmlib.ScFuncCallContext): LocalStateInternalCallCall { + let f = new LocalStateInternalCallCall(); + return f; + } + + static localStatePost(ctx: wasmlib.ScFuncCallContext): LocalStatePostCall { + let f = new LocalStatePostCall(); + return f; + } + + static localStateSandboxCall(ctx: wasmlib.ScFuncCallContext): LocalStateSandboxCallCall { + let f = new LocalStateSandboxCallCall(); + return f; + } + + static postIncrement(ctx: wasmlib.ScFuncCallContext): PostIncrementCall { + let f = new PostIncrementCall(); + return f; + } + + static repeatMany(ctx: wasmlib.ScFuncCallContext): RepeatManyCall { + let f = new RepeatManyCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static testLeb128(ctx: wasmlib.ScFuncCallContext): TestLeb128Call { + let f = new TestLeb128Call(); + return f; + } + + static whenMustIncrement(ctx: wasmlib.ScFuncCallContext): WhenMustIncrementCall { + let f = new WhenMustIncrementCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static getCounter(ctx: wasmlib.ScViewCallContext): GetCounterCall { + let f = new GetCounterCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/inccounter/ts/inccounter/inccounter.ts b/contracts/wasm/inccounter/ts/inccounter/inccounter.ts new file mode 100644 index 0000000000..eb1d9a4766 --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/inccounter.ts @@ -0,0 +1,179 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +let localStateMustIncrement: boolean = false; + +export function funcCallIncrement(ctx: wasmlib.ScFuncContext, f: sc.CallIncrementContext): void { + let counter = f.state.counter(); + let value = counter.value(); + counter.setValue(value + 1); + if (value == 0) { + sc.ScFuncs.callIncrement(ctx).func.call(); + } +} + +export function funcCallIncrementRecurse5x(ctx: wasmlib.ScFuncContext, f: sc.CallIncrementRecurse5xContext): void { + let counter = f.state.counter(); + let value = counter.value(); + counter.setValue(value + 1); + if (value < 5) { + sc.ScFuncs.callIncrementRecurse5x(ctx).func.call(); + } +} + +export function funcEndlessLoop(ctx: wasmlib.ScFuncContext, f: sc.EndlessLoopContext): void { + for (; ;) { + } +} + +export function funcIncrement(ctx: wasmlib.ScFuncContext, f: sc.IncrementContext): void { + let counter = f.state.counter(); + counter.setValue(counter.value() + 1); +} + +export function funcIncrementWithDelay(ctx: wasmlib.ScFuncContext, f: sc.IncrementWithDelayContext): void { + let delay = f.params.delay().value(); + let inc = sc.ScFuncs.callIncrement(ctx); + inc.func.delay(delay).transferIotas(1).post(); +} + +export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { + if (f.params.counter().exists()) { + let counter = f.params.counter().value(); + f.state.counter().setValue(counter); + } +} + +export function funcLocalStateInternalCall(ctx: wasmlib.ScFuncContext, f: sc.LocalStateInternalCallContext): void { + localStateMustIncrement = false; + whenMustIncrementState(ctx, f.state); + localStateMustIncrement = true; + whenMustIncrementState(ctx, f.state); + whenMustIncrementState(ctx, f.state); + // counter ends up as 2 +} + +export function funcLocalStatePost(ctx: wasmlib.ScFuncContext, f: sc.LocalStatePostContext): void { + localStateMustIncrement = false; + // prevent multiple identical posts, need a dummy param to differentiate them + localStatePost(ctx, 1); + localStateMustIncrement = true; + localStatePost(ctx, 2); + localStatePost(ctx, 3); + // counter ends up as 0 +} + +export function funcLocalStateSandboxCall(ctx: wasmlib.ScFuncContext, f: sc.LocalStateSandboxCallContext): void { + localStateMustIncrement = false; + sc.ScFuncs.whenMustIncrement(ctx).func.call(); + localStateMustIncrement = true; + sc.ScFuncs.whenMustIncrement(ctx).func.call(); + sc.ScFuncs.whenMustIncrement(ctx).func.call(); + // counter ends up as 0 +} + +export function funcPostIncrement(ctx: wasmlib.ScFuncContext, f: sc.PostIncrementContext): void { + let counter = f.state.counter(); + let value = counter.value(); + counter.setValue(value + 1); + if (value == 0) { + sc.ScFuncs.increment(ctx).func.transferIotas(1).post(); + } +} + +export function funcRepeatMany(ctx: wasmlib.ScFuncContext, f: sc.RepeatManyContext): void { + let counter = f.state.counter(); + let value = counter.value(); + counter.setValue(value + 1); + let stateRepeats = f.state.numRepeats(); + let repeats = f.params.numRepeats().value(); + if (repeats == 0) { + repeats = stateRepeats.value(); + if (repeats == 0) { + return; + } + } + stateRepeats.setValue(repeats - 1); + sc.ScFuncs.repeatMany(ctx).func.transferIotas(1).post(); +} + +let hex = "0123456789abcdef"; + +export function funcTestLeb128(ctx: wasmlib.ScFuncContext, f: sc.TestLeb128Context): void { + for (let i: i64 = -1000000; i < 1000000; i++) { + let d = new wasmlib.BytesEncoder(); + d.int64(i); + // let txt = i.toString() + " -"; + // for (let j = 0; j < d.buf.length; j++) { + // let b = d.buf[j]; + // txt += " " + hex[(b >> 4) & 0x0f] + hex[d.buf[j] & 0x0f]; + // } + let e = new wasmlib.BytesDecoder(d.buf); + let v = e.int64(); + // txt += " - " + v.toString(); + // ctx.log(txt); + ctx.require(i == v, "coder value mismatch") + } + + leb128Save(ctx, "v-1", -1); + leb128Save(ctx, "v-2", -2); + leb128Save(ctx, "v-126", -126); + leb128Save(ctx, "v-127", -127); + leb128Save(ctx, "v-128", -128); + leb128Save(ctx, "v-129", -129); + leb128Save(ctx, "v0", 0); + leb128Save(ctx, "v+1", 1); + leb128Save(ctx, "v+2", 2); + leb128Save(ctx, "v+126", 126); + leb128Save(ctx, "v+127", 127); + leb128Save(ctx, "v+128", 128); + leb128Save(ctx, "v+129", 129); +} + +export function funcWhenMustIncrement(ctx: wasmlib.ScFuncContext, f: sc.WhenMustIncrementContext): void { + whenMustIncrementState(ctx, f.state); +} + +// note that getCounter mirrors the state of the 'counter' state variable +// which means that if the state variable was not present it also will not be present in the result +export function viewGetCounter(ctx: wasmlib.ScViewContext, f: sc.GetCounterContext): void { + let counter = f.state.counter(); + if (counter.exists()) { + f.results.counter().setValue(counter.value()); + } +} + +function leb128Save(ctx: wasmlib.ScFuncContext, name: string, value: i64): void { + let encoder = new wasmlib.BytesEncoder(); + encoder.int64(value); + let spot = ctx.state().getBytes(wasmlib.Key32.fromString(name)); + spot.setValue(encoder.data()); + + let bytes = spot.value(); + let decoder = new wasmlib.BytesDecoder(bytes); + let retrieved = decoder.int64(); + if (retrieved != value) { + ctx.log(name.toString() + " in : " + value.toString()); + ctx.log(name.toString() + " out: " + retrieved.toString()); + } +} + +function localStatePost(ctx: wasmlib.ScFuncContext, nr: i64): void { + //note: we add a dummy parameter here to prevent "duplicate outputs not allowed" error + let f = sc.ScFuncs.whenMustIncrement(ctx); + f.params.dummy().setValue(nr); + f.func.transferIotas(1).post(); +} + +function whenMustIncrementState(ctx: wasmlib.ScFuncContext, state: sc.MutableIncCounterState): void { + ctx.log("whenMustIncrement called"); + if (!localStateMustIncrement) { + return; + } + let counter = state.counter(); + counter.setValue(counter.value() + 1); + ctx.log("whenMustIncrement incremented"); +} diff --git a/contracts/wasm/inccounter/ts/inccounter/index.ts b/contracts/wasm/inccounter/ts/inccounter/index.ts new file mode 100644 index 0000000000..f944f6c574 --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/index.ts @@ -0,0 +1,16 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./inccounter"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; diff --git a/contracts/wasm/inccounter/ts/inccounter/keys.ts b/contracts/wasm/inccounter/ts/inccounter/keys.ts new file mode 100644 index 0000000000..6c9deca9bc --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/keys.ts @@ -0,0 +1,29 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamCounter = 0; +export const IdxParamDelay = 1; +export const IdxParamDummy = 2; +export const IdxParamNumRepeats = 3; +export const IdxResultCounter = 4; +export const IdxStateCounter = 5; +export const IdxStateNumRepeats = 6; + +export let keyMap: string[] = [ + sc.ParamCounter, + sc.ParamDelay, + sc.ParamDummy, + sc.ParamNumRepeats, + sc.ResultCounter, + sc.StateCounter, + sc.StateNumRepeats, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/inccounter/ts/inccounter/lib.ts b/contracts/wasm/inccounter/ts/inccounter/lib.ts new file mode 100644 index 0000000000..991f71f494 --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/lib.ts @@ -0,0 +1,153 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncCallIncrement, funcCallIncrementThunk); + exports.addFunc(sc.FuncCallIncrementRecurse5x, funcCallIncrementRecurse5xThunk); + exports.addFunc(sc.FuncEndlessLoop, funcEndlessLoopThunk); + exports.addFunc(sc.FuncIncrement, funcIncrementThunk); + exports.addFunc(sc.FuncIncrementWithDelay, funcIncrementWithDelayThunk); + exports.addFunc(sc.FuncInit, funcInitThunk); + exports.addFunc(sc.FuncLocalStateInternalCall, funcLocalStateInternalCallThunk); + exports.addFunc(sc.FuncLocalStatePost, funcLocalStatePostThunk); + exports.addFunc(sc.FuncLocalStateSandboxCall, funcLocalStateSandboxCallThunk); + exports.addFunc(sc.FuncPostIncrement, funcPostIncrementThunk); + exports.addFunc(sc.FuncRepeatMany, funcRepeatManyThunk); + exports.addFunc(sc.FuncTestLeb128, funcTestLeb128Thunk); + exports.addFunc(sc.FuncWhenMustIncrement, funcWhenMustIncrementThunk); + exports.addView(sc.ViewGetCounter, viewGetCounterThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcCallIncrementThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcCallIncrement"); + let f = new sc.CallIncrementContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcCallIncrement(ctx, f); + ctx.log("inccounter.funcCallIncrement ok"); +} + +function funcCallIncrementRecurse5xThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcCallIncrementRecurse5x"); + let f = new sc.CallIncrementRecurse5xContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcCallIncrementRecurse5x(ctx, f); + ctx.log("inccounter.funcCallIncrementRecurse5x ok"); +} + +function funcEndlessLoopThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcEndlessLoop"); + let f = new sc.EndlessLoopContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcEndlessLoop(ctx, f); + ctx.log("inccounter.funcEndlessLoop ok"); +} + +function funcIncrementThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcIncrement"); + let f = new sc.IncrementContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcIncrement(ctx, f); + ctx.log("inccounter.funcIncrement ok"); +} + +function funcIncrementWithDelayThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcIncrementWithDelay"); + let f = new sc.IncrementWithDelayContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.delay().exists(), "missing mandatory delay") + sc.funcIncrementWithDelay(ctx, f); + ctx.log("inccounter.funcIncrementWithDelay ok"); +} + +function funcInitThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcInit"); + let f = new sc.InitContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcInit(ctx, f); + ctx.log("inccounter.funcInit ok"); +} + +function funcLocalStateInternalCallThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcLocalStateInternalCall"); + let f = new sc.LocalStateInternalCallContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcLocalStateInternalCall(ctx, f); + ctx.log("inccounter.funcLocalStateInternalCall ok"); +} + +function funcLocalStatePostThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcLocalStatePost"); + let f = new sc.LocalStatePostContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcLocalStatePost(ctx, f); + ctx.log("inccounter.funcLocalStatePost ok"); +} + +function funcLocalStateSandboxCallThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcLocalStateSandboxCall"); + let f = new sc.LocalStateSandboxCallContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcLocalStateSandboxCall(ctx, f); + ctx.log("inccounter.funcLocalStateSandboxCall ok"); +} + +function funcPostIncrementThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcPostIncrement"); + let f = new sc.PostIncrementContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcPostIncrement(ctx, f); + ctx.log("inccounter.funcPostIncrement ok"); +} + +function funcRepeatManyThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcRepeatMany"); + let f = new sc.RepeatManyContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcRepeatMany(ctx, f); + ctx.log("inccounter.funcRepeatMany ok"); +} + +function funcTestLeb128Thunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcTestLeb128"); + let f = new sc.TestLeb128Context(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestLeb128(ctx, f); + ctx.log("inccounter.funcTestLeb128 ok"); +} + +function funcWhenMustIncrementThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("inccounter.funcWhenMustIncrement"); + let f = new sc.WhenMustIncrementContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcWhenMustIncrement(ctx, f); + ctx.log("inccounter.funcWhenMustIncrement ok"); +} + +function viewGetCounterThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("inccounter.viewGetCounter"); + let f = new sc.GetCounterContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewGetCounter(ctx, f); + ctx.log("inccounter.viewGetCounter ok"); +} diff --git a/contracts/wasm/inccounter/ts/inccounter/params.ts b/contracts/wasm/inccounter/ts/inccounter/params.ts new file mode 100644 index 0000000000..539fd9dc65 --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/params.ts @@ -0,0 +1,65 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableIncrementWithDelayParams extends wasmlib.ScMapID { + + delay(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamDelay]); + } +} + +export class MutableIncrementWithDelayParams extends wasmlib.ScMapID { + + delay(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamDelay]); + } +} + +export class ImmutableInitParams extends wasmlib.ScMapID { + + counter(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamCounter]); + } +} + +export class MutableInitParams extends wasmlib.ScMapID { + + counter(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamCounter]); + } +} + +export class ImmutableRepeatManyParams extends wasmlib.ScMapID { + + numRepeats(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamNumRepeats]); + } +} + +export class MutableRepeatManyParams extends wasmlib.ScMapID { + + numRepeats(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamNumRepeats]); + } +} + +export class ImmutableWhenMustIncrementParams extends wasmlib.ScMapID { + + dummy(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamDummy]); + } +} + +export class MutableWhenMustIncrementParams extends wasmlib.ScMapID { + + dummy(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamDummy]); + } +} diff --git a/contracts/wasm/inccounter/ts/inccounter/results.ts b/contracts/wasm/inccounter/ts/inccounter/results.ts new file mode 100644 index 0000000000..7d2c0a489b --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/results.ts @@ -0,0 +1,23 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableGetCounterResults extends wasmlib.ScMapID { + + counter(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultCounter]); + } +} + +export class MutableGetCounterResults extends wasmlib.ScMapID { + + counter(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultCounter]); + } +} diff --git a/contracts/wasm/inccounter/ts/inccounter/state.ts b/contracts/wasm/inccounter/ts/inccounter/state.ts new file mode 100644 index 0000000000..be2ff82d28 --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/state.ts @@ -0,0 +1,31 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableIncCounterState extends wasmlib.ScMapID { + + counter(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateCounter]); + } + + numRepeats(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateNumRepeats]); + } +} + +export class MutableIncCounterState extends wasmlib.ScMapID { + + counter(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateCounter]); + } + + numRepeats(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateNumRepeats]); + } +} diff --git a/contracts/wasm/inccounter/ts/inccounter/tsconfig.json b/contracts/wasm/inccounter/ts/inccounter/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/inccounter/ts/inccounter/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/rust_all.cmd b/contracts/wasm/rust_all.cmd new file mode 100644 index 0000000000..b691f34f01 --- /dev/null +++ b/contracts/wasm/rust_all.cmd @@ -0,0 +1,3 @@ +@echo off +for /d %%f in (*.) do call rust_build.cmd %%f %1 + diff --git a/contracts/wasm/rust_build.cmd b/contracts/wasm/rust_build.cmd new file mode 100644 index 0000000000..1ede3fd3e6 --- /dev/null +++ b/contracts/wasm/rust_build.cmd @@ -0,0 +1,9 @@ +@echo off +cd %1 +if not exist schema.yaml goto :xit +echo Building %1 +schema -rust %2 +echo compiling %1_bg.wasm +wasm-pack build +:xit +cd .. diff --git a/contracts/wasm/testcore/Cargo.toml b/contracts/wasm/testcore/Cargo.toml index 996510f103..05b874a9e1 100644 --- a/contracts/wasm/testcore/Cargo.toml +++ b/contracts/wasm/testcore/Cargo.toml @@ -6,7 +6,7 @@ name = "testcore" description = "Core test for ISCP wasmlib Rust/Wasm library" license = "Apache-2.0" version = "0.1.0" -authors = ["lunfardo314 "] +authors = ["lunfardo314 ", "Eric Hop "] edition = "2018" repository = "https://github.com/iotaledger/wasp" @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/testcore/wasmmain/main.go b/contracts/wasm/testcore/go/main.go similarity index 80% rename from contracts/wasm/testcore/wasmmain/main.go rename to contracts/wasm/testcore/go/main.go index 762e7170d4..44c2dc0972 100644 --- a/contracts/wasm/testcore/wasmmain/main.go +++ b/contracts/wasm/testcore/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/testcore" + +import "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() testcore.OnLoad() diff --git a/contracts/wasm/testcore/consts.go b/contracts/wasm/testcore/go/testcore/consts.go similarity index 98% rename from contracts/wasm/testcore/consts.go rename to contracts/wasm/testcore/go/testcore/consts.go index 6a7b5cece4..678fc04456 100644 --- a/contracts/wasm/testcore/consts.go +++ b/contracts/wasm/testcore/go/testcore/consts.go @@ -7,7 +7,7 @@ package testcore -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "testcore" diff --git a/contracts/wasm/testcore/contract.go b/contracts/wasm/testcore/go/testcore/contract.go similarity index 99% rename from contracts/wasm/testcore/contract.go rename to contracts/wasm/testcore/go/testcore/contract.go index 77987e2405..6799a98352 100644 --- a/contracts/wasm/testcore/contract.go +++ b/contracts/wasm/testcore/go/testcore/contract.go @@ -7,7 +7,7 @@ package testcore -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type CallOnChainCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/testcore/keys.go b/contracts/wasm/testcore/go/testcore/keys.go similarity index 96% rename from contracts/wasm/testcore/keys.go rename to contracts/wasm/testcore/go/testcore/keys.go index c0aeb7e578..eeed6a32d3 100644 --- a/contracts/wasm/testcore/keys.go +++ b/contracts/wasm/testcore/go/testcore/keys.go @@ -7,7 +7,7 @@ package testcore -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamAddress = 0 diff --git a/contracts/wasm/testcore/lib.go b/contracts/wasm/testcore/go/testcore/lib.go similarity index 99% rename from contracts/wasm/testcore/lib.go rename to contracts/wasm/testcore/go/testcore/lib.go index 9386ba85a1..d24d89a3b2 100644 --- a/contracts/wasm/testcore/lib.go +++ b/contracts/wasm/testcore/go/testcore/lib.go @@ -8,7 +8,7 @@ //nolint:dupl package testcore -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/testcore/params.go b/contracts/wasm/testcore/go/testcore/params.go similarity index 99% rename from contracts/wasm/testcore/params.go rename to contracts/wasm/testcore/go/testcore/params.go index 599f312c84..184b9a295d 100644 --- a/contracts/wasm/testcore/params.go +++ b/contracts/wasm/testcore/go/testcore/params.go @@ -7,7 +7,7 @@ package testcore -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableCallOnChainParams struct { id int32 diff --git a/contracts/wasm/testcore/results.go b/contracts/wasm/testcore/go/testcore/results.go similarity index 98% rename from contracts/wasm/testcore/results.go rename to contracts/wasm/testcore/go/testcore/results.go index 8e953a81ef..1dd993ca93 100644 --- a/contracts/wasm/testcore/results.go +++ b/contracts/wasm/testcore/go/testcore/results.go @@ -7,7 +7,7 @@ package testcore -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableCallOnChainResults struct { id int32 diff --git a/contracts/wasm/testcore/state.go b/contracts/wasm/testcore/go/testcore/state.go similarity index 96% rename from contracts/wasm/testcore/state.go rename to contracts/wasm/testcore/go/testcore/state.go index 8ae9d82970..d0ccc022f7 100644 --- a/contracts/wasm/testcore/state.go +++ b/contracts/wasm/testcore/go/testcore/state.go @@ -7,7 +7,7 @@ package testcore -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableTestCoreState struct { id int32 diff --git a/contracts/wasm/testcore/testcore.go b/contracts/wasm/testcore/go/testcore/testcore.go similarity index 97% rename from contracts/wasm/testcore/testcore.go rename to contracts/wasm/testcore/go/testcore/testcore.go index 88611afd31..7d83b31df4 100644 --- a/contracts/wasm/testcore/testcore.go +++ b/contracts/wasm/testcore/go/testcore/testcore.go @@ -5,9 +5,9 @@ package testcore import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" - "github.com/iotaledger/wasp/packages/vm/wasmlib/corecontracts/coreaccounts" - "github.com/iotaledger/wasp/packages/vm/wasmlib/corecontracts/coregovernance" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreaccounts" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coregovernance" ) const ( diff --git a/contracts/wasm/testcore/src/lib.rs b/contracts/wasm/testcore/src/lib.rs index 86d9b39ce0..e114448688 100644 --- a/contracts/wasm/testcore/src/lib.rs +++ b/contracts/wasm/testcore/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use testcore::*; diff --git a/contracts/wasm/testcore/src/testcore.rs b/contracts/wasm/testcore/src/testcore.rs index cc84285736..645f8fd803 100644 --- a/contracts/wasm/testcore/src/testcore.rs +++ b/contracts/wasm/testcore/src/testcore.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use wasmlib::*; -use wasmlib::corecontracts::*; use crate::*; use crate::contract::*; @@ -100,7 +99,7 @@ pub fn func_run_recursion(ctx: &ScFuncContext, f: &RunRecursionContext) { } pub fn func_send_to_address(ctx: &ScFuncContext, f: &SendToAddressContext) { - let balances = ScTransfers::new_transfers_from_balances(ctx.balances()); + let balances = ScTransfers::from_balances(ctx.balances()); ctx.transfer_to_address(&f.params.address().value(), balances); } @@ -108,6 +107,25 @@ pub fn func_set_int(_ctx: &ScFuncContext, f: &SetIntContext) { f.state.ints().get_int64(&f.params.name().value()).set_value(f.params.int_value().value()); } +pub fn func_spawn(ctx: &ScFuncContext, f: &SpawnContext) { + let spawn_name = SC_NAME.to_string() + "_spawned"; + let spawn_descr = "spawned contract description"; + ctx.deploy(&f.params.prog_hash().value(), &spawn_name, spawn_descr, None); + + let spawn_hname = ScHname::new(&spawn_name); + for _i in 0..5 { + ctx.call(spawn_hname, HFUNC_INC_COUNTER, None, None); + } +} + +pub fn func_test_block_context1(ctx: &ScFuncContext, _f: &TestBlockContext1Context) { + ctx.panic(MSG_CORE_ONLY_PANIC); +} + +pub fn func_test_block_context2(ctx: &ScFuncContext, _f: &TestBlockContext2Context) { + ctx.panic(MSG_CORE_ONLY_PANIC); +} + pub fn func_test_call_panic_full_ep(ctx: &ScFuncContext, _f: &TestCallPanicFullEPContext) { ScFuncs::test_panic_full_ep(ctx).func.call(); } @@ -181,6 +199,10 @@ pub fn view_get_int(ctx: &ScViewContext, f: &GetIntContext) { f.results.values().get_int64(&name).set_value(value.value()); } +pub fn view_get_string_value(ctx: &ScViewContext, _f: &GetStringValueContext) { + ctx.panic(MSG_CORE_ONLY_PANIC); +} + pub fn view_just_view(ctx: &ScViewContext, _f: &JustViewContext) { ctx.log("doing nothing..."); } @@ -213,26 +235,3 @@ pub fn view_test_sandbox_call(ctx: &ScViewContext, f: &TestSandboxCallContext) { get_chain_info.func.call(); f.results.sandbox_call().set_value(&get_chain_info.results.description().value()); } - -pub fn func_test_block_context1(ctx: &ScFuncContext, _f: &TestBlockContext1Context) { - ctx.panic(MSG_CORE_ONLY_PANIC); -} - -pub fn func_test_block_context2(ctx: &ScFuncContext, _f: &TestBlockContext2Context) { - ctx.panic(MSG_CORE_ONLY_PANIC); -} - -pub fn view_get_string_value(ctx: &ScViewContext, _f: &GetStringValueContext) { - ctx.panic(MSG_CORE_ONLY_PANIC); -} - -pub fn func_spawn(ctx: &ScFuncContext, f: &SpawnContext) { - let spawn_name = SC_NAME.to_string() + "_spawned"; - let spawn_descr = "spawned contract description"; - ctx.deploy(&f.params.prog_hash().value(), &spawn_name, spawn_descr, None); - - let spawn_hname = ScHname::new(&spawn_name); - for _i in 0..5 { - ctx.call(spawn_hname, HFUNC_INC_COUNTER, None, None); - } -} diff --git a/contracts/wasm/testcore/test/2chains_test.go b/contracts/wasm/testcore/test/2chains_test.go index 68ea653576..633808182a 100644 --- a/contracts/wasm/testcore/test/2chains_test.go +++ b/contracts/wasm/testcore/test/2chains_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/core" "github.com/iotaledger/wasp/packages/vm/wasmsolo" diff --git a/contracts/wasm/testcore/test/block_context_test.go b/contracts/wasm/testcore/test/block_context_test.go index e853820c4c..277f65d7d5 100644 --- a/contracts/wasm/testcore/test/block_context_test.go +++ b/contracts/wasm/testcore/test/block_context_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/testcore/test/call_test.go b/contracts/wasm/testcore/test/call_test.go index 11d527bfd8..8adb54bf94 100644 --- a/contracts/wasm/testcore/test/call_test.go +++ b/contracts/wasm/testcore/test/call_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) @@ -19,6 +19,10 @@ func fibo(n int64) int64 { func TestCallFibonacci(t *testing.T) { run2(t, func(t *testing.T, w bool) { + if *wasmsolo.TsWasm { + t.SkipNow() + } + ctx := deployTestCore(t, w) f := testcore.ScFuncs.Fibonacci(ctx) @@ -33,6 +37,10 @@ func TestCallFibonacci(t *testing.T) { func TestCallFibonacciIndirect(t *testing.T) { run2(t, func(t *testing.T, w bool) { + if *wasmsolo.TsWasm { + t.SkipNow() + } + ctx := deployTestCore(t, w) f := testcore.ScFuncs.CallOnChain(ctx) @@ -57,7 +65,7 @@ func TestCallFibonacciIndirect(t *testing.T) { func TestCallRecursive(t *testing.T) { run2(t, func(t *testing.T, w bool) { // TODO need to adjust stack size for Go Wasm for this to succeed - if *wasmsolo.GoWasm { + if *wasmsolo.GoWasm || *wasmsolo.TsWasm { t.SkipNow() } diff --git a/contracts/wasm/testcore/test/check_ctx_test.go b/contracts/wasm/testcore/test/check_ctx_test.go index a226787414..e16cff74de 100644 --- a/contracts/wasm/testcore/test/check_ctx_test.go +++ b/contracts/wasm/testcore/test/check_ctx_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/solo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/testcore/test/concurrency_test.go b/contracts/wasm/testcore/test/concurrency_test.go index c13987ad7d..f380b86a1a 100644 --- a/contracts/wasm/testcore/test/concurrency_test.go +++ b/contracts/wasm/testcore/test/concurrency_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/wasmsolo" @@ -32,6 +32,10 @@ func TestCounter(t *testing.T) { func TestSynchronous(t *testing.T) { run2(t, func(t *testing.T, w bool) { + // TODO fails with 999 instead of 1000 at WaitForPendingRequests + if *wasmsolo.GoDebug { + t.SkipNow() + } ctx := deployTestCore(t, w) f := testcore.ScFuncs.IncCounter(ctx) diff --git a/contracts/wasm/testcore/test/init_fail_test.go b/contracts/wasm/testcore/test/init_fail_test.go index 1f6be7afc8..a39fdfcffa 100644 --- a/contracts/wasm/testcore/test/init_fail_test.go +++ b/contracts/wasm/testcore/test/init_fail_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/vm/core" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" diff --git a/contracts/wasm/testcore/test/misc_call_test.go b/contracts/wasm/testcore/test/misc_call_test.go index 52042b682d..95ad9c205f 100644 --- a/contracts/wasm/testcore/test/misc_call_test.go +++ b/contracts/wasm/testcore/test/misc_call_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/testcore/test/offledger_test.go b/contracts/wasm/testcore/test/offledger_test.go index 1f718b81c0..f1a5d588eb 100644 --- a/contracts/wasm/testcore/test/offledger_test.go +++ b/contracts/wasm/testcore/test/offledger_test.go @@ -4,7 +4,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/solo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/testcore/test/sandbox_panic_test.go b/contracts/wasm/testcore/test/sandbox_panic_test.go index fcf2dc00ae..53208ffe5d 100644 --- a/contracts/wasm/testcore/test/sandbox_panic_test.go +++ b/contracts/wasm/testcore/test/sandbox_panic_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/vm/core/testcore/sbtests/sbtestsc" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" diff --git a/contracts/wasm/testcore/test/spawn_test.go b/contracts/wasm/testcore/test/spawn_test.go index c7a03fd5c2..71d11c0256 100644 --- a/contracts/wasm/testcore/test/spawn_test.go +++ b/contracts/wasm/testcore/test/spawn_test.go @@ -3,13 +3,19 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/vm/core" + "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) func TestSpawn(t *testing.T) { run2(t, func(t *testing.T, w bool) { + // TODO need to save globals for TypeScript Wasm for this to succeed + if *wasmsolo.TsWasm { + t.SkipNow() + } + ctx := deployTestCore(t, w) f := testcore.ScFuncs.Spawn(ctx) diff --git a/contracts/wasm/testcore/test/testcore_bg.wasm b/contracts/wasm/testcore/test/testcore_bg.wasm index 732ae2c262..c7701951f2 100644 Binary files a/contracts/wasm/testcore/test/testcore_bg.wasm and b/contracts/wasm/testcore/test/testcore_bg.wasm differ diff --git a/contracts/wasm/testcore/test/testcore_test.go b/contracts/wasm/testcore/test/testcore_test.go index f722b79f14..19cbe1e0fe 100644 --- a/contracts/wasm/testcore/test/testcore_test.go +++ b/contracts/wasm/testcore/test/testcore_test.go @@ -4,14 +4,14 @@ import ( "fmt" "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/util" "github.com/iotaledger/wasp/packages/vm/core/testcore/sbtests/sbtestsc" - "github.com/iotaledger/wasp/packages/vm/wasmlib" - "github.com/iotaledger/wasp/packages/vm/wasmlib/corecontracts/coreaccounts" - "github.com/iotaledger/wasp/packages/vm/wasmlib/corecontracts/coregovernance" - "github.com/iotaledger/wasp/packages/vm/wasmlib/corecontracts/coreroot" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" + coreaccounts2 "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreaccounts" + coregovernance2 "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coregovernance" + coreroot2 "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreroot" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) @@ -50,29 +50,40 @@ func run2(t *testing.T, test func(*testing.T, bool), skipWasm ...bool) { saveGoDebug := *wasmsolo.GoDebug saveGoWasm := *wasmsolo.GoWasm + saveTsWasm := *wasmsolo.TsWasm + *wasmsolo.GoDebug = false + *wasmsolo.GoWasm = false + *wasmsolo.TsWasm = false exists, _ := util.ExistsFilePath("../pkg/testcore_bg.wasm") if exists { - *wasmsolo.GoDebug = false - *wasmsolo.GoWasm = false wasmlib.ConnectHost(nil) t.Run(fmt.Sprintf("run RUST version of %s", t.Name()), func(t *testing.T) { test(t, true) }) } - exists, _ = util.ExistsFilePath("../wasmmain/pkg/testcore_go.wasm") + exists, _ = util.ExistsFilePath("../go/pkg/testcore_go.wasm") if exists { - *wasmsolo.GoDebug = false *wasmsolo.GoWasm = true wasmlib.ConnectHost(nil) t.Run(fmt.Sprintf("run GO version of %s", t.Name()), func(t *testing.T) { test(t, true) }) + *wasmsolo.GoWasm = false + } + + exists, _ = util.ExistsFilePath("../ts/pkg/testcore_ts.wasm") + if exists { + *wasmsolo.TsWasm = true + wasmlib.ConnectHost(nil) + t.Run(fmt.Sprintf("run TS version of %s", t.Name()), func(t *testing.T) { + test(t, true) + }) + *wasmsolo.TsWasm = false } *wasmsolo.GoDebug = true - *wasmsolo.GoWasm = false wasmlib.ConnectHost(nil) t.Run(fmt.Sprintf("run GOVM version of %s", t.Name()), func(t *testing.T) { test(t, true) @@ -80,6 +91,7 @@ func run2(t *testing.T, test func(*testing.T, bool), skipWasm ...bool) { *wasmsolo.GoDebug = saveGoDebug *wasmsolo.GoWasm = saveGoWasm + *wasmsolo.TsWasm = saveTsWasm } func TestDeployTestCore(t *testing.T) { @@ -119,8 +131,8 @@ func originatorBalanceReducedBy(ctx *wasmsolo.SoloContext, w bool, minus uint64) } func deposit(t *testing.T, ctx *wasmsolo.SoloContext, user, target *wasmsolo.SoloAgent, amount int64) { - ctxAcc := ctx.SoloContextForCore(t, coreaccounts.ScName, coreaccounts.OnLoad) - f := coreaccounts.ScFuncs.Deposit(ctxAcc.Sign(user)) + ctxAcc := ctx.SoloContextForCore(t, coreaccounts2.ScName, coreaccounts2.OnLoad) + f := coreaccounts2.ScFuncs.Deposit(ctxAcc.Sign(user)) if target != nil { f.Params.AgentID().SetValue(target.ScAgentID()) } @@ -129,16 +141,16 @@ func deposit(t *testing.T, ctx *wasmsolo.SoloContext, user, target *wasmsolo.Sol } func setDeployer(t *testing.T, ctx *wasmsolo.SoloContext, deployer *wasmsolo.SoloAgent) { - ctxRoot := ctx.SoloContextForCore(t, coreroot.ScName, coreroot.OnLoad) - f := coreroot.ScFuncs.GrantDeployPermission(ctxRoot) + ctxRoot := ctx.SoloContextForCore(t, coreroot2.ScName, coreroot2.OnLoad) + f := coreroot2.ScFuncs.GrantDeployPermission(ctxRoot) f.Params.Deployer().SetValue(deployer.ScAgentID()) f.Func.TransferIotas(1).Post() require.NoError(t, ctxRoot.Err) } func setOwnerFee(t *testing.T, ctx *wasmsolo.SoloContext, amount int64) { - ctxGov := ctx.SoloContextForCore(t, coregovernance.ScName, coregovernance.OnLoad) - f := coregovernance.ScFuncs.SetContractFee(ctxGov) + ctxGov := ctx.SoloContextForCore(t, coregovernance2.ScName, coregovernance2.OnLoad) + f := coregovernance2.ScFuncs.SetContractFee(ctxGov) f.Params.Hname().SetValue(testcore.HScName) f.Params.OwnerFee().SetValue(amount) f.Func.TransferIotas(1).Post() @@ -146,8 +158,8 @@ func setOwnerFee(t *testing.T, ctx *wasmsolo.SoloContext, amount int64) { } func withdraw(t *testing.T, ctx *wasmsolo.SoloContext, user *wasmsolo.SoloAgent) { - ctxAcc := ctx.SoloContextForCore(t, coreaccounts.ScName, coreaccounts.OnLoad) - f := coreaccounts.ScFuncs.Withdraw(ctxAcc.Sign(user)) + ctxAcc := ctx.SoloContextForCore(t, coreaccounts2.ScName, coreaccounts2.OnLoad) + f := coreaccounts2.ScFuncs.Withdraw(ctxAcc.Sign(user)) f.Func.TransferIotas(1).Post() require.NoError(t, ctxAcc.Err) } diff --git a/contracts/wasm/testcore/test/transfer_test.go b/contracts/wasm/testcore/test/transfer_test.go index f733ae5f42..54401de8f8 100644 --- a/contracts/wasm/testcore/test/transfer_test.go +++ b/contracts/wasm/testcore/test/transfer_test.go @@ -3,7 +3,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/solo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/testcore/test/types_test.go b/contracts/wasm/testcore/test/types_test.go index 35bee8bbd8..1ba4b9f23c 100644 --- a/contracts/wasm/testcore/test/types_test.go +++ b/contracts/wasm/testcore/test/types_test.go @@ -3,9 +3,9 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/testcore" + "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" "github.com/iotaledger/wasp/packages/hashing" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/testcore/ts/testcore/consts.ts b/contracts/wasm/testcore/ts/testcore/consts.ts new file mode 100644 index 0000000000..bb40094455 --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/consts.ts @@ -0,0 +1,114 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "testcore"; +export const ScDescription = "Core test for ISCP wasmlib Rust/Wasm library"; +export const HScName = new wasmlib.ScHname(0x370d33ad); + +export const ParamAddress = "address"; +export const ParamAgentID = "agentID"; +export const ParamCaller = "caller"; +export const ParamChainID = "chainID"; +export const ParamChainOwnerID = "chainOwnerID"; +export const ParamContractCreator = "contractCreator"; +export const ParamContractID = "contractID"; +export const ParamCounter = "counter"; +export const ParamFail = "initFailParam"; +export const ParamHash = "Hash"; +export const ParamHname = "Hname"; +export const ParamHnameContract = "hnameContract"; +export const ParamHnameEP = "hnameEP"; +export const ParamHnameZero = "Hname-0"; +export const ParamInt64 = "int64"; +export const ParamInt64Zero = "int64-0"; +export const ParamIntValue = "intParamValue"; +export const ParamName = "intParamName"; +export const ParamProgHash = "progHash"; +export const ParamString = "string"; +export const ParamStringZero = "string-0"; +export const ParamVarName = "varName"; + +export const ResultChainOwnerID = "chainOwnerID"; +export const ResultCounter = "counter"; +export const ResultIntValue = "intParamValue"; +export const ResultMintedColor = "mintedColor"; +export const ResultMintedSupply = "mintedSupply"; +export const ResultSandboxCall = "sandboxCall"; + +export const StateCounter = "counter"; +export const StateHnameEP = "hnameEP"; +export const StateInts = "ints"; +export const StateMintedColor = "mintedColor"; +export const StateMintedSupply = "mintedSupply"; + +export const FuncCallOnChain = "callOnChain"; +export const FuncCheckContextFromFullEP = "checkContextFromFullEP"; +export const FuncDoNothing = "doNothing"; +export const FuncGetMintedSupply = "getMintedSupply"; +export const FuncIncCounter = "incCounter"; +export const FuncInit = "init"; +export const FuncPassTypesFull = "passTypesFull"; +export const FuncRunRecursion = "runRecursion"; +export const FuncSendToAddress = "sendToAddress"; +export const FuncSetInt = "setInt"; +export const FuncSpawn = "spawn"; +export const FuncTestBlockContext1 = "testBlockContext1"; +export const FuncTestBlockContext2 = "testBlockContext2"; +export const FuncTestCallPanicFullEP = "testCallPanicFullEP"; +export const FuncTestCallPanicViewEPFromFull = "testCallPanicViewEPFromFull"; +export const FuncTestChainOwnerIDFull = "testChainOwnerIDFull"; +export const FuncTestEventLogDeploy = "testEventLogDeploy"; +export const FuncTestEventLogEventData = "testEventLogEventData"; +export const FuncTestEventLogGenericData = "testEventLogGenericData"; +export const FuncTestPanicFullEP = "testPanicFullEP"; +export const FuncWithdrawToChain = "withdrawToChain"; +export const ViewCheckContextFromViewEP = "checkContextFromViewEP"; +export const ViewFibonacci = "fibonacci"; +export const ViewGetCounter = "getCounter"; +export const ViewGetInt = "getInt"; +export const ViewGetStringValue = "getStringValue"; +export const ViewJustView = "justView"; +export const ViewPassTypesView = "passTypesView"; +export const ViewTestCallPanicViewEPFromView = "testCallPanicViewEPFromView"; +export const ViewTestChainOwnerIDView = "testChainOwnerIDView"; +export const ViewTestPanicViewEP = "testPanicViewEP"; +export const ViewTestSandboxCall = "testSandboxCall"; + +export const HFuncCallOnChain = new wasmlib.ScHname(0x95a3d123); +export const HFuncCheckContextFromFullEP = new wasmlib.ScHname(0xa56c24ba); +export const HFuncDoNothing = new wasmlib.ScHname(0xdda4a6de); +export const HFuncGetMintedSupply = new wasmlib.ScHname(0x0c2d113c); +export const HFuncIncCounter = new wasmlib.ScHname(0x7b287419); +export const HFuncInit = new wasmlib.ScHname(0x1f44d644); +export const HFuncPassTypesFull = new wasmlib.ScHname(0x733ea0ea); +export const HFuncRunRecursion = new wasmlib.ScHname(0x833425fd); +export const HFuncSendToAddress = new wasmlib.ScHname(0x63ce4634); +export const HFuncSetInt = new wasmlib.ScHname(0x62056f74); +export const HFuncSpawn = new wasmlib.ScHname(0xec929d12); +export const HFuncTestBlockContext1 = new wasmlib.ScHname(0x796d4136); +export const HFuncTestBlockContext2 = new wasmlib.ScHname(0x758b0452); +export const HFuncTestCallPanicFullEP = new wasmlib.ScHname(0x4c878834); +export const HFuncTestCallPanicViewEPFromFull = new wasmlib.ScHname(0xfd7e8c1d); +export const HFuncTestChainOwnerIDFull = new wasmlib.ScHname(0x2aff1167); +export const HFuncTestEventLogDeploy = new wasmlib.ScHname(0x96ff760a); +export const HFuncTestEventLogEventData = new wasmlib.ScHname(0x0efcf939); +export const HFuncTestEventLogGenericData = new wasmlib.ScHname(0x6a16629d); +export const HFuncTestPanicFullEP = new wasmlib.ScHname(0x24fdef07); +export const HFuncWithdrawToChain = new wasmlib.ScHname(0x437bc026); +export const HViewCheckContextFromViewEP = new wasmlib.ScHname(0x88ff0167); +export const HViewFibonacci = new wasmlib.ScHname(0x7940873c); +export const HViewGetCounter = new wasmlib.ScHname(0xb423e607); +export const HViewGetInt = new wasmlib.ScHname(0x1887e5ef); +export const HViewGetStringValue = new wasmlib.ScHname(0xcf0a4d32); +export const HViewJustView = new wasmlib.ScHname(0x33b8972e); +export const HViewPassTypesView = new wasmlib.ScHname(0x1a5b87ea); +export const HViewTestCallPanicViewEPFromView = new wasmlib.ScHname(0x91b10c99); +export const HViewTestChainOwnerIDView = new wasmlib.ScHname(0x26586c33); +export const HViewTestPanicViewEP = new wasmlib.ScHname(0x22bc4d72); +export const HViewTestSandboxCall = new wasmlib.ScHname(0x42d72b63); diff --git a/contracts/wasm/testcore/ts/testcore/contract.ts b/contracts/wasm/testcore/ts/testcore/contract.ts new file mode 100644 index 0000000000..a65695a48b --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/contract.ts @@ -0,0 +1,498 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class CallOnChainCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncCallOnChain); + params: sc.MutableCallOnChainParams = new sc.MutableCallOnChainParams(); + results: sc.ImmutableCallOnChainResults = new sc.ImmutableCallOnChainResults(); +} + +export class CallOnChainContext { + params: sc.ImmutableCallOnChainParams = new sc.ImmutableCallOnChainParams(); + results: sc.MutableCallOnChainResults = new sc.MutableCallOnChainResults(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class CheckContextFromFullEPCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncCheckContextFromFullEP); + params: sc.MutableCheckContextFromFullEPParams = new sc.MutableCheckContextFromFullEPParams(); +} + +export class CheckContextFromFullEPContext { + params: sc.ImmutableCheckContextFromFullEPParams = new sc.ImmutableCheckContextFromFullEPParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class DoNothingCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncDoNothing); +} + +export class DoNothingContext { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class GetMintedSupplyCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncGetMintedSupply); + results: sc.ImmutableGetMintedSupplyResults = new sc.ImmutableGetMintedSupplyResults(); +} + +export class GetMintedSupplyContext { + results: sc.MutableGetMintedSupplyResults = new sc.MutableGetMintedSupplyResults(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class IncCounterCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncIncCounter); +} + +export class IncCounterContext { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class InitCall { + func: wasmlib.ScInitFunc = new wasmlib.ScInitFunc(sc.HScName, sc.HFuncInit); + params: sc.MutableInitParams = new sc.MutableInitParams(); +} + +export class InitContext { + params: sc.ImmutableInitParams = new sc.ImmutableInitParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class PassTypesFullCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncPassTypesFull); + params: sc.MutablePassTypesFullParams = new sc.MutablePassTypesFullParams(); +} + +export class PassTypesFullContext { + params: sc.ImmutablePassTypesFullParams = new sc.ImmutablePassTypesFullParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class RunRecursionCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncRunRecursion); + params: sc.MutableRunRecursionParams = new sc.MutableRunRecursionParams(); + results: sc.ImmutableRunRecursionResults = new sc.ImmutableRunRecursionResults(); +} + +export class RunRecursionContext { + params: sc.ImmutableRunRecursionParams = new sc.ImmutableRunRecursionParams(); + results: sc.MutableRunRecursionResults = new sc.MutableRunRecursionResults(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class SendToAddressCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSendToAddress); + params: sc.MutableSendToAddressParams = new sc.MutableSendToAddressParams(); +} + +export class SendToAddressContext { + params: sc.ImmutableSendToAddressParams = new sc.ImmutableSendToAddressParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class SetIntCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSetInt); + params: sc.MutableSetIntParams = new sc.MutableSetIntParams(); +} + +export class SetIntContext { + params: sc.ImmutableSetIntParams = new sc.ImmutableSetIntParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class SpawnCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSpawn); + params: sc.MutableSpawnParams = new sc.MutableSpawnParams(); +} + +export class SpawnContext { + params: sc.ImmutableSpawnParams = new sc.ImmutableSpawnParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestBlockContext1Call { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestBlockContext1); +} + +export class TestBlockContext1Context { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestBlockContext2Call { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestBlockContext2); +} + +export class TestBlockContext2Context { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestCallPanicFullEPCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestCallPanicFullEP); +} + +export class TestCallPanicFullEPContext { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestCallPanicViewEPFromFullCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestCallPanicViewEPFromFull); +} + +export class TestCallPanicViewEPFromFullContext { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestChainOwnerIDFullCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestChainOwnerIDFull); + results: sc.ImmutableTestChainOwnerIDFullResults = new sc.ImmutableTestChainOwnerIDFullResults(); +} + +export class TestChainOwnerIDFullContext { + results: sc.MutableTestChainOwnerIDFullResults = new sc.MutableTestChainOwnerIDFullResults(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestEventLogDeployCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestEventLogDeploy); +} + +export class TestEventLogDeployContext { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestEventLogEventDataCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestEventLogEventData); +} + +export class TestEventLogEventDataContext { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestEventLogGenericDataCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestEventLogGenericData); + params: sc.MutableTestEventLogGenericDataParams = new sc.MutableTestEventLogGenericDataParams(); +} + +export class TestEventLogGenericDataContext { + params: sc.ImmutableTestEventLogGenericDataParams = new sc.ImmutableTestEventLogGenericDataParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class TestPanicFullEPCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTestPanicFullEP); +} + +export class TestPanicFullEPContext { + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class WithdrawToChainCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncWithdrawToChain); + params: sc.MutableWithdrawToChainParams = new sc.MutableWithdrawToChainParams(); +} + +export class WithdrawToChainContext { + params: sc.ImmutableWithdrawToChainParams = new sc.ImmutableWithdrawToChainParams(); + state: sc.MutableTestCoreState = new sc.MutableTestCoreState(); +} + +export class CheckContextFromViewEPCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewCheckContextFromViewEP); + params: sc.MutableCheckContextFromViewEPParams = new sc.MutableCheckContextFromViewEPParams(); +} + +export class CheckContextFromViewEPContext { + params: sc.ImmutableCheckContextFromViewEPParams = new sc.ImmutableCheckContextFromViewEPParams(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class FibonacciCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewFibonacci); + params: sc.MutableFibonacciParams = new sc.MutableFibonacciParams(); + results: sc.ImmutableFibonacciResults = new sc.ImmutableFibonacciResults(); +} + +export class FibonacciContext { + params: sc.ImmutableFibonacciParams = new sc.ImmutableFibonacciParams(); + results: sc.MutableFibonacciResults = new sc.MutableFibonacciResults(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class GetCounterCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetCounter); + results: sc.ImmutableGetCounterResults = new sc.ImmutableGetCounterResults(); +} + +export class GetCounterContext { + results: sc.MutableGetCounterResults = new sc.MutableGetCounterResults(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class GetIntCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetInt); + params: sc.MutableGetIntParams = new sc.MutableGetIntParams(); + results: sc.ImmutableGetIntResults = new sc.ImmutableGetIntResults(); +} + +export class GetIntContext { + params: sc.ImmutableGetIntParams = new sc.ImmutableGetIntParams(); + results: sc.MutableGetIntResults = new sc.MutableGetIntResults(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class GetStringValueCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetStringValue); + params: sc.MutableGetStringValueParams = new sc.MutableGetStringValueParams(); + results: sc.ImmutableGetStringValueResults = new sc.ImmutableGetStringValueResults(); +} + +export class GetStringValueContext { + params: sc.ImmutableGetStringValueParams = new sc.ImmutableGetStringValueParams(); + results: sc.MutableGetStringValueResults = new sc.MutableGetStringValueResults(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class JustViewCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewJustView); +} + +export class JustViewContext { + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class PassTypesViewCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewPassTypesView); + params: sc.MutablePassTypesViewParams = new sc.MutablePassTypesViewParams(); +} + +export class PassTypesViewContext { + params: sc.ImmutablePassTypesViewParams = new sc.ImmutablePassTypesViewParams(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class TestCallPanicViewEPFromViewCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewTestCallPanicViewEPFromView); +} + +export class TestCallPanicViewEPFromViewContext { + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class TestChainOwnerIDViewCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewTestChainOwnerIDView); + results: sc.ImmutableTestChainOwnerIDViewResults = new sc.ImmutableTestChainOwnerIDViewResults(); +} + +export class TestChainOwnerIDViewContext { + results: sc.MutableTestChainOwnerIDViewResults = new sc.MutableTestChainOwnerIDViewResults(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class TestPanicViewEPCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewTestPanicViewEP); +} + +export class TestPanicViewEPContext { + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class TestSandboxCallCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewTestSandboxCall); + results: sc.ImmutableTestSandboxCallResults = new sc.ImmutableTestSandboxCallResults(); +} + +export class TestSandboxCallContext { + results: sc.MutableTestSandboxCallResults = new sc.MutableTestSandboxCallResults(); + state: sc.ImmutableTestCoreState = new sc.ImmutableTestCoreState(); +} + +export class ScFuncs { + + static callOnChain(ctx: wasmlib.ScFuncCallContext): CallOnChainCall { + let f = new CallOnChainCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static checkContextFromFullEP(ctx: wasmlib.ScFuncCallContext): CheckContextFromFullEPCall { + let f = new CheckContextFromFullEPCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static doNothing(ctx: wasmlib.ScFuncCallContext): DoNothingCall { + let f = new DoNothingCall(); + return f; + } + + static getMintedSupply(ctx: wasmlib.ScFuncCallContext): GetMintedSupplyCall { + let f = new GetMintedSupplyCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static incCounter(ctx: wasmlib.ScFuncCallContext): IncCounterCall { + let f = new IncCounterCall(); + return f; + } + + static init(ctx: wasmlib.ScFuncCallContext): InitCall { + let f = new InitCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static passTypesFull(ctx: wasmlib.ScFuncCallContext): PassTypesFullCall { + let f = new PassTypesFullCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static runRecursion(ctx: wasmlib.ScFuncCallContext): RunRecursionCall { + let f = new RunRecursionCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static sendToAddress(ctx: wasmlib.ScFuncCallContext): SendToAddressCall { + let f = new SendToAddressCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static setInt(ctx: wasmlib.ScFuncCallContext): SetIntCall { + let f = new SetIntCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static spawn(ctx: wasmlib.ScFuncCallContext): SpawnCall { + let f = new SpawnCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static testBlockContext1(ctx: wasmlib.ScFuncCallContext): TestBlockContext1Call { + let f = new TestBlockContext1Call(); + return f; + } + + static testBlockContext2(ctx: wasmlib.ScFuncCallContext): TestBlockContext2Call { + let f = new TestBlockContext2Call(); + return f; + } + + static testCallPanicFullEP(ctx: wasmlib.ScFuncCallContext): TestCallPanicFullEPCall { + let f = new TestCallPanicFullEPCall(); + return f; + } + + static testCallPanicViewEPFromFull(ctx: wasmlib.ScFuncCallContext): TestCallPanicViewEPFromFullCall { + let f = new TestCallPanicViewEPFromFullCall(); + return f; + } + + static testChainOwnerIDFull(ctx: wasmlib.ScFuncCallContext): TestChainOwnerIDFullCall { + let f = new TestChainOwnerIDFullCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static testEventLogDeploy(ctx: wasmlib.ScFuncCallContext): TestEventLogDeployCall { + let f = new TestEventLogDeployCall(); + return f; + } + + static testEventLogEventData(ctx: wasmlib.ScFuncCallContext): TestEventLogEventDataCall { + let f = new TestEventLogEventDataCall(); + return f; + } + + static testEventLogGenericData(ctx: wasmlib.ScFuncCallContext): TestEventLogGenericDataCall { + let f = new TestEventLogGenericDataCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static testPanicFullEP(ctx: wasmlib.ScFuncCallContext): TestPanicFullEPCall { + let f = new TestPanicFullEPCall(); + return f; + } + + static withdrawToChain(ctx: wasmlib.ScFuncCallContext): WithdrawToChainCall { + let f = new WithdrawToChainCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static checkContextFromViewEP(ctx: wasmlib.ScViewCallContext): CheckContextFromViewEPCall { + let f = new CheckContextFromViewEPCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static fibonacci(ctx: wasmlib.ScViewCallContext): FibonacciCall { + let f = new FibonacciCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getCounter(ctx: wasmlib.ScViewCallContext): GetCounterCall { + let f = new GetCounterCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static getInt(ctx: wasmlib.ScViewCallContext): GetIntCall { + let f = new GetIntCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getStringValue(ctx: wasmlib.ScViewCallContext): GetStringValueCall { + let f = new GetStringValueCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static justView(ctx: wasmlib.ScViewCallContext): JustViewCall { + let f = new JustViewCall(); + return f; + } + + static passTypesView(ctx: wasmlib.ScViewCallContext): PassTypesViewCall { + let f = new PassTypesViewCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static testCallPanicViewEPFromView(ctx: wasmlib.ScViewCallContext): TestCallPanicViewEPFromViewCall { + let f = new TestCallPanicViewEPFromViewCall(); + return f; + } + + static testChainOwnerIDView(ctx: wasmlib.ScViewCallContext): TestChainOwnerIDViewCall { + let f = new TestChainOwnerIDViewCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static testPanicViewEP(ctx: wasmlib.ScViewCallContext): TestPanicViewEPCall { + let f = new TestPanicViewEPCall(); + return f; + } + + static testSandboxCall(ctx: wasmlib.ScViewCallContext): TestSandboxCallCall { + let f = new TestSandboxCallCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/testcore/ts/testcore/index.ts b/contracts/wasm/testcore/ts/testcore/index.ts new file mode 100644 index 0000000000..02a05c8ac8 --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/index.ts @@ -0,0 +1,16 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./testcore"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; diff --git a/contracts/wasm/testcore/ts/testcore/keys.ts b/contracts/wasm/testcore/ts/testcore/keys.ts new file mode 100644 index 0000000000..cee489bf53 --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/keys.ts @@ -0,0 +1,81 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamAddress = 0; +export const IdxParamAgentID = 1; +export const IdxParamCaller = 2; +export const IdxParamChainID = 3; +export const IdxParamChainOwnerID = 4; +export const IdxParamContractCreator = 5; +export const IdxParamContractID = 6; +export const IdxParamCounter = 7; +export const IdxParamFail = 8; +export const IdxParamHash = 9; +export const IdxParamHname = 10; +export const IdxParamHnameContract = 11; +export const IdxParamHnameEP = 12; +export const IdxParamHnameZero = 13; +export const IdxParamInt64 = 14; +export const IdxParamInt64Zero = 15; +export const IdxParamIntValue = 16; +export const IdxParamName = 17; +export const IdxParamProgHash = 18; +export const IdxParamString = 19; +export const IdxParamStringZero = 20; +export const IdxParamVarName = 21; +export const IdxResultChainOwnerID = 22; +export const IdxResultCounter = 23; +export const IdxResultIntValue = 24; +export const IdxResultMintedColor = 25; +export const IdxResultMintedSupply = 26; +export const IdxResultSandboxCall = 27; +export const IdxStateCounter = 28; +export const IdxStateHnameEP = 29; +export const IdxStateInts = 30; +export const IdxStateMintedColor = 31; +export const IdxStateMintedSupply = 32; + +export let keyMap: string[] = [ + sc.ParamAddress, + sc.ParamAgentID, + sc.ParamCaller, + sc.ParamChainID, + sc.ParamChainOwnerID, + sc.ParamContractCreator, + sc.ParamContractID, + sc.ParamCounter, + sc.ParamFail, + sc.ParamHash, + sc.ParamHname, + sc.ParamHnameContract, + sc.ParamHnameEP, + sc.ParamHnameZero, + sc.ParamInt64, + sc.ParamInt64Zero, + sc.ParamIntValue, + sc.ParamName, + sc.ParamProgHash, + sc.ParamString, + sc.ParamStringZero, + sc.ParamVarName, + sc.ResultChainOwnerID, + sc.ResultCounter, + sc.ResultIntValue, + sc.ResultMintedColor, + sc.ResultMintedSupply, + sc.ResultSandboxCall, + sc.StateCounter, + sc.StateHnameEP, + sc.StateInts, + sc.StateMintedColor, + sc.StateMintedSupply, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/testcore/ts/testcore/lib.ts b/contracts/wasm/testcore/ts/testcore/lib.ts new file mode 100644 index 0000000000..468c8b992f --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/lib.ts @@ -0,0 +1,378 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncCallOnChain, funcCallOnChainThunk); + exports.addFunc(sc.FuncCheckContextFromFullEP, funcCheckContextFromFullEPThunk); + exports.addFunc(sc.FuncDoNothing, funcDoNothingThunk); + exports.addFunc(sc.FuncGetMintedSupply, funcGetMintedSupplyThunk); + exports.addFunc(sc.FuncIncCounter, funcIncCounterThunk); + exports.addFunc(sc.FuncInit, funcInitThunk); + exports.addFunc(sc.FuncPassTypesFull, funcPassTypesFullThunk); + exports.addFunc(sc.FuncRunRecursion, funcRunRecursionThunk); + exports.addFunc(sc.FuncSendToAddress, funcSendToAddressThunk); + exports.addFunc(sc.FuncSetInt, funcSetIntThunk); + exports.addFunc(sc.FuncSpawn, funcSpawnThunk); + exports.addFunc(sc.FuncTestBlockContext1, funcTestBlockContext1Thunk); + exports.addFunc(sc.FuncTestBlockContext2, funcTestBlockContext2Thunk); + exports.addFunc(sc.FuncTestCallPanicFullEP, funcTestCallPanicFullEPThunk); + exports.addFunc(sc.FuncTestCallPanicViewEPFromFull, funcTestCallPanicViewEPFromFullThunk); + exports.addFunc(sc.FuncTestChainOwnerIDFull, funcTestChainOwnerIDFullThunk); + exports.addFunc(sc.FuncTestEventLogDeploy, funcTestEventLogDeployThunk); + exports.addFunc(sc.FuncTestEventLogEventData, funcTestEventLogEventDataThunk); + exports.addFunc(sc.FuncTestEventLogGenericData, funcTestEventLogGenericDataThunk); + exports.addFunc(sc.FuncTestPanicFullEP, funcTestPanicFullEPThunk); + exports.addFunc(sc.FuncWithdrawToChain, funcWithdrawToChainThunk); + exports.addView(sc.ViewCheckContextFromViewEP, viewCheckContextFromViewEPThunk); + exports.addView(sc.ViewFibonacci, viewFibonacciThunk); + exports.addView(sc.ViewGetCounter, viewGetCounterThunk); + exports.addView(sc.ViewGetInt, viewGetIntThunk); + exports.addView(sc.ViewGetStringValue, viewGetStringValueThunk); + exports.addView(sc.ViewJustView, viewJustViewThunk); + exports.addView(sc.ViewPassTypesView, viewPassTypesViewThunk); + exports.addView(sc.ViewTestCallPanicViewEPFromView, viewTestCallPanicViewEPFromViewThunk); + exports.addView(sc.ViewTestChainOwnerIDView, viewTestChainOwnerIDViewThunk); + exports.addView(sc.ViewTestPanicViewEP, viewTestPanicViewEPThunk); + exports.addView(sc.ViewTestSandboxCall, viewTestSandboxCallThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcCallOnChainThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcCallOnChain"); + let f = new sc.CallOnChainContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.intValue().exists(), "missing mandatory intValue") + sc.funcCallOnChain(ctx, f); + ctx.log("testcore.funcCallOnChain ok"); +} + +function funcCheckContextFromFullEPThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcCheckContextFromFullEP"); + let f = new sc.CheckContextFromFullEPContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.agentID().exists(), "missing mandatory agentID") + ctx.require(f.params.caller().exists(), "missing mandatory caller") + ctx.require(f.params.chainID().exists(), "missing mandatory chainID") + ctx.require(f.params.chainOwnerID().exists(), "missing mandatory chainOwnerID") + ctx.require(f.params.contractCreator().exists(), "missing mandatory contractCreator") + sc.funcCheckContextFromFullEP(ctx, f); + ctx.log("testcore.funcCheckContextFromFullEP ok"); +} + +function funcDoNothingThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcDoNothing"); + let f = new sc.DoNothingContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcDoNothing(ctx, f); + ctx.log("testcore.funcDoNothing ok"); +} + +function funcGetMintedSupplyThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcGetMintedSupply"); + let f = new sc.GetMintedSupplyContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcGetMintedSupply(ctx, f); + ctx.log("testcore.funcGetMintedSupply ok"); +} + +function funcIncCounterThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcIncCounter"); + let f = new sc.IncCounterContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcIncCounter(ctx, f); + ctx.log("testcore.funcIncCounter ok"); +} + +function funcInitThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcInit"); + let f = new sc.InitContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcInit(ctx, f); + ctx.log("testcore.funcInit ok"); +} + +function funcPassTypesFullThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcPassTypesFull"); + let f = new sc.PassTypesFullContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.address().exists(), "missing mandatory address") + ctx.require(f.params.agentID().exists(), "missing mandatory agentID") + ctx.require(f.params.chainID().exists(), "missing mandatory chainID") + ctx.require(f.params.contractID().exists(), "missing mandatory contractID") + ctx.require(f.params.hash().exists(), "missing mandatory hash") + ctx.require(f.params.hname().exists(), "missing mandatory hname") + ctx.require(f.params.hnameZero().exists(), "missing mandatory hnameZero") + ctx.require(f.params.int64().exists(), "missing mandatory int64") + ctx.require(f.params.int64Zero().exists(), "missing mandatory int64Zero") + ctx.require(f.params.string().exists(), "missing mandatory string") + ctx.require(f.params.stringZero().exists(), "missing mandatory stringZero") + sc.funcPassTypesFull(ctx, f); + ctx.log("testcore.funcPassTypesFull ok"); +} + +function funcRunRecursionThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcRunRecursion"); + let f = new sc.RunRecursionContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.intValue().exists(), "missing mandatory intValue") + sc.funcRunRecursion(ctx, f); + ctx.log("testcore.funcRunRecursion ok"); +} + +function funcSendToAddressThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcSendToAddress"); + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.SendToAddressContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.address().exists(), "missing mandatory address") + sc.funcSendToAddress(ctx, f); + ctx.log("testcore.funcSendToAddress ok"); +} + +function funcSetIntThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcSetInt"); + let f = new sc.SetIntContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.intValue().exists(), "missing mandatory intValue") + ctx.require(f.params.name().exists(), "missing mandatory name") + sc.funcSetInt(ctx, f); + ctx.log("testcore.funcSetInt ok"); +} + +function funcSpawnThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcSpawn"); + let f = new sc.SpawnContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.progHash().exists(), "missing mandatory progHash") + sc.funcSpawn(ctx, f); + ctx.log("testcore.funcSpawn ok"); +} + +function funcTestBlockContext1Thunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestBlockContext1"); + let f = new sc.TestBlockContext1Context(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestBlockContext1(ctx, f); + ctx.log("testcore.funcTestBlockContext1 ok"); +} + +function funcTestBlockContext2Thunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestBlockContext2"); + let f = new sc.TestBlockContext2Context(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestBlockContext2(ctx, f); + ctx.log("testcore.funcTestBlockContext2 ok"); +} + +function funcTestCallPanicFullEPThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestCallPanicFullEP"); + let f = new sc.TestCallPanicFullEPContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestCallPanicFullEP(ctx, f); + ctx.log("testcore.funcTestCallPanicFullEP ok"); +} + +function funcTestCallPanicViewEPFromFullThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestCallPanicViewEPFromFull"); + let f = new sc.TestCallPanicViewEPFromFullContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestCallPanicViewEPFromFull(ctx, f); + ctx.log("testcore.funcTestCallPanicViewEPFromFull ok"); +} + +function funcTestChainOwnerIDFullThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestChainOwnerIDFull"); + let f = new sc.TestChainOwnerIDFullContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestChainOwnerIDFull(ctx, f); + ctx.log("testcore.funcTestChainOwnerIDFull ok"); +} + +function funcTestEventLogDeployThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestEventLogDeploy"); + let f = new sc.TestEventLogDeployContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestEventLogDeploy(ctx, f); + ctx.log("testcore.funcTestEventLogDeploy ok"); +} + +function funcTestEventLogEventDataThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestEventLogEventData"); + let f = new sc.TestEventLogEventDataContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestEventLogEventData(ctx, f); + ctx.log("testcore.funcTestEventLogEventData ok"); +} + +function funcTestEventLogGenericDataThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestEventLogGenericData"); + let f = new sc.TestEventLogGenericDataContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.counter().exists(), "missing mandatory counter") + sc.funcTestEventLogGenericData(ctx, f); + ctx.log("testcore.funcTestEventLogGenericData ok"); +} + +function funcTestPanicFullEPThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcTestPanicFullEP"); + let f = new sc.TestPanicFullEPContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcTestPanicFullEP(ctx, f); + ctx.log("testcore.funcTestPanicFullEP ok"); +} + +function funcWithdrawToChainThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testcore.funcWithdrawToChain"); + let f = new sc.WithdrawToChainContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.chainID().exists(), "missing mandatory chainID") + sc.funcWithdrawToChain(ctx, f); + ctx.log("testcore.funcWithdrawToChain ok"); +} + +function viewCheckContextFromViewEPThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewCheckContextFromViewEP"); + let f = new sc.CheckContextFromViewEPContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.agentID().exists(), "missing mandatory agentID") + ctx.require(f.params.chainID().exists(), "missing mandatory chainID") + ctx.require(f.params.chainOwnerID().exists(), "missing mandatory chainOwnerID") + ctx.require(f.params.contractCreator().exists(), "missing mandatory contractCreator") + sc.viewCheckContextFromViewEP(ctx, f); + ctx.log("testcore.viewCheckContextFromViewEP ok"); +} + +function viewFibonacciThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewFibonacci"); + let f = new sc.FibonacciContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.intValue().exists(), "missing mandatory intValue") + sc.viewFibonacci(ctx, f); + ctx.log("testcore.viewFibonacci ok"); +} + +function viewGetCounterThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewGetCounter"); + let f = new sc.GetCounterContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewGetCounter(ctx, f); + ctx.log("testcore.viewGetCounter ok"); +} + +function viewGetIntThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewGetInt"); + let f = new sc.GetIntContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.name().exists(), "missing mandatory name") + sc.viewGetInt(ctx, f); + ctx.log("testcore.viewGetInt ok"); +} + +function viewGetStringValueThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewGetStringValue"); + let f = new sc.GetStringValueContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.varName().exists(), "missing mandatory varName") + sc.viewGetStringValue(ctx, f); + ctx.log("testcore.viewGetStringValue ok"); +} + +function viewJustViewThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewJustView"); + let f = new sc.JustViewContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewJustView(ctx, f); + ctx.log("testcore.viewJustView ok"); +} + +function viewPassTypesViewThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewPassTypesView"); + let f = new sc.PassTypesViewContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.address().exists(), "missing mandatory address") + ctx.require(f.params.agentID().exists(), "missing mandatory agentID") + ctx.require(f.params.chainID().exists(), "missing mandatory chainID") + ctx.require(f.params.contractID().exists(), "missing mandatory contractID") + ctx.require(f.params.hash().exists(), "missing mandatory hash") + ctx.require(f.params.hname().exists(), "missing mandatory hname") + ctx.require(f.params.hnameZero().exists(), "missing mandatory hnameZero") + ctx.require(f.params.int64().exists(), "missing mandatory int64") + ctx.require(f.params.int64Zero().exists(), "missing mandatory int64Zero") + ctx.require(f.params.string().exists(), "missing mandatory string") + ctx.require(f.params.stringZero().exists(), "missing mandatory stringZero") + sc.viewPassTypesView(ctx, f); + ctx.log("testcore.viewPassTypesView ok"); +} + +function viewTestCallPanicViewEPFromViewThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewTestCallPanicViewEPFromView"); + let f = new sc.TestCallPanicViewEPFromViewContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewTestCallPanicViewEPFromView(ctx, f); + ctx.log("testcore.viewTestCallPanicViewEPFromView ok"); +} + +function viewTestChainOwnerIDViewThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewTestChainOwnerIDView"); + let f = new sc.TestChainOwnerIDViewContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewTestChainOwnerIDView(ctx, f); + ctx.log("testcore.viewTestChainOwnerIDView ok"); +} + +function viewTestPanicViewEPThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewTestPanicViewEP"); + let f = new sc.TestPanicViewEPContext(); + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewTestPanicViewEP(ctx, f); + ctx.log("testcore.viewTestPanicViewEP ok"); +} + +function viewTestSandboxCallThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testcore.viewTestSandboxCall"); + let f = new sc.TestSandboxCallContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewTestSandboxCall(ctx, f); + ctx.log("testcore.viewTestSandboxCall ok"); +} diff --git a/contracts/wasm/testcore/ts/testcore/params.ts b/contracts/wasm/testcore/ts/testcore/params.ts new file mode 100644 index 0000000000..6d961d9498 --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/params.ts @@ -0,0 +1,459 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableCallOnChainParams extends wasmlib.ScMapID { + + hnameContract(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameContract]); + } + + hnameEP(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameEP]); + } + + intValue(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } +} + +export class MutableCallOnChainParams extends wasmlib.ScMapID { + + hnameContract(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameContract]); + } + + hnameEP(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameEP]); + } + + intValue(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } +} + +export class ImmutableCheckContextFromFullEPParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + caller(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamCaller]); + } + + chainID(): wasmlib.ScImmutableChainID { + return new wasmlib.ScImmutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + chainOwnerID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamChainOwnerID]); + } + + contractCreator(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractCreator]); + } +} + +export class MutableCheckContextFromFullEPParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + caller(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamCaller]); + } + + chainID(): wasmlib.ScMutableChainID { + return new wasmlib.ScMutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + chainOwnerID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamChainOwnerID]); + } + + contractCreator(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractCreator]); + } +} + +export class ImmutableInitParams extends wasmlib.ScMapID { + + fail(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamFail]); + } +} + +export class MutableInitParams extends wasmlib.ScMapID { + + fail(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamFail]); + } +} + +export class ImmutablePassTypesFullParams extends wasmlib.ScMapID { + + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + chainID(): wasmlib.ScImmutableChainID { + return new wasmlib.ScImmutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + contractID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractID]); + } + + hash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamHash]); + } + + hname(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxParamHname]); + } + + hnameZero(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameZero]); + } + + int64(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64]); + } + + int64Zero(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64Zero]); + } + + string(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamString]); + } + + stringZero(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamStringZero]); + } +} + +export class MutablePassTypesFullParams extends wasmlib.ScMapID { + + address(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + chainID(): wasmlib.ScMutableChainID { + return new wasmlib.ScMutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + contractID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractID]); + } + + hash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamHash]); + } + + hname(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxParamHname]); + } + + hnameZero(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameZero]); + } + + int64(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64]); + } + + int64Zero(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64Zero]); + } + + string(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamString]); + } + + stringZero(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamStringZero]); + } +} + +export class ImmutableRunRecursionParams extends wasmlib.ScMapID { + + intValue(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } +} + +export class MutableRunRecursionParams extends wasmlib.ScMapID { + + intValue(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } +} + +export class ImmutableSendToAddressParams extends wasmlib.ScMapID { + + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } +} + +export class MutableSendToAddressParams extends wasmlib.ScMapID { + + address(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } +} + +export class ImmutableSetIntParams extends wasmlib.ScMapID { + + intValue(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class MutableSetIntParams extends wasmlib.ScMapID { + + intValue(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class ImmutableSpawnParams extends wasmlib.ScMapID { + + progHash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamProgHash]); + } +} + +export class MutableSpawnParams extends wasmlib.ScMapID { + + progHash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamProgHash]); + } +} + +export class ImmutableTestEventLogGenericDataParams extends wasmlib.ScMapID { + + counter(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamCounter]); + } +} + +export class MutableTestEventLogGenericDataParams extends wasmlib.ScMapID { + + counter(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamCounter]); + } +} + +export class ImmutableWithdrawToChainParams extends wasmlib.ScMapID { + + chainID(): wasmlib.ScImmutableChainID { + return new wasmlib.ScImmutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } +} + +export class MutableWithdrawToChainParams extends wasmlib.ScMapID { + + chainID(): wasmlib.ScMutableChainID { + return new wasmlib.ScMutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } +} + +export class ImmutableCheckContextFromViewEPParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + chainID(): wasmlib.ScImmutableChainID { + return new wasmlib.ScImmutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + chainOwnerID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamChainOwnerID]); + } + + contractCreator(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractCreator]); + } +} + +export class MutableCheckContextFromViewEPParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + chainID(): wasmlib.ScMutableChainID { + return new wasmlib.ScMutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + chainOwnerID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamChainOwnerID]); + } + + contractCreator(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractCreator]); + } +} + +export class ImmutableFibonacciParams extends wasmlib.ScMapID { + + intValue(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } +} + +export class MutableFibonacciParams extends wasmlib.ScMapID { + + intValue(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamIntValue]); + } +} + +export class ImmutableGetIntParams extends wasmlib.ScMapID { + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class MutableGetIntParams extends wasmlib.ScMapID { + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class ImmutableGetStringValueParams extends wasmlib.ScMapID { + + varName(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamVarName]); + } +} + +export class MutableGetStringValueParams extends wasmlib.ScMapID { + + varName(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamVarName]); + } +} + +export class ImmutablePassTypesViewParams extends wasmlib.ScMapID { + + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + chainID(): wasmlib.ScImmutableChainID { + return new wasmlib.ScImmutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + contractID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractID]); + } + + hash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamHash]); + } + + hname(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxParamHname]); + } + + hnameZero(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameZero]); + } + + int64(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64]); + } + + int64Zero(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64Zero]); + } + + string(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamString]); + } + + stringZero(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamStringZero]); + } +} + +export class MutablePassTypesViewParams extends wasmlib.ScMapID { + + address(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + chainID(): wasmlib.ScMutableChainID { + return new wasmlib.ScMutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + contractID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamContractID]); + } + + hash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamHash]); + } + + hname(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxParamHname]); + } + + hnameZero(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxParamHnameZero]); + } + + int64(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64]); + } + + int64Zero(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64Zero]); + } + + string(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamString]); + } + + stringZero(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamStringZero]); + } +} diff --git a/contracts/wasm/testcore/ts/testcore/results.ts b/contracts/wasm/testcore/ts/testcore/results.ts new file mode 100644 index 0000000000..1c64fa616f --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/results.ts @@ -0,0 +1,213 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableCallOnChainResults extends wasmlib.ScMapID { + + intValue(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultIntValue]); + } +} + +export class MutableCallOnChainResults extends wasmlib.ScMapID { + + intValue(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultIntValue]); + } +} + +export class ImmutableGetMintedSupplyResults extends wasmlib.ScMapID { + + mintedColor(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxResultMintedColor]); + } + + mintedSupply(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultMintedSupply]); + } +} + +export class MutableGetMintedSupplyResults extends wasmlib.ScMapID { + + mintedColor(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxResultMintedColor]); + } + + mintedSupply(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultMintedSupply]); + } +} + +export class ImmutableRunRecursionResults extends wasmlib.ScMapID { + + intValue(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultIntValue]); + } +} + +export class MutableRunRecursionResults extends wasmlib.ScMapID { + + intValue(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultIntValue]); + } +} + +export class ImmutableTestChainOwnerIDFullResults extends wasmlib.ScMapID { + + chainOwnerID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultChainOwnerID]); + } +} + +export class MutableTestChainOwnerIDFullResults extends wasmlib.ScMapID { + + chainOwnerID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultChainOwnerID]); + } +} + +export class ImmutableFibonacciResults extends wasmlib.ScMapID { + + intValue(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultIntValue]); + } +} + +export class MutableFibonacciResults extends wasmlib.ScMapID { + + intValue(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultIntValue]); + } +} + +export class ImmutableGetCounterResults extends wasmlib.ScMapID { + + counter(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultCounter]); + } +} + +export class MutableGetCounterResults extends wasmlib.ScMapID { + + counter(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultCounter]); + } +} + +export class MapStringToImmutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getInt64(key: string): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class ImmutableGetIntResults extends wasmlib.ScMapID { + + values(): sc.MapStringToImmutableInt64 { + return new sc.MapStringToImmutableInt64(this.mapID); + } +} + +export class MapStringToMutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getInt64(key: string): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class MutableGetIntResults extends wasmlib.ScMapID { + + values(): sc.MapStringToMutableInt64 { + return new sc.MapStringToMutableInt64(this.mapID); + } +} + +export class MapStringToImmutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getString(key: string): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class ImmutableGetStringValueResults extends wasmlib.ScMapID { + + vars(): sc.MapStringToImmutableString { + return new sc.MapStringToImmutableString(this.mapID); + } +} + +export class MapStringToMutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getString(key: string): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class MutableGetStringValueResults extends wasmlib.ScMapID { + + vars(): sc.MapStringToMutableString { + return new sc.MapStringToMutableString(this.mapID); + } +} + +export class ImmutableTestChainOwnerIDViewResults extends wasmlib.ScMapID { + + chainOwnerID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultChainOwnerID]); + } +} + +export class MutableTestChainOwnerIDViewResults extends wasmlib.ScMapID { + + chainOwnerID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultChainOwnerID]); + } +} + +export class ImmutableTestSandboxCallResults extends wasmlib.ScMapID { + + sandboxCall(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultSandboxCall]); + } +} + +export class MutableTestSandboxCallResults extends wasmlib.ScMapID { + + sandboxCall(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultSandboxCall]); + } +} diff --git a/contracts/wasm/testcore/ts/testcore/state.ts b/contracts/wasm/testcore/ts/testcore/state.ts new file mode 100644 index 0000000000..f2fe2ddfa6 --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/state.ts @@ -0,0 +1,57 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableTestCoreState extends wasmlib.ScMapID { + + counter(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateCounter]); + } + + hnameEP(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxStateHnameEP]); + } + + ints(): sc.MapStringToImmutableInt64 { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateInts], wasmlib.TYPE_MAP); + return new sc.MapStringToImmutableInt64(mapID); + } + + mintedColor(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxStateMintedColor]); + } + + mintedSupply(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateMintedSupply]); + } +} + +export class MutableTestCoreState extends wasmlib.ScMapID { + + counter(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateCounter]); + } + + hnameEP(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxStateHnameEP]); + } + + ints(): sc.MapStringToMutableInt64 { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateInts], wasmlib.TYPE_MAP); + return new sc.MapStringToMutableInt64(mapID); + } + + mintedColor(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxStateMintedColor]); + } + + mintedSupply(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateMintedSupply]); + } +} diff --git a/contracts/wasm/testcore/ts/testcore/testcore.ts b/contracts/wasm/testcore/ts/testcore/testcore.ts new file mode 100644 index 0000000000..2d72873ae3 --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/testcore.ts @@ -0,0 +1,236 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "wasmlib" +import * as coreaccounts from "wasmlib/coreaccounts" +import * as coregovernance from "wasmlib/coregovernance" +import * as sc from "./index"; + +const CONTRACT_NAME_DEPLOYED = "exampleDeployTR"; +const MSG_CORE_ONLY_PANIC = "========== core only ========="; +const MSG_FULL_PANIC = "========== panic FULL ENTRY POINT ========="; +const MSG_VIEW_PANIC = "========== panic VIEW ========="; + +export function funcCallOnChain(ctx: wasmlib.ScFuncContext, f: sc.CallOnChainContext): void { + let paramInt = f.params.intValue().value(); + + let hnameContract = ctx.contract(); + if (f.params.hnameContract().exists()) { + hnameContract = f.params.hnameContract().value(); + } + + let hnameEP = sc.HFuncCallOnChain; + if (f.params.hnameEP().exists()) { + hnameEP = f.params.hnameEP().value(); + } + + let counter = f.state.counter(); + ctx.log("call depth = " + f.params.intValue().toString() + + ", hnameContract = " + hnameContract.toString() + + ", hnameEP = " + hnameEP.toString() + + ", counter = " + counter.toString()) + + counter.setValue(counter.value() + 1); + + let parms = wasmlib.ScMutableMap.create(); + parms.getInt64(wasmlib.Key32.fromString(sc.ParamIntValue)).setValue(paramInt); + let ret = ctx.call(hnameContract, hnameEP, parms, null); + let retVal = ret.getInt64(wasmlib.Key32.fromString(sc.ResultIntValue)); + f.results.intValue().setValue(retVal.value()); +} + +export function funcCheckContextFromFullEP(ctx: wasmlib.ScFuncContext, f: sc.CheckContextFromFullEPContext): void { + ctx.require(f.params.agentID().value().equals(ctx.accountID()), "fail: agentID"); + ctx.require(f.params.caller().value().equals(ctx.caller()), "fail: caller"); + ctx.require(f.params.chainID().value().equals(ctx.chainID()), "fail: chainID"); + ctx.require(f.params.chainOwnerID().value().equals(ctx.chainOwnerID()), "fail: chainOwnerID"); + ctx.require(f.params.contractCreator().value().equals(ctx.contractCreator()), "fail: contractCreator"); +} + +export function funcDoNothing(ctx: wasmlib.ScFuncContext, f: sc.DoNothingContext): void { + ctx.log("doing nothing..."); +} + +export function funcGetMintedSupply(ctx: wasmlib.ScFuncContext, f: sc.GetMintedSupplyContext): void { + let minted = ctx.minted(); + let mintedColors = minted.colors(); + ctx.require(mintedColors.length() == 1, "test only supports one minted color"); + let color = mintedColors.getColor(0).value(); + let amount = minted.balance(color); + f.results.mintedSupply().setValue(amount); + f.results.mintedColor().setValue(color); +} + +export function funcIncCounter(ctx: wasmlib.ScFuncContext, f: sc.IncCounterContext): void { + let counter = f.state.counter(); + counter.setValue(counter.value() + 1); +} + +export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { + if (f.params.fail().exists()) { + ctx.panic("failing on purpose"); + } +} + +export function funcPassTypesFull(ctx: wasmlib.ScFuncContext, f: sc.PassTypesFullContext): void { + let hash = ctx.utility().hashBlake2b(wasmlib.Convert.fromString(sc.ParamHash)); + ctx.require(f.params.hash().value().equals(hash), "Hash wrong"); + ctx.require(f.params.int64().value() == 42, "int64 wrong"); + ctx.require(f.params.int64Zero().value() == 0, "int64-0 wrong"); + ctx.require(f.params.string().value() == sc.ParamString, "string wrong"); + ctx.require(f.params.stringZero().value() == "", "string-0 wrong"); + ctx.require(f.params.hname().value().equals(wasmlib.ScHname.fromName(sc.ParamHname)), "Hname wrong"); + ctx.require(f.params.hnameZero().value().equals(new wasmlib.ScHname(0)), "Hname-0 wrong"); +} + +export function funcRunRecursion(ctx: wasmlib.ScFuncContext, f: sc.RunRecursionContext): void { + let depth = f.params.intValue().value(); + if (depth <= 0) { + return; + } + + let callOnChain = sc.ScFuncs.callOnChain(ctx); + callOnChain.params.intValue().setValue(depth - 1); + callOnChain.params.hnameEP().setValue(sc.HFuncRunRecursion); + callOnChain.func.call(); + let retVal = callOnChain.results.intValue().value(); + f.results.intValue().setValue(retVal); +} + +export function funcSendToAddress(ctx: wasmlib.ScFuncContext, f: sc.SendToAddressContext): void { + let balances = wasmlib.ScTransfers.fromBalances(ctx.balances()); + ctx.transferToAddress(f.params.address().value(), balances); +} + +export function funcSetInt(ctx: wasmlib.ScFuncContext, f: sc.SetIntContext): void { + f.state.ints().getInt64(f.params.name().value()).setValue(f.params.intValue().value()); +} + +export function funcSpawn(ctx: wasmlib.ScFuncContext, f: sc.SpawnContext): void { + let spawnName = sc.ScName + "Spawned"; + let spawnDescr = "spawned contract description"; + ctx.deploy(f.params.progHash().value(), spawnName, spawnDescr, null); + + let spawnHname = wasmlib.ScHname.fromName(spawnName); + for (let i = 0; i < 5; i++) { + ctx.call(spawnHname, sc.HFuncIncCounter, null, null); + } +} + +export function funcTestBlockContext1(ctx: wasmlib.ScFuncContext, f: sc.TestBlockContext1Context): void { + ctx.panic(MSG_CORE_ONLY_PANIC); +} + +export function funcTestBlockContext2(ctx: wasmlib.ScFuncContext, f: sc.TestBlockContext2Context): void { + ctx.panic(MSG_CORE_ONLY_PANIC); +} + +export function funcTestCallPanicFullEP(ctx: wasmlib.ScFuncContext, f: sc.TestCallPanicFullEPContext): void { + sc.ScFuncs.testPanicFullEP(ctx).func.call(); +} + +export function funcTestCallPanicViewEPFromFull(ctx: wasmlib.ScFuncContext, f: sc.TestCallPanicViewEPFromFullContext): void { + sc.ScFuncs.testPanicViewEP(ctx).func.call(); +} + +export function funcTestChainOwnerIDFull(ctx: wasmlib.ScFuncContext, f: sc.TestChainOwnerIDFullContext): void { + f.results.chainOwnerID().setValue(ctx.chainOwnerID()); +} + +export function funcTestEventLogDeploy(ctx: wasmlib.ScFuncContext, f: sc.TestEventLogDeployContext): void { + // deploy the same contract with another name + let programHash = ctx.utility().hashBlake2b(wasmlib.Convert.fromString("testcore")); + ctx.deploy(programHash, CONTRACT_NAME_DEPLOYED, "test contract deploy log", null); +} + +export function funcTestEventLogEventData(ctx: wasmlib.ScFuncContext, f: sc.TestEventLogEventDataContext): void { + ctx.event("[Event] - Testing Event..."); +} + +export function funcTestEventLogGenericData(ctx: wasmlib.ScFuncContext, f: sc.TestEventLogGenericDataContext): void { + let event = "[GenericData] Counter Number: ".toString() + f.params.counter().toString(); + ctx.event(event); +} + +export function funcTestPanicFullEP(ctx: wasmlib.ScFuncContext, f: sc.TestPanicFullEPContext): void { + ctx.panic(MSG_FULL_PANIC); +} + +export function funcWithdrawToChain(ctx: wasmlib.ScFuncContext, f: sc.WithdrawToChainContext): void { + let xx = coreaccounts.ScFuncs.withdraw(ctx); + xx.func.transferIotas(1).postToChain(f.params.chainID().value()); +} + +export function viewCheckContextFromViewEP(ctx: wasmlib.ScViewContext, f: sc.CheckContextFromViewEPContext): void { + ctx.require(f.params.agentID().value().equals(ctx.accountID()), "fail: agentID"); + ctx.require(f.params.chainID().value().equals(ctx.chainID()), "fail: chainID"); + ctx.require(f.params.chainOwnerID().value().equals(ctx.chainOwnerID()), "fail: chainOwnerID"); + ctx.require(f.params.contractCreator().value().equals(ctx.contractCreator()), "fail: contractCreator"); +} + +export function viewFibonacci(ctx: wasmlib.ScViewContext, f: sc.FibonacciContext): void { + let n = f.params.intValue().value(); + if (n == 0 || n == 1) { + f.results.intValue().setValue(n); + return; + } + + let fib = sc.ScFuncs.fibonacci(ctx); + fib.params.intValue().setValue(n - 1); + fib.func.call(); + let n1 = fib.results.intValue().value(); + + fib.params.intValue().setValue(n - 2); + fib.func.call(); + let n2 = fib.results.intValue().value(); + + f.results.intValue().setValue(n1 + n2); +} + +export function viewGetCounter(ctx: wasmlib.ScViewContext, f: sc.GetCounterContext): void { + f.results.counter().setValue(f.state.counter().value()); +} + +export function viewGetInt(ctx: wasmlib.ScViewContext, f: sc.GetIntContext): void { + let name = f.params.name().value(); + let value = f.state.ints().getInt64(name); + ctx.require(value.exists(), "param 'value' not found"); + f.results.values().getInt64(name).setValue(value.value()); +} + +export function viewGetStringValue(ctx: wasmlib.ScViewContext, f: sc.GetStringValueContext): void { + ctx.panic(MSG_CORE_ONLY_PANIC); +} + +export function viewJustView(ctx: wasmlib.ScViewContext, f: sc.JustViewContext): void { + ctx.log("doing nothing..."); +} + +export function viewPassTypesView(ctx: wasmlib.ScViewContext, f: sc.PassTypesViewContext): void { + let hash = ctx.utility().hashBlake2b(wasmlib.Convert.fromString(sc.ParamHash)); + ctx.require(f.params.hash().value().equals(hash), "Hash wrong"); + ctx.require(f.params.int64().value() == 42, "int64 wrong"); + ctx.require(f.params.int64Zero().value() == 0, "int64-0 wrong"); + ctx.require(f.params.string().value() == sc.ParamString, "string wrong"); + ctx.require(f.params.stringZero().value() == "", "string-0 wrong"); + ctx.require(f.params.hname().value().equals(wasmlib.ScHname.fromName(sc.ParamHname)), "Hname wrong"); + ctx.require(f.params.hnameZero().value().equals(new wasmlib.ScHname(0)), "Hname-0 wrong"); +} + +export function viewTestCallPanicViewEPFromView(ctx: wasmlib.ScViewContext, f: sc.TestCallPanicViewEPFromViewContext): void { + sc.ScFuncs.testPanicViewEP(ctx).func.call(); +} + +export function viewTestChainOwnerIDView(ctx: wasmlib.ScViewContext, f: sc.TestChainOwnerIDViewContext): void { + f.results.chainOwnerID().setValue(ctx.chainOwnerID()); +} + +export function viewTestPanicViewEP(ctx: wasmlib.ScViewContext, f: sc.TestPanicViewEPContext): void { + ctx.panic(MSG_VIEW_PANIC); +} + +export function viewTestSandboxCall(ctx: wasmlib.ScViewContext, f: sc.TestSandboxCallContext): void { + let getChainInfo = coregovernance.ScFuncs.getChainInfo(ctx); + getChainInfo.func.call(); + f.results.sandboxCall().setValue(getChainInfo.results.description().value()); +} diff --git a/contracts/wasm/testcore/ts/testcore/tsconfig.json b/contracts/wasm/testcore/ts/testcore/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/testcore/ts/testcore/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/testwasmlib/Cargo.toml b/contracts/wasm/testwasmlib/Cargo.toml index 6471bca267..e789cadf3e 100644 --- a/contracts/wasm/testwasmlib/Cargo.toml +++ b/contracts/wasm/testwasmlib/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } console_error_panic_hook = { version = "0.1.6", optional = true } wee_alloc = { version = "0.4.5", optional = true } diff --git a/contracts/wasm/testwasmlib/wasmmain/main.go b/contracts/wasm/testwasmlib/go/main.go similarity index 79% rename from contracts/wasm/testwasmlib/wasmmain/main.go rename to contracts/wasm/testwasmlib/go/main.go index 30b07e78b4..c213bd8591 100644 --- a/contracts/wasm/testwasmlib/wasmmain/main.go +++ b/contracts/wasm/testwasmlib/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/testwasmlib" + +import "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() testwasmlib.OnLoad() diff --git a/contracts/wasm/testwasmlib/consts.go b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go similarity index 96% rename from contracts/wasm/testwasmlib/consts.go rename to contracts/wasm/testwasmlib/go/testwasmlib/consts.go index ab29f5fdcf..17f1e726ee 100644 --- a/contracts/wasm/testwasmlib/consts.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go @@ -7,7 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "testwasmlib" diff --git a/contracts/wasm/testwasmlib/contract.go b/contracts/wasm/testwasmlib/go/testwasmlib/contract.go similarity index 97% rename from contracts/wasm/testwasmlib/contract.go rename to contracts/wasm/testwasmlib/go/testwasmlib/contract.go index b230a143c3..b4e9802136 100644 --- a/contracts/wasm/testwasmlib/contract.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/contract.go @@ -7,7 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayClearCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/testwasmlib/keys.go b/contracts/wasm/testwasmlib/go/testwasmlib/keys.go similarity index 94% rename from contracts/wasm/testwasmlib/keys.go rename to contracts/wasm/testwasmlib/go/testwasmlib/keys.go index 4ca38dca78..8dbbd0d026 100644 --- a/contracts/wasm/testwasmlib/keys.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/keys.go @@ -7,7 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamAddress = 0 diff --git a/contracts/wasm/testwasmlib/lib.go b/contracts/wasm/testwasmlib/go/testwasmlib/lib.go similarity index 98% rename from contracts/wasm/testwasmlib/lib.go rename to contracts/wasm/testwasmlib/go/testwasmlib/lib.go index 2966c0e9fc..d5b74fed67 100644 --- a/contracts/wasm/testwasmlib/lib.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/lib.go @@ -7,7 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/testwasmlib/params.go b/contracts/wasm/testwasmlib/go/testwasmlib/params.go similarity index 99% rename from contracts/wasm/testwasmlib/params.go rename to contracts/wasm/testwasmlib/go/testwasmlib/params.go index 5790346cc5..668e7b8e07 100644 --- a/contracts/wasm/testwasmlib/params.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/params.go @@ -7,7 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableArrayClearParams struct { id int32 diff --git a/contracts/wasm/testwasmlib/results.go b/contracts/wasm/testwasmlib/go/testwasmlib/results.go similarity index 96% rename from contracts/wasm/testwasmlib/results.go rename to contracts/wasm/testwasmlib/go/testwasmlib/results.go index 5c4dfbf207..2d85a02d1f 100644 --- a/contracts/wasm/testwasmlib/results.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/results.go @@ -7,7 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableArrayLengthResults struct { id int32 diff --git a/contracts/wasm/testwasmlib/state.go b/contracts/wasm/testwasmlib/go/testwasmlib/state.go similarity index 95% rename from contracts/wasm/testwasmlib/state.go rename to contracts/wasm/testwasmlib/go/testwasmlib/state.go index 6e7f477328..5389e238b3 100644 --- a/contracts/wasm/testwasmlib/state.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/state.go @@ -7,7 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type MapStringToImmutableStringArray struct { objID int32 diff --git a/contracts/wasm/testwasmlib/testwasmlib.go b/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go similarity index 96% rename from contracts/wasm/testwasmlib/testwasmlib.go rename to contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go index 54a9b5c544..b466b76b91 100644 --- a/contracts/wasm/testwasmlib/testwasmlib.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go @@ -6,8 +6,8 @@ package testwasmlib import ( "bytes" - "github.com/iotaledger/wasp/packages/vm/wasmlib" - "github.com/iotaledger/wasp/packages/vm/wasmlib/corecontracts/coreblocklog" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreblocklog" ) func funcParamTypes(ctx wasmlib.ScFuncContext, f *ParamTypesContext) { diff --git a/contracts/wasm/testwasmlib/typedefs.go b/contracts/wasm/testwasmlib/go/testwasmlib/typedefs.go similarity index 93% rename from contracts/wasm/testwasmlib/typedefs.go rename to contracts/wasm/testwasmlib/go/testwasmlib/typedefs.go index 6a9e0e3869..28df5b3ef7 100644 --- a/contracts/wasm/testwasmlib/typedefs.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/typedefs.go @@ -7,9 +7,7 @@ package testwasmlib -import "github.com/iotaledger/wasp/packages/vm/wasmlib" - -type ImmutableStringArray = ArrayOfImmutableString +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayOfImmutableString struct { objID int32 @@ -23,7 +21,7 @@ func (a ArrayOfImmutableString) GetString(index int32) wasmlib.ScImmutableString return wasmlib.NewScImmutableString(a.objID, wasmlib.Key32(index)) } -type MutableStringArray = ArrayOfMutableString +type ImmutableStringArray = ArrayOfImmutableString type ArrayOfMutableString struct { objID int32 @@ -40,3 +38,5 @@ func (a ArrayOfMutableString) Length() int32 { func (a ArrayOfMutableString) GetString(index int32) wasmlib.ScMutableString { return wasmlib.NewScMutableString(a.objID, wasmlib.Key32(index)) } + +type MutableStringArray = ArrayOfMutableString diff --git a/contracts/wasm/testwasmlib/src/lib.rs b/contracts/wasm/testwasmlib/src/lib.rs index 99848c3a3d..e24d7b3394 100644 --- a/contracts/wasm/testwasmlib/src/lib.rs +++ b/contracts/wasm/testwasmlib/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use testwasmlib::*; diff --git a/contracts/wasm/testwasmlib/src/testwasmlib.rs b/contracts/wasm/testwasmlib/src/testwasmlib.rs index 36ce1ff15c..3c40d4a018 100644 --- a/contracts/wasm/testwasmlib/src/testwasmlib.rs +++ b/contracts/wasm/testwasmlib/src/testwasmlib.rs @@ -2,10 +2,29 @@ // SPDX-License-Identifier: Apache-2.0 use wasmlib::*; -use wasmlib::corecontracts::*; use crate::*; +pub fn func_array_clear(_ctx: &ScFuncContext, f: &ArrayClearContext) { + let name = f.params.name().value(); + let array = f.state.arrays().get_string_array(&name); + array.clear(); +} + +pub fn func_array_create(_ctx: &ScFuncContext, f: &ArrayCreateContext) { + let name = f.params.name().value(); + let array = f.state.arrays().get_string_array(&name); + array.clear(); +} + +pub fn func_array_set(_ctx: &ScFuncContext, f: &ArraySetContext) { + let name = f.params.name().value(); + let array = f.state.arrays().get_string_array(&name); + let index = f.params.index().value(); + let value = f.params.value().value(); + array.get_string(index).set_value(&value); +} + pub fn func_param_types(ctx: &ScFuncContext, f: &ParamTypesContext) { if f.params.address().exists() { ctx.require(f.params.address().value() == ctx.account_id().address(), "mismatch: Address"); @@ -49,6 +68,21 @@ pub fn func_param_types(ctx: &ScFuncContext, f: &ParamTypesContext) { } } +pub fn view_array_length(_ctx: &ScViewContext, f: &ArrayLengthContext) { + let name = f.params.name().value(); + let array = f.state.arrays().get_string_array(&name); + let length = array.length(); + f.results.length().set_value(length); +} + +pub fn view_array_value(_ctx: &ScViewContext, f: &ArrayValueContext) { + let name = f.params.name().value(); + let array = f.state.arrays().get_string_array(&name); + let index = f.params.index().value(); + let value = array.get_string(index).value(); + f.results.value().set_value(&value); +} + pub fn view_block_record(ctx: &ScViewContext, f: &BlockRecordContext) { let records = coreblocklog::ScFuncs::get_request_receipts_for_block(ctx); records.params.block_index().set_value(f.params.block_index().value()); @@ -65,41 +99,6 @@ pub fn view_block_records(ctx: &ScViewContext, f: &BlockRecordsContext) { f.results.count().set_value(records.results.request_record().length()); } -pub fn func_array_clear(_ctx: &ScFuncContext, f: &ArrayClearContext) { - let name = f.params.name().value(); - let array = f.state.arrays().get_string_array(&name); - array.clear(); -} - -pub fn func_array_create(_ctx: &ScFuncContext, f: &ArrayCreateContext) { - let name = f.params.name().value(); - let array = f.state.arrays().get_string_array(&name); - array.clear(); -} - -pub fn func_array_set(_ctx: &ScFuncContext, f: &ArraySetContext) { - let name = f.params.name().value(); - let array = f.state.arrays().get_string_array(&name); - let index = f.params.index().value(); - let value = f.params.value().value(); - array.get_string(index).set_value(&value); -} - -pub fn view_array_length(_ctx: &ScViewContext, f: &ArrayLengthContext) { - let name = f.params.name().value(); - let array = f.state.arrays().get_string_array(&name); - let length = array.length(); - f.results.length().set_value(length); -} - -pub fn view_array_value(_ctx: &ScViewContext, f: &ArrayValueContext) { - let name = f.params.name().value(); - let array = f.state.arrays().get_string_array(&name); - let index = f.params.index().value(); - let value = array.get_string(index).value(); - f.results.value().set_value(&value); -} - pub fn view_iota_balance(ctx: &ScViewContext, f: &IotaBalanceContext) { f.results.iotas().set_value(ctx.balances().balance(&ScColor::IOTA)); } diff --git a/contracts/wasm/testwasmlib/src/typedefs.rs b/contracts/wasm/testwasmlib/src/typedefs.rs index b4a43d0203..aa1dff8c02 100644 --- a/contracts/wasm/testwasmlib/src/typedefs.rs +++ b/contracts/wasm/testwasmlib/src/typedefs.rs @@ -12,8 +12,6 @@ use wasmlib::*; use wasmlib::host::*; -pub type ImmutableStringArray = ArrayOfImmutableString; - pub struct ArrayOfImmutableString { pub(crate) obj_id: i32, } @@ -28,7 +26,7 @@ impl ArrayOfImmutableString { } } -pub type MutableStringArray = ArrayOfMutableString; +pub type ImmutableStringArray = ArrayOfImmutableString; pub struct ArrayOfMutableString { pub(crate) obj_id: i32, @@ -48,4 +46,6 @@ impl ArrayOfMutableString { } } +pub type MutableStringArray = ArrayOfMutableString; + // @formatter:on diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm b/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm index fa48f973e7..a7eb32fe2b 100644 Binary files a/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm and b/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm differ diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index 2b6e806c64..4082f81e8f 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -5,9 +5,9 @@ import ( "strings" "testing" - "github.com/iotaledger/wasp/contracts/wasm/testwasmlib" + "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" "github.com/iotaledger/wasp/packages/solo" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts new file mode 100644 index 0000000000..0919d7d32f --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts @@ -0,0 +1,58 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "testwasmlib"; +export const ScDescription = "Exercise all aspects of WasmLib"; +export const HScName = new wasmlib.ScHname(0x89703a45); + +export const ParamAddress = "address"; +export const ParamAgentID = "agentID"; +export const ParamBlockIndex = "blockIndex"; +export const ParamBytes = "bytes"; +export const ParamChainID = "chainID"; +export const ParamColor = "color"; +export const ParamHash = "hash"; +export const ParamHname = "hname"; +export const ParamIndex = "index"; +export const ParamInt16 = "int16"; +export const ParamInt32 = "int32"; +export const ParamInt64 = "int64"; +export const ParamName = "name"; +export const ParamRecordIndex = "recordIndex"; +export const ParamRequestID = "requestID"; +export const ParamString = "string"; +export const ParamValue = "value"; + +export const ResultCount = "count"; +export const ResultIotas = "iotas"; +export const ResultLength = "length"; +export const ResultRecord = "record"; +export const ResultValue = "value"; + +export const StateArrays = "arrays"; + +export const FuncArrayClear = "arrayClear"; +export const FuncArrayCreate = "arrayCreate"; +export const FuncArraySet = "arraySet"; +export const FuncParamTypes = "paramTypes"; +export const ViewArrayLength = "arrayLength"; +export const ViewArrayValue = "arrayValue"; +export const ViewBlockRecord = "blockRecord"; +export const ViewBlockRecords = "blockRecords"; +export const ViewIotaBalance = "iotaBalance"; + +export const HFuncArrayClear = new wasmlib.ScHname(0x88021821); +export const HFuncArrayCreate = new wasmlib.ScHname(0x1ed5b23b); +export const HFuncArraySet = new wasmlib.ScHname(0x2c4150b3); +export const HFuncParamTypes = new wasmlib.ScHname(0x6921c4cd); +export const HViewArrayLength = new wasmlib.ScHname(0x3a831021); +export const HViewArrayValue = new wasmlib.ScHname(0x662dbd81); +export const HViewBlockRecord = new wasmlib.ScHname(0xad13b2f8); +export const HViewBlockRecords = new wasmlib.ScHname(0x16e249ea); +export const HViewIotaBalance = new wasmlib.ScHname(0x9d3920bd); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts new file mode 100644 index 0000000000..1cf201fa0a --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts @@ -0,0 +1,164 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayClearCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncArrayClear); + params: sc.MutableArrayClearParams = new sc.MutableArrayClearParams(); +} + +export class ArrayClearContext { + params: sc.ImmutableArrayClearParams = new sc.ImmutableArrayClearParams(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + +export class ArrayCreateCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncArrayCreate); + params: sc.MutableArrayCreateParams = new sc.MutableArrayCreateParams(); +} + +export class ArrayCreateContext { + params: sc.ImmutableArrayCreateParams = new sc.ImmutableArrayCreateParams(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + +export class ArraySetCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncArraySet); + params: sc.MutableArraySetParams = new sc.MutableArraySetParams(); +} + +export class ArraySetContext { + params: sc.ImmutableArraySetParams = new sc.ImmutableArraySetParams(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + +export class ParamTypesCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncParamTypes); + params: sc.MutableParamTypesParams = new sc.MutableParamTypesParams(); +} + +export class ParamTypesContext { + params: sc.ImmutableParamTypesParams = new sc.ImmutableParamTypesParams(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + +export class ArrayLengthCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewArrayLength); + params: sc.MutableArrayLengthParams = new sc.MutableArrayLengthParams(); + results: sc.ImmutableArrayLengthResults = new sc.ImmutableArrayLengthResults(); +} + +export class ArrayLengthContext { + params: sc.ImmutableArrayLengthParams = new sc.ImmutableArrayLengthParams(); + results: sc.MutableArrayLengthResults = new sc.MutableArrayLengthResults(); + state: sc.ImmutableTestWasmLibState = new sc.ImmutableTestWasmLibState(); +} + +export class ArrayValueCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewArrayValue); + params: sc.MutableArrayValueParams = new sc.MutableArrayValueParams(); + results: sc.ImmutableArrayValueResults = new sc.ImmutableArrayValueResults(); +} + +export class ArrayValueContext { + params: sc.ImmutableArrayValueParams = new sc.ImmutableArrayValueParams(); + results: sc.MutableArrayValueResults = new sc.MutableArrayValueResults(); + state: sc.ImmutableTestWasmLibState = new sc.ImmutableTestWasmLibState(); +} + +export class BlockRecordCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewBlockRecord); + params: sc.MutableBlockRecordParams = new sc.MutableBlockRecordParams(); + results: sc.ImmutableBlockRecordResults = new sc.ImmutableBlockRecordResults(); +} + +export class BlockRecordContext { + params: sc.ImmutableBlockRecordParams = new sc.ImmutableBlockRecordParams(); + results: sc.MutableBlockRecordResults = new sc.MutableBlockRecordResults(); + state: sc.ImmutableTestWasmLibState = new sc.ImmutableTestWasmLibState(); +} + +export class BlockRecordsCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewBlockRecords); + params: sc.MutableBlockRecordsParams = new sc.MutableBlockRecordsParams(); + results: sc.ImmutableBlockRecordsResults = new sc.ImmutableBlockRecordsResults(); +} + +export class BlockRecordsContext { + params: sc.ImmutableBlockRecordsParams = new sc.ImmutableBlockRecordsParams(); + results: sc.MutableBlockRecordsResults = new sc.MutableBlockRecordsResults(); + state: sc.ImmutableTestWasmLibState = new sc.ImmutableTestWasmLibState(); +} + +export class IotaBalanceCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewIotaBalance); + results: sc.ImmutableIotaBalanceResults = new sc.ImmutableIotaBalanceResults(); +} + +export class IotaBalanceContext { + results: sc.MutableIotaBalanceResults = new sc.MutableIotaBalanceResults(); + state: sc.ImmutableTestWasmLibState = new sc.ImmutableTestWasmLibState(); +} + +export class ScFuncs { + + static arrayClear(ctx: wasmlib.ScFuncCallContext): ArrayClearCall { + let f = new ArrayClearCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static arrayCreate(ctx: wasmlib.ScFuncCallContext): ArrayCreateCall { + let f = new ArrayCreateCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static arraySet(ctx: wasmlib.ScFuncCallContext): ArraySetCall { + let f = new ArraySetCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static paramTypes(ctx: wasmlib.ScFuncCallContext): ParamTypesCall { + let f = new ParamTypesCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static arrayLength(ctx: wasmlib.ScViewCallContext): ArrayLengthCall { + let f = new ArrayLengthCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static arrayValue(ctx: wasmlib.ScViewCallContext): ArrayValueCall { + let f = new ArrayValueCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static blockRecord(ctx: wasmlib.ScViewCallContext): BlockRecordCall { + let f = new BlockRecordCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static blockRecords(ctx: wasmlib.ScViewCallContext): BlockRecordsCall { + let f = new BlockRecordsCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static iotaBalance(ctx: wasmlib.ScViewCallContext): IotaBalanceCall { + let f = new IotaBalanceCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/index.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/index.ts new file mode 100644 index 0000000000..528a39b4da --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/index.ts @@ -0,0 +1,17 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./testwasmlib"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./results"; +export * from "./state"; +export * from "./typedefs"; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/keys.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/keys.ts new file mode 100644 index 0000000000..d530270667 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/keys.ts @@ -0,0 +1,61 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamAddress = 0; +export const IdxParamAgentID = 1; +export const IdxParamBlockIndex = 2; +export const IdxParamBytes = 3; +export const IdxParamChainID = 4; +export const IdxParamColor = 5; +export const IdxParamHash = 6; +export const IdxParamHname = 7; +export const IdxParamIndex = 8; +export const IdxParamInt16 = 9; +export const IdxParamInt32 = 10; +export const IdxParamInt64 = 11; +export const IdxParamName = 12; +export const IdxParamRecordIndex = 13; +export const IdxParamRequestID = 14; +export const IdxParamString = 15; +export const IdxParamValue = 16; +export const IdxResultCount = 17; +export const IdxResultIotas = 18; +export const IdxResultLength = 19; +export const IdxResultRecord = 20; +export const IdxResultValue = 21; +export const IdxStateArrays = 22; + +export let keyMap: string[] = [ + sc.ParamAddress, + sc.ParamAgentID, + sc.ParamBlockIndex, + sc.ParamBytes, + sc.ParamChainID, + sc.ParamColor, + sc.ParamHash, + sc.ParamHname, + sc.ParamIndex, + sc.ParamInt16, + sc.ParamInt32, + sc.ParamInt64, + sc.ParamName, + sc.ParamRecordIndex, + sc.ParamRequestID, + sc.ParamString, + sc.ParamValue, + sc.ResultCount, + sc.ResultIotas, + sc.ResultLength, + sc.ResultRecord, + sc.ResultValue, + sc.StateArrays, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts new file mode 100644 index 0000000000..64720fe3b6 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts @@ -0,0 +1,126 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncArrayClear, funcArrayClearThunk); + exports.addFunc(sc.FuncArrayCreate, funcArrayCreateThunk); + exports.addFunc(sc.FuncArraySet, funcArraySetThunk); + exports.addFunc(sc.FuncParamTypes, funcParamTypesThunk); + exports.addView(sc.ViewArrayLength, viewArrayLengthThunk); + exports.addView(sc.ViewArrayValue, viewArrayValueThunk); + exports.addView(sc.ViewBlockRecord, viewBlockRecordThunk); + exports.addView(sc.ViewBlockRecords, viewBlockRecordsThunk); + exports.addView(sc.ViewIotaBalance, viewIotaBalanceThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcArrayClearThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcArrayClear"); + let f = new sc.ArrayClearContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.name().exists(), "missing mandatory name") + sc.funcArrayClear(ctx, f); + ctx.log("testwasmlib.funcArrayClear ok"); +} + +function funcArrayCreateThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcArrayCreate"); + let f = new sc.ArrayCreateContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.name().exists(), "missing mandatory name") + sc.funcArrayCreate(ctx, f); + ctx.log("testwasmlib.funcArrayCreate ok"); +} + +function funcArraySetThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcArraySet"); + let f = new sc.ArraySetContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.index().exists(), "missing mandatory index") + ctx.require(f.params.name().exists(), "missing mandatory name") + ctx.require(f.params.value().exists(), "missing mandatory value") + sc.funcArraySet(ctx, f); + ctx.log("testwasmlib.funcArraySet ok"); +} + +function funcParamTypesThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcParamTypes"); + let f = new sc.ParamTypesContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcParamTypes(ctx, f); + ctx.log("testwasmlib.funcParamTypes ok"); +} + +function viewArrayLengthThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testwasmlib.viewArrayLength"); + let f = new sc.ArrayLengthContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.name().exists(), "missing mandatory name") + sc.viewArrayLength(ctx, f); + ctx.log("testwasmlib.viewArrayLength ok"); +} + +function viewArrayValueThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testwasmlib.viewArrayValue"); + let f = new sc.ArrayValueContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.index().exists(), "missing mandatory index") + ctx.require(f.params.name().exists(), "missing mandatory name") + sc.viewArrayValue(ctx, f); + ctx.log("testwasmlib.viewArrayValue ok"); +} + +function viewBlockRecordThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testwasmlib.viewBlockRecord"); + let f = new sc.BlockRecordContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.blockIndex().exists(), "missing mandatory blockIndex") + ctx.require(f.params.recordIndex().exists(), "missing mandatory recordIndex") + sc.viewBlockRecord(ctx, f); + ctx.log("testwasmlib.viewBlockRecord ok"); +} + +function viewBlockRecordsThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testwasmlib.viewBlockRecords"); + let f = new sc.BlockRecordsContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.blockIndex().exists(), "missing mandatory blockIndex") + sc.viewBlockRecords(ctx, f); + ctx.log("testwasmlib.viewBlockRecords ok"); +} + +function viewIotaBalanceThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testwasmlib.viewIotaBalance"); + let f = new sc.IotaBalanceContext(); + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.viewIotaBalance(ctx, f); + ctx.log("testwasmlib.viewIotaBalance ok"); +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts new file mode 100644 index 0000000000..f70524c36c --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts @@ -0,0 +1,277 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableArrayClearParams extends wasmlib.ScMapID { + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class MutableArrayClearParams extends wasmlib.ScMapID { + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class ImmutableArrayCreateParams extends wasmlib.ScMapID { + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class MutableArrayCreateParams extends wasmlib.ScMapID { + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class ImmutableArraySetParams extends wasmlib.ScMapID { + + index(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamIndex]); + } + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } + + value(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamValue]); + } +} + +export class MutableArraySetParams extends wasmlib.ScMapID { + + index(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamIndex]); + } + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } + + value(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamValue]); + } +} + +export class MapStringToImmutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getBytes(key: string): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class ImmutableParamTypesParams extends wasmlib.ScMapID { + + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + bytes(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, sc.idxMap[sc.IdxParamBytes]); + } + + chainID(): wasmlib.ScImmutableChainID { + return new wasmlib.ScImmutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } + + hash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamHash]); + } + + hname(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, sc.idxMap[sc.IdxParamHname]); + } + + int16(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, sc.idxMap[sc.IdxParamInt16]); + } + + int32(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamInt32]); + } + + int64(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64]); + } + + param(): sc.MapStringToImmutableBytes { + return new sc.MapStringToImmutableBytes(this.mapID); + } + + requestID(): wasmlib.ScImmutableRequestID { + return new wasmlib.ScImmutableRequestID(this.mapID, sc.idxMap[sc.IdxParamRequestID]); + } + + string(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamString]); + } +} + +export class MapStringToMutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getBytes(key: string): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class MutableParamTypesParams extends wasmlib.ScMapID { + + address(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamAgentID]); + } + + bytes(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, sc.idxMap[sc.IdxParamBytes]); + } + + chainID(): wasmlib.ScMutableChainID { + return new wasmlib.ScMutableChainID(this.mapID, sc.idxMap[sc.IdxParamChainID]); + } + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } + + hash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamHash]); + } + + hname(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, sc.idxMap[sc.IdxParamHname]); + } + + int16(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, sc.idxMap[sc.IdxParamInt16]); + } + + int32(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamInt32]); + } + + int64(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamInt64]); + } + + param(): sc.MapStringToMutableBytes { + return new sc.MapStringToMutableBytes(this.mapID); + } + + requestID(): wasmlib.ScMutableRequestID { + return new wasmlib.ScMutableRequestID(this.mapID, sc.idxMap[sc.IdxParamRequestID]); + } + + string(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamString]); + } +} + +export class ImmutableArrayLengthParams extends wasmlib.ScMapID { + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class MutableArrayLengthParams extends wasmlib.ScMapID { + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class ImmutableArrayValueParams extends wasmlib.ScMapID { + + index(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamIndex]); + } + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class MutableArrayValueParams extends wasmlib.ScMapID { + + index(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamIndex]); + } + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class ImmutableBlockRecordParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamBlockIndex]); + } + + recordIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamRecordIndex]); + } +} + +export class MutableBlockRecordParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamBlockIndex]); + } + + recordIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamRecordIndex]); + } +} + +export class ImmutableBlockRecordsParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamBlockIndex]); + } +} + +export class MutableBlockRecordsParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamBlockIndex]); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/results.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/results.ts new file mode 100644 index 0000000000..a4427f0b3b --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/results.ts @@ -0,0 +1,79 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableArrayLengthResults extends wasmlib.ScMapID { + + length(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxResultLength]); + } +} + +export class MutableArrayLengthResults extends wasmlib.ScMapID { + + length(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxResultLength]); + } +} + +export class ImmutableArrayValueResults extends wasmlib.ScMapID { + + value(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultValue]); + } +} + +export class MutableArrayValueResults extends wasmlib.ScMapID { + + value(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultValue]); + } +} + +export class ImmutableBlockRecordResults extends wasmlib.ScMapID { + + record(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, sc.idxMap[sc.IdxResultRecord]); + } +} + +export class MutableBlockRecordResults extends wasmlib.ScMapID { + + record(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, sc.idxMap[sc.IdxResultRecord]); + } +} + +export class ImmutableBlockRecordsResults extends wasmlib.ScMapID { + + count(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxResultCount]); + } +} + +export class MutableBlockRecordsResults extends wasmlib.ScMapID { + + count(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxResultCount]); + } +} + +export class ImmutableIotaBalanceResults extends wasmlib.ScMapID { + + iotas(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultIotas]); + } +} + +export class MutableIotaBalanceResults extends wasmlib.ScMapID { + + iotas(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultIotas]); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts new file mode 100644 index 0000000000..5f63655ae7 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts @@ -0,0 +1,55 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class MapStringToImmutableStringArray { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getStringArray(key: string): sc.ImmutableStringArray { + let subID = wasmlib.getObjectID(this.objID, wasmlib.Key32.fromString(key).getKeyID(), wasmlib.TYPE_ARRAY|wasmlib.TYPE_STRING); + return new sc.ImmutableStringArray(subID); + } +} + +export class ImmutableTestWasmLibState extends wasmlib.ScMapID { + + arrays(): sc.MapStringToImmutableStringArray { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateArrays], wasmlib.TYPE_MAP); + return new sc.MapStringToImmutableStringArray(mapID); + } +} + +export class MapStringToMutableStringArray { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getStringArray(key: string): sc.MutableStringArray { + let subID = wasmlib.getObjectID(this.objID, wasmlib.Key32.fromString(key).getKeyID(), wasmlib.TYPE_ARRAY|wasmlib.TYPE_STRING); + return new sc.MutableStringArray(subID); + } +} + +export class MutableTestWasmLibState extends wasmlib.ScMapID { + + arrays(): sc.MapStringToMutableStringArray { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateArrays], wasmlib.TYPE_MAP); + return new sc.MapStringToMutableStringArray(mapID); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts new file mode 100644 index 0000000000..79592fd43b --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts @@ -0,0 +1,104 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "wasmlib" +import * as coreblocklog from "wasmlib/coreblocklog" +import * as sc from "./index"; + +export function funcArrayClear(ctx: wasmlib.ScFuncContext, f: sc.ArrayClearContext): void { + let name = f.params.name().value(); + let array = f.state.arrays().getStringArray(name); + array.clear(); +} + +export function funcArrayCreate(ctx: wasmlib.ScFuncContext, f: sc.ArrayCreateContext): void { + let name = f.params.name().value(); + let array = f.state.arrays().getStringArray(name); + array.clear(); +} + +export function funcArraySet(ctx: wasmlib.ScFuncContext, f: sc.ArraySetContext): void { + let name = f.params.name().value(); + let array = f.state.arrays().getStringArray(name); + let index = f.params.index().value(); + let value = f.params.value().value(); + array.getString(index).setValue(value); +} + +export function funcParamTypes(ctx: wasmlib.ScFuncContext, f: sc.ParamTypesContext): void { + if (f.params.address().exists()) { + ctx.require(f.params.address().value().equals(ctx.accountID().address()), "mismatch: Address"); + } + if (f.params.agentID().exists()) { + ctx.require(f.params.agentID().value().equals(ctx.accountID()), "mismatch: AgentID"); + } + if (f.params.bytes().exists()) { + let byteData = wasmlib.Convert.fromString("these are bytes"); + ctx.require(wasmlib.Convert.equals(f.params.bytes().value(), byteData), "mismatch: Bytes"); + } + if (f.params.chainID().exists()) { + ctx.require(f.params.chainID().value().equals(ctx.chainID()), "mismatch: ChainID"); + } + if (f.params.color().exists()) { + let color = wasmlib.ScColor.fromBytes(wasmlib.Convert.fromString("RedGreenBlueYellowCyanBlackWhite")); + ctx.require(f.params.color().value().equals(color), "mismatch: Color"); + } + if (f.params.hash().exists()) { + let hash = wasmlib.ScHash.fromBytes(wasmlib.Convert.fromString("0123456789abcdeffedcba9876543210")); + ctx.require(f.params.hash().value().equals(hash), "mismatch: Hash"); + } + if (f.params.hname().exists()) { + ctx.require(f.params.hname().value().equals(ctx.accountID().hname()), "mismatch: Hname"); + } + if (f.params.int16().exists()) { + ctx.require(f.params.int16().value() == 12345, "mismatch: Int16"); + } + if (f.params.int32().exists()) { + ctx.require(f.params.int32().value() == 1234567890, "mismatch: Int32"); + } + if (f.params.int64().exists()) { + ctx.require(f.params.int64().value() == 1234567890123456789, "mismatch: Int64"); + } + if (f.params.requestID().exists()) { + let requestId = wasmlib.ScRequestID.fromBytes(wasmlib.Convert.fromString("abcdefghijklmnopqrstuvwxyz123456\x00\x00")); + ctx.require(f.params.requestID().value().equals(requestId), "mismatch: RequestID"); + } + if (f.params.string().exists()) { + ctx.require(f.params.string().value() == "this is a string", "mismatch: String"); + } +} + +export function viewArrayLength(ctx: wasmlib.ScViewContext, f: sc.ArrayLengthContext): void { + let name = f.params.name().value(); + let array = f.state.arrays().getStringArray(name); + let length = array.length(); + f.results.length().setValue(length); +} + +export function viewArrayValue(ctx: wasmlib.ScViewContext, f: sc.ArrayValueContext): void { + let name = f.params.name().value(); + let array = f.state.arrays().getStringArray(name); + let index = f.params.index().value(); + let value = array.getString(index).value(); + f.results.value().setValue(value); +} + +export function viewBlockRecord(ctx: wasmlib.ScViewContext, f: sc.BlockRecordContext): void { + let records = coreblocklog.ScFuncs.getRequestReceiptsForBlock(ctx); + records.params.blockIndex().setValue(f.params.blockIndex().value()); + records.func.call(); + let recordIndex = f.params.recordIndex().value(); + ctx.require(recordIndex < records.results.requestRecord().length(), "invalid recordIndex"); + f.results.record().setValue(records.results.requestRecord().getBytes(recordIndex).value()); +} + +export function viewBlockRecords(ctx: wasmlib.ScViewContext, f: sc.BlockRecordsContext): void { + let records = coreblocklog.ScFuncs.getRequestReceiptsForBlock(ctx); + records.params.blockIndex().setValue(f.params.blockIndex().value()); + records.func.call(); + f.results.count().setValue(records.results.requestRecord().length()); +} + +export function viewIotaBalance(ctx: wasmlib.ScViewContext, f: sc.IotaBalanceContext): void { + f.results.iotas().setValue(ctx.balances().balance(wasmlib.ScColor.IOTA)); +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/tsconfig.json b/contracts/wasm/testwasmlib/ts/testwasmlib/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/typedefs.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/typedefs.ts new file mode 100644 index 0000000000..872ea82c21 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/typedefs.ts @@ -0,0 +1,51 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getString(index: i32): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableStringArray extends ArrayOfImmutableString { +}; + +export class ArrayOfMutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getString(index: i32): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableStringArray extends ArrayOfMutableString { +}; diff --git a/contracts/wasm/tokenregistry/Cargo.toml b/contracts/wasm/tokenregistry/Cargo.toml index 027f6ba2d6..77d8171015 100644 --- a/contracts/wasm/tokenregistry/Cargo.toml +++ b/contracts/wasm/tokenregistry/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../wasmlib" } +wasmlib = { path = "../../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/contracts/wasm/tokenregistry/wasmmain/main.go b/contracts/wasm/tokenregistry/go/main.go similarity index 79% rename from contracts/wasm/tokenregistry/wasmmain/main.go rename to contracts/wasm/tokenregistry/go/main.go index 6aef8c0d38..e00719edd8 100644 --- a/contracts/wasm/tokenregistry/wasmmain/main.go +++ b/contracts/wasm/tokenregistry/go/main.go @@ -10,13 +10,14 @@ package main import "github.com/iotaledger/wasp/packages/vm/wasmclient" -import "github.com/iotaledger/wasp/contracts/wasm/tokenregistry" + +import "github.com/iotaledger/wasp/contracts/wasm/tokenregistry/go/tokenregistry" func main() { } //export on_load -func OnLoad() { +func onLoad() { h := &wasmclient.WasmVMHost{} h.ConnectWasmHost() tokenregistry.OnLoad() diff --git a/contracts/wasm/tokenregistry/consts.go b/contracts/wasm/tokenregistry/go/tokenregistry/consts.go similarity index 93% rename from contracts/wasm/tokenregistry/consts.go rename to contracts/wasm/tokenregistry/go/tokenregistry/consts.go index c38719b8b5..c4983f23a6 100644 --- a/contracts/wasm/tokenregistry/consts.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/consts.go @@ -7,7 +7,7 @@ package tokenregistry -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "tokenregistry" diff --git a/contracts/wasm/tokenregistry/contract.go b/contracts/wasm/tokenregistry/go/tokenregistry/contract.go similarity index 95% rename from contracts/wasm/tokenregistry/contract.go rename to contracts/wasm/tokenregistry/go/tokenregistry/contract.go index 7e4f708368..c254b5d29d 100644 --- a/contracts/wasm/tokenregistry/contract.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/contract.go @@ -7,7 +7,7 @@ package tokenregistry -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type MintSupplyCall struct { Func *wasmlib.ScFunc diff --git a/contracts/wasm/tokenregistry/keys.go b/contracts/wasm/tokenregistry/go/tokenregistry/keys.go similarity index 88% rename from contracts/wasm/tokenregistry/keys.go rename to contracts/wasm/tokenregistry/go/tokenregistry/keys.go index 3fee8a368c..fc2b5ee642 100644 --- a/contracts/wasm/tokenregistry/keys.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/keys.go @@ -7,7 +7,7 @@ package tokenregistry -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( IdxParamColor = 0 diff --git a/contracts/wasm/tokenregistry/lib.go b/contracts/wasm/tokenregistry/go/tokenregistry/lib.go similarity index 97% rename from contracts/wasm/tokenregistry/lib.go rename to contracts/wasm/tokenregistry/go/tokenregistry/lib.go index 610ec808fa..a8a1e20fd8 100644 --- a/contracts/wasm/tokenregistry/lib.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/lib.go @@ -7,7 +7,7 @@ package tokenregistry -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" func OnLoad() { exports := wasmlib.NewScExports() diff --git a/contracts/wasm/tokenregistry/params.go b/contracts/wasm/tokenregistry/go/tokenregistry/params.go similarity index 96% rename from contracts/wasm/tokenregistry/params.go rename to contracts/wasm/tokenregistry/go/tokenregistry/params.go index 11a56bfad0..844d234539 100644 --- a/contracts/wasm/tokenregistry/params.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/params.go @@ -7,7 +7,7 @@ package tokenregistry -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableMintSupplyParams struct { id int32 diff --git a/contracts/wasm/tokenregistry/state.go b/contracts/wasm/tokenregistry/go/tokenregistry/state.go similarity index 97% rename from contracts/wasm/tokenregistry/state.go rename to contracts/wasm/tokenregistry/go/tokenregistry/state.go index 6dc2b4fab2..8b7ca0c52a 100644 --- a/contracts/wasm/tokenregistry/state.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/state.go @@ -7,7 +7,7 @@ package tokenregistry -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayOfImmutableColor struct { objID int32 diff --git a/contracts/wasm/tokenregistry/types.go b/contracts/wasm/tokenregistry/go/tokenregistry/structs.go similarity index 96% rename from contracts/wasm/tokenregistry/types.go rename to contracts/wasm/tokenregistry/go/tokenregistry/structs.go index 46621962e0..71c0207191 100644 --- a/contracts/wasm/tokenregistry/types.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/structs.go @@ -7,7 +7,7 @@ package tokenregistry -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type Token struct { Created int64 // creation timestamp diff --git a/contracts/wasm/tokenregistry/tokenregistry.go b/contracts/wasm/tokenregistry/go/tokenregistry/tokenregistry.go similarity index 95% rename from contracts/wasm/tokenregistry/tokenregistry.go rename to contracts/wasm/tokenregistry/go/tokenregistry/tokenregistry.go index de56ad7194..cc6d7d48dc 100644 --- a/contracts/wasm/tokenregistry/tokenregistry.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/tokenregistry.go @@ -4,7 +4,7 @@ package tokenregistry import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) func funcMintSupply(ctx wasmlib.ScFuncContext, f *MintSupplyContext) { diff --git a/contracts/wasm/tokenregistry/src/contract.rs b/contracts/wasm/tokenregistry/src/contract.rs index 4f08f67004..e6fb2016c3 100644 --- a/contracts/wasm/tokenregistry/src/contract.rs +++ b/contracts/wasm/tokenregistry/src/contract.rs @@ -15,7 +15,6 @@ use wasmlib::*; use crate::consts::*; use crate::params::*; -use crate::results::*; pub struct MintSupplyCall { pub func: ScFunc, diff --git a/contracts/wasm/tokenregistry/src/lib.rs b/contracts/wasm/tokenregistry/src/lib.rs index 5a9d66b1a1..64d86e35f5 100644 --- a/contracts/wasm/tokenregistry/src/lib.rs +++ b/contracts/wasm/tokenregistry/src/lib.rs @@ -8,7 +8,6 @@ // @formatter:off #![allow(dead_code)] - #![allow(unused_imports)] use tokenregistry::*; @@ -18,16 +17,14 @@ use wasmlib::host::*; use crate::consts::*; use crate::keys::*; use crate::params::*; -use crate::results::*; use crate::state::*; mod consts; mod contract; mod keys; mod params; -mod results; mod state; -mod types; +mod structs; mod tokenregistry; #[no_mangle] diff --git a/contracts/wasm/tokenregistry/src/state.rs b/contracts/wasm/tokenregistry/src/state.rs index b9ad8617aa..d879c8403d 100644 --- a/contracts/wasm/tokenregistry/src/state.rs +++ b/contracts/wasm/tokenregistry/src/state.rs @@ -13,7 +13,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; -use crate::types::*; +use crate::structs::*; pub struct ArrayOfImmutableColor { pub(crate) obj_id: i32, diff --git a/contracts/wasm/tokenregistry/src/types.rs b/contracts/wasm/tokenregistry/src/structs.rs similarity index 100% rename from contracts/wasm/tokenregistry/src/types.rs rename to contracts/wasm/tokenregistry/src/structs.rs diff --git a/contracts/wasm/tokenregistry/src/tokenregistry.rs b/contracts/wasm/tokenregistry/src/tokenregistry.rs index 865b52b84e..52058a4c8d 100644 --- a/contracts/wasm/tokenregistry/src/tokenregistry.rs +++ b/contracts/wasm/tokenregistry/src/tokenregistry.rs @@ -4,7 +4,7 @@ use wasmlib::*; use crate::*; -use crate::types::*; +use crate::structs::*; pub fn func_mint_supply(ctx: &ScFuncContext, f: &MintSupplyContext) { let minted = ctx.minted(); diff --git a/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm b/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm index 7f02643504..31ceef70e0 100644 Binary files a/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm and b/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm differ diff --git a/contracts/wasm/tokenregistry/test/tokenregistry_test.go b/contracts/wasm/tokenregistry/test/tokenregistry_test.go index de30b9d307..2583c11e6d 100644 --- a/contracts/wasm/tokenregistry/test/tokenregistry_test.go +++ b/contracts/wasm/tokenregistry/test/tokenregistry_test.go @@ -6,7 +6,7 @@ package test import ( "testing" - "github.com/iotaledger/wasp/contracts/wasm/tokenregistry" + "github.com/iotaledger/wasp/contracts/wasm/tokenregistry/go/tokenregistry" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/consts.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/consts.ts new file mode 100644 index 0000000000..27aab17d0d --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/consts.ts @@ -0,0 +1,28 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "tokenregistry"; +export const HScName = new wasmlib.ScHname(0xe1ba0c78); + +export const ParamColor = "color"; +export const ParamDescription = "description"; +export const ParamUserDefined = "userDefined"; + +export const StateColorList = "colorList"; +export const StateRegistry = "registry"; + +export const FuncMintSupply = "mintSupply"; +export const FuncTransferOwnership = "transferOwnership"; +export const FuncUpdateMetadata = "updateMetadata"; +export const ViewGetInfo = "getInfo"; + +export const HFuncMintSupply = new wasmlib.ScHname(0x564349a7); +export const HFuncTransferOwnership = new wasmlib.ScHname(0xbb9eb5af); +export const HFuncUpdateMetadata = new wasmlib.ScHname(0xa26b23b6); +export const HViewGetInfo = new wasmlib.ScHname(0xcfedba5f); diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/contract.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/contract.ts new file mode 100644 index 0000000000..8570ad323a --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/contract.ts @@ -0,0 +1,76 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class MintSupplyCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncMintSupply); + params: sc.MutableMintSupplyParams = new sc.MutableMintSupplyParams(); +} + +export class MintSupplyContext { + params: sc.ImmutableMintSupplyParams = new sc.ImmutableMintSupplyParams(); + state: sc.MutableTokenRegistryState = new sc.MutableTokenRegistryState(); +} + +export class TransferOwnershipCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTransferOwnership); + params: sc.MutableTransferOwnershipParams = new sc.MutableTransferOwnershipParams(); +} + +export class TransferOwnershipContext { + params: sc.ImmutableTransferOwnershipParams = new sc.ImmutableTransferOwnershipParams(); + state: sc.MutableTokenRegistryState = new sc.MutableTokenRegistryState(); +} + +export class UpdateMetadataCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncUpdateMetadata); + params: sc.MutableUpdateMetadataParams = new sc.MutableUpdateMetadataParams(); +} + +export class UpdateMetadataContext { + params: sc.ImmutableUpdateMetadataParams = new sc.ImmutableUpdateMetadataParams(); + state: sc.MutableTokenRegistryState = new sc.MutableTokenRegistryState(); +} + +export class GetInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetInfo); + params: sc.MutableGetInfoParams = new sc.MutableGetInfoParams(); +} + +export class GetInfoContext { + params: sc.ImmutableGetInfoParams = new sc.ImmutableGetInfoParams(); + state: sc.ImmutableTokenRegistryState = new sc.ImmutableTokenRegistryState(); +} + +export class ScFuncs { + + static mintSupply(ctx: wasmlib.ScFuncCallContext): MintSupplyCall { + let f = new MintSupplyCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static transferOwnership(ctx: wasmlib.ScFuncCallContext): TransferOwnershipCall { + let f = new TransferOwnershipCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static updateMetadata(ctx: wasmlib.ScFuncCallContext): UpdateMetadataCall { + let f = new UpdateMetadataCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static getInfo(ctx: wasmlib.ScViewCallContext): GetInfoCall { + let f = new GetInfoCall(); + f.func.setPtrs(f.params, null); + return f; + } +} diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/index.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/index.ts new file mode 100644 index 0000000000..ff035c3e66 --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/index.ts @@ -0,0 +1,16 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./tokenregistry"; + +export * from "./consts"; +export * from "./contract"; +export * from "./keys"; +export * from "./lib"; +export * from "./params"; +export * from "./state"; +export * from "./structs"; diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/keys.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/keys.ts new file mode 100644 index 0000000000..d4325599ea --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/keys.ts @@ -0,0 +1,25 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export const IdxParamColor = 0; +export const IdxParamDescription = 1; +export const IdxParamUserDefined = 2; +export const IdxStateColorList = 3; +export const IdxStateRegistry = 4; + +export let keyMap: string[] = [ + sc.ParamColor, + sc.ParamDescription, + sc.ParamUserDefined, + sc.StateColorList, + sc.StateRegistry, +]; + +export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/lib.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/lib.ts new file mode 100644 index 0000000000..7678df9ec0 --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/lib.ts @@ -0,0 +1,70 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function on_call(index: i32): void { + return wasmlib.onCall(index); +} + +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncMintSupply, funcMintSupplyThunk); + exports.addFunc(sc.FuncTransferOwnership, funcTransferOwnershipThunk); + exports.addFunc(sc.FuncUpdateMetadata, funcUpdateMetadataThunk); + exports.addView(sc.ViewGetInfo, viewGetInfoThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} + +function funcMintSupplyThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("tokenregistry.funcMintSupply"); + let f = new sc.MintSupplyContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + sc.funcMintSupply(ctx, f); + ctx.log("tokenregistry.funcMintSupply ok"); +} + +function funcTransferOwnershipThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("tokenregistry.funcTransferOwnership"); + // TODO the one who can transfer token ownership + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.TransferOwnershipContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.color().exists(), "missing mandatory color") + sc.funcTransferOwnership(ctx, f); + ctx.log("tokenregistry.funcTransferOwnership ok"); +} + +function funcUpdateMetadataThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("tokenregistry.funcUpdateMetadata"); + // TODO the one who can change the token info + ctx.require(ctx.caller().equals(ctx.contractCreator()), "no permission"); + + let f = new sc.UpdateMetadataContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.color().exists(), "missing mandatory color") + sc.funcUpdateMetadata(ctx, f); + ctx.log("tokenregistry.funcUpdateMetadata ok"); +} + +function viewGetInfoThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("tokenregistry.viewGetInfo"); + let f = new sc.GetInfoContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.color().exists(), "missing mandatory color") + sc.viewGetInfo(ctx, f); + ctx.log("tokenregistry.viewGetInfo ok"); +} diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/params.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/params.ts new file mode 100644 index 0000000000..6c55fd2bfc --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/params.ts @@ -0,0 +1,73 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableMintSupplyParams extends wasmlib.ScMapID { + + description(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamDescription]); + } + + userDefined(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamUserDefined]); + } +} + +export class MutableMintSupplyParams extends wasmlib.ScMapID { + + description(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamDescription]); + } + + userDefined(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamUserDefined]); + } +} + +export class ImmutableTransferOwnershipParams extends wasmlib.ScMapID { + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class MutableTransferOwnershipParams extends wasmlib.ScMapID { + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class ImmutableUpdateMetadataParams extends wasmlib.ScMapID { + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class MutableUpdateMetadataParams extends wasmlib.ScMapID { + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class ImmutableGetInfoParams extends wasmlib.ScMapID { + + color(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} + +export class MutableGetInfoParams extends wasmlib.ScMapID { + + color(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + } +} diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/state.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/state.ts new file mode 100644 index 0000000000..e82e0fe03d --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/state.ts @@ -0,0 +1,99 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableColor { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getColor(index: i32): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.objID, new wasmlib.Key32(index)); + } +} + +export class MapColorToImmutableToken { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getToken(key: wasmlib.ScColor): sc.ImmutableToken { + return new sc.ImmutableToken(this.objID, key.getKeyID()); + } +} + +export class ImmutableTokenRegistryState extends wasmlib.ScMapID { + + colorList(): sc.ArrayOfImmutableColor { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateColorList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_COLOR); + return new sc.ArrayOfImmutableColor(arrID) + } + + registry(): sc.MapColorToImmutableToken { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateRegistry], wasmlib.TYPE_MAP); + return new sc.MapColorToImmutableToken(mapID); + } +} + +export class ArrayOfMutableColor { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getColor(index: i32): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.objID, new wasmlib.Key32(index)); + } +} + +export class MapColorToMutableToken { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getToken(key: wasmlib.ScColor): sc.MutableToken { + return new sc.MutableToken(this.objID, key.getKeyID()); + } +} + +export class MutableTokenRegistryState extends wasmlib.ScMapID { + + colorList(): sc.ArrayOfMutableColor { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateColorList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_COLOR); + return new sc.ArrayOfMutableColor(arrID) + } + + registry(): sc.MapColorToMutableToken { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateRegistry], wasmlib.TYPE_MAP); + return new sc.MapColorToMutableToken(mapID); + } +} diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/structs.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/structs.ts new file mode 100644 index 0000000000..8bdb6867c1 --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/structs.ts @@ -0,0 +1,84 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export class Token { + created : i64 = 0; // creation timestamp + description: string = ""; // description what minted token represents + mintedBy : wasmlib.ScAgentID = new wasmlib.ScAgentID(); // original minter + owner : wasmlib.ScAgentID = new wasmlib.ScAgentID(); // current owner + supply : i64 = 0; // amount of tokens originally minted + updated : i64 = 0; // last update timestamp + userDefined: string = ""; // any user defined text + + static fromBytes(bytes: u8[]): Token { + let decode = new wasmlib.BytesDecoder(bytes); + let data = new Token(); + data.created = decode.int64(); + data.description = decode.string(); + data.mintedBy = decode.agentID(); + data.owner = decode.agentID(); + data.supply = decode.int64(); + data.updated = decode.int64(); + data.userDefined = decode.string(); + decode.close(); + return data; + } + + bytes(): u8[] { + return new wasmlib.BytesEncoder(). + int64(this.created). + string(this.description). + agentID(this.mintedBy). + agentID(this.owner). + int64(this.supply). + int64(this.updated). + string(this.userDefined). + data(); + } +} + +export class ImmutableToken { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + value(): Token { + return Token.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} + +export class MutableToken { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + setValue(value: Token): void { + wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes()); + } + + value(): Token { + return Token.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/tokenregistry.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/tokenregistry.ts new file mode 100644 index 0000000000..ab1cb2a2c2 --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/tokenregistry.ts @@ -0,0 +1,43 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export function funcMintSupply(ctx: wasmlib.ScFuncContext, f: sc.MintSupplyContext): void { + let minted = ctx.minted(); + let mintedColors = minted.colors(); + ctx.require(mintedColors.length() == 1, "need single minted color"); + let mintedColor = mintedColors.getColor(0).value(); + let currentToken = f.state.registry().getToken(mintedColor); + if (currentToken.exists()) { + // should never happen, because transaction id is unique + ctx.panic("TokenRegistry: registry for color already exists"); + } + let token = new sc.Token(); + token.supply = minted.balance(mintedColor); + token.mintedBy = ctx.caller(); + token.owner = ctx.caller(); + token.created = ctx.timestamp(); + token.updated = ctx.timestamp(); + token.description = f.params.description().value(); + token.userDefined = f.params.userDefined().value(); + if (token.description == "") { + token.description = "no dscr"; + } + currentToken.setValue(token); + let colorList = f.state.colorList(); + colorList.getColor(colorList.length()).setValue(mintedColor); +} + +export function funcTransferOwnership(ctx: wasmlib.ScFuncContext, f: sc.TransferOwnershipContext): void { + // TODO +} + +export function funcUpdateMetadata(ctx: wasmlib.ScFuncContext, f: sc.UpdateMetadataContext): void { + // TODO +} + +export function viewGetInfo(ctx: wasmlib.ScViewContext, f: sc.GetInfoContext): void { + // TODO +} diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/tsconfig.json b/contracts/wasm/tokenregistry/ts/tokenregistry/tsconfig.json new file mode 100644 index 0000000000..6fb4265c72 --- /dev/null +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./*.ts"] +} diff --git a/contracts/wasm/ts_all.cmd b/contracts/wasm/ts_all.cmd new file mode 100644 index 0000000000..e145de88bb --- /dev/null +++ b/contracts/wasm/ts_all.cmd @@ -0,0 +1,2 @@ +@echo off +for /d %%f in (*.) do call ts_build.cmd %%f %1 diff --git a/contracts/wasm/ts_build.cmd b/contracts/wasm/ts_build.cmd new file mode 100644 index 0000000000..17634aa427 --- /dev/null +++ b/contracts/wasm/ts_build.cmd @@ -0,0 +1,9 @@ +@echo off +cd %1 +if not exist schema.yaml goto :xit +echo Building %1 +schema -ts %2 +call asc ts/%1/lib.ts --lib d:/work/node_modules --binaryFile ts/pkg/%1_ts.wasm +rem call asc ts/%1/lib.ts --lib d:/work/node_modules --binaryFile ts/pkg/%1_ts.wasm --textFile ts/pkg/%1_ts.wat +:xit +cd .. diff --git a/contracts/wasm/update_hardcoded.cmd b/contracts/wasm/update_hardcoded.cmd new file mode 100644 index 0000000000..e93c60ebf6 --- /dev/null +++ b/contracts/wasm/update_hardcoded.cmd @@ -0,0 +1,5 @@ +@echo off +for /d %%f in (*.) do if exist %%f\pkg\%%f*_bg.wasm copy /y %%f\pkg\%%f*_bg.wasm %%f\test\*.* +if exist testcore\pkg\testcore_bg.wasm copy /y testcore\pkg\testcore_bg.wasm ..\..\packages\vm\core\testcore\sbtests\sbtestsc\*.* +if exist inccounter\pkg\inccounter_bg.wasm copy /y inccounter\pkg\inccounter_bg.wasm ..\..\tools\cluster\tests\wasm\*.* + diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreroot/mod.rs b/contracts/wasm/wasmlib/src/corecontracts/coreroot/mod.rs deleted file mode 100644 index c6b0582479..0000000000 --- a/contracts/wasm/wasmlib/src/corecontracts/coreroot/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![allow(unused_imports)] - -pub use consts::*; -pub use contract::*; -use params::*; -use results::*; - -pub mod consts; -pub mod contract; -pub mod params; -pub mod results; - diff --git a/contracts/wasm/wasmlib/src/corecontracts/mod.rs b/contracts/wasm/wasmlib/src/corecontracts/mod.rs deleted file mode 100644 index e756767558..0000000000 --- a/contracts/wasm/wasmlib/src/corecontracts/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub use coreaccounts::*; -pub use coreblob::*; -pub use coreblocklog::*; -pub use coregovernance::*; -pub use coreroot::*; - -pub mod coreaccounts; -pub mod coreblob; -pub mod coreblocklog; -pub mod coregovernance; -pub mod coreroot; - diff --git a/documentation/docs/guide/chains_and_nodes/running-a-node.md b/documentation/docs/guide/chains_and_nodes/running-a-node.md index ff27c75f30..f8e8922df9 100644 --- a/documentation/docs/guide/chains_and_nodes/running-a-node.md +++ b/documentation/docs/guide/chains_and_nodes/running-a-node.md @@ -240,4 +240,4 @@ wasp --webapi.adminWhitelist=127.0.0.1,YOUR_IP ## Video Tutorial - \ No newline at end of file + diff --git a/documentation/docs/guide/chains_and_nodes/setting-up-a-chain.md b/documentation/docs/guide/chains_and_nodes/setting-up-a-chain.md index ef1648563f..cb0f07f2d6 100644 --- a/documentation/docs/guide/chains_and_nodes/setting-up-a-chain.md +++ b/documentation/docs/guide/chains_and_nodes/setting-up-a-chain.md @@ -113,6 +113,10 @@ The `--quorum` flag indicates the minimum amount of nodes required to form a con You can check that the chain was properly deployed in the Wasp node dashboard (e.g. `127.0.0.1:7000`). Note that the chain was deployed with some [core contracts](../core_concepts/core_contracts/overview.md). +## Video Tutorial + + + ### Deploying a Wasm Contract Now you can deploy a Wasm contract to the chain: @@ -174,6 +178,11 @@ Example response: counter: 1 ``` + +## Video Tutorial + + + ### Troubleshooting Common issues can be caused by using an incompatible version of `wasp` / `wasp-cli`. diff --git a/documentation/docs/guide/chains_and_nodes/testnet.md b/documentation/docs/guide/chains_and_nodes/testnet.md index 4a412f7838..9f38d6a557 100644 --- a/documentation/docs/guide/chains_and_nodes/testnet.md +++ b/documentation/docs/guide/chains_and_nodes/testnet.md @@ -51,6 +51,8 @@ The testnet can be accessed via a series of endpoints that have been made availa - Information about GoShimmer via the API - https://demo.sc.iota.org - Our FairRoulette demo application to see a live smart contract in action +- https://metrics.wasp.sc.iota.org + - System metrics ## Configuring _wasp-cli_ @@ -120,4 +122,4 @@ It should look similar to this image. :::note The other values (network name and currency symbol) can be whatever value you like. -::: \ No newline at end of file +::: diff --git a/documentation/docs/guide/schema/access.mdx b/documentation/docs/guide/schema/access.mdx index 2e5a823033..d1b70b2124 100644 --- a/documentation/docs/guide/schema/access.mdx +++ b/documentation/docs/guide/schema/access.mdx @@ -20,10 +20,10 @@ following: * `self`: Only the smart contract itself can call this function. * `chain`: Only the chain owner can call this function. * `creator`: Only the contract creator can call this function -* anything else: The name of an AgentID or []AgentID variable in state storage. Only the +* anything else: The name of an AgentID or AgentID[] variable in state storage. Only the agent(s) defined there can call this function. When this option is used you should also provide functionality that can initialize and/or modify this variable. As long as this - state variable has not been set, nobody is allowed to call this function. + state variable has not been set, nobody will be allowed to call this function. The schema tool will automatically generate code to properly check the access rights of the agent that called the function before the actual function is called. diff --git a/documentation/docs/guide/schema/call.mdx b/documentation/docs/guide/schema/call.mdx index 30fb4f7b4f..88edf44c71 100644 --- a/documentation/docs/guide/schema/call.mdx +++ b/documentation/docs/guide/schema/call.mdx @@ -16,11 +16,11 @@ import TabItem from "@theme/TabItem" Synchronous function calls between smart contracts act very similar to how normal function calls work in any programming language, but with a slight twist. With normal function -calls you share all the memory that you can access with every function that you call. -However, when calling a smart contract function you can only access the memory assigned to -that specific smart contract. Remember, each smart contract runs in its own sandbox -environment. Therefore, the only way to share data between smart contracts that call each -other is through function parameters and return values. +calls you share all the global memory that you can access with every function that you +call. However, when calling a smart contract function you can only access the memory +assigned to that specific smart contract. Remember, each smart contract runs in its own +sandbox environment. Therefore, the only way to share data between smart contracts that +call each other is through function parameters and return values. Synchronous calls can only be made between contracts that are running on the same contract chain. The ISCP host knows about all the contracts it is running on a chain, and therefore @@ -28,27 +28,29 @@ is able to dispatch the call to the correct contract function. The function desc used to specify the call parameters (if any) through its `params` proxy, and to invoke the function through its `func` interface. -In addition, when the function that is called is not a [View](views.mdx), it is possible to pass tokens -to the function call through this interface. Note that the only way to call a function and -properly pass tokens to it _within the same contract_ is through the function descriptor. -Otherwise, the incoming() function will not register any incoming tokens. +In addition, when the function that is called is not a [View](views.mdx), it is possible +to pass tokens to the function call through this interface. Note that the only way to call +a function and properly pass tokens to it _within the same chain_ is through the function +descriptor. Otherwise, the incoming() function will not register any incoming tokens. -When the call is made, the calling function will be paused and wait for the called function -to complete. After completion, it may access the returned values (if any) through +When the call is made, the calling function will be paused and wait for the called +function to complete. After completion, it may access the returned values (if any) through the [`results` proxy](results.mdx) of the function descriptor. -When calling a function from a View function, it is only possible to call other View functions. The -ScFuncs interface enforces this at compile-time through the ISCP function context that -needs to be passed to the member function that creates the function descriptor. +When calling a function from a View function, it is only possible to call other View +functions. The ScFuncs interface enforces this at compile-time through the ISCP function +context that needs to be passed to the function that creates the function descriptor. Here's how a smart contract would tell a `dividend` contract on the same chain to divide the 1000 tokens it passes to the function: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -65,6 +67,14 @@ let f = dividend::ScFuncs::divide(ctx); f.func.transfer_iotas(1000).call(); ``` + + + +```ts +let f = dividend.ScFuncs.divide(ctx); +f.func.transferIotas(1000).call(); +``` + @@ -72,9 +82,11 @@ And here is how a smart contract would ask a `dividend` contract on the same cha return the dispersion factor for a specific address: @@ -91,7 +103,17 @@ factor := f.Results.Factor().Value() ```rust let f = dividend::ScFuncs::get_factor(ctx); -f.params.address().set_value( & address); +f.params.address().set_value(&address); +f.func.call(); +let factor = f.results.factor().value(); +``` + + + + +```ts +let f = dividend.ScFuncs.getFactor(ctx); +f.params.address().setValue(address); f.func.call(); let factor = f.results.factor().value(); ``` @@ -105,16 +127,18 @@ let factor = f.results.factor().value(); 4. Use the `results` proxy in the function descriptor to retrieve any results we are interested in. -The function descriptors assume that the function to be called is associated -with the default Hname of the contract, in this case ScHname::new("dividend"). If you -deployed the contract that contains the function you want to call under a different name, -then you would have to provide its associated Hname to the `func` member through the -of_contract() member function like this: +The function descriptors assume that the function to be called is associated with the +default Hname of the contract, in this case ScHname::new("dividend"). If you deployed the +contract that contains the function you want to call under a different name, then you +would have to provide its associated Hname to the `func` member through the of_contract() +member function like this: @@ -129,9 +153,18 @@ f.Func.OfContract(altContract).TransferIotas(1000).Call() ```rust -let altContract = ScHname::new("alternateName"); +let alt_contract = ScHname::new("alternateName"); let f = dividend::ScFuncs::divide(ctx); -f.func.of_contract(altContract).transfer_iotas(1000).call(); +f.func.of_contract(alt_contract).transfer_iotas(1000).call(); +``` + + + + +```ts +let altContract = ScHname.fromString("alternateName"); +let f = dividend.ScFuncs.divide(ctx); +f.func.ofContract(altContract).transferIotas(1000).call(); ``` diff --git a/documentation/docs/guide/schema/examples.mdx b/documentation/docs/guide/schema/examples.mdx index 042279d3d6..2dfd6b3d6b 100644 --- a/documentation/docs/guide/schema/examples.mdx +++ b/documentation/docs/guide/schema/examples.mdx @@ -16,10 +16,10 @@ import TabItem from "@theme/TabItem" # Example Tests -In the previous sections we showed you how you can [call()](call.mdx) or [post()](post.mdx) function requests. We also -created a few wrapper functions to simplify calling these functions even further. Now we -will look at how to use the SoloContext to create full-blown tests for the -`dividend` example smart contract. +In the previous sections we showed you how you can [call()](call.mdx) or +[post()](post.mdx) function requests. We also created a few wrapper functions to simplify +calling these functions even further. Now we will look at how to use the SoloContext to +create full-blown tests for the `dividend` example smart contract. Let's start with a simple test. We are going to use the `member` function to add a valid new member/factor combination to the member group. @@ -97,12 +97,12 @@ func TestAddMemberFailMissingFactor(t \*testing.T) { -Each test has to set up the chain/contract/context from scratch. We will often -use a specific setupTest() function to do all setup work that is shared by many tests. +Each test has to set up the chain/contract/context from scratch. We will often use a +specific setupTest() function to do all setup work that is shared by many tests. -We cannot use the `dividendMember` wrapper function in these two tests -because of the missing required function parameters. So we have copy/pasted the code, and -removed the Params initialization we wanted to be missing. +We cannot use the `dividendMember` wrapper function in these two tests because of the +missing required function parameters. So we have copy/pasted the code, and removed the +Params initialization we wanted to be missing. Now let's see a more complex example: @@ -143,9 +143,9 @@ inside the `dividendMember()` function. Next we transfer 99 iotas as part of `thePost()` request to the `divide` function. We subsequently check that no error has occurred. Finally, we expect the balance of member1 -address to have increased by the total of 100 tokens that were stored in the -`dividend` smart contract account, as 100/100th of the tokens should have been sent to -member1. And the contract account should end up empty. +address to have increased by the total of 100 tokens that were stored in the `dividend` +smart contract account, as 100/100th of the tokens should have been sent to member1. +And the contract account should end up empty. Now let's skip to the most complex test of all: @@ -235,15 +235,15 @@ this: -After the final `divide` call, we ended up with the exact amounts to disperse, -so no remainder iotas were left in the contract account. +After the final `divide` call, we ended up with the exact amounts to disperse, so no +remainder iotas were left in the contract account. -Each divide is cumulative to the balances of the members. We have -highlighted this by indicating the separate increases after every `divide` call. +Each divide is cumulative to the balances of the members. We have highlighted this by +indicating the separate increases after every `divide` call. -Finally, we will show how to test [Views](views.mdx) and/or [Funcs](funcs.mdx) that return a result. -Since solo executes [post()](post.mdx) requests synchronously, it is possible to have a Func return a result and -test for certain result values. +Finally, we will show how to test [Views](views.mdx) and/or [Funcs](funcs.mdx) that return +a result. Since solo executes [post()](post.mdx) requests synchronously, it is possible to +have a Func return a result and test for certain result values. + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -189,17 +191,92 @@ impl ScFuncs { } ``` + + + +```ts +import * as wasmlib from "../wasmlib" +import * as sc from "./index"; + +export class DivideCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncDivide); +} + +export class InitCall { + func: wasmlib.ScInitFunc = new wasmlib.ScInitFunc(sc.HScName, sc.HFuncInit); + params: sc.MutableInitParams = new sc.MutableInitParams(); +} + +export class MemberCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncMember); + params: sc.MutableMemberParams = new sc.MutableMemberParams(); +} + +export class SetOwnerCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSetOwner); + params: sc.MutableSetOwnerParams = new sc.MutableSetOwnerParams(); +} + +export class GetFactorCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetFactor); + params: sc.MutableGetFactorParams = new sc.MutableGetFactorParams(); + results: sc.ImmutableGetFactorResults = new sc.ImmutableGetFactorResults(); +} + +export class GetOwnerCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetOwner); + results: sc.ImmutableGetOwnerResults = new sc.ImmutableGetOwnerResults(); +} + +export class ScFuncs { + static divide(ctx: wasmlib.ScFuncCallContext): DivideCall { + let f = new DivideCall(); + return f; + } + + static init(ctx: wasmlib.ScFuncCallContext): InitCall { + let f = new InitCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static member(ctx: wasmlib.ScFuncCallContext): MemberCall { + let f = new MemberCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static setOwner(ctx: wasmlib.ScFuncCallContext): SetOwnerCall { + let f = new SetOwnerCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static getFactor(ctx: wasmlib.ScViewCallContext): GetFactorCall { + let f = new GetFactorCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getOwner(ctx: wasmlib.ScViewCallContext): GetOwnerCall { + let f = new GetOwnerCall(); + f.func.setPtrs(null, f.results); + return f; + } +} +``` + -As you can see a struct has been generated for each of the funcs and views. The -structs only provide access to `params` or `results` when these are specified for the -function. Each struct has a `func` member that can be used to initiate the -function call in certain ways. The `func` member will be of type ScFunc or ScView, -depending on whether the function is a func or a view. +As you can see a struct has been generated for each of the funcs and views. The structs +only provide access to `params` or `results` when these are specified for the function. +Each struct has a `func` member that can be used to initiate the function call in certain +ways. The `func` member will be of type ScFunc or ScView, depending on whether the +function is a func or a view. The ScFuncs struct provides a member function for each func or view that will create their respective function descriptor, initialize it properly, and returns it. -In the next section we will look at how to use function descriptors to [call a smart -contract function synchronously](call.mdx). +In the next section we will look at how to use function descriptors to +[call a smart contract function synchronously](call.mdx). diff --git a/documentation/docs/guide/schema/funcs.mdx b/documentation/docs/guide/schema/funcs.mdx index 87238abde4..32baa08f65 100644 --- a/documentation/docs/guide/schema/funcs.mdx +++ b/documentation/docs/guide/schema/funcs.mdx @@ -115,8 +115,8 @@ them. The code generated for Funcs will be able to inspect and modify the smart state, whereas the code generated for Views will only be able to inspect the state. Functions are defined as named subsections in the schema definition file. The name of the -subsection will become the name of the function. In turn, there can be 3 optional subsections -under each function subsection. +subsection will become the name of the function. In turn, there can be 3 optional +subsections under each function subsection. * `access` indicates who is allowed to access the function. * `params` holds the field definitions that describe the function parameters. diff --git a/documentation/docs/guide/schema/init.mdx b/documentation/docs/guide/schema/init.mdx index 14d89a7967..ba7e46e1b0 100644 --- a/documentation/docs/guide/schema/init.mdx +++ b/documentation/docs/guide/schema/init.mdx @@ -15,11 +15,11 @@ import TabItem from "@theme/TabItem" # Smart Contract Initialization -Smart contracts start out with a completely blank state. Sometimes you may want to be able to -define initial state, for example if your contract is configurable. You may want to be able to -pass this configuration to the contract upon deployment, so that its state reflects that -configuration once the first request comes in. To support this initialization we allow the -smart contract creator to provide an optional function named `init`. +Smart contracts start out with a completely blank state. Sometimes you may want to be able +to define initial state, for example if your contract is configurable. You may want to be +able to pass this configuration to the contract upon deployment, so that its state +reflects that configuration once the first request comes in. Such initialization can be +provided through an optional function named `init`. When provided, the `init` function will automatically be called immediately after the first time the contract has been deployed to the VM. Note that this is a one-time @@ -29,15 +29,17 @@ reconfigure the contract later on, you will need to provide a separate configura function, and guard it from being accessed by anyone else than properly authorized entities. -To show how creating a smart contract with WasmLib works, we will slowly start fleshing out -the smart contract functions of the `dividend` example in this tutorial. Here is the first -part of the Rust code that implements it, which contains the 'init' function: +To show how creating a smart contract with WasmLib works, we will slowly start fleshing +out the smart contract functions of the `dividend` example in this tutorial. Here is the +first part of the Rust code that implements it, which contains the `'`init`'` function: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -135,6 +137,53 @@ pub fn func_init(ctx: &ScFuncContext, f: &InitContext) { } ``` + + + +```ts +// This example implements 'dividend', a simple smart contract that will +// automatically disperse iota tokens which are sent to the contract to a group +// of member addresses according to predefined division factors. The intent is +// to showcase basic functionality of WasmLib through a minimal implementation +// and not to come up with a complete robust real-world solution. +// Note that we have drawn sometimes out constructs that could have been done +// in a single line over multiple statements to be able to properly document +// step by step what is happening in the code. We also unnecessarily annotate +// all 'let' statements with their assignment type to improve understanding. + +import * as wasmlib from "../wasmlib" +import * as sc from "./index"; + +// 'init' is used as a way to initialize a smart contract. It is an optional +// function that will automatically be called upon contract deployment. In this +// case we use it to initialize the 'owner' state variable so that we can later +// use this information to prevent non-owners from calling certain functions. +// The 'init' function takes a single optional parameter: +// - 'owner', which is the agent id of the entity owning the contract. +// When this parameter is omitted the owner will default to the contract creator. +export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { + // The schema tool has already created a proper InitContext for this function that + // allows us to access call parameters and state storage in a type-safe manner. + + // First we set up a default value for the owner in case the optional + // 'owner' parameter was omitted. + let owner: wasmlib.ScAgentID = ctx.contractCreator(); + + // Now we check if the optional 'owner' parameter is present in the params map. + if (f.params.owner().exists()) { + // Yes, it was present, so now we overwrite the default owner with + // the one specified by the 'owner' parameter. + owner = f.params.owner().value(); + } + + // Now that we have sorted out which agent will be the owner of this contract + // we will save this value in the 'owner' variable in state storage on the host. + // Read the documentation on schema.json to understand why this state variable is + // supported at compile-time by code generated from schema.json by the schema tool. + f.state.owner().setValue(owner); +} +``` + @@ -146,10 +195,13 @@ through situations that could require change in advance, and allow the contract itself to handle such changes through its state by providing a proper function interface: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> + ```go @@ -187,13 +239,33 @@ pub fn func_set_owner(_ctx: &ScFuncContext, f: &SetOwnerContext) { } ``` + + + +```ts +// 'setOwner' is used to change the owner of the smart contract. +// It updates the 'owner' state variable with the provided agent id. +// The 'setOwner' function takes a single mandatory parameter: +// - 'owner', which is the agent id of the entity that will own the contract. +// Only the current owner can change the owner. +export function funcSetOwner(ctx: wasmlib.ScFuncContext, f: sc.SetOwnerContext): void { + // Note that the schema tool has already dealt with making sure that this function + // can only be called by the owner and that the required parameter is present. + // So once we get to this point in the code we can take that as a given. + + // Save the new owner parameter value in the 'owner' variable in state storage. + f.state.owner().setValue(f.params.owner().value()); +} +``` + Note that we only define a single owner here. Proper fall-back could require multiple owners in case the owner entity could disappear, which would allow others to take over -instead of the contract becoming immutable with regard to owner functionality. Again, we cannot -stress enough how important it is to **think through every aspect of a smart contract before -deployment**. +instead of the contract becoming immutable with regard to owner functionality. Again, we +cannot stress enough how important it is to **think through every aspect of a smart +contract before deployment**. -In the next section we will look at how a smart contract can [transfer tokens](transfers.mdx). +In the next section we will look at how a smart contract can +[transfer tokens](transfers.mdx). diff --git a/documentation/docs/guide/schema/params.mdx b/documentation/docs/guide/schema/params.mdx index 1dee1a70d8..325d57c72f 100644 --- a/documentation/docs/guide/schema/params.mdx +++ b/documentation/docs/guide/schema/params.mdx @@ -35,10 +35,12 @@ For example, here is the structure generated for the immutable params for the `m function: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -76,6 +78,21 @@ impl ImmutableMemberParams { } ``` + + + +```ts +export class ImmutableMemberParams extends wasmlib.ScMapID { + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + factor(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamFactor]); + } +} +``` + diff --git a/documentation/docs/guide/schema/post.mdx b/documentation/docs/guide/schema/post.mdx index 08d6cb4733..874a436942 100644 --- a/documentation/docs/guide/schema/post.mdx +++ b/documentation/docs/guide/schema/post.mdx @@ -18,21 +18,24 @@ Asynchronous function calls between smart contracts are posted as requests on th They allow you to invoke any smart contract function that is not a View on any smart contract chain. You will notice that the behavior is very similar to a normal function call, but instead of using the `call()` method of the `func` member in the function -descriptor, you will now use the `post()` or `post_to_chain()` methods. `post()` posts the -request within the current chain, while `post_to_chain()` takes the chain ID of the desired chain -as parameter. +descriptor, you will now use the `post()` or `postToChain()` methods. `post()` posts the +request within the current chain, while `postToChain()` takes the chain ID of the +desired chain as parameter. -In addition to the previously discussed [transfer_iotas()](call.mdx) and [of_contract()](call.mdx) methods, you can -modify the behavior further by providing a delay() in seconds, which enables delayed -execution of the request. This is of particular interest to smart contracts that -need a delayed action like betting contracts with a timed betting round, or to create -time-lock functionality in a smart contract. Here's how that works: +In addition to the previously discussed [transferIotas()](call.mdx) and +[ofContract()](call.mdx) methods, you can modify the behavior further by providing a +delay() in seconds, which enables delayed execution of the request. This is of particular +interest to smart contracts that need a delayed action like betting contracts with a timed +betting round, or to create time-lock functionality in a smart contract. +Here's how that works: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -49,19 +52,28 @@ let eor = ScFuncs::end_of_round(ctx); eor.func.delay(3600).transfer_iotas(1).post(); ``` + + + +```ts +let eor = sc.ScFuncs.endOfRound(ctx); +eor.func.delay(3600).transferIotas(1).post(); +``` + -Because it is posted as a request on the Tangle, and it is not possible to have a request without a -transfer, _an asynchronous request always needs to send at least 1 token_. So, if you post to a function -that expects tokens you just specify the amount of tokens required, but if you post to a function that does not expect -any tokens then you still have to provide 1 iota. +Because it is posted as a request on the Tangle, and it is not possible to have a request +without a transfer, _an asynchronous request always needs to send at least 1 token_. So, +if you post to a function that expects tokens you just specify the amount of tokens +required, but if you post to a function that does not expect any tokens then you still +have to provide 1 iota. -**Providing a delay() before a call() will result in a run-time error**. We do -not know the intention of the user until the actual call() or post() is encountered, so we -cannot check for this at compile-time unless we are willing to accept a lot of extra -overhead. It should not really be a problem because using delay() is rare and -using it with call() cannot have been the intention. +**Providing a delay() before a call() will result in a run-time error**. We do not know +the intention of the user until the actual call() or post() is encountered, so we cannot +check for this at compile-time unless we are willing to accept a lot of extra overhead. +It should not really be a problem because using delay() is rare and using it with call() +cannot have been the intention. The function that posts the request through the function descriptor will immediately continue execution and does not wait for its completion. Therefore, it is not possible to @@ -74,5 +86,5 @@ asynchronously post the results to the indicated function. It will require a cer degree of cooperation between both smart contracts. In the future we will probably be looking at providing a generic mechanism for this. -In the next section we will look at how we can use the function descriptors when [testing -smart contracts with Solo](test.mdx). +In the next section we will look at how we can use the function descriptors when +[testing smart contracts with Solo](test.mdx). diff --git a/documentation/docs/guide/schema/results.mdx b/documentation/docs/guide/schema/results.mdx index b959d8c6f0..60653ecfea 100644 --- a/documentation/docs/guide/schema/results.mdx +++ b/documentation/docs/guide/schema/results.mdx @@ -31,10 +31,12 @@ For example, here is the structure generated for the mutable results for the `ge function: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -64,11 +66,27 @@ impl MutableGetFactorResults { } ``` + + + +```ts +export class ImmutableMemberParams extends wasmlib.ScMapID { + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + factor(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamFactor]); + } +} +``` + Note that the schema tool will also generate an immutable version of the structure, suitable for accessing the results after calling this smart contract function. -In the next section we will look at how so-called [thunk functions](thunks.mdx) encapsulate access and -parameter checking and set up the type-safe function-specific contexts. +In the next section we will look at how so-called [thunk functions](thunks.mdx) +encapsulate access and parameter checking and set up the type-safe function-specific +contexts. diff --git a/documentation/docs/guide/schema/schema.mdx b/documentation/docs/guide/schema/schema.mdx index e0c23c03b5..f24e8babf9 100644 --- a/documentation/docs/guide/schema/schema.mdx +++ b/documentation/docs/guide/schema/schema.mdx @@ -12,11 +12,11 @@ image: /img/logo/WASP_logo_dark.png # Smart Contract Schema Tool Smart contracts need to be very robust. The generic nature of WasmLib allows for a lot of -flexibility, but it also a lot of opportunities to make mistakes. In -addition, there is a lot of repetitive coding involved. The setup code that is needed for -every smart contract must follow strict rules. You want to assure that certain -functions can only be called by specific entities, and that function parameters values -have been properly checked before their usage. +flexibility, but it also a lot of opportunities to make mistakes. In addition, there is a +lot of repetitive coding involved. The setup code that is needed for every smart contract +must follow strict rules. You want to assure that certain functions can only be called by +specific entities, and that function parameters values have been properly checked before +their usage. The best way to increase robustness is by using a code generator that will take care of most repetitive coding tasks. A code generator only needs to be debugged once, after which @@ -39,20 +39,21 @@ examples of repetition were: * Defining common strings as constants To facilitate the code generation, we decided to use a _schema definition file_ for smart -contracts. All aspects of a smart contract that should be known by someone who wants to use -the contract are clearly defined in a schema definition file. This schema definition file -then becomes the source of truth for how the smart contract works. +contracts. All aspects of a smart contract that should be known by someone who wants to +use the contract are clearly defined in a schema definition file. This schema definition +file then becomes the source of truth for how the smart contract works. -The schema definition file defines things like the [state](state.mdx) variables that the smart -contract uses, the [Funcs](funcs.mdx) and [Views](views.mdx) that the contract implements, -the [access rights](access.mdx) for each function, the [input parameters](params.mdx) -and [output results](results.mdx) for each function, and additional data structures that the contract uses. +The schema definition file defines things like the [state](state.mdx) variables that the +smart contract uses, the [Funcs](funcs.mdx) and [Views](views.mdx) that the contract +implements, the [access rights](access.mdx) for each function, the +[input parameters](params.mdx) and [output results](results.mdx) for each function, and +additional data structures that the contract uses. -With detailed schema information readily available in a single location, it -becomes possible to do a lot more than just generating repetitive code fragments. You can -use the schema information to generate interfaces for functions, parameters, results, and -state that use strict compile-time type-checking. This reduces the likelihood of introducing errors -significantly. +With detailed schema information readily available in a single location, it becomes +possible to do a lot more than just generating repetitive code fragments. You can use the +schema information to generate interfaces for functions, parameters, results, and state +that use strict compile-time type-checking. This reduces the likelihood of introducing +errors significantly. Another advantage of knowing everything about important smart contract aspects is that it is possible to generate constants to prevent repeating of typo-prone key strings, and @@ -68,9 +69,10 @@ The previous two optimizations mean that the code becomes both simpler and more Note that all the improvements described above are independent of the programming language used. -Future additions that we envision for the schema tool are the automatic generation of smart -contract interface classes to use with client side Javascript, and automatic generation -of a web API for smart contracts. The schema definition file can also provide a starting -point for other tooling, for example a tool that automatically audits a smart contract. +Future additions that we envision for the schema tool are the automatic generation of +smart contract interface classes to use with client side Javascript, and automatic +generation of a web API for smart contracts. The schema definition file can also provide +a starting point for other tooling, for example a tool that automatically audits a smart +contract. In the next section we will look at [how the schema tool works](usage.mdx). diff --git a/documentation/docs/guide/schema/state.mdx b/documentation/docs/guide/schema/state.mdx index 5312777517..67d17d90eb 100644 --- a/documentation/docs/guide/schema/state.mdx +++ b/documentation/docs/guide/schema/state.mdx @@ -76,40 +76,42 @@ the brackets is the homogenous type of the keys, which in this case are of the p Address type. The brackets are immediately followed by the homogenous type of the values in the map, which in this case are of the predefined Int64 type. -Here is part of the Rust code in `state.rs` that the schema tool has generated. The +Here is part of the code in `state.xx` that the schema tool has generated. The MutableDividendState struct defines a type-safe interface to access each of the state variables through mutable proxies: - + ```go - type MutableDividendState struct { - id int32 - } +type MutableDividendState struct { + id int32 +} - func (s MutableDividendState) MemberList() ArrayOfMutableAddress { - arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS) - return ArrayOfMutableAddress{objID: arrID} - } +func (s MutableDividendState) MemberList() ArrayOfMutableAddress { + arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS) + return ArrayOfMutableAddress{objID: arrID} +} - func (s MutableDividendState) Members() MapAddressToMutableInt64 { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMembers], wasmlib.TYPE_MAP) - return MapAddressToMutableInt64{objID: mapID} - } +func (s MutableDividendState) Members() MapAddressToMutableInt64 { + mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMembers], wasmlib.TYPE_MAP) + return MapAddressToMutableInt64{objID: mapID} +} - func (s MutableDividendState) Owner() wasmlib.ScMutableAgentID { - return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxStateOwner]) - } +func (s MutableDividendState) Owner() wasmlib.ScMutableAgentID { + return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxStateOwner]) +} - func (s MutableDividendState) TotalFactor() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateTotalFactor]) - } +func (s MutableDividendState) TotalFactor() wasmlib.ScMutableInt64 { + return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateTotalFactor]) +} ``` @@ -142,6 +144,32 @@ impl MutableDividendState { } ``` + + + +```ts +export class MutableDividendState extends wasmlib.ScMapID { + + memberList(): sc.ArrayOfMutableAddress { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS); + return new sc.ArrayOfMutableAddress(arrID) + } + + members(): sc.MapAddressToMutableInt64 { + let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateMembers], wasmlib.TYPE_MAP); + return new sc.MapAddressToMutableInt64(mapID); + } + + owner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxStateOwner]); + } + + totalFactor(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateTotalFactor]); + } +} +``` + @@ -152,7 +180,7 @@ object for the corresponding variable. In addition, the schema tool generates an necessary intermediate map and array proxy types that force the usage of their respective homogenous types. In the above example both `ArrayOfMutableAddress` and `MapAddressToMutableInt64` are examples of such automatically generated proxy types. -See the full `state.rs` for more details. +See the full `state.xx` for more details. In the next section we will explore how the schema tool helps to simplify [function definitions](funcs.mdx). \ No newline at end of file diff --git a/documentation/docs/guide/schema/structs.mdx b/documentation/docs/guide/schema/structs.mdx index a7eefd3b3b..d80d6568cd 100644 --- a/documentation/docs/guide/schema/structs.mdx +++ b/documentation/docs/guide/schema/structs.mdx @@ -13,19 +13,19 @@ import TabItem from "@theme/TabItem" # Structured Data Types -The [schema tool](usage.mdx) allows you to define your own structured data types that are composed of -the predefined WasmLib value data types. The tool will generate a struct with named fields -according to the definition in the schema definition file, and also generate code to -serialize and deserialize the structure to a byte array, so that it can be saved as a -single unit of data, for example in state storage. +The [schema tool](usage.mdx) allows you to define your own structured data types that are +composed of the predefined WasmLib value data types. The tool will generate a struct with +named fields according to the definition in the schema definition file, and also generate +code to serialize and deserialize the structure to a byte array, so that it can be saved +as a single unit of data, for example in state storage. -You can use structs directly as a type in state storage definitions and the schema -tool will automatically generate the proxy code to access it properly. +You can use structs directly as a type in state storage definitions and the schema tool +will automatically generate the proxy code to access it properly. For example, let's say you are creating a `betting` smart contract. Then you would want to -store information for each bet. The Bet structure could consist of the bet amount and time -, the number of the item that was bet on, and the agent ID of the one who placed -the bet. And you would keep track of all bets in state storage in an array of Bet structs. +store information for each bet. The Bet structure could consist of the bet amount and +time, the number of the item that was bet on, and the agent ID of the one who placed the +bet. And you would keep track of all bets in state storage in an array of Bet structs. To do so, you would insert the following into the schema definition file: -The schema tool will generate `types.rs` which contains the following code for the Bet +The schema tool will generate `types.xx` which contains the following code for the Bet struct: + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -88,60 +89,60 @@ package betting import "github.com/iotaledger/wasp/packages/vm/wasmlib" type Bet struct { - Amount int64 // bet amount - Better wasmlib.ScAgentID // Who placed this bet - Number int32 // number of item we bet on - Time int64 // timestamp of this bet + Amount int64 // bet amount + Better wasmlib.ScAgentID // Who placed this bet + Number int32 // number of item we bet on + Time int64 // timestamp of this bet } func NewBetFromBytes(bytes []byte) *Bet { - decode := wasmlib.NewBytesDecoder(bytes) - data := &Bet{} - data.Amount = decode.Int64() - data.Better = decode.AgentID() - data.Number = decode.Int32() - data.Time = decode.Int64() - decode.Close() - return data + decode := wasmlib.NewBytesDecoder(bytes) + data := &Bet{} + data.Amount = decode.Int64() + data.Better = decode.AgentID() + data.Number = decode.Int32() + data.Time = decode.Int64() + decode.Close() + return data } func (o *Bet) Bytes() []byte { - return wasmlib.NewBytesEncoder(). - Int64(o.Amount). - AgentID(o.Better). - Int32(o.Number). - Int64(o.Time). - Data() + return wasmlib.NewBytesEncoder(). + Int64(o.Amount). + AgentID(o.Better). + Int32(o.Number). + Int64(o.Time). + Data() } type ImmutableBet struct { - objID int32 - keyID wasmlib.Key32 + objID int32 + keyID wasmlib.Key32 } func (o ImmutableBet) Exists() bool { - return wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES) + return wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES) } func (o ImmutableBet) Value() *Bet { - return NewBetFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES)) + return NewBetFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES)) } type MutableBet struct { - objID int32 - keyID wasmlib.Key32 + objID int32 + keyID wasmlib.Key32 } func (o MutableBet) Exists() bool { - return wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES) + return wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES) } func (o MutableBet) SetValue(value *Bet) { - wasmlib.SetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES, value.Bytes()) + wasmlib.SetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES, value.Bytes()) } func (o MutableBet) Value() *Bet { - return NewBetFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES)) + return NewBetFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES)) } ``` @@ -215,20 +216,95 @@ impl MutableBet { } ``` + + + +```ts +import * as wasmlib from "../wasmlib" + +export class Bet { + amount: i64 = 0; // bet amount + better: wasmlib.ScAgentID = new wasmlib.ScAgentID(); // Who placed this bet + number: i32 = 0; // number of item we bet on + time : i64 = 0; // timestamp of this bet + + static fromBytes(bytes: u8[]): Bet { + let decode = new wasmlib.BytesDecoder(bytes); + let data = new Bet(); + data.amount = decode.int64(); + data.better = decode.agentID(); + data.number = decode.int32(); + data.time = decode.int64(); + decode.close(); + return data; + } + + bytes(): u8[] { + return new wasmlib.BytesEncoder(). + int64(this.amount). + agentID(this.better). + int32(this.number). + int64(this.time). + data(); + } +} + +export class ImmutableBet { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + value(): Bet { + return Bet.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} + +export class MutableBet { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + setValue(value: Bet): void { + wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes()); + } + + value(): Bet { + return Bet.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES)); + } +} +``` + -Notice how the generated ImmutableBet and MutableBet proxies use the from_bytes() and -to_bytes() (de)serialization code to automatically transform byte arrays into Bet structs. +Notice how the generated ImmutableBet and MutableBet proxies use the fromBytes() and +toBytes() (de)serialization code to automatically transform byte arrays into Bet structs. -The generated code in `state.rs` that implements the state interface is shown here: +The generated code in `state.xx` that implements the state interface is shown here: + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -238,57 +314,57 @@ package betting import "github.com/iotaledger/wasp/packages/vm/wasmlib" type ArrayOfImmutableBet struct { - objID int32 + objID int32 } func (a ArrayOfImmutableBet) Length() int32 { - return wasmlib.GetLength(a.objID) + return wasmlib.GetLength(a.objID) } func (a ArrayOfImmutableBet) GetBet(index int32) ImmutableBet { - return ImmutableBet{objID: a.objID, keyID: wasmlib.Key32(index)} + return ImmutableBet{objID: a.objID, keyID: wasmlib.Key32(index)} } type ImmutableBettingState struct { - id int32 + id int32 } func (s ImmutableBettingState) Bets() ArrayOfImmutableBet { - arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) - return ArrayOfImmutableBet{objID: arrID} + arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) + return ArrayOfImmutableBet{objID: arrID} } func (s ImmutableBettingState) Owner() wasmlib.ScImmutableAgentID { - return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxStateOwner]) + return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxStateOwner]) } type ArrayOfMutableBet struct { - objID int32 + objID int32 } func (a ArrayOfMutableBet) Clear() { - wasmlib.Clear(a.objID) + wasmlib.Clear(a.objID) } func (a ArrayOfMutableBet) Length() int32 { - return wasmlib.GetLength(a.objID) + return wasmlib.GetLength(a.objID) } func (a ArrayOfMutableBet) GetBet(index int32) MutableBet { - return MutableBet{objID: a.objID, keyID: wasmlib.Key32(index)} + return MutableBet{objID: a.objID, keyID: wasmlib.Key32(index)} } type MutableBettingState struct { - id int32 + id int32 } func (s MutableBettingState) Bets() ArrayOfMutableBet { - arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) - return ArrayOfMutableBet{objID: arrID} + arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) + return ArrayOfMutableBet{objID: arrID} } func (s MutableBettingState) Owner() wasmlib.ScMutableAgentID { - return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxStateOwner]) + return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxStateOwner]) } ``` @@ -368,10 +444,77 @@ impl MutableBettingState { } ``` + + + +```ts +import * as wasmlib from "../wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableBet { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBet(index: i32): sc.ImmutableBet { + return new sc.ImmutableBet(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableBettingState extends wasmlib.ScMapID { + bets(): sc.ArrayOfImmutableBet { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBet(arrID) + } + + owner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxStateOwner]); + } +} + +export class ArrayOfMutableBet { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBet(index: i32): sc.MutableBet { + return new sc.MutableBet(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableBettingState extends wasmlib.ScMapID { + bets(): sc.ArrayOfMutableBet { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBet(arrID) + } + + owner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxStateOwner]); + } +} +``` + The end result is an ImmutableBettingState and MutableBettingState structure that can directly interface to the state of the betting contract. -In the next section we will look at how to make even more [complex type definitions](typedefs.mdx). +In the next section we will look at how to make even more + [complex type definitions](typedefs.mdx). diff --git a/documentation/docs/guide/schema/test.mdx b/documentation/docs/guide/schema/test.mdx index 7d31e3c9ca..f99c84b3b9 100644 --- a/documentation/docs/guide/schema/test.mdx +++ b/documentation/docs/guide/schema/test.mdx @@ -15,17 +15,19 @@ import TabItem from "@theme/TabItem" # Testing Smart Contracts -Testing of smart contracts happens in the [Solo testing environment](../solo/what-is-solo.md). This enables -synchronous, deterministic testing of smart contract functionalities without the overhead of +Testing of smart contracts happens in the +[Solo testing environment](../solo/what-is-solo.md). This enables synchronous, +deterministic testing of smart contract functionalities without the overhead of having to start nodes, set up a committee, and send transactions over the Tangle. Instead, you can use Go's built-in test environment in combination with Solo to deploy chains and smart contracts and simulate transactions. Solo directly interacts with the ISCP code, and uses all the data types that are [defined in the ISCP code](https://github.com/iotaledger/wasp/blob/develop/documentation/docs/misc/coretypes.md) -directly. Because they run in a sandboxed environment our Wasm smart contracts cannot access these types directly. -Therefore, WasmLib implements its [own versions](../wasm_vm/types.mdx) of these data types, and the VM layer acts as a data type -translator between both systems. +directly. Because they run in a sandboxed environment our Wasm smart contracts cannot +access these types directly. Therefore, WasmLib implements its +[own versions](../wasm_vm/types.mdx) of these data types, and the VM layer acts as a data +type translator between both systems. The impact of this type transformation used to be that to be able to write tests in the solo environment the user also needed to know about the ISCP-specific data types and type @@ -43,10 +45,10 @@ interface hidden as much as possible, but available when necessary. The only concession we still have to make is to the language used. Because Solo only works in the Go language environment, we have to use the Go language version of the interface code that the schema tool generates when testing our smart contracts. Because WasmLib -programming for Rust and Go are practically identical, we feel that this is not unsurmountable. -They only differ where language idiosyncrasies force differences in syntax or naming -conventions. This hurdle used to be a lot bigger, when direct programming of Solo -had to be used, and type conversions had to be done manually. Now we get to use +programming for Rust, TypeScript, and Go are practically identical, we feel that this is +not unsurmountable. They only differ where language idiosyncrasies force differences in +syntax or naming conventions. This hurdle used to be a lot bigger, when direct programming +of Solo had to be used, and type conversions had to be done manually. Now we get to use the generated compile-time type-checked interface to our smart contract functions that we are already familiar with. @@ -116,9 +118,9 @@ parameters through the `Params` proxy, and then either post the function request the function. Any results returned are extracted through the `Results` proxy, and returned by the wrapper. -As you can see there is literally no difference in the way the function interface is used -with the ISCP function context in WasmLib and with the SoloContext. This makes for -seamless testing of smart contracts. +There is hardly difference in the way the function interface is used with the ISCP +function context in WasmLib and with the SoloContext. This makes for seamless testing of +smart contracts. -In the [next section](examples.mdx) we will go deeper into how the helper member functions of the -SoloContext are used to simplify tests. +In the [next section](examples.mdx) we will go deeper into how the helper member functions +of the SoloContext are used to simplify tests. diff --git a/documentation/docs/guide/schema/thunks.mdx b/documentation/docs/guide/schema/thunks.mdx index 16fbaea54e..95301a82b8 100644 --- a/documentation/docs/guide/schema/thunks.mdx +++ b/documentation/docs/guide/schema/thunks.mdx @@ -15,14 +15,16 @@ In computer programming, a thunk is a function used to inject a calculation into function. Thunks are used to insert operations at the beginning or end of the other function to adapt it to changing requirements. If you remember from the [function call context](../wasm_vm/context.mdx) section, the `on_load` function and -skeleton -function signatures looked like this: +skeleton function signatures looked like this: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> + ```go @@ -66,6 +68,28 @@ fn view_get_factor(ctx: &ScViewContext) {} fn view_get_owner(ctx: &ScViewContext) {} ``` + + + +```ts +export function on_load() { + let exports = new ScExports(); + exports.addFunc("divide", funcDivide); + exports.addFunc("init", funcInit); + exports.addFunc("member", funcMember); + exports.addFunc("setOwner", funcSetOwner); + exports.addView("getFactor", viewGetFactor); + exports.addView("getOwner", viewGetOwner); +} + +function funcDivide(ctx: ScFuncContext) {} +function funcInit(ctx: ScFuncContext) {} +function funcMember(ctx: ScFuncContext) {} +function funcSetOwner(ctx: ScFuncContext) {} +function viewGetFactor(ctx: ScViewContext) {} +function viewGetOwner(ctx: ScViewContext) {} +``` + @@ -75,10 +99,13 @@ features, before calling the function implementations that are maintained by the Here is the new `on_load` function for the `dividend` contract: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> + ```go @@ -118,6 +145,25 @@ fn on_load() { } ``` + + + +```ts +export function on_load(): void { + let exports = new wasmlib.ScExports(); + exports.addFunc(sc.FuncDivide, funcDivideThunk); + exports.addFunc(sc.FuncInit, funcInitThunk); + exports.addFunc(sc.FuncMember, funcMemberThunk); + exports.addFunc(sc.FuncSetOwner, funcSetOwnerThunk); + exports.addView(sc.ViewGetFactor, viewGetFactorThunk); + exports.addView(sc.ViewGetOwner, viewGetOwnerThunk); + + for (let i = 0; i < sc.keyMap.length; i++) { + sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); + } +} +``` + @@ -128,13 +174,16 @@ instead of having to negotiate these IDs each time we need them. The rest of the code will use those indexes whenever a known key is used. Here is an example of a thunk function for the `setOwner` contract function. You can -examine the other thunks that all follow the same pattern in the generated `lib.rs`: +examine the other thunks that all follow the same pattern in the generated `lib.xx`: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> + ```go @@ -152,7 +201,7 @@ func funcSetOwnerThunk(ctx wasmlib.ScFuncContext) { f := &SetOwnerContext{ Params: ImmutableSetOwnerParams{ - id: wasmlib.OBJ_ID_PARAMS, + id: wasmlib.OBJ_ID_PARAMS, }, State: MutableDividendState{ id: wasmlib.OBJ_ID_STATE, @@ -194,6 +243,31 @@ fn func_set_owner_thunk(ctx: &ScFuncContext) { } ``` + + + +```ts +export class SetOwnerContext { + params: sc.ImmutableSetOwnerParams = new sc.ImmutableSetOwnerParams(); + state: sc.MutableDividendState = new sc.MutableDividendState(); +} + +function funcSetOwnerThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("dividend.funcSetOwner"); + // only defined owner of contract can change owner + let access = ctx.state().getAgentID(wasmlib.Key32.fromString("owner")); + ctx.require(access.exists(), "access not set: owner"); + ctx.require(ctx.caller().equals(access.value()), "no permission"); + + let f = new sc.SetOwnerContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.owner().exists(), "missing mandatory owner") + sc.funcSetOwner(ctx, f); + ctx.log("dividend.funcSetOwner ok"); +} +``` + @@ -207,18 +281,18 @@ the contract can call this function. Next, we set up a strongly typed function-specific context structure. First, we add the function-specific immutable `params` interface structure, which is only present when the function can have parameters. Then we add the contract-specific `state` interface -structure. In this case it is mutable because setOwner is a [Func](funcs.mdx). For [Views](views.mdx) this will be -an immutable state interface. Finally, we add the function-specific mutable `results` -interface structure, which is only present when the function returns results. Obviously, -this is not the case for this setOwner function. +structure. In this case it is mutable because setOwner is a [Func](funcs.mdx). For +[Views](views.mdx) this will be an immutable state interface. Finally, we add the +function-specific mutable `results` interface structure, which is only present when the +function returns results. Obviously, this is not the case for this setOwner function. Now we get to the point where we can use the function-specific `params` interface to check for mandatory parameters. Each mandatory parameter is required to exist, or else we will panic out of the function with an error message. -With the automated checks and setup completed, we now call the function implementation that -is maintained by the user. After the user function has completed, we log that the contract -function has completed successfully. Remember that any error within the user function will -cause a panic, so this logging will never happen in that case. +With the automated checks and setup completed, we now call the function implementation +that is maintained by the user. After the user function has completed, we log that the +contract function has completed successfully. Remember that any error within the user +function will cause a panic, so this logging will never happen in that case. In the next section we will look at the specifics of [view functions](views.mdx). diff --git a/documentation/docs/guide/schema/timelock.mdx b/documentation/docs/guide/schema/timelock.mdx index 3221406f88..ac436fede5 100644 --- a/documentation/docs/guide/schema/timelock.mdx +++ b/documentation/docs/guide/schema/timelock.mdx @@ -17,13 +17,12 @@ import TabItem from "@theme/TabItem" # Colored Tokens and Time Locks Let's examine some less commonly used member functions of the SoloContext. We will switch -to the `fairauction` example to show their usage. Here is the startAuction() -function of the `fairauction` test suite: +to the `fairauction` example to show their usage. Here is the startAuction() function of +the `fairauction` test suite: @@ -95,10 +94,9 @@ example. After the parameters have been set up, we see something new happening. a `Transfer` proxy and initialize it with the 25 iota that we need to deposit, plus the 10 tokens of the saved tokenColor that we are auctioning. Next we use the `Transfer()` method to pass this proxy before posting the request. This is exactly how we would do it from -within the smart contract code. We also have a shorthand function called TransferIotas() that -can be used when all you need to transfer is plain iotas which encapsulates -the creation of the Transfer proxy, and the initialization with the required amount of -iotas. +within the smart contract code. We also have a shorthand function called TransferIotas() +that can be used when all you need to transfer is plain iotas which encapsulates the +creation of the Transfer proxy, and the initialization with the required amount of iotas. Finally, we make sure there was no error while posting the request and return the SoloContext. That concludes the startAuction() function. @@ -108,7 +106,6 @@ Here is the first test function that uses our startAuction() function: diff --git a/documentation/docs/guide/schema/transfers.mdx b/documentation/docs/guide/schema/transfers.mdx index 420e27cf94..96ec7d47b5 100644 --- a/documentation/docs/guide/schema/transfers.mdx +++ b/documentation/docs/guide/schema/transfers.mdx @@ -18,16 +18,16 @@ import TabItem from "@theme/TabItem" There are two methods in the ISCP function context that deal with token balances. The first one is the `balances()` method, which can be used to determine the current total -balance per token color that is governed by the smart contract. The second one is -the `incoming()` method, which can be used to determine the amounts of incoming tokens per +balance per token color that is governed by the smart contract. The second one is the +`incoming()` method, which can be used to determine the amounts of incoming tokens per token color that were sent with the request to call the smart contract function. Both methods provide access to zero or more balances of tokens, each for a different token color, through a special `ScBalances` map proxy. Note that the `incoming()` balances are provided to the smart contract function as if they have already been deposited in the -smart contract's account. But, if any error occurs which causes the function to panic, these -incoming() tokens will be returned to where they came from, and it will be as if they were -never sent to the smart contract. +smart contract's account. But, if any error occurs which causes the function to panic, +these incoming() tokens will be returned to where they came from, and it will be as if +they were never sent to the smart contract. There is also a `transfer_to_address()` method in the ISCP function context that can transfer tokens from the smart contract account to any Tangle address. The tokens to be @@ -40,18 +40,20 @@ members, consisting of address/factor pairs, and knowing the total sum of the fa can automatically pay out a dividend to each of the members in the list according to the factors involved. Whatever amount of tokens gets sent to the `divide` function will be divided over the members in proportion based on their respective factors. For example, you -could set it up so that address A has a factor of 50, B has 30, and C has 20, for a total of 100 -to divide. Then whenever an amount of tokens gets sent to the 'divide' function, address A -will receive 50/100th, address B will receive 30/100th, and address C will receive -20/100th of that amount. +could set it up so that address A has a factor of 50, B has 30, and C has 20, for a total +of 100 to divide. Then whenever an amount of tokens gets sent to the 'divide' function, +address A will receive 50/100th, address B will receive 30/100th, and address C will +receive 20/100th of that amount. Here is the `divide` function: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -132,6 +134,7 @@ func funcDivide(ctx wasmlib.ScFuncContext, f *DivideContext) { // to any next round of tokens received prior to calculation of the new // dividend amounts. pub fn func_divide(ctx: &ScFuncContext, f: &DivideContext) { + // Create an ScBalances map proxy to the account balances for this // smart contract. Note that ScBalances wraps an ScImmutableMap of // token color/amount combinations in a simpler to use interface. @@ -184,8 +187,75 @@ pub fn func_divide(ctx: &ScFuncContext, f: &DivideContext) { } ``` + + + +```ts +// 'divide' is a function that will take any iotas it receives and properly +// disperse them to the addresses in the member list according to the dispersion +// factors associated with these addresses. +// Anyone can send iota tokens to this function and they will automatically be +// divided over the member list. Note that this function does not deal with +// fractions. It simply truncates the calculated amount to the nearest lower +// integer and keeps any remaining iotas in its own account. They will be added +// to any next round of tokens received prior to calculation of the new +// dividend amounts. +export function funcDivide(ctx: wasmlib.ScFuncContext, f: sc.DivideContext): void { + + // Create an ScBalances map proxy to the account balances for this + // smart contract. Note that ScBalances wraps an ScImmutableMap of + // token color/amount combinations in a simpler to use interface. + let balances: wasmlib.ScBalances = ctx.balances(); + + // Retrieve the amount of plain iota tokens from the account balance. + let amount: i64 = balances.balance(wasmlib.ScColor.IOTA); + + // Retrieve the pre-calculated totalFactor value from the state storage. + let totalFactor: i64 = f.state.totalFactor().value(); + + // Get the proxy to the 'members' map in the state storage. + let members: sc.MapAddressToMutableInt64 = f.state.members(); + + // Get the proxy to the 'memberList' array in the state storage. + let memberList: sc.ArrayOfMutableAddress = f.state.memberList(); + + // Determine the current length of the memberList array. + let size: i32 = memberList.length(); + + // Loop through all indexes of the memberList array. + for (let i = 0; i < size; i++) { + // Retrieve the next indexed address from the memberList array. + let address: wasmlib.ScAddress = memberList.getAddress(i).value(); + + // Retrieve the factor associated with the address from the members map. + let factor: i64 = members.getInt64(address).value(); + + // Calculate the fair share of iotas to disperse to this member based on the + // factor we just retrieved. Note that the result will be truncated. + let share: i64 = amount * factor / totalFactor; + + // Is there anything to disperse to this member? + if (share > 0) { + // Yes, so let's set up an ScTransfers map proxy that transfers the + // calculated amount of iotas. Note that ScTransfers wraps an + // ScMutableMap of token color/amount combinations in a simpler to use + // interface. The constructor we use here creates and initializes a + // single token color transfer in a single statement. The actual color + // and amount values passed in will be stored in a new map on the host. + let transfers: wasmlib.ScTransfers = wasmlib.ScTransfers.iotas(share); + + // Perform the actual transfer of tokens from the smart contract to the + // member address. The transferToAddress() method receives the address + // value and the proxy to the new transfers map on the host, and will + // call the corresponding host sandbox function with these values. + ctx.transferToAddress(address, transfers); + } + } +} +``` + -In the next section we will introduce [function descriptors](funcdesc.mdx) that can be used to initiate -smart contract functions. +In the next section we will introduce [function descriptors](funcdesc.mdx) that can be +used to initiate smart contract functions. diff --git a/documentation/docs/guide/schema/typedefs.mdx b/documentation/docs/guide/schema/typedefs.mdx index 65cb69de6b..274f078c89 100644 --- a/documentation/docs/guide/schema/typedefs.mdx +++ b/documentation/docs/guide/schema/typedefs.mdx @@ -15,18 +15,18 @@ import TabItem from "@theme/TabItem" # Type Definitions Since we allow nesting of [container types](../wasm_vm/proxies.mdx#container-proxies), it -is a difficult to create proper -declarations for such nested types. Especially because you can only indicate either a single type, -or an array of single type, or a map of single type in a field definition. +is a difficult to create proper declarations for such nested types. Especially because you +can only indicate either a single type, or an array of single type, or a map of single +type in a field definition. There is a simple solution to this problem. You can add a `typedefs` section to the schema definition file, where you can define a single type name for a container type. That way you can easily create containers that contain such container types. The schema tool will automatically generate the in-between proxy types necessary to make this work. -To keep it at the `betting` smart contract from [the previous section](structs.mdx), imagine you want to keep -track of all betting rounds. Since a betting round contains an array of all bets in a -round, if it weren't for typedefs you could not define it easily. +To keep it at the `betting` smart contract from [the previous section](structs.mdx), +imagine you want to keep track of all betting rounds. Since a betting round contains an +array of all bets in a round, if it weren't for typedefs you could not define it easily. Instead, now you add the following to your schema definition file: @@ -63,7 +63,7 @@ state: -The schema tool will generate the following proxies in `typedefs.rs`: +The schema tool will generate the following proxies in `typedefs.xx`: + + +```ts +import * as wasmlib from "../wasmlib" +import * as sc from "./index"; + +export class ImmutableBettingRound extends ArrayOfImmutableBet { +}; + +export class ArrayOfImmutableBet { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBet(index: i32): sc.ImmutableBet { + return new sc.ImmutableBet(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableBettingRound extends ArrayOfMutableBet { +}; + +export class ArrayOfMutableBet { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBet(index: i32): sc.MutableBet { + return new sc.MutableBet(this.objID, new wasmlib.Key32(index)); + } +} +``` + Note how ImmutableBettingRound and MutableBettingRound type aliases are created for the types ArrayOfImmutableBet and ArrayOfMutableBet. These are subsequently used in the state -definition in `state.rs`: +definition in `state.xx`: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -194,6 +246,11 @@ type ImmutableBettingState struct { id int32 } +func (s ImmutableBettingState) Bets() ArrayOfImmutableBet { + arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) + return ArrayOfImmutableBet{objID: arrID} +} + func (s ImmutableBettingState) Owner() wasmlib.ScImmutableAgentID { return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxStateOwner]) } @@ -224,6 +281,11 @@ type MutableBettingState struct { id int32 } +func (s MutableBettingState) Bets() ArrayOfMutableBet { + arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) + return ArrayOfMutableBet{objID: arrID} +} + func (s MutableBettingState) Owner() wasmlib.ScMutableAgentID { return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxStateOwner]) } @@ -243,7 +305,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; -use crate::subtypes::*; +use crate::typedefs::*; use crate::types::*; pub struct ArrayOfImmutableBettingRound { @@ -267,6 +329,11 @@ pub struct ImmutableBettingState { } impl ImmutableBettingState { + pub fn bets(&self) -> ArrayOfImmutableBet { + let arr_id = get_object_id(self.id, idx_map(IDX_STATE_BETS), TYPE_ARRAY | TYPE_BYTES); + ArrayOfImmutableBet { obj_id: arr_id } + } + pub fn owner(&self) -> ScImmutableAgentID { ScImmutableAgentID::new(self.id, idx_map(IDX_STATE_OWNER)) } @@ -302,6 +369,11 @@ pub struct MutableBettingState { } impl MutableBettingState { + pub fn bets(&self) -> ArrayOfMutableBet { + let arr_id = get_object_id(self.id, idx_map(IDX_STATE_BETS), TYPE_ARRAY | TYPE_BYTES); + ArrayOfMutableBet { obj_id: arr_id } + } + pub fn owner(&self) -> ScMutableAgentID { ScMutableAgentID::new(self.id, idx_map(IDX_STATE_OWNER)) } @@ -313,11 +385,91 @@ impl MutableBettingState { } ``` + + + +```ts +import * as wasmlib from "../wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableBettingRound { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBettingRound(index: i32): sc.ImmutableBettingRound { + let subID = wasmlib.getObjectID(this.objID, new wasmlib.Key32(index), wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ImmutableBettingRound(subID); + } +} + +export class ImmutableBettingState extends wasmlib.ScMapID { + bets(): sc.ArrayOfImmutableBet { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBet(arrID) + } + + owner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxStateOwner]); + } + + rounds(): sc.ArrayOfImmutableBettingRound { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateRounds], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBettingRound(arrID) + } +} + +export class ArrayOfMutableBettingRound { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBettingRound(index: i32): sc.MutableBettingRound { + let subID = wasmlib.getObjectID(this.objID, new wasmlib.Key32(index), wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.MutableBettingRound(subID); + } +} + +export class MutableBettingState extends wasmlib.ScMapID { + bets(): sc.ArrayOfMutableBet { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBet(arrID) + } + + owner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxStateOwner]); + } + + rounds(): sc.ArrayOfMutableBettingRound { + let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateRounds], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBettingRound(arrID) + } +} +``` + -Notice how the rounds() member function returns a proxy to an array of BettingRound. Which -in turn is an array of Bet. So, the desired result has been achieved. And every access step -along the way only allows you to take the path laid out which is checked at compile-time. +Notice how the rounds() member function returns a proxy to an array of BettingRound. +Which in turn is an array of Bet. So, the desired result has been achieved. And every +access step along the way only allows you to take the path laid out which is checked at +compile-time. -In the next section we will explore how the schema tool generates a proxy interface for mutable [states](state.mdx). \ No newline at end of file +In the next section we will explore how the schema tool generates a proxy interface for +mutable [states](state.mdx). \ No newline at end of file diff --git a/documentation/docs/guide/schema/usage.mdx b/documentation/docs/guide/schema/usage.mdx index d8ab292a7f..0fbb5e03f3 100644 --- a/documentation/docs/guide/schema/usage.mdx +++ b/documentation/docs/guide/schema/usage.mdx @@ -20,10 +20,11 @@ We tried to make the creation of smart contracts as simple as possible. The `sch tool will assist you along the way as unobtrusively as possible. This section will walk you through the steps to create a new smart contract from scratch. -First, you need to decide on a central folder where you want to keep all your smart contracts. Each -smart contract you create will be maintained in a separate subfolder in this folder. We -will use certain naming conventions that the schema tool expects throughout this section. -First we will select a camel case name for our smart contract. For our example, `MySmartContract`. +First, you need to decide on a central folder where you want to keep all your smart +contracts. Each smart contract you create will be maintained in a separate subfolder in +this folder. We will use certain naming conventions that the schema tool expects +throughout this section. First we will select a camel case name for our smart contract. +For our example, `MySmartContract`. Once you know what your smart contract will be named, it is time to set up your subfolder. Simply navigate to the central smart contract folder, and run the schema tool's @@ -34,15 +35,16 @@ schema -init MySmartContract ``` This command will create a subfolder named `mysmartcontract` and generate an initial YAML -schema definition file inside this subfolder. B ecause a YAML file is easier to read and edit manually, -YAML is the default configuration file over JSON. If you prefer to use JSON instead, you can run the schema tool like this: +schema definition file inside this subfolder. B ecause a YAML file is easier to read and +edit manually, YAML is the default configuration file over JSON. If you prefer to use JSON +instead, you can run the schema tool like this: ```bash schema -init MySmartContract -type=json ``` Note that the generated subfolder name is all lower case. This is due to best practices -for package names both in Rust and in Go. The generated schema definition file looks like +for package names in most languages. The generated schema definition file looks like this: + + + +If you want to generate Go code, you should run the schema tool with the +`-go` option like this: + +```bash +schema -go +``` + + + + +If you want to generate Rust code, you should run the schema tool with the +`-rust` option like this: ```bash schema -rust ``` -If you want to generate Go code, you should run the schema tool with the `-go` option like -this: + + + +If you want to generate TypeScript code, you should run the schema tool with the +`-ts` option like this: ```bash -schema -go +schema -ts ``` -If you want to generate both Rust and Go code you need to specify both options like -this: + + + +If you want to generate more than one language your can simply specify multiple options. +For example, to generate both Rust and Go code you would specify both options like this: ```bash schema -rust -go @@ -150,33 +180,70 @@ schema -rust -go Note that the schema tool will automatically determine the type of the schema definition file (YAML or JSON) by its file extension. -The schema tool will generate a complete set of source files for the desired language(s). -After generating the Rust code, you should modify the Cargo.toml file to -your liking, and potentially add the new project to a Rust workspace. Cargo.toml will not -be regenerated once it already exists. The generated files together readily compile into a -Wasm file by using the appropriate command: - -- **Rust**: `wasm-pack build`. This will use the `src` subfolder that contains all Rust - source files. The only file in this folder that you should edit manually is - `mysmartcontract.rs`. All other files will be regenerated and overwritten whenever the - schema tool is run again. -- **Go**: `tinygo build -target wasm wasmmain/main.go`. This will use the go source files - in the current folder. The only file in this folder that you should edit manually is - `mysmartcontract.go`. All other files will be regenerated and overwritten whenever the - schema tool is run again. - -For now, we will focus on the Rust code that is generated, but the Go code is essentially -identical, barring some language idiosyncrasy differences. Just view .rs files next to .go -files with the same name, and you will see what we mean. - -Here is an example of the initially generated Rust code, `mysmartcontract.rs` +The schema tool will generate a complete set of source files for the desired language(s), +that will compile successfully into a Wasm code file. You compile these as follows: + + + + + +```bash +tinygo build -target wasm wasmmain/main.go +``` + +This will use the Go source files in the current folder. The only file in this folder that +you should edit manually is `mysmartcontract.go`. All other files will be regenerated and +overwritten whenever the schema tool is run again. + + + + +After generating the Rust code, you should first modify the Cargo.toml file to your +liking, and potentially add the new project to a Rust workspace. Cargo.toml will not be +regenerated once it already exists. Then build the code as follows: + +```bash +wasm-pack build +``` + +This will use the `src` subfolder that contains all Rust source files. The only file in +this folder that you should edit manually is `mysmartcontract.rs`. All other files will be +regenerated and overwritten whenever the schema tool is run again. + + + + +```bash +asc lib.ts --binaryFile output_ts.wasm +``` + +This will use the TypeScript source files in the current folder. The only file in this +folder that you should edit manually is `mysmartcontract.ts`. All other files will be +regenerated and overwritten whenever the schema tool is run again. + + + + +The generated code is essentially identical for each language, barring some language +idiosyncrasy differences. Just view different language files with the same name next to, +each other, and you will see what we mean. + +Here is an example of the initially generated code, `mysmartcontract.xx` looks like this before you even start modifying it: + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -185,20 +252,21 @@ package mysmartcontract import "github.com/iotaledger/wasp/packages/vm/wasmlib" + func funcInit(ctx wasmlib.ScFuncContext, f *InitContext) { - if f.Params.Owner().Exists() { - f.State.Owner().SetValue(f.Params.Owner().Value()) - return - } - f.State.Owner().SetValue(ctx.ContractCreator()) + if f.Params.Owner().Exists() { + f.State.Owner().SetValue(f.Params.Owner().Value()) + return + } + f.State.Owner().SetValue(ctx.ContractCreator()) } func funcSetOwner(ctx wasmlib.ScFuncContext, f *SetOwnerContext) { - f.State.Owner().SetValue(f.Params.Owner().Value()) + f.State.Owner().SetValue(f.Params.Owner().Value()) } func viewGetOwner(ctx wasmlib.ScViewContext, f *GetOwnerContext) { - f.Results.Owner().SetValue(f.State.Owner().Value()) + f.Results.Owner().SetValue(f.State.Owner().Value()) } ``` @@ -212,18 +280,42 @@ use crate::*; pub fn func_init(ctx: &ScFuncContext, f: &InitContext) { if f.params.owner().exists() { - f.state.owner().set_value(f.params.owner().value()); + f.state.owner().set_value(&f.params.owner().value()); return; } - f.state.owner().set_value(ctx.contract_creator()); + f.state.owner().set_value(&ctx.contract_creator()); } pub fn func_set_owner(_ctx: &ScFuncContext, f: &SetOwnerContext) { - f.state.owner().set_value(f.params.owner().value()); + f.state.owner().set_value(&f.params.owner().value()); } pub fn view_get_owner(_ctx: &ScViewContext, f: &GetOwnerContext) { - f.results.owner().set_value(f.state.owner().value()); + f.results.owner().set_value(&f.state.owner().value()); +} +``` + + + + +```ts +import * as wasmlib from "../wasmlib" +import * as sc from "./index"; + +export function funcInit(ctx: wasmlib.ScFuncContext, f: sc.InitContext): void { + if (f.params.owner().exists()) { + f.state.owner().setValue(f.params.owner().value()); + return; + } + f.state.owner().setValue(ctx.contractCreator()); +} + +export function funcSetOwner(ctx: wasmlib.ScFuncContext, f: sc.SetOwnerContext): void { + f.state.owner().setValue(f.params.owner().value()); +} + +export function viewGetOwner(ctx: wasmlib.ScViewContext, f: sc.GetOwnerContext): void { + f.results.owner().setValue(f.state.owner().value()); } ``` @@ -237,10 +329,10 @@ For a smooth building experience it is a good idea to set up a build rule in you environment that runs the schema tool with the required parameters whenever the schema definition file changes. That way regeneration of files is automatic, and you no longer have to start the schema tool manually each time after changing the schema definition -file. The schema tool will only regenerate the code when it finds that the -schema definition file has been modified since the last time it generated the code. -You can force the schema tool to regenerate all code by adding the `-force` flag to its -command line parameter. +file. The schema tool will only regenerate the code when it finds that the schema +definition file has been modified since the last time it generated the code. You can force +the schema tool to regenerate all code by adding the `-force` flag to its command line +parameter. -In the next section we will look at how a smart contract uses [Structured Data Types -](structs.mdx). +In the next section we will look at how a smart contract uses +[Structured Data Types](structs.mdx). diff --git a/documentation/docs/guide/schema/views.mdx b/documentation/docs/guide/schema/views.mdx index a9bf4d2cf8..04b7cad6d0 100644 --- a/documentation/docs/guide/schema/views.mdx +++ b/documentation/docs/guide/schema/views.mdx @@ -30,10 +30,12 @@ For demonstration purposes we provided a View function with the `dividend` smart called 'getFactor': + groupId="language" + values={[ + {label: 'Go', value: 'go'}, + {label: 'Rust', value: 'rust'}, + {label: 'TypeScript', value: 'ts'}, + ]}> @@ -66,11 +68,12 @@ func viewGetFactor(ctx wasmlib.ScViewContext, f *GetFactorContext) { // 'getFactor' is a simple View function. It will retrieve the factor // associated with the (mandatory) address parameter it was provided with. pub fn view_get_factor(_ctx: &ScViewContext, f: &GetFactorContext) { + // Since we are sure that the 'address' parameter actually exists we can // retrieve its actual value into an ScAddress value type. let address: ScAddress = f.params.address().value(); - // Create an immutable map proxy to the 'members' map in the state storage. + // Create an ScImmutableMap proxy to the 'members' map in the state storage. // Note that for views this is an *immutable* map as opposed to the *mutable* // map we can access from the *mutable* state that gets passed to funcs. let members: MapAddressToImmutableInt64 = f.state.members(); @@ -84,6 +87,32 @@ pub fn view_get_factor(_ctx: &ScViewContext, f: &GetFactorContext) { } ``` + + + +```ts +// 'getFactor' is a simple View function. It will retrieve the factor +// associated with the (mandatory) address parameter it was provided with. +export function viewGetFactor(ctx: wasmlib.ScViewContext, f: sc.GetFactorContext): void { + + // Since we are sure that the 'address' parameter actually exists we can + // retrieve its actual value into an ScAddress value type. + let address: wasmlib.ScAddress = f.params.address().value(); + + // Create an ScImmutableMap proxy to the 'members' map in the state storage. + // Note that for views this is an *immutable* map as opposed to the *mutable* + // map we can access from the *mutable* state that gets passed to funcs. + let members: sc.MapAddressToImmutableInt64 = f.state.members(); + + // Retrieve the factor associated with the address parameter. + let factor: i64 = members.getInt64(address).value(); + + // Set the factor in the results map of the function context. + // The contents of this results map is returned to the caller of the function. + f.results.factor().setValue(factor); +} +``` + diff --git a/documentation/docs/guide/wasm_vm/context.mdx b/documentation/docs/guide/wasm_vm/context.mdx index 79cd1a1916..7f5d52f836 100644 --- a/documentation/docs/guide/wasm_vm/context.mdx +++ b/documentation/docs/guide/wasm_vm/context.mdx @@ -30,35 +30,37 @@ distinguish two separate flavors of smart contract functions in the ISCP: To support this function type distinction, Func and View functions each receive a separate, different function call context. Only the functionality that is necessary for -their implementation can be accessed through their respective contexts, -named `ScFuncContext` -and `ScViewContext`. ScViewContext only provides a limited, immutable subset of the full +their implementation can be accessed through their respective contexts, `ScFuncContext` +and `ScViewContext`. ScViewContext provides a limited, immutable subset of the full functionality provided by ScFuncContext. By having separate context types, compile-time type-checking can be used to enforce their usage constraints. An important part of setting up a smart contract is defining exactly which Funcs and Views -are available and informing the host about them. The host will have to dispatch -the function calls to the corresponding smart contract code. To that end, the smart -contract Wasm code will expose an externally callable function named `on_load` that will -be called by the host upon initial loading of the smart contract code. The `on_load` -function must provide the host with the list of Funcs and Views, and specific identifiers -that can be used to invoke them. It uses a special temporary function context named -`ScExports`. That context can be used to provide the host with a function, type, name, and -identifier for each Func and View that can be called in the smart contract. - -When the host calls a smart contract function it has to do it by invoking a second +are available and informing the host about them. The host will have to dispatch the +function calls to the corresponding smart contract code. To that end, the smart contract +Wasm code will expose an externally callable function named `on_load` that will be called +by the host upon initial loading of the smart contract code. The `on_load` function must +provide the host with the list of Funcs and Views, and specific identifiers that can be +used to invoke them. It uses a special temporary function context named `ScExports`. That +context can be used to provide the host with a function, type, name, and identifier for +each Func and View that can be called in the smart contract. + +When the host need to call a smart contract function, it has to do it by invoking a second externally callable function named `on_call`. The host passes the identifier for the smart contract Func or View that needs to be invoked. The client Wasm code will then use this identifier to set up the corresponding function context and call the function. Note that there are no other parameters necessary because the function can subsequently access any other function-related data through its context object. -Here is a (simplified) example from the `dividend` example smart contract that showcases some features of WasmLib: +Here is a (simplified) example from the `dividend` example smart contract that showcases +some features of WasmLib: @@ -76,7 +78,7 @@ func OnLoad() { ``` - + ```rust fn on_load() { @@ -90,13 +92,28 @@ fn on_load() { } ``` + + + +```ts +export function on_load(): void { + let exports = new ScExports(); + exports.addFunc("divide", funcDivide); + exports.addFunc("init", funcInit); + exports.addFunc("member", funcMember); + exports.addFunc("setOwner", funcSetOwner); + exports.addView("getFactor", viewGetFactor); + exports.addView("getOwner", viewGetOwner); +} +``` + The on_load() function first creates the required ScExports context, and then proceeds to define four Funcs named `divide`, `init`, `member`, and `setOwner`. It does this by calling the add_func() method of the ScExports context. Next it defines two -Views named `getFactor` and `getOwner` by calling the add_view() method of the ScExports +Views named `getFactor` and `getOwner` by calling the addView() method of the ScExports context. The second parameter to these methods is the actual smart contract function associated with the name specified. These methods will also automatically assign a unique identifier to the function and then send everything to the host. @@ -106,9 +123,11 @@ finalize this example, here is what the skeleton function implementations for th smart contract definition would look like: @@ -168,6 +187,35 @@ fn view_get_owner(ctx: &ScViewContext) { } ``` + + + +```ts +export function func_divide(ctx: &ScFuncContext) { + ctx.log("Calling dividend.divide"); +} + +export function func_init(ctx: &ScFuncContext) { + ctx.log("Calling dividend.init"); +} + +export function func_member(ctx: &ScFuncContext) { + ctx.log("Calling dividend.member"); +} + +export function func_set_owner(ctx: &ScFuncContext) { + ctx.log("Calling dividend.setOwner"); +} + +export function view_get_factor(ctx: &ScViewContext) { + ctx.log("Calling dividend.getFactor"); +} + +export function view_get_owner(ctx: &ScViewContext) { + ctx.log("Calling dividend.getOwner"); +} +``` + @@ -175,13 +223,12 @@ As you can see, each function is provided with a context parameter, which is conventionally named _ctx_. Notice that the four Funcs are passed an ScFuncContext, whereas the two Views receive an ScViewContext. -This example also showcases an important feature of the contexts: the `log()` method. -This can be used to log human-readable text to the host's log output. -Logging text is the only way to add tracing to a smart contract, -because it does not have any I/O capabilities other than what the host provides. There is -a second logging method, called `trace()`, that can be used to provide extra debug -information to the host's log output. This output can be selectively turned on and off at -the host. +This example also showcases an important feature of the contexts: the `log()` method. This +can be used to log human-readable text to the host's log output. Logging text is the only +way to add tracing to a smart contract, because it does not have any I/O capabilities +other than what the host provides. There is a second logging method, called `trace()`, +that can be used to provide extra debug information to the host's log output. The trace +output can be selectively turned on and off at the host. In the next section we will introduce the [`schema`](../schema/schema.mdx) tool that simplifies smart contract programming a lot. diff --git a/documentation/docs/guide/wasm_vm/intro.mdx b/documentation/docs/guide/wasm_vm/intro.mdx index d457d8a8ea..296cb413d6 100644 --- a/documentation/docs/guide/wasm_vm/intro.mdx +++ b/documentation/docs/guide/wasm_vm/intro.mdx @@ -1,6 +1,8 @@ --- keywords: - Rust +- Go +- TypeScript - WASM - memory space - smart contract state @@ -8,7 +10,7 @@ keywords: - Access - store - state -description: The Iota Smart Contracts Protocol (ISCP) provides a very flexible way of programming smart contracts by providing an API to a sandboxed environment that allows you to interact with the ISCP deterministically without any security risks. +description: The Iota Smart Contracts Protocol (ISCP) provides a very flexible way of programming smart contracts by providing an API to a sandboxed environment that allows you to interact with the ISCP deterministically without any security risks. image: /img/wasm_vm/IscpHost.png --- # Introduction to the Wasm VM for ISCP @@ -64,17 +66,22 @@ The ISCP sandbox environment enables the following functionality: - Access to logging functionality - Access to several utility functions provided by the host -The initial implementation of WasmLib has been created for the Rust programming language. -Rust had the most advanced and stable support for generating Wasm code at the time when we -started implementing our Wasm VM environment. In the meantime, we have also implemented a -fully functional [TinyGo](https://tinygo.org/) version. +The initial WasmLib implementation was created for the [Rust](https://www.rust-lang.org/) +programming language. Rust had the most advanced and stable support for generating Wasm +code at the time when we started implementing our Wasm VM environment. In the meantime, we +have also implemented fully functional [Go](https://golang.org/) and +[TypeScript](https://www.typescriptlang.org/) implementations. +The Go implementation uses the [TinyGo](https://tinygo.org/) compiler to generate Wasm +code. +The TypeScript implementation uses the [AssemblyScript](https://www.assemblyscript.org/) +compiler to generate Wasm code. -Both implementations use only a very small common subset of their host languages. This +All implementations use only a very small common subset of their host languages. This keeps the coding style very similar, barring some syntactic idiosyncrasies. The reason for this is that we wanted to make it as easy as possible for anyone to start working with our smart contract system. If you have any previous experience in any C-style language you -should quickly feel comfortable writing smart contracts in either language, without having -to dive deeply into all aspects of the chosen language. +should quickly feel comfortable writing smart contracts in any of the supported +languages, without having to dive deeply into all aspects of the chosen language. We will now dive deeper into the concepts that are central to WasmLib smart contract programming. diff --git a/documentation/docs/metrics.md b/documentation/docs/metrics.md new file mode 100644 index 0000000000..3120cdaa30 --- /dev/null +++ b/documentation/docs/metrics.md @@ -0,0 +1,24 @@ +# Exposed Metrics + +You can see all exposed metrics on https://metrics.wasp.sc.iota.org. + +### wasp_off_ledger_requests_counter +Off ledger requests per chain + +### wasp_on_ledger_request_counter +On ledger requests per chain + +### wasp_processed_request_counter +Total number of requests processed + +### messages_received_per_chain +Number of messages received per chain + +### receive_requests_acknowledgement_message +Number of request acknowledgement messages per chain + +### request_processing_time +Time to process request + +### vm_run_time +Time it takes to run the vm diff --git a/documentation/docs/tutorial/03.md b/documentation/docs/tutorial/03.md index 47c4350256..392110189d 100644 --- a/documentation/docs/tutorial/03.md +++ b/documentation/docs/tutorial/03.md @@ -111,7 +111,7 @@ fn withdraw_iota(ctx: &ScFuncContext) { let bal = ctx.balances().balance(&ScColor::IOTA); if bal > 0 { - ctx.transfer_to_address(&caller.address(), ScTransfers::new(&ScColor::IOTA, bal)) + ctx.transfer_to_address(&caller.address(), ScTransfers::transfer(&ScColor::IOTA, bal)) } } ``` diff --git a/documentation/sidebars.js b/documentation/sidebars.js index be74fa6681..61eb907815 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -431,6 +431,11 @@ module.exports = { type: 'doc', label: 'Contribute', id: 'contribute', + }, + { + type: 'doc', + label: 'Metrics', + id: 'metrics', } ], }; diff --git a/documentation/tutorial-examples/Cargo.toml b/documentation/tutorial-examples/Cargo.toml index 89a4ce70d8..f8fe8d12c8 100644 --- a/documentation/tutorial-examples/Cargo.toml +++ b/documentation/tutorial-examples/Cargo.toml @@ -17,7 +17,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -wasmlib = { path = "../../contracts/wasm/wasmlib" } +wasmlib = { path = "../../packages/vm/wasmlib" } #wasmlib = { git = "https://github.com/iotaledger/wasp", branch = "develop" } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/documentation/tutorial-examples/src/lib.rs b/documentation/tutorial-examples/src/lib.rs index bd296b3bb4..4519d685cc 100644 --- a/documentation/tutorial-examples/src/lib.rs +++ b/documentation/tutorial-examples/src/lib.rs @@ -55,6 +55,6 @@ fn withdraw_iota(ctx: &ScFuncContext) { let bal = ctx.balances().balance(&ScColor::IOTA); if bal > 0 { - ctx.transfer_to_address(&caller.address(), ScTransfers::new(&ScColor::IOTA, bal)) + ctx.transfer_to_address(&caller.address(), ScTransfers::transfer(&ScColor::IOTA, bal)) } } diff --git a/documentation/tutorial-examples/test/example_tutorial_bg.wasm b/documentation/tutorial-examples/test/example_tutorial_bg.wasm index a538dbc0f7..0fdb6aa458 100644 Binary files a/documentation/tutorial-examples/test/example_tutorial_bg.wasm and b/documentation/tutorial-examples/test/example_tutorial_bg.wasm differ diff --git a/go.mod b/go.mod index 2f0d85a6ea..06bfcd5faf 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/anthdm/hbbft v0.0.0-20190702061856-0826ffdcf567 github.com/bygui86/multi-profile/v2 v2.1.0 github.com/bytecodealliance/wasmtime-go v0.21.0 - github.com/ethereum/go-ethereum v1.10.8 + github.com/ethereum/go-ethereum v1.10.10 github.com/iotaledger/goshimmer v0.7.5-0.20210811162925-25c827e8326a github.com/iotaledger/hive.go v0.0.0-20210625103722-68b2cf52ef4e github.com/knadh/koanf v0.15.0 @@ -39,8 +39,9 @@ require ( nhooyr.io/websocket v1.8.7 ) -replace github.com/anthdm/hbbft => github.com/kape1395/hbbft v0.0.0-20210824083459-b949585b7515 - -replace go.dedis.ch/kyber/v3 v3.0.13 => github.com/kape1395/kyber/v3 v3.0.14-0.20210622094514-fefb81148dc3 - -replace github.com/linxGnu/grocksdb => github.com/gohornet/grocksdb v1.6.38-0.20211012114404-55f425442260 +replace ( + github.com/anthdm/hbbft => github.com/kape1395/hbbft v0.0.0-20210824083459-b949585b7515 + github.com/ethereum/go-ethereum => github.com/dessaya/go-ethereum v1.10.10-0.20211102133541-45878bcd628c + github.com/linxGnu/grocksdb => github.com/gohornet/grocksdb v1.6.38-0.20211012114404-55f425442260 + go.dedis.ch/kyber/v3 v3.0.13 => github.com/kape1395/kyber/v3 v3.0.14-0.20210622094514-fefb81148dc3 +) diff --git a/go.sum b/go.sum index ddad4650b2..c6ebbb0c29 100644 --- a/go.sum +++ b/go.sum @@ -224,6 +224,8 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vs github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= +github.com/dessaya/go-ethereum v1.10.10-0.20211102133541-45878bcd628c h1:dVcpMbGEVK3K0uHwXjB0RAoKnxeNx9z6MB211FjgJbE= +github.com/dessaya/go-ethereum v1.10.10-0.20211102133541-45878bcd628c/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -241,9 +243,10 @@ github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/drand/bls12-381 v0.3.2/go.mod h1:dtcLgPtYT38L3NO6mPDYH0nbpc5tjPassDqiniuAt4Y= github.com/drand/drand v1.1.1/go.mod h1:KFcGL4SJFSOMLWoLLOL77+yLoL2d7nIXYOvUOdMlNBM= github.com/drand/kyber v1.0.1-0.20200110225416-8de27ed8c0e2/go.mod h1:UpXoA0Upd1N9l4TvRPHr1qAUBBERj6JQ/mnKI3BPEmw= @@ -270,8 +273,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.10.8 h1:0UP5WUR8hh46ffbjJV7PK499+uGEyasRIfffS0vy06o= -github.com/ethereum/go-ethereum v1.10.8/go.mod h1:pJNuIUYfX5+JKzSD/BTdNsvJSZ1TJqmz0dVyXMAbf6M= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -341,7 +342,7 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -437,8 +438,9 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -508,6 +510,8 @@ github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoP github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= @@ -676,8 +680,7 @@ github.com/kape1395/hbbft v0.0.0-20210824083459-b949585b7515 h1:5oWqeyYqb0A+XNeM github.com/kape1395/hbbft v0.0.0-20210824083459-b949585b7515/go.mod h1:XadZ7V3kvoIfFGVb8s/ndgVitoHRNRjzgPlHhhb9HlI= github.com/kape1395/kyber/v3 v3.0.14-0.20210622094514-fefb81148dc3 h1:b7VGpy3biHI8Q0O8BthxiJfOXhx5F0Ej4TNuJ9A1omo= github.com/kape1395/kyber/v3 v3.0.14-0.20210622094514-fefb81148dc3/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= @@ -1046,8 +1049,11 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4= github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -1379,8 +1385,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= -github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= diff --git a/packages/apilib/deploychain.go b/packages/apilib/deploychain.go index 42ffce51f2..4f71778df4 100644 --- a/packages/apilib/deploychain.go +++ b/packages/apilib/deploychain.go @@ -6,7 +6,6 @@ package apilib import ( "fmt" "io" - "io/ioutil" "math/rand" "time" @@ -64,7 +63,7 @@ func DeployChainWithDKG(par CreateChainParams) (*iscp.ChainID, ledgerstate.Addre func DeployChain(par CreateChainParams, stateControllerAddr ledgerstate.Address) (*iscp.ChainID, error) { var err error - textout := ioutil.Discard + textout := io.Discard if par.Textout != nil { textout = par.Textout } diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index bb1ea8922a..63b7e2aab2 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -348,7 +348,7 @@ func (c *chainObj) publishNewBlockEvents(blockIndex uint32) { go func() { for _, msg := range evts { - c.log.Infof("publishNewBlockEvents: '%s'", msg) + c.log.Debugf("publishNewBlockEvents: '%s'", msg) publisher.Publish("vmmsg", c.chainID.Base58(), msg) } }() diff --git a/packages/chain/messages/req_ack_msg.go b/packages/chain/messages/req_ack_msg.go index 1c8f1a4f83..59b5054c56 100644 --- a/packages/chain/messages/req_ack_msg.go +++ b/packages/chain/messages/req_ack_msg.go @@ -6,7 +6,6 @@ package messages import ( "bytes" "io" - "io/ioutil" "github.com/iotaledger/wasp/packages/iscp" "golang.org/x/xerrors" @@ -36,7 +35,7 @@ func (msg *RequestAcKMsg) Bytes() []byte { } func (msg *RequestAcKMsg) read(r io.Reader) error { - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { return xerrors.Errorf("failed to read requestIDs: %w", err) } diff --git a/packages/database/dbmanager/dbmanager.go b/packages/database/dbmanager/dbmanager.go index f797f1d122..4343335274 100644 --- a/packages/database/dbmanager/dbmanager.go +++ b/packages/database/dbmanager/dbmanager.go @@ -11,6 +11,7 @@ import ( "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/timeutil" + "github.com/iotaledger/wasp/packages/database/registrykvstore" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/parameters" ) @@ -37,7 +38,7 @@ func NewDBManager(log *logger.Logger, inMemory bool) *DBManager { } // registry db is created with an empty chainID dbm.registryDB = dbm.createDB(nil) - dbm.registryStore = dbm.registryDB.NewStore() + dbm.registryStore = registrykvstore.New(dbm.registryDB.NewStore()) return &dbm } @@ -52,12 +53,10 @@ func (m *DBManager) createDB(chainID *iscp.ChainID) database.DB { m.mutex.Lock() defer m.mutex.Unlock() - if chainID == nil { - m.log.Infof("creating new registry database. Persistent: %v", !m.inMemory) - } else { - m.log.Infof("creating new database for chain %s. Persistent: %v", chainID.String(), !m.inMemory) - } + chainIDBase58 := getChainBase58(chainID) + if m.inMemory { + m.log.Infof("creating new in-memory database for: %s.", chainIDBase58) db, err := database.NewMemDB() if err != nil { m.log.Fatal(err) @@ -74,7 +73,14 @@ func (m *DBManager) createDB(chainID *iscp.ChainID) database.DB { return nil } } - instanceDir := fmt.Sprintf("%s/%s", dbDir, getChainBase58(chainID)) + + instanceDir := fmt.Sprintf("%s/%s", dbDir, chainIDBase58) + if _, err := os.Stat(dbDir); os.IsNotExist(err) { + m.log.Infof("creating new database for: %s.", chainIDBase58) + } else { + m.log.Infof("using existing database for: %s.", chainIDBase58) + } + db, err := database.NewDB(instanceDir) if err != nil { m.log.Fatal(err) diff --git a/packages/database/registrykvstore/registrykvstore.go b/packages/database/registrykvstore/registrykvstore.go new file mode 100644 index 0000000000..634e85f9a2 --- /dev/null +++ b/packages/database/registrykvstore/registrykvstore.go @@ -0,0 +1,78 @@ +package registrykvstore + +import "github.com/iotaledger/hive.go/kvstore" + +// registrykvstore is just a wrapper to any kv store that flushes the changes to disk immediately (Sets or Dels) +// this is to prevent that the registry database is corrupted if the node is not shutdown gracefully + +type RegistryKVStore struct { + store kvstore.KVStore +} + +func New(store kvstore.KVStore) kvstore.KVStore { + return &RegistryKVStore{store} +} + +func (s *RegistryKVStore) WithRealm(realm kvstore.Realm) kvstore.KVStore { + return s.store.WithRealm(realm) +} + +func (s *RegistryKVStore) Realm() kvstore.Realm { + return s.store.Realm() +} + +func (s *RegistryKVStore) Shutdown() { + s.store.Shutdown() +} + +func (s *RegistryKVStore) Iterate(prefix kvstore.KeyPrefix, consumerFunc kvstore.IteratorKeyValueConsumerFunc) error { + return s.store.Iterate(prefix, consumerFunc) +} + +func (s *RegistryKVStore) IterateKeys(prefix kvstore.KeyPrefix, consumerFunc kvstore.IteratorKeyConsumerFunc) error { + return s.store.IterateKeys(prefix, consumerFunc) +} + +func (s *RegistryKVStore) Clear() error { + return s.store.Clear() +} + +func (s *RegistryKVStore) Get(key kvstore.Key) (value kvstore.Value, err error) { + return s.store.Get(key) +} + +func (s *RegistryKVStore) Set(key kvstore.Key, value kvstore.Value) error { + err := s.store.Set(key, value) + if err != nil { + return err + } + return s.store.Flush() +} + +func (s *RegistryKVStore) Has(key kvstore.Key) (bool, error) { + return s.store.Has(key) +} + +func (s *RegistryKVStore) Delete(key kvstore.Key) error { + err := s.store.Delete(key) + if err != nil { + return err + } + return s.store.Flush() +} + +func (s *RegistryKVStore) DeletePrefix(prefix kvstore.KeyPrefix) error { + return s.store.DeletePrefix(prefix) +} + +func (s *RegistryKVStore) Batched() kvstore.BatchedMutations { + return s.store.Batched() +} + +func (s *RegistryKVStore) Flush() error { + return s.store.Flush() +} + +func (s *RegistryKVStore) Close() error { + return s.store.Close() +} diff --git a/packages/downloader/downloader.go b/packages/downloader/downloader.go index 168a4f1283..dd00764131 100644 --- a/packages/downloader/downloader.go +++ b/packages/downloader/downloader.go @@ -2,7 +2,7 @@ package downloader import ( "fmt" - "io/ioutil" + "io" "net/http" "strings" "sync" @@ -139,5 +139,5 @@ func (*Downloader) donwloadFromHTTP(url string) ([]byte, error) { } defer response.Body.Close() - return ioutil.ReadAll(response.Body) + return io.ReadAll(response.Body) } diff --git a/packages/evm/evmflavors/util.go b/packages/evm/evmflavors/util.go new file mode 100644 index 0000000000..71cb599717 --- /dev/null +++ b/packages/evm/evmflavors/util.go @@ -0,0 +1,20 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmflavors + +import ( + "github.com/iotaledger/wasp/contracts/native/evm/evmchain" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight" + "github.com/iotaledger/wasp/packages/iscp/coreutil" +) + +var All = map[string]*coreutil.ContractInfo{ + evmchain.Contract.Name: evmchain.Contract, + evmlight.Contract.Name: evmlight.Contract, +} + +var Processors = map[string]*coreutil.ContractProcessor{ + evmlight.Contract.Name: evmlight.Processor, + evmchain.Contract.Name: evmchain.Processor, +} diff --git a/contracts/native/evmchain/block.go b/packages/evm/evmtypes/block.go similarity index 96% rename from contracts/native/evmchain/block.go rename to packages/evm/evmtypes/block.go index 80573c980b..3af507a4c6 100644 --- a/contracts/native/evmchain/block.go +++ b/packages/evm/evmtypes/block.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evmchain +package evmtypes import ( "bytes" diff --git a/contracts/native/evmchain/callargs.go b/packages/evm/evmtypes/callargs.go similarity index 98% rename from contracts/native/evmchain/callargs.go rename to packages/evm/evmtypes/callargs.go index e2add23338..816286909f 100644 --- a/contracts/native/evmchain/callargs.go +++ b/packages/evm/evmtypes/callargs.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evmchain +package evmtypes import ( "math/big" diff --git a/contracts/native/evmchain/genesis.go b/packages/evm/evmtypes/genesis.go similarity index 99% rename from contracts/native/evmchain/genesis.go rename to packages/evm/evmtypes/genesis.go index e82b46b33c..f63ef496ad 100644 --- a/contracts/native/evmchain/genesis.go +++ b/packages/evm/evmtypes/genesis.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evmchain +package evmtypes import ( "math/big" diff --git a/contracts/native/evmchain/logs.go b/packages/evm/evmtypes/logs.go similarity index 99% rename from contracts/native/evmchain/logs.go rename to packages/evm/evmtypes/logs.go index 89aaea02d9..05c03bbf4b 100644 --- a/contracts/native/evmchain/logs.go +++ b/packages/evm/evmtypes/logs.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evmchain +package evmtypes import ( "bytes" diff --git a/packages/evm/evmtypes/receipt.go b/packages/evm/evmtypes/receipt.go new file mode 100644 index 0000000000..5fdd3547bd --- /dev/null +++ b/packages/evm/evmtypes/receipt.go @@ -0,0 +1,79 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmtypes + +import ( + "bytes" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/iotaledger/hive.go/marshalutil" +) + +// EncodeReceipt serializes the receipt in RLP format +func EncodeReceipt(receipt *types.Receipt) []byte { + var b bytes.Buffer + err := receipt.EncodeRLP(&b) + if err != nil { + panic(err) + } + return b.Bytes() +} + +func DecodeReceipt(b []byte) (*types.Receipt, error) { + receipt := new(types.Receipt) + err := receipt.DecodeRLP(rlp.NewStream(bytes.NewReader(b), 0)) + return receipt, err +} + +// EncodeReceiptFull encodes the receipt including fields not serialized by EncodeReceipt +func EncodeReceiptFull(r *types.Receipt) []byte { + m := marshalutil.New() + writeBytes(m, EncodeReceipt(r)) + m.WriteBytes(r.TxHash.Bytes()) + m.WriteBytes(r.BlockHash.Bytes()) + writeBytes(m, r.BlockNumber.Bytes()) + m.WriteBytes(r.ContractAddress.Bytes()) + return m.Bytes() +} + +func DecodeReceiptFull(receiptBytes []byte) (*types.Receipt, error) { + m := marshalutil.New(receiptBytes) + var err error + var b []byte + + if b, err = readBytes(m); err != nil { + return nil, err + } + var r *types.Receipt + if r, err = DecodeReceipt(b); err != nil { + return nil, err + } + + if b, err = m.ReadBytes(common.HashLength); err != nil { + return nil, err + } + r.TxHash.SetBytes(b) + + if b, err = m.ReadBytes(common.HashLength); err != nil { + return nil, err + } + r.BlockHash.SetBytes(b) + + if b, err = readBytes(m); err != nil { + return nil, err + } + r.BlockNumber = new(big.Int).SetBytes(b) + + if b, err = m.ReadBytes(common.AddressLength); err != nil { + return nil, err + } + r.ContractAddress.SetBytes(b) + + r.GasUsed = r.CumulativeGasUsed + + return r, nil +} diff --git a/packages/evm/evmtypes/signer.go b/packages/evm/evmtypes/signer.go new file mode 100644 index 0000000000..ae88cd938f --- /dev/null +++ b/packages/evm/evmtypes/signer.go @@ -0,0 +1,14 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package evmtypes + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/core/types" +) + +func Signer(chainID *big.Int) types.Signer { + return types.NewEIP155Signer(chainID) +} diff --git a/contracts/native/evmchain/transaction.go b/packages/evm/evmtypes/transaction.go similarity index 76% rename from contracts/native/evmchain/transaction.go rename to packages/evm/evmtypes/transaction.go index 58d0e3eb0f..b4b7e9b156 100644 --- a/contracts/native/evmchain/transaction.go +++ b/packages/evm/evmtypes/transaction.go @@ -1,7 +1,7 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -package evmchain +package evmtypes import ( "bytes" @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" - "github.com/iotaledger/wasp/packages/evm" ) func EncodeTransaction(tx *types.Transaction) []byte { @@ -27,7 +26,7 @@ func DecodeTransaction(b []byte) (*types.Transaction, error) { return tx, err } -func getSender(tx *types.Transaction) common.Address { - sender, _ := types.Sender(evm.Signer(tx.ChainId()), tx) +func GetSender(tx *types.Transaction) common.Address { + sender, _ := types.Sender(Signer(tx.ChainId()), tx) return sender } diff --git a/packages/evm/jsonrpc/evmchain.go b/packages/evm/jsonrpc/evmchain.go index 5ff403d9ab..cd7984ecf2 100644 --- a/packages/evm/jsonrpc/evmchain.go +++ b/packages/evm/jsonrpc/evmchain.go @@ -11,8 +11,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/wasp/contracts/native/evmchain" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/evm/evmtypes" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/colored" "github.com/iotaledger/wasp/packages/kv/codec" @@ -33,31 +33,31 @@ func NewEVMChain(backend ChainBackend, chainID int, contractName string) *EVMCha } func (e *EVMChain) Signer() types.Signer { - return evm.Signer(big.NewInt(int64(e.chainID))) + return evmtypes.Signer(big.NewInt(int64(e.chainID))) } func (e *EVMChain) GasPerIota() (uint64, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetGasPerIota.Name, nil) + ret, err := e.backend.CallView(e.contractName, evm.FuncGetGasPerIota.Name, nil) if err != nil { return 0, err } - return codec.DecodeUint64(ret.MustGet(evmchain.FieldResult)) + return codec.DecodeUint64(ret.MustGet(evm.FieldResult)) } func (e *EVMChain) BlockNumber() (*big.Int, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetBlockNumber.Name, nil) + ret, err := e.backend.CallView(e.contractName, evm.FuncGetBlockNumber.Name, nil) if err != nil { return nil, err } bal := big.NewInt(0) - bal.SetBytes(ret.MustGet(evmchain.FieldResult)) + bal.SetBytes(ret.MustGet(evm.FieldResult)) return bal, nil } func (e *EVMChain) FeeColor() (colored.Color, error) { feeInfo, err := e.backend.CallView(governance.Contract.Name, governance.FuncGetFeeInfo.Name, dict.Dict{ - root.ParamHname: evmchain.Contract.Hname().Bytes(), + root.ParamHname: iscp.Hn(e.contractName).Bytes(), }) if err != nil { return colored.Color{}, err @@ -109,9 +109,9 @@ func (e *EVMChain) SendTransaction(tx *types.Transaction) error { if err != nil { return err } - // send the Ethereum transaction to the evmchain contract - return e.backend.PostOffLedgerRequest(e.contractName, evmchain.FuncSendTransaction.Name, fee, dict.Dict{ - evmchain.FieldTransactionData: txdata, + // send the Ethereum transaction + return e.backend.PostOffLedgerRequest(e.contractName, evm.FuncSendTransaction.Name, fee, dict.Dict{ + evm.FieldTransactionData: txdata, }) } @@ -121,7 +121,7 @@ func paramsWithOptionalBlockNumber(blockNumber *big.Int, params dict.Dict) dict. ret = dict.Dict{} } if blockNumber != nil { - ret.Set(evmchain.FieldBlockNumber, blockNumber.Bytes()) + ret.Set(evm.FieldBlockNumber, blockNumber.Bytes()) } return ret } @@ -135,44 +135,44 @@ func paramsWithOptionalBlockNumberOrHash(blockNumberOrHash rpc.BlockNumberOrHash ret = dict.Dict{} } blockHash, _ := blockNumberOrHash.Hash() - ret.Set(evmchain.FieldBlockHash, blockHash.Bytes()) + ret.Set(evm.FieldBlockHash, blockHash.Bytes()) return ret } func (e *EVMChain) Balance(address common.Address, blockNumberOrHash rpc.BlockNumberOrHash) (*big.Int, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetBalance.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ - evmchain.FieldAddress: address.Bytes(), + ret, err := e.backend.CallView(e.contractName, evm.FuncGetBalance.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ + evm.FieldAddress: address.Bytes(), })) if err != nil { return nil, err } bal := big.NewInt(0) - bal.SetBytes(ret.MustGet(evmchain.FieldResult)) + bal.SetBytes(ret.MustGet(evm.FieldResult)) return bal, nil } func (e *EVMChain) Code(address common.Address, blockNumberOrHash rpc.BlockNumberOrHash) ([]byte, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetCode.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ - evmchain.FieldAddress: address.Bytes(), + ret, err := e.backend.CallView(e.contractName, evm.FuncGetCode.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ + evm.FieldAddress: address.Bytes(), })) if err != nil { return nil, err } - return ret.MustGet(evmchain.FieldResult), nil + return ret.MustGet(evm.FieldResult), nil } func (e *EVMChain) BlockByNumber(blockNumber *big.Int) (*types.Block, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetBlockByNumber.Name, paramsWithOptionalBlockNumber(blockNumber, nil)) + ret, err := e.backend.CallView(e.contractName, evm.FuncGetBlockByNumber.Name, paramsWithOptionalBlockNumber(blockNumber, nil)) if err != nil { return nil, err } - if !ret.MustHas(evmchain.FieldResult) { + if !ret.MustHas(evm.FieldResult) { return nil, nil } - block, err := evmchain.DecodeBlock(ret.MustGet(evmchain.FieldResult)) + block, err := evmtypes.DecodeBlock(ret.MustGet(evm.FieldResult)) if err != nil { return nil, err } @@ -186,74 +186,74 @@ func (e *EVMChain) getTransactionBy(funcName string, args dict.Dict) (tx *types. return } - if !ret.MustHas(evmchain.FieldTransaction) { + if !ret.MustHas(evm.FieldTransaction) { return } - tx, err = evmchain.DecodeTransaction(ret.MustGet(evmchain.FieldTransaction)) + tx, err = evmtypes.DecodeTransaction(ret.MustGet(evm.FieldTransaction)) if err != nil { return } - blockHash = common.BytesToHash(ret.MustGet(evmchain.FieldBlockHash)) - blockNumber, err = codec.DecodeUint64(ret.MustGet(evmchain.FieldBlockNumber), 0) + blockHash = common.BytesToHash(ret.MustGet(evm.FieldBlockHash)) + blockNumber, err = codec.DecodeUint64(ret.MustGet(evm.FieldBlockNumber), 0) if err != nil { return } - index, err = codec.DecodeUint64(ret.MustGet(evmchain.FieldTransactionIndex), 0) + index, err = codec.DecodeUint64(ret.MustGet(evm.FieldTransactionIndex), 0) return } func (e *EVMChain) TransactionByHash(hash common.Hash) (tx *types.Transaction, blockHash common.Hash, blockNumber, index uint64, err error) { - return e.getTransactionBy(evmchain.FuncGetTransactionByHash.Name, dict.Dict{ - evmchain.FieldTransactionHash: hash.Bytes(), + return e.getTransactionBy(evm.FuncGetTransactionByHash.Name, dict.Dict{ + evm.FieldTransactionHash: hash.Bytes(), }) } func (e *EVMChain) TransactionByBlockHashAndIndex(hash common.Hash, index uint64) (tx *types.Transaction, blockHash common.Hash, blockNumber, indexRet uint64, err error) { - return e.getTransactionBy(evmchain.FuncGetTransactionByBlockHashAndIndex.Name, dict.Dict{ - evmchain.FieldBlockHash: hash.Bytes(), - evmchain.FieldTransactionIndex: codec.EncodeUint64(index), + return e.getTransactionBy(evm.FuncGetTransactionByBlockHashAndIndex.Name, dict.Dict{ + evm.FieldBlockHash: hash.Bytes(), + evm.FieldTransactionIndex: codec.EncodeUint64(index), }) } func (e *EVMChain) TransactionByBlockNumberAndIndex(blockNumber *big.Int, index uint64) (tx *types.Transaction, blockHash common.Hash, blockNumberRet, indexRet uint64, err error) { - return e.getTransactionBy(evmchain.FuncGetTransactionByBlockNumberAndIndex.Name, paramsWithOptionalBlockNumber(blockNumber, dict.Dict{ - evmchain.FieldTransactionIndex: codec.EncodeUint64(index), + return e.getTransactionBy(evm.FuncGetTransactionByBlockNumberAndIndex.Name, paramsWithOptionalBlockNumber(blockNumber, dict.Dict{ + evm.FieldTransactionIndex: codec.EncodeUint64(index), })) } func (e *EVMChain) BlockByHash(hash common.Hash) (*types.Block, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetBlockByHash.Name, dict.Dict{ - evmchain.FieldBlockHash: hash.Bytes(), + ret, err := e.backend.CallView(e.contractName, evm.FuncGetBlockByHash.Name, dict.Dict{ + evm.FieldBlockHash: hash.Bytes(), }) if err != nil { return nil, err } - if !ret.MustHas(evmchain.FieldResult) { + if !ret.MustHas(evm.FieldResult) { return nil, nil } - block, err := evmchain.DecodeBlock(ret.MustGet(evmchain.FieldResult)) + block, err := evmtypes.DecodeBlock(ret.MustGet(evm.FieldResult)) if err != nil { return nil, err } return block, nil } -func (e *EVMChain) TransactionReceipt(txHash common.Hash) (*evmchain.Receipt, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetReceipt.Name, dict.Dict{ - evmchain.FieldTransactionHash: txHash.Bytes(), +func (e *EVMChain) TransactionReceipt(txHash common.Hash) (*types.Receipt, error) { + ret, err := e.backend.CallView(e.contractName, evm.FuncGetReceipt.Name, dict.Dict{ + evm.FieldTransactionHash: txHash.Bytes(), }) if err != nil { return nil, err } - if !ret.MustHas(evmchain.FieldResult) { + if !ret.MustHas(evm.FieldResult) { return nil, nil } - receipt, err := evmchain.DecodeReceipt(ret.MustGet(evmchain.FieldResult)) + receipt, err := evmtypes.DecodeReceiptFull(ret.MustGet(evm.FieldResult)) if err != nil { return nil, err } @@ -261,70 +261,70 @@ func (e *EVMChain) TransactionReceipt(txHash common.Hash) (*evmchain.Receipt, er } func (e *EVMChain) TransactionCount(address common.Address, blockNumberOrHash rpc.BlockNumberOrHash) (uint64, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetNonce.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ - evmchain.FieldAddress: address.Bytes(), + ret, err := e.backend.CallView(e.contractName, evm.FuncGetNonce.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ + evm.FieldAddress: address.Bytes(), })) if err != nil { return 0, err } - return codec.DecodeUint64(ret.MustGet(evmchain.FieldResult), 0) + return codec.DecodeUint64(ret.MustGet(evm.FieldResult), 0) } func (e *EVMChain) CallContract(args ethereum.CallMsg, blockNumberOrHash rpc.BlockNumberOrHash) ([]byte, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncCallContract.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ - evmchain.FieldCallMsg: evmchain.EncodeCallMsg(args), + ret, err := e.backend.CallView(e.contractName, evm.FuncCallContract.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ + evm.FieldCallMsg: evmtypes.EncodeCallMsg(args), })) if err != nil { return nil, err } - return ret.MustGet(evmchain.FieldResult), nil + return ret.MustGet(evm.FieldResult), nil } func (e *EVMChain) EstimateGas(args ethereum.CallMsg) (uint64, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncEstimateGas.Name, dict.Dict{ - evmchain.FieldCallMsg: evmchain.EncodeCallMsg(args), + ret, err := e.backend.CallView(e.contractName, evm.FuncEstimateGas.Name, dict.Dict{ + evm.FieldCallMsg: evmtypes.EncodeCallMsg(args), }) if err != nil { return 0, err } - return codec.DecodeUint64(ret.MustGet(evmchain.FieldResult), 0) + return codec.DecodeUint64(ret.MustGet(evm.FieldResult), 0) } func (e *EVMChain) StorageAt(address common.Address, key common.Hash, blockNumberOrHash rpc.BlockNumberOrHash) ([]byte, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetStorage.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ - evmchain.FieldAddress: address.Bytes(), - evmchain.FieldKey: key.Bytes(), + ret, err := e.backend.CallView(e.contractName, evm.FuncGetStorage.Name, paramsWithOptionalBlockNumberOrHash(blockNumberOrHash, dict.Dict{ + evm.FieldAddress: address.Bytes(), + evm.FieldKey: key.Bytes(), })) if err != nil { return nil, err } - return ret.MustGet(evmchain.FieldResult), nil + return ret.MustGet(evm.FieldResult), nil } func (e *EVMChain) BlockTransactionCountByHash(blockHash common.Hash) (uint64, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetBlockTransactionCountByHash.Name, dict.Dict{ - evmchain.FieldBlockHash: blockHash.Bytes(), + ret, err := e.backend.CallView(e.contractName, evm.FuncGetTransactionCountByBlockHash.Name, dict.Dict{ + evm.FieldBlockHash: blockHash.Bytes(), }) if err != nil { return 0, err } - return codec.DecodeUint64(ret.MustGet(evmchain.FieldResult), 0) + return codec.DecodeUint64(ret.MustGet(evm.FieldResult), 0) } func (e *EVMChain) BlockTransactionCountByNumber(blockNumber *big.Int) (uint64, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetBlockTransactionCountByNumber.Name, paramsWithOptionalBlockNumber(blockNumber, nil)) + ret, err := e.backend.CallView(e.contractName, evm.FuncGetTransactionCountByBlockNumber.Name, paramsWithOptionalBlockNumber(blockNumber, nil)) if err != nil { return 0, err } - return codec.DecodeUint64(ret.MustGet(evmchain.FieldResult), 0) + return codec.DecodeUint64(ret.MustGet(evm.FieldResult), 0) } func (e *EVMChain) Logs(q *ethereum.FilterQuery) ([]*types.Log, error) { - ret, err := e.backend.CallView(e.contractName, evmchain.FuncGetLogs.Name, dict.Dict{ - evmchain.FieldFilterQuery: evmchain.EncodeFilterQuery(q), + ret, err := e.backend.CallView(e.contractName, evm.FuncGetLogs.Name, dict.Dict{ + evm.FieldFilterQuery: evmtypes.EncodeFilterQuery(q), }) if err != nil { return nil, err } - return evmchain.DecodeLogs(ret.MustGet(evmchain.FieldResult)) + return evmtypes.DecodeLogs(ret.MustGet(evm.FieldResult)) } diff --git a/packages/evm/jsonrpc/jsonrpctest/env.go b/packages/evm/jsonrpc/jsonrpctest/env.go index 7a87904161..a750e10c5d 100644 --- a/packages/evm/jsonrpc/jsonrpctest/env.go +++ b/packages/evm/jsonrpc/jsonrpctest/env.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package jsonrpctest import ( @@ -15,15 +18,19 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" "github.com/iotaledger/wasp/packages/evm/evmtest" + "github.com/iotaledger/wasp/packages/evm/evmtypes" "github.com/iotaledger/wasp/packages/evm/jsonrpc" + "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/stretchr/testify/require" ) type Env struct { T *testing.T + EVMFlavor *coreutil.ContractInfo Server *rpc.Server Client *ethclient.Client RawClient *rpc.Client @@ -31,7 +38,7 @@ type Env struct { } func (e *Env) signer() types.Signer { - return evm.Signer(big.NewInt(int64(e.ChainID))) + return evmtypes.Signer(big.NewInt(int64(e.ChainID))) } var RequestFundsAmount = big.NewInt(1e18) // 1 ETH @@ -40,7 +47,7 @@ func (e *Env) RequestFunds(target common.Address) *types.Transaction { nonce, err := e.Client.NonceAt(context.Background(), evmtest.FaucetAddress, nil) require.NoError(e.T, err) tx, err := types.SignTx( - types.NewTransaction(nonce, target, RequestFundsAmount, evm.TxGas, evm.GasPrice, nil), + types.NewTransaction(nonce, target, RequestFundsAmount, params.TxGas, evm.GasPrice, nil), e.signer(), evmtest.FaucetKey, ) @@ -269,7 +276,10 @@ func (e *Env) TestRPCGetLogs() { require.Empty(e.T, e.getLogs(filterQuery)) - e.DeployEVMContract(creator, contractABI, evmtest.ERC20ContractBytecode, "TestCoin", "TEST") + tx, _ := e.DeployEVMContract(creator, contractABI, evmtest.ERC20ContractBytecode, "TestCoin", "TEST") + + receipt := e.MustTxReceipt(tx.Hash()) + require.Equal(e.T, 1, len(receipt.Logs)) require.Equal(e.T, 1, len(e.getLogs(filterQuery))) @@ -284,7 +294,7 @@ func (e *Env) TestRPCGetLogs() { Data: callArguments, })) require.NoError(e.T, err) - e.MustSendTransaction(&jsonrpc.SendTxArgs{ + txHash := e.MustSendTransaction(&jsonrpc.SendTxArgs{ From: creatorAddress, To: &contractAddress, Gas: &gas, @@ -294,6 +304,9 @@ func (e *Env) TestRPCGetLogs() { Data: (*hexutil.Bytes)(&callArguments), }) + receipt = e.MustTxReceipt(txHash) + require.Equal(e.T, 1, len(receipt.Logs)) + require.Equal(e.T, 2, len(e.getLogs(filterQuery))) } @@ -302,7 +315,7 @@ func (e *Env) TestRPCGasLimit() { toAddress := evmtest.AccountAddress(1) value := big.NewInt(1) nonce := e.NonceAt(fromAddress) - gasLimit := evm.TxGas - 1 + gasLimit := params.TxGas - 1 tx, err := types.SignTx( types.NewTransaction(nonce, toAddress, value, gasLimit, evm.GasPrice, nil), e.signer(), @@ -320,7 +333,7 @@ func (e *Env) TestRPCInvalidNonce() { toAddress := evmtest.AccountAddress(1) value := big.NewInt(1) nonce := e.NonceAt(fromAddress) + 1 - gasLimit := evm.TxGas - 1 + gasLimit := params.TxGas - 1 tx, err := types.SignTx( types.NewTransaction(nonce, toAddress, value, gasLimit, evm.GasPrice, nil), e.signer(), diff --git a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go index deaa79ce21..d1b03b9622 100644 --- a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go +++ b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go @@ -18,39 +18,48 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "github.com/iotaledger/wasp/contracts/native/evmchain" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/evm/evmflavors" "github.com/iotaledger/wasp/packages/evm/evmtest" + "github.com/iotaledger/wasp/packages/evm/evmtypes" "github.com/iotaledger/wasp/packages/evm/jsonrpc" + "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/solo" "github.com/stretchr/testify/require" ) +func withEVMFlavors(t *testing.T, f func(*testing.T, *coreutil.ContractInfo)) { + for _, evmFlavor := range evmflavors.All { + t.Run(evmFlavor.Name, func(t *testing.T) { f(t, evmFlavor) }) + } +} + type soloTestEnv struct { Env solo *solo.Solo } -func newSoloTestEnv(t *testing.T) *soloTestEnv { +func newSoloTestEnv(t *testing.T, evmFlavor *coreutil.ContractInfo) *soloTestEnv { evmtest.InitGoEthLogger(t) chainID := evm.DefaultChainID - s := solo.New(t, true, false).WithNativeContract(evmchain.Processor) + s := solo.New(t, true, false).WithNativeContract(evmflavors.Processors[evmFlavor.Name]) chainOwner, _ := s.NewKeyPairWithFunds() chain := s.NewChain(chainOwner, "iscpchain") - err := chain.DeployContract(chainOwner, "evmchain", evmchain.Contract.ProgramHash, - evmchain.FieldChainID, codec.EncodeUint16(uint16(chainID)), - evmchain.FieldGenesisAlloc, evmchain.EncodeGenesisAlloc(core.GenesisAlloc{ + err := chain.DeployContract(chainOwner, evmFlavor.Name, evmFlavor.ProgramHash, + evm.FieldChainID, codec.EncodeUint16(uint16(chainID)), + evm.FieldGenesisAlloc, evmtypes.EncodeGenesisAlloc(core.GenesisAlloc{ evmtest.FaucetAddress: {Balance: evmtest.FaucetSupply}, }), ) require.NoError(t, err) signer, _ := s.NewKeyPairWithFunds() backend := jsonrpc.NewSoloBackend(s, chain, signer) - evmChain := jsonrpc.NewEVMChain(backend, chainID, evmchain.Contract.Name) + evmChain := jsonrpc.NewEVMChain(backend, chainID, evmFlavor.Name) accountManager := jsonrpc.NewAccountManager(evmtest.Accounts) @@ -64,6 +73,7 @@ func newSoloTestEnv(t *testing.T) *soloTestEnv { return &soloTestEnv{ Env: Env{ T: t, + EVMFlavor: evmFlavor, Server: rpcsrv, Client: client, RawClient: rawClient, @@ -81,316 +91,370 @@ func generateKey(t *testing.T) (*ecdsa.PrivateKey, common.Address) { } func TestRPCGetBalance(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.Zero(t, big.NewInt(0).Cmp(env.Balance(receiverAddress))) - env.RequestFunds(receiverAddress) - require.Zero(t, big.NewInt(1e18).Cmp(env.Balance(receiverAddress))) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.Zero(t, big.NewInt(0).Cmp(env.Balance(receiverAddress))) + env.RequestFunds(receiverAddress) + require.Zero(t, big.NewInt(1e18).Cmp(env.Balance(receiverAddress))) + }) } func TestRPCGetCode(t *testing.T) { - env := newSoloTestEnv(t) - creator, creatorAddress := generateKey(t) - - // account address - { - env.RequestFunds(creatorAddress) - require.Empty(t, env.Code(creatorAddress)) - } - // contract address - { - contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) - require.NoError(t, err) - _, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) - require.NotEmpty(t, env.Code(contractAddress)) - } + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + creator, creatorAddress := generateKey(t) + + // account address + { + env.RequestFunds(creatorAddress) + require.Empty(t, env.Code(creatorAddress)) + } + // contract address + { + contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) + require.NoError(t, err) + _, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) + require.NotEmpty(t, env.Code(contractAddress)) + } + }) } func TestRPCGetStorage(t *testing.T) { - env := newSoloTestEnv(t) - creator, creatorAddress := generateKey(t) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + creator, creatorAddress := generateKey(t) - env.RequestFunds(creatorAddress) + env.RequestFunds(creatorAddress) - contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) - require.NoError(t, err) - _, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) + contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) + require.NoError(t, err) + _, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) - // first static variable in contract (uint32 n) has slot 0. See: - // https://docs.soliditylang.org/en/v0.6.6/miscellaneous.html#layout-of-state-variables-in-storage - slot := common.Hash{} - ret := env.Storage(contractAddress, slot) + // first static variable in contract (uint32 n) has slot 0. See: + // https://docs.soliditylang.org/en/v0.6.6/miscellaneous.html#layout-of-state-variables-in-storage + slot := common.Hash{} + ret := env.Storage(contractAddress, slot) - var v uint32 - err = contractABI.UnpackIntoInterface(&v, "retrieve", ret) - require.NoError(t, err) - require.Equal(t, uint32(42), v) + var v uint32 + err = contractABI.UnpackIntoInterface(&v, "retrieve", ret) + require.NoError(t, err) + require.Equal(t, uint32(42), v) + }) } func TestRPCBlockNumber(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.EqualValues(t, 0, env.BlockNumber()) - env.RequestFunds(receiverAddress) - require.EqualValues(t, 1, env.BlockNumber()) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.EqualValues(t, 0, env.BlockNumber()) + env.RequestFunds(receiverAddress) + require.EqualValues(t, 1, env.BlockNumber()) + }) } func TestRPCGetTransactionCount(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.EqualValues(t, 0, env.NonceAt(evmtest.FaucetAddress)) - env.RequestFunds(receiverAddress) - require.EqualValues(t, 1, env.NonceAt(evmtest.FaucetAddress)) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.EqualValues(t, 0, env.NonceAt(evmtest.FaucetAddress)) + env.RequestFunds(receiverAddress) + require.EqualValues(t, 1, env.NonceAt(evmtest.FaucetAddress)) + }) } func TestRPCGetBlockByNumber(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.EqualValues(t, 0, env.BlockByNumber(big.NewInt(0)).Number().Uint64()) - env.RequestFunds(receiverAddress) - require.EqualValues(t, 1, env.BlockByNumber(big.NewInt(1)).Number().Uint64()) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.EqualValues(t, 0, env.BlockByNumber(big.NewInt(0)).Number().Uint64()) + env.RequestFunds(receiverAddress) + require.EqualValues(t, 1, env.BlockByNumber(big.NewInt(1)).Number().Uint64()) + }) } func TestRPCGetBlockByHash(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.Nil(t, env.BlockByHash(common.Hash{})) - require.EqualValues(t, 0, env.BlockByHash(env.BlockByNumber(big.NewInt(0)).Hash()).Number().Uint64()) - env.RequestFunds(receiverAddress) - require.EqualValues(t, 1, env.BlockByHash(env.BlockByNumber(big.NewInt(1)).Hash()).Number().Uint64()) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.Nil(t, env.BlockByHash(common.Hash{})) + require.EqualValues(t, 0, env.BlockByHash(env.BlockByNumber(big.NewInt(0)).Hash()).Number().Uint64()) + env.RequestFunds(receiverAddress) + require.EqualValues(t, 1, env.BlockByHash(env.BlockByNumber(big.NewInt(1)).Hash()).Number().Uint64()) + }) } func TestRPCGetTransactionByHash(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.Nil(t, env.TransactionByHash(common.Hash{})) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - tx := env.TransactionByHash(block1.Transactions()[0].Hash()) - require.Equal(t, block1.Transactions()[0].Hash(), tx.Hash()) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.Nil(t, env.TransactionByHash(common.Hash{})) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + tx := env.TransactionByHash(block1.Transactions()[0].Hash()) + require.Equal(t, block1.Transactions()[0].Hash(), tx.Hash()) + }) } func TestRPCGetTransactionByBlockHashAndIndex(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.Nil(t, env.TransactionByBlockHashAndIndex(common.Hash{}, 0)) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - tx := env.TransactionByBlockHashAndIndex(block1.Hash(), 0) - require.Equal(t, block1.Transactions()[0].Hash(), tx.Hash()) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.Nil(t, env.TransactionByBlockHashAndIndex(common.Hash{}, 0)) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + tx := env.TransactionByBlockHashAndIndex(block1.Hash(), 0) + require.Equal(t, block1.Transactions()[0].Hash(), tx.Hash()) + }) } func TestRPCGetUncleByBlockHashAndIndex(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.Nil(t, env.UncleByBlockHashAndIndex(common.Hash{}, 0)) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - require.Nil(t, env.UncleByBlockHashAndIndex(block1.Hash(), 0)) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.Nil(t, env.UncleByBlockHashAndIndex(common.Hash{}, 0)) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + require.Nil(t, env.UncleByBlockHashAndIndex(block1.Hash(), 0)) + }) } func TestRPCGetTransactionByBlockNumberAndIndex(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.Nil(t, env.TransactionByBlockNumberAndIndex(big.NewInt(3), 0)) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - tx := env.TransactionByBlockNumberAndIndex(block1.Number(), 0) - require.EqualValues(t, block1.Hash(), *tx.BlockHash) - require.EqualValues(t, 0, *tx.TransactionIndex) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.Nil(t, env.TransactionByBlockNumberAndIndex(big.NewInt(3), 0)) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + tx := env.TransactionByBlockNumberAndIndex(block1.Number(), 0) + require.EqualValues(t, block1.Hash(), *tx.BlockHash) + require.EqualValues(t, 0, *tx.TransactionIndex) + }) } func TestRPCGetUncleByBlockNumberAndIndex(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - require.Nil(t, env.UncleByBlockNumberAndIndex(big.NewInt(3), 0)) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - require.Nil(t, env.UncleByBlockNumberAndIndex(block1.Number(), 0)) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + require.Nil(t, env.UncleByBlockNumberAndIndex(big.NewInt(3), 0)) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + require.Nil(t, env.UncleByBlockNumberAndIndex(block1.Number(), 0)) + }) } func TestRPCGetTransactionCountByHash(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - require.Positive(t, len(block1.Transactions())) - require.EqualValues(t, len(block1.Transactions()), env.BlockTransactionCountByHash(block1.Hash())) - require.EqualValues(t, 0, env.BlockTransactionCountByHash(common.Hash{})) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + require.Positive(t, len(block1.Transactions())) + require.EqualValues(t, len(block1.Transactions()), env.BlockTransactionCountByHash(block1.Hash())) + require.EqualValues(t, 0, env.BlockTransactionCountByHash(common.Hash{})) + }) } func TestRPCGetUncleCountByBlockHash(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - require.Zero(t, len(block1.Uncles())) - require.EqualValues(t, len(block1.Uncles()), env.UncleCountByBlockHash(block1.Hash())) - require.EqualValues(t, 0, env.UncleCountByBlockHash(common.Hash{})) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + require.Zero(t, len(block1.Uncles())) + require.EqualValues(t, len(block1.Uncles()), env.UncleCountByBlockHash(block1.Hash())) + require.EqualValues(t, 0, env.UncleCountByBlockHash(common.Hash{})) + }) } func TestRPCGetTransactionCountByNumber(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(nil) - require.Positive(t, len(block1.Transactions())) - require.EqualValues(t, len(block1.Transactions()), env.BlockTransactionCountByNumber()) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(nil) + require.Positive(t, len(block1.Transactions())) + require.EqualValues(t, len(block1.Transactions()), env.BlockTransactionCountByNumber()) + }) } func TestRPCGetUncleCountByBlockNumber(t *testing.T) { - env := newSoloTestEnv(t) - _, receiverAddress := generateKey(t) - env.RequestFunds(receiverAddress) - block1 := env.BlockByNumber(big.NewInt(1)) - require.Zero(t, len(block1.Uncles())) - require.EqualValues(t, len(block1.Uncles()), env.UncleCountByBlockNumber(big.NewInt(1))) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, receiverAddress := generateKey(t) + env.RequestFunds(receiverAddress) + block1 := env.BlockByNumber(big.NewInt(1)) + require.Zero(t, len(block1.Uncles())) + require.EqualValues(t, len(block1.Uncles()), env.UncleCountByBlockNumber(big.NewInt(1))) + }) } func TestRPCAccounts(t *testing.T) { - env := newSoloTestEnv(t) - accounts := env.Accounts() - require.Equal(t, len(evmtest.Accounts), len(accounts)) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + accounts := env.Accounts() + require.Equal(t, len(evmtest.Accounts), len(accounts)) + }) } func TestRPCSign(t *testing.T) { - env := newSoloTestEnv(t) - signed := env.Sign(evmtest.AccountAddress(0), []byte("hello")) - require.NotEmpty(t, signed) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + signed := env.Sign(evmtest.AccountAddress(0), []byte("hello")) + require.NotEmpty(t, signed) + }) } func TestRPCSignTransaction(t *testing.T) { - env := newSoloTestEnv(t) - - from := evmtest.AccountAddress(0) - to := evmtest.AccountAddress(1) - gas := hexutil.Uint64(evm.TxGas) - nonce := hexutil.Uint64(env.NonceAt(from)) - signed := env.SignTransaction(&jsonrpc.SendTxArgs{ - From: from, - To: &to, - Gas: &gas, - GasPrice: (*hexutil.Big)(evm.GasPrice), - Value: (*hexutil.Big)(RequestFundsAmount), - Nonce: &nonce, + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + + from := evmtest.AccountAddress(0) + to := evmtest.AccountAddress(1) + gas := hexutil.Uint64(params.TxGas) + nonce := hexutil.Uint64(env.NonceAt(from)) + signed := env.SignTransaction(&jsonrpc.SendTxArgs{ + From: from, + To: &to, + Gas: &gas, + GasPrice: (*hexutil.Big)(evm.GasPrice), + Value: (*hexutil.Big)(RequestFundsAmount), + Nonce: &nonce, + }) + require.NotEmpty(t, signed) + + // test that the signed tx can be sent + env.RequestFunds(from) + err := env.RawClient.Call(nil, "eth_sendRawTransaction", hexutil.Encode(signed)) + require.NoError(t, err) }) - require.NotEmpty(t, signed) - - // test that the signed tx can be sent - env.RequestFunds(from) - err := env.RawClient.Call(nil, "eth_sendRawTransaction", hexutil.Encode(signed)) - require.NoError(t, err) } func TestRPCSendTransaction(t *testing.T) { - env := newSoloTestEnv(t) - - from := evmtest.AccountAddress(0) - env.RequestFunds(from) - - to := evmtest.AccountAddress(1) - gas := hexutil.Uint64(evm.TxGas) - nonce := hexutil.Uint64(env.NonceAt(from)) - txHash := env.MustSendTransaction(&jsonrpc.SendTxArgs{ - From: from, - To: &to, - Gas: &gas, - GasPrice: (*hexutil.Big)(evm.GasPrice), - Value: (*hexutil.Big)(RequestFundsAmount), - Nonce: &nonce, + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + + from := evmtest.AccountAddress(0) + env.RequestFunds(from) + + to := evmtest.AccountAddress(1) + gas := hexutil.Uint64(params.TxGas) + nonce := hexutil.Uint64(env.NonceAt(from)) + txHash := env.MustSendTransaction(&jsonrpc.SendTxArgs{ + From: from, + To: &to, + Gas: &gas, + GasPrice: (*hexutil.Big)(evm.GasPrice), + Value: (*hexutil.Big)(RequestFundsAmount), + Nonce: &nonce, + }) + require.NotEqualValues(t, common.Hash{}, txHash) }) - require.NotEqualValues(t, common.Hash{}, txHash) } func TestRPCGetTxReceiptRegularTx(t *testing.T) { - env := newSoloTestEnv(t) - _, creatorAddr := generateKey(t) - - tx := env.RequestFunds(creatorAddr) - receipt := env.MustTxReceipt(tx.Hash()) - - require.EqualValues(t, types.LegacyTxType, receipt.Type) - require.EqualValues(t, types.ReceiptStatusSuccessful, receipt.Status) - require.NotZero(t, receipt.CumulativeGasUsed) - require.EqualValues(t, types.Bloom{}, receipt.Bloom) - require.EqualValues(t, 0, len(receipt.Logs)) - - require.EqualValues(t, tx.Hash(), receipt.TxHash) - require.EqualValues(t, common.Address{}, receipt.ContractAddress) - require.NotZero(t, receipt.GasUsed) - - require.EqualValues(t, big.NewInt(1), receipt.BlockNumber) - require.EqualValues(t, env.BlockByNumber(big.NewInt(1)).Hash(), receipt.BlockHash) - require.EqualValues(t, 0, receipt.TransactionIndex) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + _, creatorAddr := generateKey(t) + + tx := env.RequestFunds(creatorAddr) + receipt := env.MustTxReceipt(tx.Hash()) + + require.EqualValues(t, types.LegacyTxType, receipt.Type) + require.EqualValues(t, types.ReceiptStatusSuccessful, receipt.Status) + require.NotZero(t, receipt.CumulativeGasUsed) + require.EqualValues(t, types.Bloom{}, receipt.Bloom) + require.EqualValues(t, 0, len(receipt.Logs)) + + require.EqualValues(t, tx.Hash(), receipt.TxHash) + require.EqualValues(t, common.Address{}, receipt.ContractAddress) + require.NotZero(t, receipt.GasUsed) + + require.EqualValues(t, big.NewInt(1), receipt.BlockNumber) + require.EqualValues(t, env.BlockByNumber(big.NewInt(1)).Hash(), receipt.BlockHash) + require.EqualValues(t, 0, receipt.TransactionIndex) + }) } func TestRPCGetTxReceiptContractCreation(t *testing.T) { - env := newSoloTestEnv(t) - creator, _ := generateKey(t) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + creator, _ := generateKey(t) - contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) - require.NoError(t, err) - tx, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) - receipt := env.MustTxReceipt(tx.Hash()) - - require.EqualValues(t, types.LegacyTxType, receipt.Type) - require.EqualValues(t, types.ReceiptStatusSuccessful, receipt.Status) - require.NotZero(t, receipt.CumulativeGasUsed) - require.EqualValues(t, types.Bloom{}, receipt.Bloom) - require.EqualValues(t, 0, len(receipt.Logs)) - - require.EqualValues(t, tx.Hash(), receipt.TxHash) - require.EqualValues(t, contractAddress, receipt.ContractAddress) - require.NotZero(t, receipt.GasUsed) - - require.EqualValues(t, big.NewInt(1), receipt.BlockNumber) - require.EqualValues(t, env.BlockByNumber(big.NewInt(1)).Hash(), receipt.BlockHash) - require.EqualValues(t, 0, receipt.TransactionIndex) + contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) + require.NoError(t, err) + tx, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) + receipt := env.MustTxReceipt(tx.Hash()) + + require.EqualValues(t, types.LegacyTxType, receipt.Type) + require.EqualValues(t, types.ReceiptStatusSuccessful, receipt.Status) + require.NotZero(t, receipt.CumulativeGasUsed) + require.EqualValues(t, types.Bloom{}, receipt.Bloom) + require.EqualValues(t, 0, len(receipt.Logs)) + + require.EqualValues(t, tx.Hash(), receipt.TxHash) + require.EqualValues(t, contractAddress, receipt.ContractAddress) + require.NotZero(t, receipt.GasUsed) + + require.EqualValues(t, big.NewInt(1), receipt.BlockNumber) + require.EqualValues(t, env.BlockByNumber(big.NewInt(1)).Hash(), receipt.BlockHash) + require.EqualValues(t, 0, receipt.TransactionIndex) + }) } func TestRPCGetTxReceiptMissing(t *testing.T) { - env := newSoloTestEnv(t) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) - _, err := env.TxReceipt(common.Hash{}) - require.Error(t, err) - require.Equal(t, "not found", err.Error()) + _, err := env.TxReceipt(common.Hash{}) + require.Error(t, err) + require.Equal(t, "not found", err.Error()) + }) } func TestRPCCall(t *testing.T) { - env := newSoloTestEnv(t) - creator, creatorAddress := generateKey(t) - contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) - require.NoError(t, err) - _, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + creator, creatorAddress := generateKey(t) + contractABI, err := abi.JSON(strings.NewReader(evmtest.StorageContractABI)) + require.NoError(t, err) + _, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.StorageContractBytecode, uint32(42)) - callArguments, err := contractABI.Pack("retrieve") - require.NoError(t, err) + callArguments, err := contractABI.Pack("retrieve") + require.NoError(t, err) - ret, err := env.Client.CallContract(context.Background(), ethereum.CallMsg{ - From: creatorAddress, - To: &contractAddress, - Data: callArguments, - }, nil) - require.NoError(t, err) + ret, err := env.Client.CallContract(context.Background(), ethereum.CallMsg{ + From: creatorAddress, + To: &contractAddress, + Data: callArguments, + }, nil) + require.NoError(t, err) - var v uint32 - err = contractABI.UnpackIntoInterface(&v, "retrieve", ret) - require.NoError(t, err) - require.Equal(t, uint32(42), v) + var v uint32 + err = contractABI.UnpackIntoInterface(&v, "retrieve", ret) + require.NoError(t, err) + require.Equal(t, uint32(42), v) + }) } func TestRPCGetLogs(t *testing.T) { - newSoloTestEnv(t).TestRPCGetLogs() + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + newSoloTestEnv(t, evmFlavor).TestRPCGetLogs() + }) } func TestRPCGasLimit(t *testing.T) { - newSoloTestEnv(t).TestRPCGasLimit() + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + newSoloTestEnv(t, evmFlavor).TestRPCGasLimit() + }) } func TestRPCEthChainID(t *testing.T) { - env := newSoloTestEnv(t) - var chainID hexutil.Uint - err := env.RawClient.Call(&chainID, "eth_chainId") - require.NoError(t, err) - require.EqualValues(t, evm.DefaultChainID, chainID) + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + env := newSoloTestEnv(t, evmFlavor) + var chainID hexutil.Uint + err := env.RawClient.Call(&chainID, "eth_chainId") + require.NoError(t, err) + require.EqualValues(t, evm.DefaultChainID, chainID) + }) } diff --git a/packages/evm/jsonrpc/service.go b/packages/evm/jsonrpc/service.go index 744bf7cbae..65bf67726c 100644 --- a/packages/evm/jsonrpc/service.go +++ b/packages/evm/jsonrpc/service.go @@ -32,7 +32,7 @@ func NewEthService(evmChain *EVMChain, accounts *AccountManager) *EthService { } func (e *EthService) ProtocolVersion() hexutil.Uint { - return hexutil.Uint(eth.ETH65) + return hexutil.Uint(eth.ETH66) } func (e *EthService) GetTransactionCount(address common.Address, blockNumberOrHash rpc.BlockNumberOrHash) (hexutil.Uint64, error) { @@ -130,7 +130,11 @@ func (e *EthService) GetTransactionReceipt(txHash common.Hash) (map[string]inter if r == nil { return nil, nil } - return RPCMarshalReceipt(r), nil + tx, _, _, _, err := e.evmChain.TransactionByHash(txHash) // nolint:dogsled + if err != nil { + return nil, err + } + return RPCMarshalReceipt(r, tx), nil } func (e *EthService) SendRawTransaction(txBytes hexutil.Bytes) (common.Hash, error) { @@ -170,19 +174,19 @@ func (e *EthService) GetBlockTransactionCountByNumber(blockNumber rpc.BlockNumbe } func (e *EthService) GetUncleCountByBlockHash(blockHash common.Hash) hexutil.Uint { - return hexutil.Uint(0) // no uncles are ever generated in evmchain contract + return hexutil.Uint(0) // no uncles are ever generated } func (e *EthService) GetUncleCountByBlockNumber(blockNumber rpc.BlockNumber) hexutil.Uint { - return hexutil.Uint(0) // no uncles are ever generated in evmchain contract + return hexutil.Uint(0) // no uncles are ever generated } func (e *EthService) GetUncleByBlockHashAndIndex(blockHash common.Hash, index hexutil.Uint) map[string]interface{} { - return nil // no uncles are ever generated in evmchain contract + return nil // no uncles are ever generated } func (e *EthService) GetUncleByBlockNumberAndIndex(blockNumberOrTag rpc.BlockNumber, index hexutil.Uint) map[string]interface{} { - return nil // no uncles are ever generated in evmchain contract + return nil // no uncles are ever generated } func (e *EthService) Accounts() []common.Address { diff --git a/packages/evm/jsonrpc/types.go b/packages/evm/jsonrpc/types.go index bb5a451506..53952a7f39 100644 --- a/packages/evm/jsonrpc/types.go +++ b/packages/evm/jsonrpc/types.go @@ -15,8 +15,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/iotaledger/wasp/contracts/native/evmchain" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/evm/evmtypes" ) // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction @@ -155,14 +155,14 @@ func parseBlockNumber(bn rpc.BlockNumber) *big.Int { return big.NewInt(n) } -func RPCMarshalReceipt(r *evmchain.Receipt) map[string]interface{} { +func RPCMarshalReceipt(r *types.Receipt, tx *types.Transaction) map[string]interface{} { return map[string]interface{}{ "transactionHash": r.TxHash, "transactionIndex": hexutil.Uint64(r.TransactionIndex), "blockHash": r.BlockHash, "blockNumber": (*hexutil.Big)(r.BlockNumber), - "from": r.From, - "to": r.To, + "from": evmtypes.GetSender(tx), + "to": tx.To(), "cumulativeGasUsed": hexutil.Uint64(r.CumulativeGasUsed), "gasUsed": hexutil.Uint64(r.GasUsed), "contractAddress": r.ContractAddress, @@ -172,7 +172,7 @@ func RPCMarshalReceipt(r *evmchain.Receipt) map[string]interface{} { } } -func RPCMarshalLogs(r *evmchain.Receipt) []interface{} { +func RPCMarshalLogs(r *types.Receipt) []interface{} { ret := make([]interface{}, len(r.Logs)) for i := range r.Logs { ret[i] = RPCMarshalLog(r, uint(i)) @@ -180,7 +180,7 @@ func RPCMarshalLogs(r *evmchain.Receipt) []interface{} { return ret } -func RPCMarshalLog(r *evmchain.Receipt, logIndex uint) map[string]interface{} { +func RPCMarshalLog(r *types.Receipt, logIndex uint) map[string]interface{} { log := r.Logs[logIndex] return map[string]interface{}{ "logIndex": hexutil.Uint64(logIndex), diff --git a/packages/iscp/coreutil/contract.go b/packages/iscp/coreutil/contract.go index 38d42f40bb..16679b127c 100644 --- a/packages/iscp/coreutil/contract.go +++ b/packages/iscp/coreutil/contract.go @@ -166,7 +166,7 @@ func fallbackHandler(ctx iscp.Sandbox) (dict.Dict, error) { if ctx.IncomingTransfer() != nil { transferStr = ctx.IncomingTransfer().String() } - ctx.Log().Debugf("default full entry point handler invoked for contact %s from caller %s\nTransfer: %s", + ctx.Log().Debugf("default full entry point handler invoked for contract %s from caller %s\nTransfer: %s", ctx.Contract(), ctx.Caller(), transferStr) return nil, nil } diff --git a/packages/iscp/coreutil/globalindex.go b/packages/iscp/coreutil/globalindex.go index 6727119de1..8b46ac53b2 100644 --- a/packages/iscp/coreutil/globalindex.go +++ b/packages/iscp/coreutil/globalindex.go @@ -5,11 +5,7 @@ import ( "golang.org/x/xerrors" ) -type ErrorStateInvalidated struct { - error -} - -var ErrStateHasBeenInvalidated = &ErrorStateInvalidated{xerrors.New("virtual state has been invalidated")} +var ErrorStateInvalidated = xerrors.New("virtual state has been invalidated") // ChainStateSync and StateBaseline interfaces implements optimistic (non-blocking) access to the // global state (database) of the chain @@ -89,7 +85,7 @@ func (g *stateBaseline) IsValid() bool { func (g *stateBaseline) MustValidate() { if !g.IsValid() { - panic(ErrStateHasBeenInvalidated) + panic(ErrorStateInvalidated) } } diff --git a/packages/kv/buffered/mutation.go b/packages/kv/buffered/mutation.go index 5c575bd084..ac79049206 100644 --- a/packages/kv/buffered/mutation.go +++ b/packages/kv/buffered/mutation.go @@ -122,6 +122,9 @@ func (ms *Mutations) Get(k kv.Key) ([]byte, bool) { } func (ms *Mutations) Set(k kv.Key, v []byte) { + if v == nil { + panic("cannot Set(key, nil), use Del() to remove a key/value") + } delete(ms.Dels, k) ms.Sets[k] = v ms.modified = true diff --git a/packages/kv/hiveadapter.go b/packages/kv/hiveadapter.go index c0b8504c39..1bc807927e 100644 --- a/packages/kv/hiveadapter.go +++ b/packages/kv/hiveadapter.go @@ -25,7 +25,10 @@ func (h *HiveKVStoreReader) Get(key Key) ([]byte, error) { if errors.Is(err, kvstore.ErrKeyNotFound) { return nil, nil } - return b, wrapDBError(err) + if err != nil { + return nil, wrapDBError(err) + } + return wrapBytes(b), nil } func (h *HiveKVStoreReader) Has(key Key) (bool, error) { @@ -35,7 +38,7 @@ func (h *HiveKVStoreReader) Has(key Key) (bool, error) { func (h *HiveKVStoreReader) Iterate(prefix Key, f func(key Key, value []byte) bool) error { return wrapDBError(h.db.Iterate([]byte(prefix), func(k kvstore.Key, v kvstore.Value) bool { - return f(Key(k), v) + return f(Key(k), wrapBytes(v)) })) } @@ -53,7 +56,7 @@ func (h *HiveKVStoreReader) IterateSorted(prefix Key, f func(key Key, value []by if err != nil { return false } - return f(k, v) + return f(k, wrapBytes(v)) }) if err2 != nil { return err2 @@ -114,6 +117,14 @@ func (d *DBError) Unwrap() error { return d.error } +func wrapBytes(b []byte) []byte { + // after Set(k, []byte{}), make sure we return []byte{} instead of nil + if b == nil { + return []byte{} + } + return b +} + func wrapDBError(e error) error { if e == nil { return nil diff --git a/packages/kv/optimism/optimism.go b/packages/kv/optimism/optimism.go index 9d4041cc09..08a80e60d9 100644 --- a/packages/kv/optimism/optimism.go +++ b/packages/kv/optimism/optimism.go @@ -15,7 +15,7 @@ import ( // The user of the OptimisticKVStoreReader announces it starts reading the state with SetBaseline. Then the user reads // the state with Get's. If sequense of Get's is finished without error, it means state wasn't invalidated from // the baseline until the last read. -// If returned error is ErrStateHasBeenInvalidated ir means state was invalidated since SetBaseline. +// If returned error is ErrorStateInvalidated ir means state was invalidated since SetBaseline. // In this case read can be repeated. Any other error is a database error type OptimisticKVStoreReader struct { kvstore kv.KVStoreReader @@ -45,80 +45,80 @@ func (o *OptimisticKVStoreReader) IsStateValid() bool { func (o *OptimisticKVStoreReader) Get(key kv.Key) ([]byte, error) { if !o.baseline.IsValid() { - return nil, coreutil.ErrStateHasBeenInvalidated + return nil, coreutil.ErrorStateInvalidated } ret, err := o.kvstore.Get(key) if err != nil { return nil, err } if !o.baseline.IsValid() { - return nil, coreutil.ErrStateHasBeenInvalidated + return nil, coreutil.ErrorStateInvalidated } return ret, nil } func (o *OptimisticKVStoreReader) Has(key kv.Key) (bool, error) { if !o.baseline.IsValid() { - return false, coreutil.ErrStateHasBeenInvalidated + return false, coreutil.ErrorStateInvalidated } ret, err := o.kvstore.Has(key) if err != nil { return false, err } if !o.baseline.IsValid() { - return false, coreutil.ErrStateHasBeenInvalidated + return false, coreutil.ErrorStateInvalidated } return ret, nil } func (o *OptimisticKVStoreReader) Iterate(prefix kv.Key, f func(key kv.Key, value []byte) bool) error { if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } if err := o.kvstore.Iterate(prefix, f); err != nil { return err } if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } return nil } func (o *OptimisticKVStoreReader) IterateKeys(prefix kv.Key, f func(key kv.Key) bool) error { if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } if err := o.kvstore.IterateKeys(prefix, f); err != nil { return err } if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } return nil } func (o *OptimisticKVStoreReader) IterateSorted(prefix kv.Key, f func(key kv.Key, value []byte) bool) error { if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } if err := o.kvstore.IterateSorted(prefix, f); err != nil { return err } if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } return nil } func (o *OptimisticKVStoreReader) IterateKeysSorted(prefix kv.Key, f func(key kv.Key) bool) error { if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } if err := o.kvstore.IterateKeysSorted(prefix, f); err != nil { return err } if !o.baseline.IsValid() { - return coreutil.ErrStateHasBeenInvalidated + return coreutil.ErrorStateInvalidated } return nil } @@ -167,10 +167,10 @@ func (o *OptimisticKVStoreReader) MustIterateKeysSorted(prefix kv.Key, f func(ke const ( defaultRetryDelay = 300 * time.Millisecond - defaultRetryTimeout = 2 * time.Second + defaultRetryTimeout = 5 * time.Second ) -// RetryOnStateInvalidated repeats function while it returns ErrStateHasBeenInvalidated +// RetryOnStateInvalidated repeats function while it returns ErrorStateInvalidated // Optional parameters: // - timeouts[0] - overall timeout // - timeouts[1] - repeat delay @@ -186,7 +186,7 @@ func RetryOnStateInvalidated(fun func() error, timeouts ...time.Duration) error } var err error - for err = fun(); errors.Is(err, coreutil.ErrStateHasBeenInvalidated); err = fun() { + for err = fun(); errors.Is(err, coreutil.ErrorStateInvalidated); err = fun() { time.Sleep(retryDelay) if time.Now().After(timeoutAfter) { return xerrors.Errorf("optimistic read retry timeout. Last error: %w", err) diff --git a/packages/kv/optimism/optimism_test.go b/packages/kv/optimism/optimism_test.go index d19ccd0a16..bda5b5d88a 100644 --- a/packages/kv/optimism/optimism_test.go +++ b/packages/kv/optimism/optimism_test.go @@ -2,6 +2,7 @@ package optimism import ( "testing" + "time" "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/iotaledger/wasp/packages/kv/dict" @@ -30,9 +31,10 @@ func TestOptimismBasic(t *testing.T) { require.False(t, base.IsValid()) _, err = r.Get("a") require.Error(t, err) - require.EqualValues(t, err, coreutil.ErrStateHasBeenInvalidated) + require.Equal(t, err, coreutil.ErrorStateInvalidated) r.SetBaseline() + require.True(t, base.IsValid()) b, err = r.Get("a") require.NoError(t, err) require.EqualValues(t, "b", string(b)) @@ -40,5 +42,52 @@ func TestOptimismBasic(t *testing.T) { glb.InvalidateSolidIndex() _, err = r.Get("a") require.Error(t, err) - require.EqualValues(t, err, coreutil.ErrStateHasBeenInvalidated) + require.Equal(t, err, coreutil.ErrorStateInvalidated) +} + +// returns the number of times the read operation was executed +func tryReadFromDB(storeReader *OptimisticKVStoreReader, timeouts ...time.Duration) ([]byte, int, error) { + readCounter := 0 + var ret []byte + err := RetryOnStateInvalidated(func() error { + var err error + readCounter++ + ret, err = storeReader.Get("foo") + return err + }, timeouts...) + return ret, readCounter, err +} + +func TestRetryOnStateInvalidation(t *testing.T) { + d := dict.New() + d.Set("foo", []byte("bar")) + glb := coreutil.NewChainStateSync() + base := glb.GetSolidIndexBaseline() + r := NewOptimisticKVStoreReader(d, base) + + glb.SetSolidIndex(0) + base.Set() + require.True(t, base.IsValid()) + + _, nReads, err := tryReadFromDB(r) + require.NoError(t, err) + require.EqualValues(t, 1, nReads) + + glb.SetSolidIndex(3) + require.False(t, base.IsValid()) + + _, nReads, err = tryReadFromDB(r) + require.NotEqual(t, err, coreutil.ErrorStateInvalidated) + require.Greater(t, nReads, 1) + + // make sure it stops retrying and returns once a new baseline is set + go func() { + ret, nReads, err := tryReadFromDB(r, 5*time.Second, 10*time.Millisecond) + require.NoError(t, err) + require.Greater(t, nReads, 1) + require.Equal(t, ret, []byte("bar")) + }() + + time.Sleep(50 * time.Millisecond) + r.SetBaseline() // sets the baseline while the go-routine above is running, so } diff --git a/packages/metrics/metrics.go b/packages/metrics/metrics.go index fcc39e39db..602a38ee3f 100644 --- a/packages/metrics/metrics.go +++ b/packages/metrics/metrics.go @@ -72,7 +72,7 @@ func (m *Metrics) registerMetrics() { m.log.Info("Registering mempool metrics to prometheus") m.offLedgerRequestCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "wasp_off_ledger_request_counter", - Help: "Number of lff-ledger requests made to chain", + Help: "Number of off-ledger requests made to chain", }, []string{"chain"}) prometheus.MustRegister(m.offLedgerRequestCounter) @@ -83,7 +83,7 @@ func (m *Metrics) registerMetrics() { prometheus.MustRegister(m.onLedgerRequestCounter) m.processedRequestCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ - Name: "wasp_processed_on_ledger_request_counter", + Name: "wasp_processed_request_counter", Help: "Number of requests processed", }, []string{"chain"}) prometheus.MustRegister(m.processedRequestCounter) diff --git a/packages/peering/lpp/lppNetImpl.go b/packages/peering/lpp/lppNetImpl.go index f383ff727b..a73e447aba 100644 --- a/packages/peering/lpp/lppNetImpl.go +++ b/packages/peering/lpp/lppNetImpl.go @@ -50,7 +50,6 @@ import ( const ( maintenancePeriod = 1 * time.Second - recvQueueSize = 1024 * 5 lppProtocolPeering = "/iotaledger/wasp/peering/1.0.0" lppProtocolHeartbeat = "/iotaledger/wasp/heartbeat/1.0.0" @@ -65,8 +64,7 @@ type netImpl struct { ctxCancel context.CancelFunc // A way to close the context. peers map[libp2ppeer.ID]*peer // By remotePeer.ID() peersLock *sync.RWMutex - recvEvents *events.Event // Used to publish events to all attached clients. - recvQueue chan *peering.RecvEvent // A queue for received messages. + recvEvents *events.Event // Used to publish events to all attached clients. nodeKeyPair *ed25519.KeyPair trusted peering.TrustedNetworkManager log *logger.Logger @@ -111,7 +109,6 @@ func NewNetworkProvider( peers: make(map[libp2ppeer.ID]*peer), peersLock: &sync.RWMutex{}, recvEvents: nil, // Initialized bellow. - recvQueue: make(chan *peering.RecvEvent, recvQueueSize), nodeKeyPair: nodeKeyPair, trusted: trusted, log: log, @@ -218,10 +215,12 @@ func (n *netImpl) lppPeeringProtocolHandler(stream network.Stream) { } remotePeer.noteReceived() peerMsg.SenderNetID = remotePeer.NetID() - n.recvQueue <- &peering.RecvEvent{ - From: remotePeer, - Msg: peerMsg, - } + remotePeer.RecvMsg( + &peering.RecvEvent{ + From: remotePeer, + Msg: peerMsg, + }, + ) } func (n *netImpl) lppHeartbeatProtocolHandler(stream network.Stream) { @@ -307,7 +306,6 @@ func (n *netImpl) Run(shutdownSignal <-chan struct{}) { queueRecvStopCh := make(chan bool) receiveStopCh := make(chan bool) maintenanceStopCh := make(chan bool) - go n.queueRecvLoop(queueRecvStopCh) go n.maintenanceLoop(maintenanceStopCh) <-shutdownSignal @@ -414,10 +412,14 @@ func (n *netImpl) PubKey() *ed25519.PublicKey { // SendMsg implements peering.PeerSender for the Self() node. func (n *netImpl) SendMsg(msg *peering.PeerMessage) { // Don't go via the network, if sending a message to self. - n.recvQueue <- &peering.RecvEvent{ + n.triggerRecvEvents(&peering.RecvEvent{ From: n.Self(), Msg: msg, - } + }) +} + +func (n *netImpl) triggerRecvEvents(msg interface{}) { + n.recvEvents.Trigger(msg) } // IsAlive implements peering.PeerSender for the Self() node. @@ -484,19 +486,6 @@ func (n *netImpl) usePeer(remoteNetID string) (peering.PeerSender, error) { return nil, xerrors.Errorf("peer %v is not trusted", remoteNetID) } -func (n *netImpl) queueRecvLoop(stopCh chan bool) { - for { - select { - case <-stopCh: - return - case recvEvent, ok := <-n.recvQueue: - if ok { - n.recvEvents.Trigger(recvEvent) - } - } - } -} - func (n *netImpl) maintenanceLoop(stopCh chan bool) { for { select { diff --git a/packages/peering/lpp/lppPeer.go b/packages/peering/lpp/lppPeer.go index b60e340e3f..386fe0c9b6 100644 --- a/packages/peering/lpp/lppPeer.go +++ b/packages/peering/lpp/lppPeer.go @@ -26,6 +26,7 @@ type peer struct { remoteLppID libp2ppeer.ID accessLock *sync.RWMutex sendCh *channels.InfiniteChannel + recvCh *channels.InfiniteChannel lastMsgSent time.Time lastMsgRecv time.Time numUsers int @@ -42,6 +43,7 @@ func newPeer(remoteNetID string, remotePubKey *ed25519.PublicKey, remoteLppID li remoteLppID: remoteLppID, accessLock: &sync.RWMutex{}, sendCh: channels.NewInfiniteChannel(), + recvCh: channels.NewInfiniteChannel(), lastMsgSent: time.Time{}, lastMsgRecv: time.Time{}, numUsers: 0, @@ -50,6 +52,7 @@ func newPeer(remoteNetID string, remotePubKey *ed25519.PublicKey, remoteLppID li log: log, } go p.sendLoop() + go p.recvLoop() return p } @@ -82,6 +85,7 @@ func (p *peer) maintenanceCheck() { if numUsers == 0 && !trusted && lastMsgOld { p.net.delPeer(p) p.sendCh.Close() + p.recvCh.Close() } } @@ -115,12 +119,22 @@ func (p *peer) SendMsg(msg *peering.PeerMessage) { p.sendCh.In() <- msg } +func (p *peer) RecvMsg(msg *peering.RecvEvent) { + p.recvCh.In() <- msg +} + func (p *peer) sendLoop() { for msg := range p.sendCh.Out() { p.sendMsgDirect(msg.(*peering.PeerMessage)) } } +func (p *peer) recvLoop() { + for msg := range p.recvCh.Out() { + p.net.triggerRecvEvents(msg) + } +} + func (p *peer) sendMsgDirect(msg *peering.PeerMessage) { stream, err := p.net.lppHost.NewStream(p.net.ctx, p.remoteLppID, lppProtocolPeering) if err != nil { diff --git a/packages/solo/fun.go b/packages/solo/fun.go index cf2e836e5b..d13e3b7ad0 100644 --- a/packages/solo/fun.go +++ b/packages/solo/fun.go @@ -6,7 +6,7 @@ package solo import ( "bytes" "fmt" - "io/ioutil" + "os" "sort" "github.com/iotaledger/goshimmer/packages/ledgerstate" @@ -204,7 +204,7 @@ func (ch *Chain) UploadWasm(keyPair *ed25519.KeyPair, binaryCode []byte) (ret ha // UploadWasmFromFile is a syntactic sugar to upload file content as blob data to the chain func (ch *Chain) UploadWasmFromFile(keyPair *ed25519.KeyPair, fileName string) (ret hashing.HashValue, err error) { var binary []byte - binary, err = ioutil.ReadFile(fileName) + binary, err = os.ReadFile(fileName) if err != nil { return } @@ -239,7 +239,7 @@ func (ch *Chain) GetWasmBinary(progHash hashing.HashValue) ([]byte, error) { // The parameter 'programHash' can be one of the following: // - it is and ID of the blob stored on the chain in the format of Wasm binary // - it can be a hash (ID) of the example smart contract ("hardcoded"). The "hardcoded" -// smart contact must be made available with the call examples.AddProcessor +// smart contract must be made available with the call examples.AddProcessor func (ch *Chain) DeployContract(keyPair *ed25519.KeyPair, name string, programHash hashing.HashValue, params ...interface{}) error { par := []interface{}{root.ParamProgramHash, programHash, root.ParamName, name} par = append(par, params...) @@ -384,8 +384,8 @@ func (ch *Chain) GetTotalIotas() uint64 { // - chain owner part of the fee (number of tokens) // - validator part of the fee (number of tokens) // Total fee is sum of owner fee and validator fee -func (ch *Chain) GetFeeInfo(contactName string) (colored.Color, uint64, uint64) { - hname := iscp.Hn(contactName) +func (ch *Chain) GetFeeInfo(contractName string) (colored.Color, uint64, uint64) { + hname := iscp.Hn(contractName) ret, err := ch.CallView(governance.Contract.Name, governance.FuncGetFeeInfo.Name, governance.ParamHname, hname) require.NoError(ch.Env.T, err) require.NotEqualValues(ch.Env.T, 0, len(ret)) diff --git a/packages/solo/solobench/solobench.go b/packages/solo/solobench/solobench.go index be849fd2c4..052dc69b5e 100644 --- a/packages/solo/solobench/solobench.go +++ b/packages/solo/solobench/solobench.go @@ -11,6 +11,8 @@ import ( "github.com/stretchr/testify/require" ) +type Func func(b *testing.B, chain *solo.Chain, reqs []*solo.CallParams, keyPair *ed25519.KeyPair) + // RunBenchmarkSync processes requests synchronously, producing 1 block per request func RunBenchmarkSync(b *testing.B, chain *solo.Chain, reqs []*solo.CallParams, keyPair *ed25519.KeyPair) { b.ResetTimer() diff --git a/packages/state/loadsave.go b/packages/state/loadsave.go index 42dffae42a..ae266bf4ab 100644 --- a/packages/state/loadsave.go +++ b/packages/state/loadsave.go @@ -48,6 +48,11 @@ func (vs *virtualStateAccess) Commit(blocks ...Block) error { return err } + // call flush explicitly, because batched.Commit doesn't actually write the changes to disk. + if err := vs.db.Flush(); err != nil { + return err + } + vs.kvs.ClearMutations() vs.kvs.Mutations().ResetModified() vs.appliedBlockHashes = vs.appliedBlockHashes[:0] diff --git a/packages/state/state.go b/packages/state/state.go index ccd5b8b144..8822e90c19 100644 --- a/packages/state/state.go +++ b/packages/state/state.go @@ -27,7 +27,6 @@ import ( type virtualStateAccess struct { chainID *iscp.ChainID db kvstore.KVStore - empty bool kvs *buffered.BufferedKVStoreAccess committedHash hashing.HashValue appliedBlockHashes []hashing.HashValue @@ -39,7 +38,6 @@ func newVirtualState(db kvstore.KVStore, chainID *iscp.ChainID) *virtualStateAcc ret := &virtualStateAccess{ db: db, kvs: buffered.NewBufferedKVStoreAccess(kv.NewHiveKVStoreReader(sub)), - empty: true, appliedBlockHashes: make([]hashing.HashValue, 0), } if chainID != nil { @@ -50,8 +48,8 @@ func newVirtualState(db kvstore.KVStore, chainID *iscp.ChainID) *virtualStateAcc func newZeroVirtualState(db kvstore.KVStore, chainID *iscp.ChainID) (VirtualStateAccess, Block) { ret := newVirtualState(db, chainID) - originBlock := newOriginBlock() - if err := ret.ApplyBlock(originBlock); err != nil { + originBlock, err := ret.applyOriginBlock() + if err != nil { panic(err) } _, _ = ret.ExtractBlock() // clear the update log @@ -77,7 +75,6 @@ func (vs *virtualStateAccess) Copy() VirtualStateAccess { db: vs.db, committedHash: vs.committedHash, appliedBlockHashes: make([]hashing.HashValue, len(vs.appliedBlockHashes)), - empty: vs.empty, kvs: vs.kvs.Copy(), } copy(ret.appliedBlockHashes, vs.appliedBlockHashes) @@ -133,19 +130,27 @@ func (vs *virtualStateAccess) PreviousStateHash() hashing.HashValue { // ApplyBlock applies a block of state updates. Checks consistency of the block and previous state. Updates state hash func (vs *virtualStateAccess) ApplyBlock(b Block) error { - if vs.empty && b.BlockIndex() != 0 { + return vs.applyAnyBlock(b, false) +} + +func (vs *virtualStateAccess) applyOriginBlock() (Block, error) { + originBlock := newOriginBlock() + return originBlock, vs.applyAnyBlock(originBlock, true) +} + +func (vs *virtualStateAccess) applyAnyBlock(b Block, empty bool) error { + if empty && b.BlockIndex() != 0 { return xerrors.Errorf("ApplyBlock: b state index #%d can't be applied to the empty state", b.BlockIndex()) } - if !vs.empty && vs.BlockIndex()+1 != b.BlockIndex() { + if !empty && vs.BlockIndex()+1 != b.BlockIndex() { return xerrors.Errorf("ApplyBlock: b state index #%d can't be applied to the state with index #%d", b.BlockIndex(), vs.BlockIndex()) } - if !vs.empty && vs.Timestamp().After(b.Timestamp()) { + if !empty && vs.Timestamp().After(b.Timestamp()) { return xerrors.New("ApplyBlock: inconsistent timestamps") } vs.ApplyStateUpdates(b.(*blockImpl).stateUpdate) vs.appliedBlockHashes = append(vs.appliedBlockHashes, hashing.HashData(b.EssenceBytes())) - vs.empty = false return nil } @@ -230,7 +235,7 @@ func (r *OptimisticStateReaderImpl) Timestamp() (time.Time, error) { func (r *OptimisticStateReaderImpl) Hash() (hashing.HashValue, error) { if !r.chainState.IsStateValid() { - return [32]byte{}, coreutil.ErrStateHasBeenInvalidated + return [32]byte{}, coreutil.ErrorStateInvalidated } hashBIn, err := r.db.Get(dbkeys.MakeKey(dbkeys.ObjectTypeStateHash)) if err != nil { @@ -241,7 +246,7 @@ func (r *OptimisticStateReaderImpl) Hash() (hashing.HashValue, error) { return [32]byte{}, err } if !r.chainState.IsStateValid() { - return [32]byte{}, coreutil.ErrStateHasBeenInvalidated + return [32]byte{}, coreutil.ErrorStateInvalidated } return ret, nil } @@ -260,7 +265,7 @@ func (r *OptimisticStateReaderImpl) SetBaseline() { // MustOptimisticVirtualState is a virtual state wrapper with global state baseline // Once baseline is invalidated globally any subsequent access to the mustOptimisticVirtualStateAccess -// will lead to panic(coreutil.ErrStateHasBeenInvalidated) +// will lead to panic(coreutil.ErrorStateInvalidated) type mustOptimisticVirtualStateAccess struct { state VirtualStateAccess baseline coreutil.StateBaseline diff --git a/packages/state/state_test.go b/packages/state/state_test.go index 8037258e79..88649b57d4 100644 --- a/packages/state/state_test.go +++ b/packages/state/state_test.go @@ -55,13 +55,11 @@ func TestOriginHashes(t *testing.T) { require.EqualValues(t, calcOriginStateHash(), z.StateCommitment()) }) t.Run("origin state construct", func(t *testing.T) { - origBlock := newOriginBlock() + emptyState := newVirtualState(mapdb.NewMapDB(), nil) + origBlock, err := emptyState.applyOriginBlock() require.EqualValues(t, 0, origBlock.BlockIndex()) require.True(t, origBlock.Timestamp().IsZero()) require.EqualValues(t, hashing.NilHash, origBlock.PreviousStateHash()) - - emptyState := newVirtualState(mapdb.NewMapDB(), nil) - err := emptyState.ApplyBlock(origBlock) require.NoError(t, err) require.EqualValues(t, emptyState.StateCommitment(), calcOriginStateHash()) require.EqualValues(t, hashing.NilHash, emptyState.PreviousStateHash()) @@ -166,6 +164,70 @@ func TestStateWithDB(t *testing.T) { require.EqualValues(t, vs1.StateCommitment(), vs2.StateCommitment()) }) + t.Run("apply block after loading", func(t *testing.T) { + store := mapdb.NewMapDB() + chainID := iscp.RandomChainID([]byte("1")) + _, exists, err := LoadSolidState(store, chainID) + require.NoError(t, err) + require.False(t, exists) + + vsOrig, err := CreateOriginState(store, chainID) + require.NoError(t, err) + + time1 := time.Now() + su := NewStateUpdateWithBlocklogValues(1, time1, hashing.NilHash) + su.Mutations().Set("key", []byte("value")) + block1, err := newBlock(su.Mutations()) + require.NoError(t, err) + + err = vsOrig.ApplyBlock(block1) + require.NoError(t, err) + require.EqualValues(t, 1, vsOrig.BlockIndex()) + require.True(t, time1.Equal(vsOrig.Timestamp())) + + time2 := time.Now() + su = NewStateUpdateWithBlocklogValues(2, time2, vsOrig.PreviousStateHash()) + su.Mutations().Set("other_key", []byte("other_value")) + block2, err := newBlock(su.Mutations()) + require.NoError(t, err) + + err = vsOrig.ApplyBlock(block2) + require.NoError(t, err) + require.EqualValues(t, 2, vsOrig.BlockIndex()) + require.True(t, time2.Equal(vsOrig.Timestamp())) + + err = vsOrig.Commit(block1, block2) + require.NoError(t, err) + require.EqualValues(t, 2, vsOrig.BlockIndex()) + require.True(t, time2.Equal(vsOrig.Timestamp())) + + vsLoaded, exists, err := LoadSolidState(store, chainID) + require.NoError(t, err) + require.True(t, exists) + + require.EqualValues(t, vsOrig.StateCommitment(), vsLoaded.StateCommitment()) + require.EqualValues(t, vsOrig.BlockIndex(), vsLoaded.BlockIndex()) + require.EqualValues(t, vsOrig.Timestamp(), vsLoaded.Timestamp()) + require.EqualValues(t, 2, vsLoaded.BlockIndex()) + + time3 := time.Now() + su = NewStateUpdateWithBlocklogValues(3, time3, vsLoaded.PreviousStateHash()) + su.Mutations().Set("more_keys", []byte("more_values")) + block3, err := newBlock(su.Mutations()) + require.NoError(t, err) + + err = vsOrig.ApplyBlock(block3) + require.NoError(t, err) + require.EqualValues(t, 3, vsOrig.BlockIndex()) + require.True(t, time3.Equal(vsOrig.Timestamp())) + + err = vsLoaded.ApplyBlock(block3) + require.NoError(t, err) + require.EqualValues(t, 3, vsLoaded.BlockIndex()) + require.True(t, time3.Equal(vsLoaded.Timestamp())) + + require.EqualValues(t, vsOrig.StateCommitment(), vsLoaded.StateCommitment()) + }) t.Run("state reader", func(t *testing.T) { store := mapdb.NewMapDB() chainID := iscp.RandomChainID([]byte("1")) @@ -216,7 +278,7 @@ func TestStateWithDB(t *testing.T) { glb.InvalidateSolidIndex() _, err = rdr.Hash() require.Error(t, err) - require.EqualValues(t, err, coreutil.ErrStateHasBeenInvalidated) + require.EqualValues(t, err, coreutil.ErrorStateInvalidated) }) } @@ -358,19 +420,19 @@ func TestVirtualStateMustOptimistic1(t *testing.T) { require.EqualValues(t, 0, vsOpt.BlockIndex()) glb.InvalidateSolidIndex() - require.PanicsWithValue(t, coreutil.ErrStateHasBeenInvalidated, func() { + require.PanicsWithValue(t, coreutil.ErrorStateInvalidated, func() { _ = vsOpt.StateCommitment() }) - require.PanicsWithValue(t, coreutil.ErrStateHasBeenInvalidated, func() { + require.PanicsWithValue(t, coreutil.ErrorStateInvalidated, func() { _ = vsOpt.BlockIndex() }) - require.PanicsWithValue(t, coreutil.ErrStateHasBeenInvalidated, func() { + require.PanicsWithValue(t, coreutil.ErrorStateInvalidated, func() { _, _ = vsOpt.ExtractBlock() }) - require.PanicsWithValue(t, coreutil.ErrStateHasBeenInvalidated, func() { + require.PanicsWithValue(t, coreutil.ErrorStateInvalidated, func() { _ = vsOpt.PreviousStateHash() }) - require.PanicsWithValue(t, coreutil.ErrStateHasBeenInvalidated, func() { + require.PanicsWithValue(t, coreutil.ErrorStateInvalidated, func() { _ = vsOpt.KVStore() }) } diff --git a/packages/state/stateupdate.go b/packages/state/stateupdate.go index b427aa23b1..33ec83816b 100644 --- a/packages/state/stateupdate.go +++ b/packages/state/stateupdate.go @@ -126,7 +126,7 @@ func (su *stateUpdateImpl) String() string { if err != nil { ts = fmt.Sprintf("(%v)", err) } else if ok { - ts = fmt.Sprintf("%v", t) + ts = t.String() } bi := none idx, ok, err := su.stateIndexMutation() diff --git a/packages/testutil/testpeers/testkeys_pregenerated_test.go b/packages/testutil/testpeers/testkeys_pregenerated_test.go index 6164b64da2..3acd485926 100644 --- a/packages/testutil/testpeers/testkeys_pregenerated_test.go +++ b/packages/testutil/testpeers/testkeys_pregenerated_test.go @@ -6,7 +6,7 @@ package testpeers_test import ( "bytes" "fmt" - "io/ioutil" + "os" "testing" "github.com/iotaledger/wasp/packages/tcrypto" @@ -54,6 +54,6 @@ func testPregenerateDKS(t *testing.T, n, f uint16) { dkb = dki.Bytes() require.Nil(t, util.WriteBytes16(&buf, dkb)) } - err = ioutil.WriteFile(fmt.Sprintf("testkeys_pregenerated-%v-%v.bin", n, threshold), buf.Bytes(), 0o644) + err = os.WriteFile(fmt.Sprintf("testkeys_pregenerated-%v-%v.bin", n, threshold), buf.Bytes(), 0o644) require.Nil(t, err) } diff --git a/packages/vm/core/blocklog/impl.go b/packages/vm/core/blocklog/impl.go index 3fed8e81ef..6dbf3352a2 100644 --- a/packages/vm/core/blocklog/impl.go +++ b/packages/vm/core/blocklog/impl.go @@ -176,10 +176,17 @@ func viewGetEventsForRequest(ctx iscp.SandboxView) (dict.Dict, error) { // viewGetEventsForBlock returns a list of events for a given block. // params: -// ParamBlockIndex - index of the block +// ParamBlockIndex - index of the block (defaults to latest block) func viewGetEventsForBlock(ctx iscp.SandboxView) (dict.Dict, error) { params := kvdecoder.New(ctx.Params()) - blockIndex := params.MustGetUint32(ParamBlockIndex) + + var blockIndex uint32 + if ctx.Params().MustHas(ParamBlockIndex) { + blockIndex = params.MustGetUint32(ParamBlockIndex) + } else { + registry := collections.NewArray32ReadOnly(ctx.State(), StateVarBlockRegistry) + blockIndex = registry.MustLen() - 1 + } if blockIndex == 0 { // block 0 is an empty state diff --git a/packages/vm/core/testcore/blob_deploy_test.go b/packages/vm/core/testcore/blob_deploy_test.go index 8c4a10155a..f923964c43 100644 --- a/packages/vm/core/testcore/blob_deploy_test.go +++ b/packages/vm/core/testcore/blob_deploy_test.go @@ -120,14 +120,14 @@ func TestDeployGrant(t *testing.T) { err = chain.DeployWasmContract(user1, "testCore", wasmFile) require.NoError(t, err) - _, _, contacts := chain.GetInfo() - require.EqualValues(t, len(core.AllCoreContractsByHash)+1, len(contacts)) + _, _, contracts := chain.GetInfo() + require.EqualValues(t, len(core.AllCoreContractsByHash)+1, len(contracts)) err = chain.DeployWasmContract(user1, "testInccounter2", wasmFile) require.NoError(t, err) - _, _, contacts = chain.GetInfo() - require.EqualValues(t, len(core.AllCoreContractsByHash)+2, len(contacts)) + _, _, contracts = chain.GetInfo() + require.EqualValues(t, len(core.AllCoreContractsByHash)+2, len(contracts)) } func TestRevokeDeploy(t *testing.T) { @@ -145,8 +145,8 @@ func TestRevokeDeploy(t *testing.T) { err = chain.DeployWasmContract(user1, "testCore", wasmFile) require.NoError(t, err) - _, _, contacts := chain.GetInfo() - require.EqualValues(t, len(core.AllCoreContractsByHash)+1, len(contacts)) + _, _, contracts := chain.GetInfo() + require.EqualValues(t, len(core.AllCoreContractsByHash)+1, len(contracts)) req = solo.NewCallParams(root.Contract.Name, root.FuncRevokeDeployPermission.Name, root.ParamDeployer, user1AgentID, @@ -157,8 +157,8 @@ func TestRevokeDeploy(t *testing.T) { err = chain.DeployWasmContract(user1, "testInccounter2", wasmFile) require.Error(t, err) - _, _, contacts = chain.GetInfo() - require.EqualValues(t, len(core.AllCoreContractsByHash)+1, len(contacts)) + _, _, contracts = chain.GetInfo() + require.EqualValues(t, len(core.AllCoreContractsByHash)+1, len(contracts)) } func TestDeployGrantFail(t *testing.T) { diff --git a/packages/vm/core/testcore/blocklog_test.go b/packages/vm/core/testcore/blocklog_test.go index 3eaea707c5..d0565ab85f 100644 --- a/packages/vm/core/testcore/blocklog_test.go +++ b/packages/vm/core/testcore/blocklog_test.go @@ -5,6 +5,7 @@ import ( "github.com/iotaledger/wasp/contracts/native/inccounter" "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/core" "github.com/iotaledger/wasp/packages/vm/core/blocklog" @@ -193,10 +194,16 @@ func getEventsForRequest(t *testing.T, chain *solo.Chain, reqID iscp.RequestID) return events } -func getEventsForBlock(t *testing.T, chain *solo.Chain, blockNumber int32) []string { - res, err := chain.CallView(blocklog.Contract.Name, blocklog.FuncGetEventsForBlock.Name, - blocklog.ParamBlockIndex, blockNumber, - ) +func getEventsForBlock(t *testing.T, chain *solo.Chain, blockNumber ...int32) []string { + var res dict.Dict + var err error + if len(blockNumber) > 0 { + res, err = chain.CallView(blocklog.Contract.Name, blocklog.FuncGetEventsForBlock.Name, + blocklog.ParamBlockIndex, blockNumber[0], + ) + } else { + res, err = chain.CallView(blocklog.Contract.Name, blocklog.FuncGetEventsForBlock.Name) + } require.NoError(t, err) events, err := EventsViewResultToStringArray(res) require.NoError(t, err) @@ -247,6 +254,9 @@ func TestGetEvents(t *testing.T) { events = getEventsForBlock(t, chain, 5) require.Len(t, events, 1) require.Contains(t, events[0], "counter = 3") + events = getEventsForBlock(t, chain) // latest block should be 5 + require.Len(t, events, 1) + require.Contains(t, events[0], "counter = 3") events = getEventsForSC(t, chain, 0, 1000) require.Len(t, events, 4) diff --git a/packages/vm/core/testcore/output_limits_test.go b/packages/vm/core/testcore/output_limits_test.go index 959dfe11de..5ec6dd6d9a 100644 --- a/packages/vm/core/testcore/output_limits_test.go +++ b/packages/vm/core/testcore/output_limits_test.go @@ -62,7 +62,7 @@ func TestTooManyOutputsInASingleCall(t *testing.T) { wallet, ) require.Error(t, err) - require.Contains(t, fmt.Sprintf("%v", err), "exceeded max number of allowed outputs") + require.Contains(t, err.Error(), "exceeded max number of allowed outputs") finalBalance := env.GetAddressBalance(address, colored.IOTA) require.Equal(t, finalBalance, initialBalance) diff --git a/packages/vm/core/testcore/sbtests/sbtestsc/testcore_bg.wasm b/packages/vm/core/testcore/sbtests/sbtestsc/testcore_bg.wasm index 732ae2c262..c7701951f2 100644 Binary files a/packages/vm/core/testcore/sbtests/sbtestsc/testcore_bg.wasm and b/packages/vm/core/testcore/sbtests/sbtestsc/testcore_bg.wasm differ diff --git a/packages/vm/processors/cache.go b/packages/vm/processors/cache.go index 7a323dc9af..a79ad8c3bd 100644 --- a/packages/vm/processors/cache.go +++ b/packages/vm/processors/cache.go @@ -13,14 +13,14 @@ import ( // Cache stores all initialized VMProcessor instances used by a single chain type Cache struct { - *sync.Mutex + mutex *sync.Mutex Config *Config processors map[hashing.HashValue]iscp.VMProcessor } func MustNew(config *Config) *Cache { ret := &Cache{ - Mutex: &sync.Mutex{}, + mutex: &sync.Mutex{}, Config: config, processors: make(map[hashing.HashValue]iscp.VMProcessor), } @@ -34,8 +34,8 @@ func MustNew(config *Config) *Cache { // NewProcessor deploys new processor in the cache func (cps *Cache) NewProcessor(programHash hashing.HashValue, programCode []byte, vmtype string) error { - cps.Lock() - defer cps.Unlock() + cps.mutex.Lock() + defer cps.mutex.Unlock() return cps.newProcessor(programHash, programCode, vmtype) } @@ -80,8 +80,8 @@ func (cps *Cache) GetOrCreateProcessor(rec *root.ContractRecord, getBinary func( } func (cps *Cache) GetOrCreateProcessorByProgramHash(progHash hashing.HashValue, getBinary func(hashing.HashValue) (string, []byte, error)) (iscp.VMProcessor, error) { - cps.Lock() - defer cps.Unlock() + cps.mutex.Lock() + defer cps.mutex.Unlock() if proc, ok := cps.processors[progHash]; ok { return proc, nil @@ -101,7 +101,7 @@ func (cps *Cache) GetOrCreateProcessorByProgramHash(progHash hashing.HashValue, // RemoveProcessor deletes processor from cache func (cps *Cache) RemoveProcessor(h hashing.HashValue) { - cps.Lock() - defer cps.Unlock() + cps.mutex.Lock() + defer cps.mutex.Unlock() delete(cps.processors, h) } diff --git a/packages/vm/readme.md b/packages/vm/readme.md index 9a3a0c4302..70b6f601f8 100644 --- a/packages/vm/readme.md +++ b/packages/vm/readme.md @@ -104,7 +104,7 @@ and other related interfaces. Significant part of the VM logic is implemented as _core smart contracts_. The core smart contracts also expose core logic of each ISCP chain to outside users: the core smart contracts can be called by requests just like any other - smart contact. + smart contract. The implementation of core smart contracts is hardcoded into the Wasp. Implementations of all core contract as well as their unit tests can be found in [wasp/packages/vm/code](./core). @@ -152,7 +152,7 @@ with the Wasp, are represented by `wasm` binaries. Other VM types may take diffe executable code. To deploy a `wasmtime` smart contract on the chain, first we need to upload the corresponding `wasm` binary. -All `wasm` binaries (as well as any other files of data) are kept in the registry handled by the `blob` core contact. +All `wasm` binaries (as well as any other files of data) are kept in the registry handled by the `blob` core contract. To upload a `wasm` binary to the chain one must send a request to the `blob`. Each blob on the chain is referenced by its hash. @@ -255,7 +255,7 @@ The goal is to be able to run EVM smart contracts on ISCP chains. The EVM should the ISCP VM Abstraction. The EVM would be implemented as a processor and it will be able to access key/value store of the state through `State()` interface of the `Sandbox()`. It essentially means the whole EVM chain would be implemented as a state of one -ISCP smart contact. +ISCP smart contract. This way EVM would run in an isolated environment and Solidity code won't be able to access and manipulate native IOTA assets, hence Virtual Ethereum. To open EVM to access all spectrum of ISCP functions would be the next step. diff --git a/packages/vm/runvm/runtask.go b/packages/vm/runvm/runtask.go index a7992d2430..1fab4b550f 100644 --- a/packages/vm/runvm/runtask.go +++ b/packages/vm/runvm/runtask.go @@ -1,6 +1,8 @@ package runvm import ( + "errors" + "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/packages/hashing" "github.com/iotaledger/wasp/packages/iscp/colored" @@ -22,7 +24,7 @@ func (r VMRunner) Run(task *vm.VMTask) { if r == nil { return } - if _, ok := r.(*coreutil.ErrorStateInvalidated); ok { + if err, ok := r.(error); ok && errors.Is(err, coreutil.ErrorStateInvalidated) { task.Log.Warnf("VM task has been abandoned due to invalidated state. ACS session id: %d", task.ACSSessionID) return } diff --git a/packages/vm/sandbox/sandbox.go b/packages/vm/sandbox/sandbox.go index b06c938fde..f2310de2ed 100644 --- a/packages/vm/sandbox/sandbox.go +++ b/packages/vm/sandbox/sandbox.go @@ -38,7 +38,7 @@ func (s *sandbox) Balances() colored.Balances { return s.vmctx.GetMyBalances() } -// Call calls an entry point of contact, passes parameters and funds +// Call calls an entry point of contract, passes parameters and funds func (s *sandbox) Call(target, entryPoint iscp.Hname, params dict.Dict, transfer colored.Balances) (dict.Dict, error) { return s.vmctx.Call(target, entryPoint, params, transfer) } diff --git a/packages/vm/viewcontext/viewcontext.go b/packages/vm/viewcontext/viewcontext.go index 8ed259d447..72f712ab47 100644 --- a/packages/vm/viewcontext/viewcontext.go +++ b/packages/vm/viewcontext/viewcontext.go @@ -8,7 +8,6 @@ import ( "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/hashing" "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/kv/subrealm" @@ -53,10 +52,10 @@ func (v *Viewcontext) CallView(contractHname, epCode iscp.Hname, params dict.Dic switch err1 := r.(type) { case *kv.DBError: v.log.Panicf("DB error: %v", err1) - case *coreutil.ErrorStateInvalidated: + case error: err = err1 default: - err = xerrors.Errorf("viewcontext: panic in VM: %v", err1) + err = xerrors.Errorf("viewcontext: panic in VM: %w", err1) } v.log.Debugf("CallView: %v", err) v.log.Debugf(string(debug.Stack())) diff --git a/packages/vm/vmcontext/runreq.go b/packages/vm/vmcontext/runreq.go index f4d498b166..c0ce4dbcfb 100644 --- a/packages/vm/vmcontext/runreq.go +++ b/packages/vm/vmcontext/runreq.go @@ -1,6 +1,7 @@ package vmcontext import ( + "errors" "fmt" "runtime/debug" "time" @@ -64,17 +65,19 @@ func (vmctx *VMContext) RunTheRequest(req iscp.Request, requestIndex uint16) { if r == nil { return } + switch err := r.(type) { case *kv.DBError: panic(err) - case *coreutil.ErrorStateInvalidated: - panic(err) - default: - vmctx.lastResult = nil - vmctx.lastError = xerrors.Errorf("panic in VM: %v", err) - vmctx.Debugf("%v", vmctx.lastError) - vmctx.Debugf(string(debug.Stack())) + case error: + if errors.Is(err, coreutil.ErrorStateInvalidated) { + panic(err) + } } + vmctx.lastResult = nil + vmctx.lastError = xerrors.Errorf("panic in VM: %v", r) + vmctx.Debugf("%v", vmctx.lastError) + vmctx.Debugf(string(debug.Stack())) }() vmctx.mustCallFromRequest() }() @@ -146,7 +149,7 @@ func (vmctx *VMContext) mustSetUpRequestContext(req iscp.Request, requestIndex u var ok bool vmctx.contractRecord, ok = vmctx.findContractByHname(targetContract) if !ok { - vmctx.log.Warnf("contact not found: %s", targetContract) + vmctx.log.Warnf("contract not found: %s", targetContract) } if vmctx.contractRecord.Hname() == 0 { vmctx.log.Warn("default contract will be called") diff --git a/packages/vm/vmcontext/vmcontext.go b/packages/vm/vmcontext/vmcontext.go index 6714f54a25..64e9d3699d 100644 --- a/packages/vm/vmcontext/vmcontext.go +++ b/packages/vm/vmcontext/vmcontext.go @@ -110,7 +110,7 @@ func CreateVMContext(task *vm.VMTask) *VMContext { blockIndex := optimisticStateAccess.BlockIndex() if stateHash != stateHashFromState || blockIndex != task.ChainInput.GetStateIndex() { // leaving earlier, state is not consistent and optimistic reader sync didn't catch it - panic(coreutil.ErrStateHasBeenInvalidated) + panic(coreutil.ErrorStateInvalidated) } openingStateUpdate := state.NewStateUpdateWithBlocklogValues(blockIndex+1, task.Timestamp, stateHash) optimisticStateAccess.ApplyStateUpdates(openingStateUpdate) diff --git a/packages/vm/wasmclient/host.go b/packages/vm/wasmclient/host.go index aefa27401d..e01ed094f5 100644 --- a/packages/vm/wasmclient/host.go +++ b/packages/vm/wasmclient/host.go @@ -5,7 +5,7 @@ package wasmclient -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" //go:wasm-module WasmLib //export hostGetBytes @@ -26,21 +26,21 @@ func hostSetBytes(objID, keyID, typeID int32, value *byte, size int32) // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ type WasmVMHost struct{ - funcs []func(ctx wasmlib.ScFuncContext) - views []func(ctx wasmlib.ScViewContext) + funcs []wasmlib.ScFuncContextFunction + views []wasmlib.ScViewContextFunction } // implements wasmlib.ScHost interface var _ wasmlib.ScHost = &WasmVMHost{} -func (w *WasmVMHost) AddFunc(f func(ctx wasmlib.ScFuncContext)) []func(ctx wasmlib.ScFuncContext) { +func (w *WasmVMHost) AddFunc(f wasmlib.ScFuncContextFunction) []wasmlib.ScFuncContextFunction { if f != nil { w.funcs = append(w.funcs, f) } return w.funcs } -func (w *WasmVMHost) AddView(v func(ctx wasmlib.ScViewContext)) []func(ctx wasmlib.ScViewContext) { +func (w *WasmVMHost) AddView(v wasmlib.ScViewContextFunction) []wasmlib.ScViewContextFunction { if v != nil { w.views = append(w.views, v) } diff --git a/packages/vm/wasmhost/kvstorehost.go b/packages/vm/wasmhost/kvstorehost.go index c345bd2a4f..6c6577b98d 100644 --- a/packages/vm/wasmhost/kvstorehost.go +++ b/packages/vm/wasmhost/kvstorehost.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/iotaledger/wasp/packages/kv/codec" - "github.com/iotaledger/wasp/packages/vm/wasmlib" "github.com/mr-tron/base58" ) @@ -33,6 +32,12 @@ const ( OBJTYPE_MAP int32 = 11 OBJTYPE_REQUEST_ID int32 = 12 OBJTYPE_STRING int32 = 13 + + OBJID_NULL int32 = 0 + OBJID_ROOT int32 = 1 + OBJID_STATE int32 = 2 + OBJID_PARAMS int32 = 3 + OBJID_RESULTS int32 = 4 ) // flag to indicate that this key id originally comes from a bytes key @@ -241,7 +246,7 @@ func (h *KvStoreHost) SetBytes(objID, keyID, typeID int32, bytes []byte) { func (h *KvStoreHost) Tracef(format string, a ...interface{}) { if HostTracing { text := fmt.Sprintf(format, a...) - h.SetBytes(wasmlib.OBJ_ID_ROOT, KeyTrace, OBJTYPE_STRING, []byte(text)) + h.SetBytes(OBJID_ROOT, KeyTrace, OBJTYPE_STRING, []byte(text)) } } diff --git a/packages/vm/wasmhost/wasmgovm.go b/packages/vm/wasmhost/wasmgovm.go index b8b5a21780..29aebccafe 100644 --- a/packages/vm/wasmhost/wasmgovm.go +++ b/packages/vm/wasmhost/wasmgovm.go @@ -7,7 +7,7 @@ import ( "errors" "strings" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) type WasmGoVM struct { @@ -22,6 +22,10 @@ func NewWasmGoVM(scName string, onLoad func()) *WasmGoVM { return &WasmGoVM{scName: scName, onLoad: onLoad} } +func Connect(h wasmlib.ScHost) wasmlib.ScHost { + return wasmlib.ConnectHost(h) +} + func (vm *WasmGoVM) Interrupt() { // disabled for now // panic("implement me") diff --git a/packages/vm/wasmhost/wasmhost.go b/packages/vm/wasmhost/wasmhost.go index 16d4e73905..8e6296291d 100644 --- a/packages/vm/wasmhost/wasmhost.go +++ b/packages/vm/wasmhost/wasmhost.go @@ -7,7 +7,7 @@ import ( "errors" "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) type WasmStore interface { @@ -18,20 +18,20 @@ type WasmHost struct { codeToFunc map[uint32]string funcToCode map[string]uint32 funcToIndex map[string]int32 - funcs []func(ctx wasmlib.ScFuncContext) - views []func(ctx wasmlib.ScViewContext) + funcs []wasmlib.ScFuncContextFunction + views []wasmlib.ScViewContextFunction store WasmStore vm WasmVM } -func (host *WasmHost) AddFunc(f func(ctx wasmlib.ScFuncContext)) []func(ctx wasmlib.ScFuncContext) { +func (host *WasmHost) AddFunc(f wasmlib.ScFuncContextFunction) []wasmlib.ScFuncContextFunction { if f != nil { host.funcs = append(host.funcs, f) } return host.funcs } -func (host *WasmHost) AddView(v func(ctx wasmlib.ScViewContext)) []func(ctx wasmlib.ScViewContext) { +func (host *WasmHost) AddView(v wasmlib.ScViewContextFunction) []wasmlib.ScViewContextFunction { if v != nil { host.views = append(host.views, v) } diff --git a/packages/vm/wasmhost/wasmkeys.go b/packages/vm/wasmhost/wasmkeys.go index cd2abe280f..c925cdb123 100644 --- a/packages/vm/wasmhost/wasmkeys.go +++ b/packages/vm/wasmhost/wasmkeys.go @@ -34,25 +34,23 @@ const ( KeyLog = int32(-26) KeyMaps = int32(-27) KeyMinted = int32(-28) - KeyName = int32(-29) - KeyPanic = int32(-30) - KeyParams = int32(-31) - KeyPost = int32(-32) - KeyRandom = int32(-33) - KeyRequestID = int32(-34) - KeyResults = int32(-35) - KeyReturn = int32(-36) - KeyState = int32(-37) - KeyTimestamp = int32(-38) - KeyTrace = int32(-39) - KeyTransfers = int32(-40) - KeyUtility = int32(-41) - KeyValid = int32(-42) + KeyPanic = int32(-29) + KeyParams = int32(-30) + KeyPost = int32(-31) + KeyRandom = int32(-32) + KeyRequestID = int32(-33) + KeyResults = int32(-34) + KeyReturn = int32(-35) + KeyState = int32(-36) + KeyTimestamp = int32(-37) + KeyTrace = int32(-38) + KeyTransfers = int32(-39) + KeyUtility = int32(-40) // KeyZzzzzzz is treated like a version number. // When anything changes to the keys give this one a different value // and make sure that the client side is updated accordingly - KeyZzzzzzz = int32(-43) + KeyZzzzzzz = int32(-41) ) // associate names with predefined key ids @@ -85,7 +83,6 @@ var predefinedKeyMap = map[string]int32{ "$log": KeyLog, "$maps": KeyMaps, "$minted": KeyMinted, - "$name": KeyName, "$panic": KeyPanic, "$params": KeyParams, "$post": KeyPost, @@ -98,7 +95,6 @@ var predefinedKeyMap = map[string]int32{ "$trace": KeyTrace, "$transfers": KeyTransfers, "$utility": KeyUtility, - "$valid": KeyValid, } var predefinedKeys = initKeyMap() diff --git a/packages/vm/wasmhost/wasmtimevm.go b/packages/vm/wasmhost/wasmtimevm.go index ea4af7a3b7..136a489d69 100644 --- a/packages/vm/wasmhost/wasmtimevm.go +++ b/packages/vm/wasmhost/wasmtimevm.go @@ -39,14 +39,14 @@ func (vm *WasmTimeVM) LinkHost(impl WasmVM, host *WasmHost) error { _ = vm.WasmVMBase.LinkHost(impl, host) err := vm.linker.DefineFunc("WasmLib", "hostGetBytes", - func(objID int32, keyID int32, typeID int32, stringRef int32, size int32) int32 { + func(objID, keyID, typeID, stringRef, size int32) int32 { return vm.HostGetBytes(objID, keyID, typeID, stringRef, size) }) if err != nil { return err } err = vm.linker.DefineFunc("WasmLib", "hostGetKeyID", - func(keyRef int32, size int32) int32 { + func(keyRef, size int32) int32 { return vm.HostGetKeyID(keyRef, size) }) if err != nil { @@ -60,15 +60,24 @@ func (vm *WasmTimeVM) LinkHost(impl WasmVM, host *WasmHost) error { return err } err = vm.linker.DefineFunc("WasmLib", "hostSetBytes", - func(objID int32, keyID int32, typeID int32, stringRef int32, size int32) { + func(objID, keyID, typeID, stringRef, size int32) { vm.HostSetBytes(objID, keyID, typeID, stringRef, size) }) if err != nil { return err } + // AssemblyScript Wasm versions uses this one to write panic message to console + err = vm.linker.DefineFunc("env", "abort", + func(p1, p2, p3, p4 int32) { + vm.EnvAbort(p1, p2, p3, p4) + }) + if err != nil { + return err + } + // TinyGo Wasm versions uses this one to write panic message to console - fdWrite := func(fd int32, iovs int32, size int32, written int32) int32 { + fdWrite := func(fd, iovs, size, written int32) int32 { return vm.HostFdWrite(fd, iovs, size, written) } err = vm.linker.DefineFunc("wasi_unstable", "fd_write", fdWrite) diff --git a/packages/vm/wasmhost/wasmvm.go b/packages/vm/wasmhost/wasmvm.go index 45ae39e173..1bee0d4bf4 100644 --- a/packages/vm/wasmhost/wasmvm.go +++ b/packages/vm/wasmhost/wasmvm.go @@ -48,15 +48,22 @@ type WasmVMBase struct { timeoutStarted bool } -func (vm *WasmVMBase) LinkHost(impl WasmVM, host *WasmHost) error { - // trick vm into thinking it doesn't have to start the timeout timer - // useful when debugging to prevent timing out on breakpoints - vm.timeoutStarted = DisableWasmTimeout +func (vm *WasmVMBase) EnvAbort(errMsg, fileName, line, col int32) { + ptr := vm.impl.UnsafeMemory() - vm.impl = impl - vm.host = host - host.vm = impl - return nil + // null-terminated UTF-16 error message + str1 := make([]byte, 0) + for i := errMsg; ptr[i] != 0; i += 2 { + str1 = append(str1, ptr[i]) + } + + // null-terminated UTF-16 file name + str2 := make([]byte, 0) + for i := fileName; ptr[i] != 0; i += 2 { + str2 = append(str2, ptr[i]) + } + + panic(fmt.Sprintf("AssemblyScript panic: %s (%s %d:%d)", string(str1), string(str2), line, col)) } //nolint:unparam @@ -149,6 +156,17 @@ func (vm *WasmVMBase) HostSetBytes(objID, keyID, typeID, stringRef, size int32) host.SetBytes(objID, keyID, typeID, bytes) } +func (vm *WasmVMBase) LinkHost(impl WasmVM, host *WasmHost) error { + // trick vm into thinking it doesn't have to start the timeout timer + // useful when debugging to prevent timing out on breakpoints + vm.timeoutStarted = DisableWasmTimeout + + vm.impl = impl + vm.host = host + host.vm = impl + return nil +} + func (vm *WasmVMBase) PreCall() []byte { ptr := vm.impl.UnsafeMemory() frame := make([]byte, len(ptr)) diff --git a/contracts/wasm/wasmlib/Cargo.toml b/packages/vm/wasmlib/Cargo.toml similarity index 100% rename from contracts/wasm/wasmlib/Cargo.toml rename to packages/vm/wasmlib/Cargo.toml diff --git a/contracts/wasm/wasmlib/LICENSE b/packages/vm/wasmlib/LICENSE similarity index 100% rename from contracts/wasm/wasmlib/LICENSE rename to packages/vm/wasmlib/LICENSE diff --git a/contracts/wasm/wasmlib/README.md b/packages/vm/wasmlib/README.md similarity index 100% rename from contracts/wasm/wasmlib/README.md rename to packages/vm/wasmlib/README.md diff --git a/packages/vm/wasmlib/corecontracts/build_all_go.bat b/packages/vm/wasmlib/corecontracts/build_all_go.bat deleted file mode 100644 index 9c1e9b4ff2..0000000000 --- a/packages/vm/wasmlib/corecontracts/build_all_go.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -for /d %%f in (*.) do call build_go.bat %%f %1 diff --git a/packages/vm/wasmlib/corecontracts/build_go.bat b/packages/vm/wasmlib/corecontracts/build_go.bat deleted file mode 100644 index 0403dc279a..0000000000 --- a/packages/vm/wasmlib/corecontracts/build_go.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -cd %1 -if not exist schema.yaml if not exist schema.json goto :xit -echo Building %1 -schema -core -go %2 -:xit -cd .. diff --git a/packages/vm/wasmlib/corecontracts/coreaccounts/schema.yaml b/packages/vm/wasmlib/corecontracts/coreaccounts/schema.yaml deleted file mode 100644 index abcf03bb32..0000000000 --- a/packages/vm/wasmlib/corecontracts/coreaccounts/schema.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: CoreAccounts -description: Core chain account ledger contract -structs: {} -typedefs: {} -state: {} -funcs: - deposit: - params: - agentID=a: AgentID? // default is caller - harvest: - params: - withdrawAmount=m: Int64? // default (zero) means all - withdrawColor=c: Color? // defaults to colored.IOTA - withdraw: {} -views: - accounts: - results: - agents=this: map[AgentID]Bytes // bytes are always empty - balance: - params: - agentID=a: AgentID - results: - balances=this: map[Color]Int64 - getAccountNonce: - params: - agentID=a: AgentID - results: - accountNonce=n: Int64 // TODO should be Uint64 - totalAssets: - results: - balances=this: map[Color]Int64 diff --git a/packages/vm/wasmlib/corecontracts/coreblob/schema.yaml b/packages/vm/wasmlib/corecontracts/coreblob/schema.yaml deleted file mode 100644 index f42fdb0688..0000000000 --- a/packages/vm/wasmlib/corecontracts/coreblob/schema.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: CoreBlob -description: Core blob contract -structs: {} -typedefs: {} -state: {} -funcs: - storeBlob: - params: - blobs=this: map[String]Bytes // set of named blobs - results: - hash: Hash // calculated hash of blob set -views: - getBlobField: - params: - field: String // blob name - hash: Hash // blob set - results: - bytes: Bytes // blob data - getBlobInfo: - params: - hash: Hash // blob set - results: - blobSizes=this: map[String]Int32 // size for each named blob - listBlobs: - results: - blobSizes=this: map[Hash]Int32 // total size for each blob set diff --git a/packages/vm/wasmlib/corecontracts/coreblocklog/schema.yaml b/packages/vm/wasmlib/corecontracts/coreblocklog/schema.yaml deleted file mode 100644 index 9c07e01aba..0000000000 --- a/packages/vm/wasmlib/corecontracts/coreblocklog/schema.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: CoreBlockLog -description: Core block log contract -structs: {} -typedefs: {} -state: {} -funcs: {} -views: - controlAddresses: - results: - blockIndex=n: Int32 - governingAddress=g: Address - stateControllerAddress=s: Address - getBlockInfo: - params: - blockIndex=n: Int32 - results: - blockInfo=i: Bytes - getEventsForBlock: - params: - blockIndex=n: Int32 - results: - event=e: Bytes[] // native contract, so this is an Array16 - getEventsForContract: - params: - contractHname=h: Hname - fromBlock=f: Int32? - toBlock=t: Int32? - results: - event=e: Bytes[] // native contract, so this is an Array16 - getEventsForRequest: - params: - requestID=u: RequestID - results: - event=e: Bytes[] // native contract, so this is an Array16 - getLatestBlockInfo: - results: - blockIndex=n: Int32 - blockInfo=i: Bytes - getRequestIDsForBlock: - params: - blockIndex=n: Int32 - results: - requestID=u: RequestID[] // native contract, so this is an Array16 - getRequestReceipt: - params: - requestID=u: RequestID - results: - blockIndex=n: Int32 - requestIndex=r: Int16 - requestRecord=d: Bytes - getRequestReceiptsForBlock: - params: - blockIndex=n: Int32 - results: - requestRecord=d: Bytes[] // native contract, so this is an Array16 - isRequestProcessed: - params: - requestID=u: RequestID - results: - requestProcessed=p: String diff --git a/packages/vm/wasmlib/corecontracts/coregovernance/schema.yaml b/packages/vm/wasmlib/corecontracts/coregovernance/schema.yaml deleted file mode 100644 index 1b58e2df7b..0000000000 --- a/packages/vm/wasmlib/corecontracts/coregovernance/schema.yaml +++ /dev/null @@ -1,62 +0,0 @@ -name: CoreGovernance -description: Core governance contract -structs: {} -typedefs: {} -state: {} -funcs: - addAllowedStateControllerAddress: - params: - chainOwner=oi: AgentID - feeColor=fc: Color? // default colored.IOTA - stateControllerAddress=S: Address - claimChainOwnership: {} - delegateChainOwnership: - params: - chainOwner=oi: AgentID - removeAllowedStateControllerAddress: - params: - stateControllerAddress=S: Address - rotateStateController: - params: - stateControllerAddress=S: Address - setChainInfo: - params: - maxBlobSize=bs: Int32? // default no change - maxEventSize=es: Int16? // default no change - maxEventsPerReq=ne: Int16? // default no change - ownerFee=of: Int64? // default no change - validatorFee=vf: Int64? // default no change - setContractFee: - params: - hname=hn: Hname // contract id - ownerFee=of: Int64? // default 0 (no fee) - validatorFee=vf: Int64? // default 0 (no fee) - setDefaultFee: - params: - ownerFee=of: Int64? // default -1 (not set) - validatorFee=vf: Int64? // default -1 (not set) -views: - getAllowedStateControllerAddresses: - results: - allowedStateControllerAddresses=a: Bytes[] // native contract, so this is an Array16 - getChainInfo: - results: - chainID=c: ChainID - chainOwnerID=o: AgentID - defaultOwnerFee=do: Int64 - defaultValidatorFee=dv: Int64 - description=d: String - feeColor=f: Color - maxBlobSize=mb: Int32 - maxEventSize=me: Int16 - maxEventsPerReq=mr: Int16 - getFeeInfo: - params: - hname=hn: Hname - results: - feeColor=f: Color - ownerFee=of: Int64 - validatorFee=vf: Int64 - getMaxBlobSize: - results: - maxBlobSize=mb: Int32 diff --git a/packages/vm/wasmlib/corecontracts/coreroot/schema.yaml b/packages/vm/wasmlib/corecontracts/coreroot/schema.yaml deleted file mode 100644 index 196c9fba0d..0000000000 --- a/packages/vm/wasmlib/corecontracts/coreroot/schema.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: CoreRoot -description: Core root contract -structs: {} -typedefs: {} -state: {} -funcs: - deployContract: - params: - description=ds: String? // default 'N/A' - name=nm: String - programHash=ph: Hash //TODO variable init params for deployed contract - grantDeployPermission: - params: - deployer=dp: AgentID - revokeDeployPermission: - params: - deployer=dp: AgentID -views: - findContract: - params: - hname=hn: Hname - results: - contractFound=cf: Bytes // encoded contract record - contractRecData=dt: Bytes // encoded contract record - getContractRecords: - results: - contractRegistry=r: map[Hname]Bytes // contract records diff --git a/packages/vm/wasmlib/bytes.go b/packages/vm/wasmlib/go/wasmlib/bytes.go similarity index 100% rename from packages/vm/wasmlib/bytes.go rename to packages/vm/wasmlib/go/wasmlib/bytes.go diff --git a/packages/vm/wasmlib/context.go b/packages/vm/wasmlib/go/wasmlib/context.go similarity index 95% rename from packages/vm/wasmlib/context.go rename to packages/vm/wasmlib/go/wasmlib/context.go index 832267817a..6cd4cbfb75 100644 --- a/packages/vm/wasmlib/context.go +++ b/packages/vm/wasmlib/go/wasmlib/context.go @@ -31,26 +31,12 @@ type ScTransfers struct { transfers ScMutableMap } -// special constructor for simplifying iota transfers -func NewScTransferIotas(amount int64) ScTransfers { - return NewScTransfer(IOTA, amount) -} - -// special constructor for simplifying single transfers -func NewScTransfer(color ScColor, amount int64) ScTransfers { - transfer := NewScTransfers() - transfer.Set(color, amount) - return transfer -} - +// create a new transfers object ready to add token transfers func NewScTransfers() ScTransfers { return ScTransfers{transfers: *NewScMutableMap()} } -func NoScTransfers() ScTransfers { - return ScTransfers{} -} - +// create a new transfers object from a balances object func NewScTransfersFromBalances(balances ScBalances) ScTransfers { transfers := NewScTransfers() colors := balances.Colors() @@ -62,7 +48,20 @@ func NewScTransfersFromBalances(balances ScBalances) ScTransfers { return transfers } -// transfers the specified amount of tokens of the specified color +// create a new transfers object and initialize it with the specified amount of iotas +func NewScTransferIotas(amount int64) ScTransfers { + return NewScTransfer(IOTA, amount) +} + +// create a new transfers object and initialize it with the specified token transfer +func NewScTransfer(color ScColor, amount int64) ScTransfers { + transfer := NewScTransfers() + transfer.Set(color, amount) + return transfer +} + +// set the specified colored token transfer in the transfers object +// note that this will overwrite any previous amount for the specified color func (ctx ScTransfers) Set(color ScColor, amount int64) { ctx.transfers.GetInt64(color).SetValue(amount) } @@ -239,10 +238,7 @@ type ScFuncContext struct { ScBaseContext } -var ( - _ ScViewCallContext = &ScViewContext{} - _ ScFuncCallContext = &ScFuncContext{} -) +var _ ScFuncCallContext = &ScFuncContext{} // calls a smart contract function func (ctx ScFuncContext) Call(hContract, hFunction ScHname, params *ScMutableMap, transfer *ScTransfers) ScImmutableMap { diff --git a/packages/vm/wasmlib/contract.go b/packages/vm/wasmlib/go/wasmlib/contract.go similarity index 100% rename from packages/vm/wasmlib/contract.go rename to packages/vm/wasmlib/go/wasmlib/contract.go diff --git a/packages/vm/wasmlib/corecontracts/coreaccounts/consts.go b/packages/vm/wasmlib/go/wasmlib/coreaccounts/consts.go similarity index 94% rename from packages/vm/wasmlib/corecontracts/coreaccounts/consts.go rename to packages/vm/wasmlib/go/wasmlib/coreaccounts/consts.go index 2b5d84fb70..0feb52eb41 100644 --- a/packages/vm/wasmlib/corecontracts/coreaccounts/consts.go +++ b/packages/vm/wasmlib/go/wasmlib/coreaccounts/consts.go @@ -7,7 +7,7 @@ package coreaccounts -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "accounts" diff --git a/packages/vm/wasmlib/corecontracts/coreaccounts/contract.go b/packages/vm/wasmlib/go/wasmlib/coreaccounts/contract.go similarity index 97% rename from packages/vm/wasmlib/corecontracts/coreaccounts/contract.go rename to packages/vm/wasmlib/go/wasmlib/coreaccounts/contract.go index 52c029c8db..849636826b 100644 --- a/packages/vm/wasmlib/corecontracts/coreaccounts/contract.go +++ b/packages/vm/wasmlib/go/wasmlib/coreaccounts/contract.go @@ -7,7 +7,7 @@ package coreaccounts -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type DepositCall struct { Func *wasmlib.ScFunc diff --git a/packages/vm/wasmlib/corecontracts/coreaccounts/params.go b/packages/vm/wasmlib/go/wasmlib/coreaccounts/params.go similarity index 96% rename from packages/vm/wasmlib/corecontracts/coreaccounts/params.go rename to packages/vm/wasmlib/go/wasmlib/coreaccounts/params.go index 66dc098fd6..1ac40981d1 100644 --- a/packages/vm/wasmlib/corecontracts/coreaccounts/params.go +++ b/packages/vm/wasmlib/go/wasmlib/coreaccounts/params.go @@ -7,7 +7,7 @@ package coreaccounts -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableDepositParams struct { id int32 diff --git a/packages/vm/wasmlib/corecontracts/coreaccounts/results.go b/packages/vm/wasmlib/go/wasmlib/coreaccounts/results.go similarity index 97% rename from packages/vm/wasmlib/corecontracts/coreaccounts/results.go rename to packages/vm/wasmlib/go/wasmlib/coreaccounts/results.go index 32c2758c13..075f9e3409 100644 --- a/packages/vm/wasmlib/corecontracts/coreaccounts/results.go +++ b/packages/vm/wasmlib/go/wasmlib/coreaccounts/results.go @@ -7,7 +7,7 @@ package coreaccounts -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type MapAgentIDToImmutableBytes struct { objID int32 diff --git a/packages/vm/wasmlib/corecontracts/coreblob/consts.go b/packages/vm/wasmlib/go/wasmlib/coreblob/consts.go similarity index 92% rename from packages/vm/wasmlib/corecontracts/coreblob/consts.go rename to packages/vm/wasmlib/go/wasmlib/coreblob/consts.go index 4d5e68c23a..9f2192c17c 100644 --- a/packages/vm/wasmlib/corecontracts/coreblob/consts.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblob/consts.go @@ -7,7 +7,7 @@ package coreblob -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "blob" diff --git a/packages/vm/wasmlib/corecontracts/coreblob/contract.go b/packages/vm/wasmlib/go/wasmlib/coreblob/contract.go similarity index 96% rename from packages/vm/wasmlib/corecontracts/coreblob/contract.go rename to packages/vm/wasmlib/go/wasmlib/coreblob/contract.go index 3e713434a4..57b72f9f2e 100644 --- a/packages/vm/wasmlib/corecontracts/coreblob/contract.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblob/contract.go @@ -7,7 +7,7 @@ package coreblob -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type StoreBlobCall struct { Func *wasmlib.ScFunc diff --git a/packages/vm/wasmlib/corecontracts/coreblob/params.go b/packages/vm/wasmlib/go/wasmlib/coreblob/params.go similarity index 96% rename from packages/vm/wasmlib/corecontracts/coreblob/params.go rename to packages/vm/wasmlib/go/wasmlib/coreblob/params.go index a7168c8359..bda5540fe7 100644 --- a/packages/vm/wasmlib/corecontracts/coreblob/params.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblob/params.go @@ -7,7 +7,7 @@ package coreblob -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type MapStringToImmutableBytes struct { objID int32 diff --git a/packages/vm/wasmlib/corecontracts/coreblob/results.go b/packages/vm/wasmlib/go/wasmlib/coreblob/results.go similarity index 97% rename from packages/vm/wasmlib/corecontracts/coreblob/results.go rename to packages/vm/wasmlib/go/wasmlib/coreblob/results.go index f980b595c1..b9a5976423 100644 --- a/packages/vm/wasmlib/corecontracts/coreblob/results.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblob/results.go @@ -7,7 +7,7 @@ package coreblob -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableStoreBlobResults struct { id int32 diff --git a/packages/vm/wasmlib/corecontracts/coreblocklog/consts.go b/packages/vm/wasmlib/go/wasmlib/coreblocklog/consts.go similarity index 97% rename from packages/vm/wasmlib/corecontracts/coreblocklog/consts.go rename to packages/vm/wasmlib/go/wasmlib/coreblocklog/consts.go index fe4691f1f1..f50b04564c 100644 --- a/packages/vm/wasmlib/corecontracts/coreblocklog/consts.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblocklog/consts.go @@ -7,7 +7,7 @@ package coreblocklog -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "blocklog" diff --git a/packages/vm/wasmlib/corecontracts/coreblocklog/contract.go b/packages/vm/wasmlib/go/wasmlib/coreblocklog/contract.go similarity index 98% rename from packages/vm/wasmlib/corecontracts/coreblocklog/contract.go rename to packages/vm/wasmlib/go/wasmlib/coreblocklog/contract.go index d640109523..19da3392e3 100644 --- a/packages/vm/wasmlib/corecontracts/coreblocklog/contract.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblocklog/contract.go @@ -7,7 +7,7 @@ package coreblocklog -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ControlAddressesCall struct { Func *wasmlib.ScView diff --git a/packages/vm/wasmlib/corecontracts/coreblocklog/params.go b/packages/vm/wasmlib/go/wasmlib/coreblocklog/params.go similarity index 98% rename from packages/vm/wasmlib/corecontracts/coreblocklog/params.go rename to packages/vm/wasmlib/go/wasmlib/coreblocklog/params.go index 25f8029f75..08f7955b9c 100644 --- a/packages/vm/wasmlib/corecontracts/coreblocklog/params.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblocklog/params.go @@ -7,7 +7,7 @@ package coreblocklog -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableGetBlockInfoParams struct { id int32 diff --git a/packages/vm/wasmlib/corecontracts/coreblocklog/results.go b/packages/vm/wasmlib/go/wasmlib/coreblocklog/results.go similarity index 99% rename from packages/vm/wasmlib/corecontracts/coreblocklog/results.go rename to packages/vm/wasmlib/go/wasmlib/coreblocklog/results.go index 8768fd92fc..153ced4bae 100644 --- a/packages/vm/wasmlib/corecontracts/coreblocklog/results.go +++ b/packages/vm/wasmlib/go/wasmlib/coreblocklog/results.go @@ -7,7 +7,7 @@ package coreblocklog -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableControlAddressesResults struct { id int32 diff --git a/packages/vm/wasmlib/corecontracts/coregovernance/consts.go b/packages/vm/wasmlib/go/wasmlib/coregovernance/consts.go similarity index 97% rename from packages/vm/wasmlib/corecontracts/coregovernance/consts.go rename to packages/vm/wasmlib/go/wasmlib/coregovernance/consts.go index 9dde3881ff..aec160544d 100644 --- a/packages/vm/wasmlib/corecontracts/coregovernance/consts.go +++ b/packages/vm/wasmlib/go/wasmlib/coregovernance/consts.go @@ -7,7 +7,7 @@ package coregovernance -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "governance" diff --git a/packages/vm/wasmlib/corecontracts/coregovernance/contract.go b/packages/vm/wasmlib/go/wasmlib/coregovernance/contract.go similarity index 98% rename from packages/vm/wasmlib/corecontracts/coregovernance/contract.go rename to packages/vm/wasmlib/go/wasmlib/coregovernance/contract.go index f4425bc190..1a6fd89a82 100644 --- a/packages/vm/wasmlib/corecontracts/coregovernance/contract.go +++ b/packages/vm/wasmlib/go/wasmlib/coregovernance/contract.go @@ -7,7 +7,7 @@ package coregovernance -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type AddAllowedStateControllerAddressCall struct { Func *wasmlib.ScFunc diff --git a/packages/vm/wasmlib/corecontracts/coregovernance/params.go b/packages/vm/wasmlib/go/wasmlib/coregovernance/params.go similarity index 98% rename from packages/vm/wasmlib/corecontracts/coregovernance/params.go rename to packages/vm/wasmlib/go/wasmlib/coregovernance/params.go index 484c8214b0..0ae22c402d 100644 --- a/packages/vm/wasmlib/corecontracts/coregovernance/params.go +++ b/packages/vm/wasmlib/go/wasmlib/coregovernance/params.go @@ -7,7 +7,7 @@ package coregovernance -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableAddAllowedStateControllerAddressParams struct { id int32 diff --git a/packages/vm/wasmlib/corecontracts/coregovernance/results.go b/packages/vm/wasmlib/go/wasmlib/coregovernance/results.go similarity index 98% rename from packages/vm/wasmlib/corecontracts/coregovernance/results.go rename to packages/vm/wasmlib/go/wasmlib/coregovernance/results.go index 2f5ae54326..0b2c92f4c5 100644 --- a/packages/vm/wasmlib/corecontracts/coregovernance/results.go +++ b/packages/vm/wasmlib/go/wasmlib/coregovernance/results.go @@ -7,7 +7,7 @@ package coregovernance -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ArrayOfImmutableBytes struct { objID int32 diff --git a/packages/vm/wasmlib/corecontracts/coreroot/consts.go b/packages/vm/wasmlib/go/wasmlib/coreroot/consts.go similarity index 94% rename from packages/vm/wasmlib/corecontracts/coreroot/consts.go rename to packages/vm/wasmlib/go/wasmlib/coreroot/consts.go index 8c44793529..c607e01f0b 100644 --- a/packages/vm/wasmlib/corecontracts/coreroot/consts.go +++ b/packages/vm/wasmlib/go/wasmlib/coreroot/consts.go @@ -7,7 +7,7 @@ package coreroot -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "root" diff --git a/packages/vm/wasmlib/corecontracts/coreroot/contract.go b/packages/vm/wasmlib/go/wasmlib/coreroot/contract.go similarity index 97% rename from packages/vm/wasmlib/corecontracts/coreroot/contract.go rename to packages/vm/wasmlib/go/wasmlib/coreroot/contract.go index 500c323e82..400eb1237e 100644 --- a/packages/vm/wasmlib/corecontracts/coreroot/contract.go +++ b/packages/vm/wasmlib/go/wasmlib/coreroot/contract.go @@ -7,7 +7,7 @@ package coreroot -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type DeployContractCall struct { Func *wasmlib.ScFunc diff --git a/packages/vm/wasmlib/corecontracts/coreroot/params.go b/packages/vm/wasmlib/go/wasmlib/coreroot/params.go similarity index 97% rename from packages/vm/wasmlib/corecontracts/coreroot/params.go rename to packages/vm/wasmlib/go/wasmlib/coreroot/params.go index a3679094e1..673849677e 100644 --- a/packages/vm/wasmlib/corecontracts/coreroot/params.go +++ b/packages/vm/wasmlib/go/wasmlib/coreroot/params.go @@ -7,7 +7,7 @@ package coreroot -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableDeployContractParams struct { id int32 diff --git a/packages/vm/wasmlib/corecontracts/coreroot/results.go b/packages/vm/wasmlib/go/wasmlib/coreroot/results.go similarity index 96% rename from packages/vm/wasmlib/corecontracts/coreroot/results.go rename to packages/vm/wasmlib/go/wasmlib/coreroot/results.go index 606150a1d1..3ade4f3e44 100644 --- a/packages/vm/wasmlib/corecontracts/coreroot/results.go +++ b/packages/vm/wasmlib/go/wasmlib/coreroot/results.go @@ -7,7 +7,7 @@ package coreroot -import "github.com/iotaledger/wasp/packages/vm/wasmlib" +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" type ImmutableFindContractResults struct { id int32 diff --git a/packages/vm/wasmlib/exports.go b/packages/vm/wasmlib/go/wasmlib/exports.go similarity index 85% rename from packages/vm/wasmlib/exports.go rename to packages/vm/wasmlib/go/wasmlib/exports.go index 199ad8fcc7..5787959311 100644 --- a/packages/vm/wasmlib/exports.go +++ b/packages/vm/wasmlib/go/wasmlib/exports.go @@ -36,16 +36,16 @@ func NewScExports() ScExports { exports := Root.GetStringArray(KeyExports) // tell host what our highest predefined key is // this helps detect missing or extra keys - exports.GetString(int32(KeyZzzzzzz)).SetValue("Go:KeyZzzzzzz") + exports.GetString(int32(KeyZzzzzzz)).SetValue("Go:KEY_ZZZZZZZ") return ScExports{exports: exports} } -func (ctx ScExports) AddFunc(name string, f func(ctx ScFuncContext)) { +func (ctx ScExports) AddFunc(name string, f ScFuncContextFunction) { index := int32(len(AddFunc(f))) - 1 ctx.exports.GetString(index).SetValue(name) } -func (ctx ScExports) AddView(name string, v func(ctx ScViewContext)) { +func (ctx ScExports) AddView(name string, v ScViewContextFunction) { index := int32(len(AddView(v))) - 1 ctx.exports.GetString(index | 0x8000).SetValue(name) } diff --git a/packages/vm/wasmlib/hashtypes.go b/packages/vm/wasmlib/go/wasmlib/hashtypes.go similarity index 100% rename from packages/vm/wasmlib/hashtypes.go rename to packages/vm/wasmlib/go/wasmlib/hashtypes.go diff --git a/packages/vm/wasmlib/host.go b/packages/vm/wasmlib/go/wasmlib/host.go similarity index 77% rename from packages/vm/wasmlib/host.go rename to packages/vm/wasmlib/go/wasmlib/host.go index 9037c68318..899305216f 100644 --- a/packages/vm/wasmlib/host.go +++ b/packages/vm/wasmlib/go/wasmlib/host.go @@ -35,26 +35,31 @@ const ( var TypeSizes = [...]uint8{0, 33, 37, 0, 33, 32, 32, 4, 2, 4, 8, 0, 34, 0} -type ScHost interface { - AddFunc(f func(ctx ScFuncContext)) []func(ctx ScFuncContext) - AddView(v func(ctx ScViewContext)) []func(ctx ScViewContext) - CallFunc(objID, keyID int32, params []byte) []byte - Exists(objID, keyID, typeID int32) bool - GetBytes(objID, keyID, typeID int32) []byte - GetKeyIDFromBytes(bytes []byte) int32 - GetKeyIDFromString(key string) int32 - GetObjectID(objID, keyID, typeID int32) int32 - SetBytes(objID, keyID, typeID int32, value []byte) -} +type ( + ScFuncContextFunction func(ScFuncContext) + ScViewContextFunction func(ScViewContext) + + ScHost interface { + AddFunc(f ScFuncContextFunction) []ScFuncContextFunction + AddView(v ScViewContextFunction) []ScViewContextFunction + CallFunc(objID, keyID int32, params []byte) []byte + Exists(objID, keyID, typeID int32) bool + GetBytes(objID, keyID, typeID int32) []byte + GetKeyIDFromBytes(bytes []byte) int32 + GetKeyIDFromString(key string) int32 + GetObjectID(objID, keyID, typeID int32) int32 + SetBytes(objID, keyID, typeID int32, value []byte) + } +) // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ var host ScHost -func AddFunc(f func(ctx ScFuncContext)) []func(ctx ScFuncContext) { +func AddFunc(f ScFuncContextFunction) []ScFuncContextFunction { return host.AddFunc(f) } -func AddView(v func(ctx ScViewContext)) []func(ctx ScViewContext) { +func AddView(v ScViewContextFunction) []ScViewContextFunction { return host.AddView(v) } diff --git a/packages/vm/wasmlib/immutable.go b/packages/vm/wasmlib/go/wasmlib/immutable.go similarity index 100% rename from packages/vm/wasmlib/immutable.go rename to packages/vm/wasmlib/go/wasmlib/immutable.go diff --git a/packages/vm/wasmlib/inithost.go b/packages/vm/wasmlib/go/wasmlib/inithost.go similarity index 87% rename from packages/vm/wasmlib/inithost.go rename to packages/vm/wasmlib/go/wasmlib/inithost.go index 10eae67586..5dd36b5856 100644 --- a/packages/vm/wasmlib/inithost.go +++ b/packages/vm/wasmlib/go/wasmlib/inithost.go @@ -4,8 +4,8 @@ package wasmlib type InitHost struct { - funcs []func(ctx ScFuncContext) - views []func(ctx ScViewContext) + funcs []ScFuncContextFunction + views []ScViewContextFunction params map[int32][]byte } @@ -15,14 +15,14 @@ func NewInitHost() *InitHost { return &InitHost{params: make(map[int32][]byte)} } -func (h InitHost) AddFunc(f func(ctx ScFuncContext)) []func(ctx ScFuncContext) { +func (h InitHost) AddFunc(f ScFuncContextFunction) []ScFuncContextFunction { if f != nil { h.funcs = append(h.funcs, f) } return h.funcs } -func (h InitHost) AddView(v func(ctx ScViewContext)) []func(ctx ScViewContext) { +func (h InitHost) AddView(v ScViewContextFunction) []ScViewContextFunction { if v != nil { h.views = append(h.views, v) } diff --git a/packages/vm/wasmlib/keys.go b/packages/vm/wasmlib/go/wasmlib/keys.go similarity index 72% rename from packages/vm/wasmlib/keys.go rename to packages/vm/wasmlib/go/wasmlib/keys.go index 0dd7b4d202..7015419481 100644 --- a/packages/vm/wasmlib/keys.go +++ b/packages/vm/wasmlib/go/wasmlib/keys.go @@ -50,19 +50,17 @@ const ( KeyLog = Key32(-26) KeyMaps = Key32(-27) KeyMinted = Key32(-28) - KeyName = Key32(-29) - KeyPanic = Key32(-30) - KeyParams = Key32(-31) - KeyPost = Key32(-32) - KeyRandom = Key32(-33) - KeyRequestID = Key32(-34) - KeyResults = Key32(-35) - KeyReturn = Key32(-36) - KeyState = Key32(-37) - KeyTimestamp = Key32(-38) - KeyTrace = Key32(-39) - KeyTransfers = Key32(-40) - KeyUtility = Key32(-41) - KeyValid = Key32(-42) - KeyZzzzzzz = Key32(-43) + KeyPanic = Key32(-29) + KeyParams = Key32(-30) + KeyPost = Key32(-31) + KeyRandom = Key32(-32) + KeyRequestID = Key32(-33) + KeyResults = Key32(-34) + KeyReturn = Key32(-35) + KeyState = Key32(-36) + KeyTimestamp = Key32(-37) + KeyTrace = Key32(-38) + KeyTransfers = Key32(-39) + KeyUtility = Key32(-40) + KeyZzzzzzz = Key32(-41) ) diff --git a/packages/vm/wasmlib/mutable.go b/packages/vm/wasmlib/go/wasmlib/mutable.go similarity index 100% rename from packages/vm/wasmlib/mutable.go rename to packages/vm/wasmlib/go/wasmlib/mutable.go diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/schema.yaml b/packages/vm/wasmlib/interfaces/coreaccounts.yaml similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreaccounts/schema.yaml rename to packages/vm/wasmlib/interfaces/coreaccounts.yaml diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblob/schema.yaml b/packages/vm/wasmlib/interfaces/coreblob.yaml similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreblob/schema.yaml rename to packages/vm/wasmlib/interfaces/coreblob.yaml diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/schema.yaml b/packages/vm/wasmlib/interfaces/coreblocklog.yaml similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreblocklog/schema.yaml rename to packages/vm/wasmlib/interfaces/coreblocklog.yaml diff --git a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/schema.yaml b/packages/vm/wasmlib/interfaces/coregovernance.yaml similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coregovernance/schema.yaml rename to packages/vm/wasmlib/interfaces/coregovernance.yaml diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreroot/schema.yaml b/packages/vm/wasmlib/interfaces/coreroot.yaml similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreroot/schema.yaml rename to packages/vm/wasmlib/interfaces/coreroot.yaml diff --git a/contracts/wasm/wasmlib/src/bytes.rs b/packages/vm/wasmlib/src/bytes.rs similarity index 91% rename from contracts/wasm/wasmlib/src/bytes.rs rename to packages/vm/wasmlib/src/bytes.rs index ab0e1a1840..9e7a969d66 100644 --- a/contracts/wasm/wasmlib/src/bytes.rs +++ b/packages/vm/wasmlib/src/bytes.rs @@ -6,13 +6,13 @@ use crate::host::*; // decodes separate entities from a byte buffer pub struct BytesDecoder<'a> { - data: &'a [u8], + buf: &'a [u8], } impl BytesDecoder<'_> { // constructs a decoder pub fn new(data: &[u8]) -> BytesDecoder { - BytesDecoder { data: data } + BytesDecoder { buf: data } } // decodes an ScAddress from the byte buffer @@ -28,11 +28,11 @@ impl BytesDecoder<'_> { // decodes the next substring of bytes from the byte buffer pub fn bytes(&mut self) -> &[u8] { let size = self.int32() as usize; - if self.data.len() < size { + if self.buf.len() < size { panic("insufficient bytes"); } - let value = &self.data[..size]; - self.data = &self.data[size..]; + let value = &self.buf[..size]; + self.buf = &self.buf[size..]; value } @@ -79,11 +79,11 @@ impl BytesDecoder<'_> { let mut val = 0_i64; let mut s = 0; loop { - if self.data.len() == 0 { + if self.buf.len() == 0 { panic("insufficient bytes"); } - let mut b = self.data[0] as i8; - self.data = &self.data[1..]; + let mut b = self.buf[0] as i8; + self.buf = &self.buf[1..]; val |= ((b & 0x7f) as i64) << s; // termination bit set? @@ -118,7 +118,7 @@ impl BytesDecoder<'_> { impl Drop for BytesDecoder<'_> { fn drop(&mut self) { - if self.data.len() != 0 { + if self.buf.len() != 0 { panic("extra bytes"); } } @@ -128,13 +128,13 @@ impl Drop for BytesDecoder<'_> { // encodes separate entities into a byte buffer pub struct BytesEncoder { - data: Vec, + buf: Vec, } impl BytesEncoder { // constructs an encoder pub fn new() -> BytesEncoder { - BytesEncoder { data: Vec::new() } + BytesEncoder { buf: Vec::new() } } // encodes an ScAddress into the byte buffer @@ -152,7 +152,7 @@ impl BytesEncoder { // encodes a substring of bytes into the byte buffer pub fn bytes(&mut self, value: &[u8]) -> &BytesEncoder { self.int32(value.len() as i32); - self.data.extend_from_slice(value); + self.buf.extend_from_slice(value); self } @@ -170,7 +170,7 @@ impl BytesEncoder { // retrieve the encoded byte buffer pub fn data(&self) -> Vec { - self.data.clone() + self.buf.clone() } // encodes an ScHash into the byte buffer @@ -210,10 +210,10 @@ impl BytesEncoder { let s = b & 0x40; val >>= 7; if (val == 0 && s == 0) || (val == -1 && s != 0) { - self.data.push(b & 0x7f); + self.buf.push(b & 0x7f); return self; } - self.data.push(b | 0x80) + self.buf.push(b | 0x80); } } diff --git a/contracts/wasm/wasmlib/src/context.rs b/packages/vm/wasmlib/src/context.rs similarity index 95% rename from contracts/wasm/wasmlib/src/context.rs rename to packages/vm/wasmlib/src/context.rs index aa7cbf5be4..7a024fc9de 100644 --- a/contracts/wasm/wasmlib/src/context.rs +++ b/packages/vm/wasmlib/src/context.rs @@ -44,30 +44,14 @@ pub struct ScTransfers { } impl ScTransfers { - // create a new transfers object and initialize it with the specified amount of iotas - pub fn iotas(amount: i64) -> ScTransfers { - ScTransfers::new(&ScColor::IOTA, amount) - } - - // create a new transfers object and initialize it with the specified token transfer - pub fn new(color: &ScColor, amount: i64) -> ScTransfers { - let transfer = ScTransfers::new_transfers(); - transfer.set(color, amount); - transfer - } - - // create a new transfer object ready to add token transfers - pub fn new_transfers() -> ScTransfers { + // create a new transfers object ready to add token transfers + pub fn new() -> ScTransfers { ScTransfers { transfers: ScMutableMap::new() } } - pub fn none() -> ScTransfers { - ScTransfers { transfers: ScMutableMap { obj_id: 0 } } - } - - // create a new transfer object from a balances object - pub fn new_transfers_from_balances(balances: ScBalances) -> ScTransfers { - let transfers = ScTransfers::new_transfers(); + // create a new transfers object from a balances object + pub fn from_balances(balances: ScBalances) -> ScTransfers { + let transfers = ScTransfers::new(); let colors = balances.colors(); for i in 0..colors.length() { let color = colors.get_color(i).value(); @@ -76,7 +60,19 @@ impl ScTransfers { transfers } - // set the specified colored token transfer in the transfer object + // create a new transfers object and initialize it with the specified amount of iotas + pub fn iotas(amount: i64) -> ScTransfers { + ScTransfers::transfer(&ScColor::IOTA, amount) + } + + // create a new transfers object and initialize it with the specified token transfer + pub fn transfer(color: &ScColor, amount: i64) -> ScTransfers { + let transfer = ScTransfers::new(); + transfer.set(color, amount); + transfer + } + + // set the specified colored token transfer in the transfers object // note that this will overwrite any previous amount for the specified color pub fn set(&self, color: &ScColor, amount: i64) { self.transfers.get_int64(color).set_value(amount); @@ -307,7 +303,9 @@ impl ScFuncContext { } // retrieve the agent id of the caller of the smart contract - pub fn caller(&self) -> ScAgentID { ROOT.get_agent_id(&KEY_CALLER).value() } + pub fn caller(&self) -> ScAgentID { + ROOT.get_agent_id(&KEY_CALLER).value() + } // deploys a new instance of the specified smart contract on the current chain // the provided parameters are passed to the smart contract "init" function diff --git a/contracts/wasm/wasmlib/src/contract.rs b/packages/vm/wasmlib/src/contract.rs similarity index 100% rename from contracts/wasm/wasmlib/src/contract.rs rename to packages/vm/wasmlib/src/contract.rs diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/consts.rs b/packages/vm/wasmlib/src/coreaccounts/consts.rs similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreaccounts/consts.rs rename to packages/vm/wasmlib/src/coreaccounts/consts.rs diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/contract.rs b/packages/vm/wasmlib/src/coreaccounts/contract.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreaccounts/contract.rs rename to packages/vm/wasmlib/src/coreaccounts/contract.rs index ff687454da..b52b701ecb 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/contract.rs +++ b/packages/vm/wasmlib/src/coreaccounts/contract.rs @@ -12,7 +12,7 @@ use std::ptr; use crate::*; -use crate::corecontracts::coreaccounts::*; +use crate::coreaccounts::*; pub struct DepositCall { pub func: ScFunc, diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/mod.rs b/packages/vm/wasmlib/src/coreaccounts/mod.rs similarity index 59% rename from contracts/wasm/wasmlib/src/corecontracts/coreblocklog/mod.rs rename to packages/vm/wasmlib/src/coreaccounts/mod.rs index c6b0582479..8218c37b11 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/mod.rs +++ b/packages/vm/wasmlib/src/coreaccounts/mod.rs @@ -1,15 +1,18 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + #![allow(unused_imports)] pub use consts::*; pub use contract::*; -use params::*; -use results::*; +pub use params::*; +pub use results::*; pub mod consts; pub mod contract; pub mod params; pub mod results; - diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/params.rs b/packages/vm/wasmlib/src/coreaccounts/params.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreaccounts/params.rs rename to packages/vm/wasmlib/src/coreaccounts/params.rs index 5b72cef162..dca6f1eca3 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/params.rs +++ b/packages/vm/wasmlib/src/coreaccounts/params.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreaccounts::*; +use crate::coreaccounts::*; use crate::host::*; #[derive(Clone, Copy)] diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/results.rs b/packages/vm/wasmlib/src/coreaccounts/results.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreaccounts/results.rs rename to packages/vm/wasmlib/src/coreaccounts/results.rs index 072f4cc95b..21a3fae179 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/results.rs +++ b/packages/vm/wasmlib/src/coreaccounts/results.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreaccounts::*; +use crate::coreaccounts::*; use crate::host::*; pub struct MapAgentIDToImmutableBytes { diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblob/consts.rs b/packages/vm/wasmlib/src/coreblob/consts.rs similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreblob/consts.rs rename to packages/vm/wasmlib/src/coreblob/consts.rs diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblob/contract.rs b/packages/vm/wasmlib/src/coreblob/contract.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreblob/contract.rs rename to packages/vm/wasmlib/src/coreblob/contract.rs index 6e9ef72f4b..fc3969aa76 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblob/contract.rs +++ b/packages/vm/wasmlib/src/coreblob/contract.rs @@ -12,7 +12,7 @@ use std::ptr; use crate::*; -use crate::corecontracts::coreblob::*; +use crate::coreblob::*; pub struct StoreBlobCall { pub func: ScFunc, diff --git a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/mod.rs b/packages/vm/wasmlib/src/coreblob/mod.rs similarity index 59% rename from contracts/wasm/wasmlib/src/corecontracts/coregovernance/mod.rs rename to packages/vm/wasmlib/src/coreblob/mod.rs index c6b0582479..8218c37b11 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/mod.rs +++ b/packages/vm/wasmlib/src/coreblob/mod.rs @@ -1,15 +1,18 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + #![allow(unused_imports)] pub use consts::*; pub use contract::*; -use params::*; -use results::*; +pub use params::*; +pub use results::*; pub mod consts; pub mod contract; pub mod params; pub mod results; - diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblob/params.rs b/packages/vm/wasmlib/src/coreblob/params.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreblob/params.rs rename to packages/vm/wasmlib/src/coreblob/params.rs index 97408f8083..e2ecd927fa 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblob/params.rs +++ b/packages/vm/wasmlib/src/coreblob/params.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreblob::*; +use crate::coreblob::*; use crate::host::*; pub struct MapStringToImmutableBytes { diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblob/results.rs b/packages/vm/wasmlib/src/coreblob/results.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreblob/results.rs rename to packages/vm/wasmlib/src/coreblob/results.rs index 30fcdc59c8..a4e6be96a6 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblob/results.rs +++ b/packages/vm/wasmlib/src/coreblob/results.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreblob::*; +use crate::coreblob::*; use crate::host::*; #[derive(Clone, Copy)] diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/consts.rs b/packages/vm/wasmlib/src/coreblocklog/consts.rs similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreblocklog/consts.rs rename to packages/vm/wasmlib/src/coreblocklog/consts.rs diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/contract.rs b/packages/vm/wasmlib/src/coreblocklog/contract.rs similarity index 99% rename from contracts/wasm/wasmlib/src/corecontracts/coreblocklog/contract.rs rename to packages/vm/wasmlib/src/coreblocklog/contract.rs index 154cd7d4cf..fa165bdf10 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/contract.rs +++ b/packages/vm/wasmlib/src/coreblocklog/contract.rs @@ -12,7 +12,7 @@ use std::ptr; use crate::*; -use crate::corecontracts::coreblocklog::*; +use crate::coreblocklog::*; pub struct ControlAddressesCall { pub func: ScView, diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/mod.rs b/packages/vm/wasmlib/src/coreblocklog/mod.rs similarity index 59% rename from contracts/wasm/wasmlib/src/corecontracts/coreaccounts/mod.rs rename to packages/vm/wasmlib/src/coreblocklog/mod.rs index c6b0582479..8218c37b11 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreaccounts/mod.rs +++ b/packages/vm/wasmlib/src/coreblocklog/mod.rs @@ -1,15 +1,18 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + #![allow(unused_imports)] pub use consts::*; pub use contract::*; -use params::*; -use results::*; +pub use params::*; +pub use results::*; pub mod consts; pub mod contract; pub mod params; pub mod results; - diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/params.rs b/packages/vm/wasmlib/src/coreblocklog/params.rs similarity index 99% rename from contracts/wasm/wasmlib/src/corecontracts/coreblocklog/params.rs rename to packages/vm/wasmlib/src/coreblocklog/params.rs index 6a82528039..3f7adb0e83 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/params.rs +++ b/packages/vm/wasmlib/src/coreblocklog/params.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreblocklog::*; +use crate::coreblocklog::*; use crate::host::*; #[derive(Clone, Copy)] diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/results.rs b/packages/vm/wasmlib/src/coreblocklog/results.rs similarity index 99% rename from contracts/wasm/wasmlib/src/corecontracts/coreblocklog/results.rs rename to packages/vm/wasmlib/src/coreblocklog/results.rs index 61a9cf19e1..a3a0e6a750 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblocklog/results.rs +++ b/packages/vm/wasmlib/src/coreblocklog/results.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreblocklog::*; +use crate::coreblocklog::*; use crate::host::*; #[derive(Clone, Copy)] diff --git a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/consts.rs b/packages/vm/wasmlib/src/coregovernance/consts.rs similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coregovernance/consts.rs rename to packages/vm/wasmlib/src/coregovernance/consts.rs diff --git a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/contract.rs b/packages/vm/wasmlib/src/coregovernance/contract.rs similarity index 99% rename from contracts/wasm/wasmlib/src/corecontracts/coregovernance/contract.rs rename to packages/vm/wasmlib/src/coregovernance/contract.rs index aa2840b8a1..7fa127cb43 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/contract.rs +++ b/packages/vm/wasmlib/src/coregovernance/contract.rs @@ -12,7 +12,7 @@ use std::ptr; use crate::*; -use crate::corecontracts::coregovernance::*; +use crate::coregovernance::*; pub struct AddAllowedStateControllerAddressCall { pub func: ScFunc, diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreblob/mod.rs b/packages/vm/wasmlib/src/coregovernance/mod.rs similarity index 59% rename from contracts/wasm/wasmlib/src/corecontracts/coreblob/mod.rs rename to packages/vm/wasmlib/src/coregovernance/mod.rs index c6b0582479..8218c37b11 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreblob/mod.rs +++ b/packages/vm/wasmlib/src/coregovernance/mod.rs @@ -1,15 +1,18 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + #![allow(unused_imports)] pub use consts::*; pub use contract::*; -use params::*; -use results::*; +pub use params::*; +pub use results::*; pub mod consts; pub mod contract; pub mod params; pub mod results; - diff --git a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/params.rs b/packages/vm/wasmlib/src/coregovernance/params.rs similarity index 99% rename from contracts/wasm/wasmlib/src/corecontracts/coregovernance/params.rs rename to packages/vm/wasmlib/src/coregovernance/params.rs index e28e1a8257..0e80984554 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/params.rs +++ b/packages/vm/wasmlib/src/coregovernance/params.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coregovernance::*; +use crate::coregovernance::*; use crate::host::*; #[derive(Clone, Copy)] diff --git a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/results.rs b/packages/vm/wasmlib/src/coregovernance/results.rs similarity index 99% rename from contracts/wasm/wasmlib/src/corecontracts/coregovernance/results.rs rename to packages/vm/wasmlib/src/coregovernance/results.rs index 6aecdd1404..486dc86988 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coregovernance/results.rs +++ b/packages/vm/wasmlib/src/coregovernance/results.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coregovernance::*; +use crate::coregovernance::*; use crate::host::*; pub struct ArrayOfImmutableBytes { diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreroot/consts.rs b/packages/vm/wasmlib/src/coreroot/consts.rs similarity index 100% rename from contracts/wasm/wasmlib/src/corecontracts/coreroot/consts.rs rename to packages/vm/wasmlib/src/coreroot/consts.rs diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreroot/contract.rs b/packages/vm/wasmlib/src/coreroot/contract.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreroot/contract.rs rename to packages/vm/wasmlib/src/coreroot/contract.rs index 27fbdaaddb..fe9ba90cdc 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreroot/contract.rs +++ b/packages/vm/wasmlib/src/coreroot/contract.rs @@ -12,7 +12,7 @@ use std::ptr; use crate::*; -use crate::corecontracts::coreroot::*; +use crate::coreroot::*; pub struct DeployContractCall { pub func: ScFunc, diff --git a/packages/vm/wasmlib/src/coreroot/mod.rs b/packages/vm/wasmlib/src/coreroot/mod.rs new file mode 100644 index 0000000000..8218c37b11 --- /dev/null +++ b/packages/vm/wasmlib/src/coreroot/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +#![allow(unused_imports)] + +pub use consts::*; +pub use contract::*; +pub use params::*; +pub use results::*; + +pub mod consts; +pub mod contract; +pub mod params; +pub mod results; diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreroot/params.rs b/packages/vm/wasmlib/src/coreroot/params.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreroot/params.rs rename to packages/vm/wasmlib/src/coreroot/params.rs index 26bcc38d4a..dd20e78e87 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreroot/params.rs +++ b/packages/vm/wasmlib/src/coreroot/params.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreroot::*; +use crate::coreroot::*; use crate::host::*; #[derive(Clone, Copy)] diff --git a/contracts/wasm/wasmlib/src/corecontracts/coreroot/results.rs b/packages/vm/wasmlib/src/coreroot/results.rs similarity index 98% rename from contracts/wasm/wasmlib/src/corecontracts/coreroot/results.rs rename to packages/vm/wasmlib/src/coreroot/results.rs index 06c035be00..4529c608a5 100644 --- a/contracts/wasm/wasmlib/src/corecontracts/coreroot/results.rs +++ b/packages/vm/wasmlib/src/coreroot/results.rs @@ -9,7 +9,7 @@ #![allow(unused_imports)] use crate::*; -use crate::corecontracts::coreroot::*; +use crate::coreroot::*; use crate::host::*; #[derive(Clone, Copy)] diff --git a/contracts/wasm/wasmlib/src/exports.rs b/packages/vm/wasmlib/src/exports.rs similarity index 93% rename from contracts/wasm/wasmlib/src/exports.rs rename to packages/vm/wasmlib/src/exports.rs index 598693c969..128bf7bf1c 100644 --- a/contracts/wasm/wasmlib/src/exports.rs +++ b/packages/vm/wasmlib/src/exports.rs @@ -15,14 +15,14 @@ use crate::mutable::*; // There are only 2 symbols the ISCP host will actually look for // in the export table: // on_load (which must be defined by the SC code) and -// on_call_entrypoint (which is defined here as part of WasmLib) +// on_call (which is defined here as part of WasmLib) static mut FUNCS: Vec = vec![]; static mut VIEWS: Vec = vec![]; #[no_mangle] // general entrypoint for the host to call any SC function -// the host will pass the index of one of the entrypoints +// the host will pass the index of one of the entry points // that was provided by on_load during SC initialization fn on_call(index: i32) { let ctx = ScFuncContext {}; @@ -73,10 +73,10 @@ impl ScExports { // defines the external name of a smart contract view // and the entry point function associated with it - pub fn add_view(&self, name: &str, f: fn(&ScViewContext)) { + pub fn add_view(&self, name: &str, v: fn(&ScViewContext)) { unsafe { let index = VIEWS.len() as i32; - VIEWS.push(f); + VIEWS.push(v); self.exports.get_string(index | 0x8000).set_value(name); } } diff --git a/contracts/wasm/wasmlib/src/hashtypes.rs b/packages/vm/wasmlib/src/hashtypes.rs similarity index 99% rename from contracts/wasm/wasmlib/src/hashtypes.rs rename to packages/vm/wasmlib/src/hashtypes.rs index e6563cc256..d71fcadec4 100644 --- a/contracts/wasm/wasmlib/src/hashtypes.rs +++ b/packages/vm/wasmlib/src/hashtypes.rs @@ -67,7 +67,7 @@ impl ScAgentID { // construct from byte array pub fn from_bytes(bytes: &[u8]) -> ScAgentID { - ScAgentID { id: bytes.try_into().expect("invalid agent id lengths") } + ScAgentID { id: bytes.try_into().expect("invalid agent id length") } } // gets Tangle address from agent id diff --git a/contracts/wasm/wasmlib/src/host.rs b/packages/vm/wasmlib/src/host.rs similarity index 100% rename from contracts/wasm/wasmlib/src/host.rs rename to packages/vm/wasmlib/src/host.rs diff --git a/contracts/wasm/wasmlib/src/immutable.rs b/packages/vm/wasmlib/src/immutable.rs similarity index 100% rename from contracts/wasm/wasmlib/src/immutable.rs rename to packages/vm/wasmlib/src/immutable.rs diff --git a/contracts/wasm/wasmlib/src/keys.rs b/packages/vm/wasmlib/src/keys.rs similarity index 75% rename from contracts/wasm/wasmlib/src/keys.rs rename to packages/vm/wasmlib/src/keys.rs index 5f14993b21..6e1d9b2acb 100644 --- a/contracts/wasm/wasmlib/src/keys.rs +++ b/packages/vm/wasmlib/src/keys.rs @@ -66,19 +66,17 @@ pub const KEY_LENGTH : Key32 = Key32(-25); pub const KEY_LOG : Key32 = Key32(-26); pub const KEY_MAPS : Key32 = Key32(-27); pub const KEY_MINTED : Key32 = Key32(-28); -pub const KEY_NAME : Key32 = Key32(-29); -pub const KEY_PANIC : Key32 = Key32(-30); -pub const KEY_PARAMS : Key32 = Key32(-31); -pub const KEY_POST : Key32 = Key32(-32); -pub const KEY_RANDOM : Key32 = Key32(-33); -pub const KEY_REQUEST_ID : Key32 = Key32(-34); -pub const KEY_RESULTS : Key32 = Key32(-35); -pub const KEY_RETURN : Key32 = Key32(-36); -pub const KEY_STATE : Key32 = Key32(-37); -pub const KEY_TIMESTAMP : Key32 = Key32(-38); -pub const KEY_TRACE : Key32 = Key32(-39); -pub const KEY_TRANSFERS : Key32 = Key32(-40); -pub const KEY_UTILITY : Key32 = Key32(-41); -pub const KEY_VALID : Key32 = Key32(-42); -pub const KEY_ZZZZZZZ : Key32 = Key32(-43); +pub const KEY_PANIC : Key32 = Key32(-29); +pub const KEY_PARAMS : Key32 = Key32(-30); +pub const KEY_POST : Key32 = Key32(-31); +pub const KEY_RANDOM : Key32 = Key32(-32); +pub const KEY_REQUEST_ID : Key32 = Key32(-33); +pub const KEY_RESULTS : Key32 = Key32(-34); +pub const KEY_RETURN : Key32 = Key32(-35); +pub const KEY_STATE : Key32 = Key32(-36); +pub const KEY_TIMESTAMP : Key32 = Key32(-37); +pub const KEY_TRACE : Key32 = Key32(-38); +pub const KEY_TRANSFERS : Key32 = Key32(-39); +pub const KEY_UTILITY : Key32 = Key32(-40); +pub const KEY_ZZZZZZZ : Key32 = Key32(-41); // @formatter:on diff --git a/contracts/wasm/wasmlib/src/lib.rs b/packages/vm/wasmlib/src/lib.rs similarity index 90% rename from contracts/wasm/wasmlib/src/lib.rs rename to packages/vm/wasmlib/src/lib.rs index 9848363ccd..83b33eb132 100644 --- a/contracts/wasm/wasmlib/src/lib.rs +++ b/packages/vm/wasmlib/src/lib.rs @@ -15,7 +15,11 @@ pub use mutable::*; mod bytes; mod context; mod contract; -pub mod corecontracts; +pub mod coreaccounts; +pub mod coreblob; +pub mod coreblocklog; +pub mod coregovernance; +pub mod coreroot; mod exports; mod hashtypes; pub mod host; diff --git a/contracts/wasm/wasmlib/src/mutable.rs b/packages/vm/wasmlib/src/mutable.rs similarity index 100% rename from contracts/wasm/wasmlib/src/mutable.rs rename to packages/vm/wasmlib/src/mutable.rs diff --git a/packages/vm/wasmlib/ts/wasmlib/bytes.ts b/packages/vm/wasmlib/ts/wasmlib/bytes.ts new file mode 100644 index 0000000000..35cab0e438 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/bytes.ts @@ -0,0 +1,230 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import {Convert} from "./convert"; +import {ScAddress, ScAgentID, ScChainID, ScColor, ScHash, ScHname, ScRequestID} from "./hashtypes"; +import {panic} from "./host"; + +// decodes separate entities from a byte buffer +export class BytesDecoder { + buf: u8[]; + + // constructs a decoder + constructor(data: u8[]) { + this.buf = data; + } + + // decodes an ScAddress from the byte buffer + address(): ScAddress { + return ScAddress.fromBytes(this.bytes()); + } + + // decodes an ScAgentID from the byte buffer + agentID(): ScAgentID { + return ScAgentID.fromBytes(this.bytes()); + } + + // decodes the next substring of bytes from the byte buffer + bytes(): u8[] { + let size = this.int32(); + if (this.buf.length < size) { + panic("insufficient bytes"); + } + let value = this.buf.slice(0, size); + this.buf = this.buf.slice(size); + return value; + } + + // decodes an ScChainID from the byte buffer + chainID(): ScChainID { + return ScChainID.fromBytes(this.bytes()); + } + + // decodes an ScColor from the byte buffer + color(): ScColor { + return ScColor.fromBytes(this.bytes()); + } + + // decodes an ScHash from the byte buffer + hash(): ScHash { + return ScHash.fromBytes(this.bytes()); + } + + // decodes an ScHname from the byte buffer + hname(): ScHname { + return ScHname.fromBytes(this.bytes()); + } + + // decodes an int16 from the byte buffer + // note that these are encoded using leb128 encoding to conserve space + int16(): i16 { + return this.leb128Decode(16) as i16; + } + + // decodes an int32 from the byte buffer + // note that these are encoded using leb128 encoding to conserve space + int32(): i32 { + return this.leb128Decode(32) as i32; + } + + // decodes an int64 from the byte buffer + // note that these are encoded using leb128 encoding to conserve space + int64(): i64 { + return this.leb128Decode(64); + } + + // leb128 decoder + leb128Decode(bits: i32): i64 { + let val: i64 = 0; + let s = 0; + for (; ;) { + if (this.buf.length == 0) { + panic("leb128Decode: insufficient bytes"); + } + let b = this.buf.shift() as i8; + val |= ((b & 0x7f) as i64) << s; + + // termination bit set? + if ((b & -0x80) == 0) { + if ((((val >> s) as i8) & 0x7f) != (b & 0x7f)) { + panic("integer too large"); + } + + // extend int7 sign to int8 + b |= (b & 0x40) << 1; + + // extend int8 sign to int64 + val |= ((b as i64) << s); + break; + } + s += 7; + if (s >= bits) { + panic("integer representation too long"); + } + } + return val; + } + + // decodes an ScRequestID from the byte buffer + requestID(): ScRequestID { + return ScRequestID.fromBytes(this.bytes()); + } + + // decodes an UTF-8 text string from the byte buffer + string(): string { + return Convert.toString(this.bytes()); + } + + close(): void { + if (this.buf.length != 0) { + panic("extra bytes"); + } + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// encodes separate entities into a byte buffer +export class BytesEncoder { + buf: u8[]; + + // constructs an encoder + constructor() { + this.buf = [] + } + + // encodes an ScAddress into the byte buffer + address(value: ScAddress): BytesEncoder { + this.bytes(value.toBytes()); + return this; + } + + // encodes an ScAgentID into the byte buffer + agentID(value: ScAgentID): BytesEncoder { + this.bytes(value.toBytes()); + return this; + } + + // encodes a substring of bytes into the byte buffer + bytes(value: u8[]): BytesEncoder { + this.int32(value.length); + for (let i = 0; i < value.length; i++) { + this.buf.push(value[i]); + } + return this; + } + + // encodes an ScChainID into the byte buffer + chainID(value: ScChainID): BytesEncoder { + this.bytes(value.toBytes()); + return this; + } + + // encodes an ScColor into the byte buffer + color(value: ScColor): BytesEncoder { + this.bytes(value.toBytes()); + return this; + } + + // retrieve the encoded byte buffer + data(): u8[] { + return this.buf; + } + + // encodes an ScHash into the byte buffer + hash(value: ScHash): BytesEncoder { + this.bytes(value.toBytes()); + return this; + } + + // encodes an ScHname into the byte buffer + hname(value: ScHname): BytesEncoder { + this.bytes(value.toBytes()); + return this; + } + + // encodes an int16 into the byte buffer + // note that these are encoded using leb128 encoding to conserve space + int16(val: i16): BytesEncoder { + return this.leb128Encode(val as i64); + } + + // encodes an int32 into the byte buffer + // note that these are encoded using leb128 encoding to conserve space + int32(val: i32): BytesEncoder { + return this.leb128Encode(val as i64); + } + + // encodes an int64 into the byte buffer + // note that these are encoded using leb128 encoding to conserve space + int64(val: i64): BytesEncoder { + return this.leb128Encode(val); + } + + // leb128 encoder + leb128Encode(val: i64): BytesEncoder { + for (; ;) { + let b = val as u8; + let s = b & 0x40; + val >>= 7; + if ((val == 0 && s == 0) || (val == -1 && s != 0)) { + this.buf.push(b & 0x7f); + break; + } + this.buf.push(b | 0x80); + } + return this; + } + + // encodes an ScRequestID into the byte buffer + requestID(value: ScRequestID): BytesEncoder { + this.bytes(value.toBytes()); + return this; + } + + // encodes an UTF-8 text string into the byte buffer + string(value: string): BytesEncoder { + this.bytes(Convert.fromString(value)); + return this; + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/context.ts b/packages/vm/wasmlib/ts/wasmlib/context.ts new file mode 100644 index 0000000000..f6bddb0c90 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/context.ts @@ -0,0 +1,385 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// encapsulates standard host entities into a simple interface + +import {BytesDecoder, BytesEncoder} from "./bytes"; +import {Convert} from "./convert"; +import {ScFuncCallContext, ScViewCallContext} from "./contract"; +import {ScAddress, ScAgentID, ScChainID, ScColor, ScHash, ScHname, ScRequestID} from "./hashtypes"; +import {log, OBJ_ID_ROOT, panic} from "./host"; +import {ScImmutableColorArray, ScImmutableMap} from "./immutable"; +import * as keys from "./keys"; +import {ScMutableMap} from "./mutable"; + +// all access to the objects in host's object tree starts here +export let ROOT = new ScMutableMap(OBJ_ID_ROOT); + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// retrieves any information that is related to colored token balances +export class ScBalances { + balances: ScImmutableMap; + + constructor(id: keys.Key32) { + this.balances = ROOT.getMap(id).immutable() + } + + // retrieve the balance for the specified token color + balance(color: ScColor): i64 { + return this.balances.getInt64(color).value(); + } + + // retrieve an array of all token colors that have a non-zero balance + colors(): ScImmutableColorArray { + return this.balances.getColorArray(keys.KEY_COLOR); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// passes token transfer information to a function call +export class ScTransfers { + transfers: ScMutableMap; + + // create a new transfers object ready to add token transfers + constructor() { + this.transfers = ScMutableMap.create(); + } + + // create a new transfers object from a balances object + static fromBalances(balances: ScBalances): ScTransfers { + let transfers = new ScTransfers(); + let colors = balances.colors(); + for (let i = 0; i < colors.length(); i++) { + let color = colors.getColor(i).value(); + transfers.set(color, balances.balance(color)); + } + return transfers; + } + + // create a new transfers object and initialize it with the specified amount of iotas + static iotas(amount: i64): ScTransfers { + return ScTransfers.transfer(ScColor.IOTA, amount); + } + + // create a new transfers object and initialize it with the specified token transfer + static transfer(color: ScColor, amount: i64): ScTransfers { + let transfer = new ScTransfers(); + transfer.set(color, amount); + return transfer; + } + + // set the specified colored token transfer in the transfers object + // note that this will overwrite any previous amount for the specified color + set(color: ScColor, amount: i64): void { + this.transfers.getInt64(color).setValue(amount); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// provides access to utility functions that are handled by the host +export class ScUtility { + utility: ScMutableMap; + + constructor() { + this.utility = ROOT.getMap(keys.KEY_UTILITY) + } + + // decodes the specified base58-encoded string value to its original bytes + base58Decode(value: string): u8[] { + return this.utility.callFunc(keys.KEY_BASE58_DECODE, Convert.fromString(value)); + } + + // encodes the specified bytes to a base-58-encoded string + base58Encode(value: u8[]): string { + let result = this.utility.callFunc(keys.KEY_BASE58_ENCODE, value); + return Convert.toString(result); + } + + // retrieves the address for the specified BLS public key + blsAddressFromPubkey(pubKey: u8[]): ScAddress { + let result = this.utility.callFunc(keys.KEY_BLS_ADDRESS, pubKey); + return ScAddress.fromBytes(result); + } + + // aggregates the specified multiple BLS signatures and public keys into a single one + blsAggregateSignatures(pubKeysBin: u8[][], sigsBin: u8[][]): u8[][] { + let encode = new BytesEncoder(); + encode.int32(pubKeysBin.length); + for (let i = 0; i < pubKeysBin.length; i++) { + encode.bytes(pubKeysBin[i]); + } + encode.int32(sigsBin.length as i32); + for (let i = 0; i < sigsBin.length; i++) { + encode.bytes(sigsBin[i]); + } + let result = this.utility.callFunc(keys.KEY_BLS_AGGREGATE, encode.data()); + let decode = new BytesDecoder(result); + return [decode.bytes(), decode.bytes()]; + } + + // checks if the specified BLS signature is valid + blsValidSignature(data: u8[], pubKey: u8[], signature: u8[]): boolean { + let encode = new BytesEncoder(); + encode.bytes(data); + encode.bytes(pubKey); + encode.bytes(signature); + let result = this.utility.callFunc(keys.KEY_BLS_VALID, encode.data()); + return (result[0] & 0x01) != 0; + } + + // retrieves the address for the specified ED25519 public key + ed25519AddressFromPubkey(pubKey: u8[]): ScAddress { + let result = this.utility.callFunc(keys.KEY_ED25519_ADDRESS, pubKey); + return ScAddress.fromBytes(result); + } + + // checks if the specified ED25519 signature is valid + ed25519ValidSignature(data: u8[], pubKey: u8[], signature: u8[]): boolean { + let encode = new BytesEncoder(); + encode.bytes(data); + encode.bytes(pubKey); + encode.bytes(signature); + let result = this.utility.callFunc(keys.KEY_ED25519_VALID, encode.data()); + return (result[0] & 0x01) != 0; + } + + // hashes the specified value bytes using BLAKE2b hashing and returns the resulting 32-byte hash + hashBlake2b(value: u8[]): ScHash { + let hash = this.utility.callFunc(keys.KEY_HASH_BLAKE2B, value); + return ScHash.fromBytes(hash); + } + + // hashes the specified value bytes using SHA3 hashing and returns the resulting 32-byte hash + hashSha3(value: u8[]): ScHash { + let hash = this.utility.callFunc(keys.KEY_HASH_SHA3, value); + return ScHash.fromBytes(hash); + } + + // calculates 32-bit hash for the specified name string + hname(name: string): ScHname { + let result = this.utility.callFunc(keys.KEY_HNAME, Convert.fromString(name)); + return ScHname.fromBytes(result); + } + + // generates a random value from 0 to max (exclusive max) using a deterministic RNG + random(max: i64): i64 { + let result = this.utility.callFunc(keys.KEY_RANDOM, []); + let rnd = Convert.toI64(result); + return (rnd as u64 % max as u64) as i64; + } +} + +// wrapper function for simplified internal access to base58 encoding +export function base58Encode(bytes: u8[]): string { + return new ScFuncContext().utility().base58Encode(bytes); +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// shared interface part of ScFuncContext and ScViewContext +export class ScBaseContext { + // retrieve the agent id of this contract account + accountID(): ScAgentID { + return ROOT.getAgentID(keys.KEY_ACCOUNT_ID).value(); + } + + // access the current balances for all token colors + balances(): ScBalances { + return new ScBalances(keys.KEY_BALANCES); + } + + // retrieve the chain id of the chain this contract lives on + chainID(): ScChainID { + return ROOT.getChainID(keys.KEY_CHAIN_ID).value(); + } + + // retrieve the agent id of the owner of the chain this contract lives on + chainOwnerID(): ScAgentID { + return ROOT.getAgentID(keys.KEY_CHAIN_OWNER_ID).value(); + } + + // retrieve the hname of this contract + contract(): ScHname { + return ROOT.getHname(keys.KEY_CONTRACT).value(); + } + + // retrieve the agent id of the creator of this contract + contractCreator(): ScAgentID { + return ROOT.getAgentID(keys.KEY_CONTRACT_CREATOR).value(); + } + + // logs informational text message in the log on the host + log(text: string): void { + log(text); + } + + // logs error text message in the log on the host and then panics + panic(text: string): void { + panic(text); + } + + // retrieve parameters that were passed to the smart contract function + params(): ScImmutableMap { + return ROOT.getMap(keys.KEY_PARAMS).immutable(); + } + + // panics with specified message if specified condition is not satisfied + require(cond: boolean, msg: string): void { + if (!cond) { + panic(msg); + } + } + + // map that holds any results returned by the smart contract function + results(): ScMutableMap { + return ROOT.getMap(keys.KEY_RESULTS); + } + + // deterministic time stamp fixed at the moment of calling the smart contract + timestamp(): i64 { + return ROOT.getInt64(keys.KEY_TIMESTAMP).value(); + } + + // logs debugging trace text message in the log on the host + // similar to log() except this will only show in the log in a special debug mode + trace(text: string): void { + trace(text); + } + + // access diverse utility functions provided by the host + utility(): ScUtility { + return new ScUtility(); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// smart contract interface with mutable access to state +export class ScFuncContext extends ScBaseContext implements ScViewCallContext, ScFuncCallContext { + canCallFunc(): void { + panic!("canCallFunc"); + } + + canCallView(): void { + panic!("canCallView"); + } + + // synchronously calls the specified smart contract function, + // passing the provided parameters and token transfers to it + call(hcontract: ScHname, hfunction: ScHname, params: ScMutableMap | null, transfer: ScTransfers | null): ScImmutableMap { + let encode = new BytesEncoder(); + encode.hname(hcontract); + encode.hname(hfunction); + encode.int32((params === null) ? 0 : params.mapID()); + encode.int32((transfer === null) ? 0 : transfer.transfers.mapID()); + ROOT.getBytes(keys.KEY_CALL).setValue(encode.data()); + return ROOT.getMap(keys.KEY_RETURN).immutable(); + } + + // shorthand to synchronously call a smart contract function of the current contract + callSelf(hfunction: ScHname, params: ScMutableMap | null, transfers: ScTransfers | null): ScImmutableMap { + return this.call(this.contract(), hfunction, params, transfers); + } + + // retrieve the agent id of the caller of the smart contract + caller(): ScAgentID { + return ROOT.getAgentID(keys.KEY_CALLER).value(); + } + + // deploys a new instance of the specified smart contract on the current chain + // the provided parameters are passed to the smart contract "init" function + deploy(programHash: ScHash, name: string, description: string, params: ScMutableMap | null): void { + let encode = new BytesEncoder(); + encode.hash(programHash); + encode.string(name); + encode.string(description); + encode.int32((params === null) ? 0 : params.mapID()); + ROOT.getBytes(keys.KEY_DEPLOY).setValue(encode.data()); + } + + // signals an event on the host that external entities can subscribe to + event(text: string): void { + ROOT.getString(keys.KEY_EVENT).setValue(text); + } + + // access the incoming balances for all token colors + incoming(): ScBalances { + return new ScBalances(keys.KEY_INCOMING); + } + + // retrieve the tokens that were minted in this transaction + minted(): ScBalances { + return new ScBalances(keys.KEY_MINTED); + } + + // asynchronously calls the specified smart contract function, + // passing the provided parameters and token transfers to it + // it is possible to schedule the call for a later execution by specifying a delay + post(chainID: ScChainID, hcontract: ScHname, hfunction: ScHname, params: ScMutableMap | null, transfer: ScTransfers, delay: i32): void { + let encode = new BytesEncoder(); + encode.chainID(chainID); + encode.hname(hcontract); + encode.hname(hfunction); + encode.int32((params === null) ? 0 : params.mapID()); + encode.int32(transfer.transfers.mapID()); + encode.int32(delay); + ROOT.getBytes(keys.KEY_POST).setValue(encode.data()); + } + + // shorthand to asynchronously call a smart contract function of the current contract + postSelf(hfunction: ScHname, params: ScMutableMap | null, transfer: ScTransfers, delay: i32): void { + this.post(this.chainID(), this.contract(), hfunction, params, transfer, delay); + } + + // retrieve the request id of this transaction + requestID(): ScRequestID { + return ROOT.getRequestID(keys.KEY_REQUEST_ID).value(); + } + + // access mutable state storage on the host + state(): ScMutableMap { + return ROOT.getMap(keys.KEY_STATE); + } + + // transfers the specified tokens to the specified Tangle ledger address + transferToAddress(address: ScAddress, transfer: ScTransfers): void { + let transfers = ROOT.getMapArray(keys.KEY_TRANSFERS); + let tx = transfers.getMap(transfers.length()); + tx.getAddress(keys.KEY_ADDRESS).setValue(address); + tx.getInt32(keys.KEY_BALANCES).setValue(transfer.transfers.mapID()); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// smart contract view interface which has only immutable access to state +export class ScViewContext extends ScBaseContext implements ScViewCallContext { + canCallView(): void { + panic!("canCallView"); + } + + // synchronously calls the specified smart contract view, + // passing the provided parameters to it + call(hcontract: ScHname, hfunction: ScHname, params: ScMutableMap | null): ScImmutableMap { + let encode = new BytesEncoder(); + encode.hname(hcontract); + encode.hname(hfunction); + encode.int32((params === null) ? 0 : params.mapID()); + encode.int32(0); + ROOT.getBytes(keys.KEY_CALL).setValue(encode.data()); + return ROOT.getMap(keys.KEY_RETURN).immutable(); + } + + // shorthand to synchronously call a smart contract view of the current contract + callSelf(hfunction: ScHname, params: ScMutableMap | null): ScImmutableMap { + return this.call(this.contract(), hfunction, params); + } + + // access immutable state storage on the host + state(): ScImmutableMap { + return ROOT.getMap(keys.KEY_STATE).immutable(); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/contract.ts b/packages/vm/wasmlib/ts/wasmlib/contract.ts new file mode 100644 index 0000000000..b7cbfc1d11 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/contract.ts @@ -0,0 +1,188 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// base contract objects + +import {BytesEncoder} from "./bytes"; +import {ROOT,ScTransfers} from "./context"; +import {ScChainID,ScHname} from "./hashtypes"; +import {getObjectID, panic, TYPE_MAP} from "./host"; +import * as keys from "./keys"; +import {ScMutableMap} from "./mutable"; + +export interface ScFuncCallContext { + canCallFunc():void; +} + +export interface ScViewCallContext { + canCallView():void; +} + +export class ScMapID { + mapID: i32 = 0; +} + +type NullableScMapID = ScMapID | null; + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +export class ScView { + hContract: ScHname; + hFunction: ScHname; + paramsID: NullableScMapID; + resultsID: NullableScMapID; + + constructor(hContract: ScHname, hFunction: ScHname) { + this.hContract = hContract; + this.hFunction = hFunction; + this.paramsID = null; + this.resultsID = null; + } + + setPtrs(paramsID: NullableScMapID, resultsID: NullableScMapID): void { + this.paramsID = paramsID; + this.resultsID = resultsID; + + if (paramsID === null) { + } else { + paramsID.mapID = ScMutableMap.create().mapID(); + } + } + + call(): void { + this.callWithTransfer(0); + } + + callWithTransfer(transferID: i32): void { + let encode = new BytesEncoder(); + encode.hname(this.hContract); + encode.hname(this.hFunction); + encode.int32(this.id(this.paramsID)); + encode.int32(transferID); + ROOT.getBytes(keys.KEY_CALL).setValue(encode.data()); + + let resultsID = this.resultsID; + if (resultsID === null) { + } else { + resultsID.mapID = getObjectID(1, keys.KEY_RETURN, TYPE_MAP); + } + } + + clone(): ScView { + let o = new ScView(this.hContract, this.hFunction); + o.paramsID = this.paramsID; + o.resultsID = this.resultsID; + return o; + } + + ofContract(hContract: ScHname): ScView { + let ret = this.clone(); + ret.hContract = hContract; + return ret; + } + + id(paramsID: NullableScMapID): i32 { + if (paramsID === null) { + return 0; + } + return paramsID.mapID; + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +export class ScInitFunc { + view: ScView; + + constructor(hContract: ScHname, hFunction: ScHname) { + this.view = new ScView(hContract, hFunction); + } + + setPtrs(paramsID: NullableScMapID, resultsID: NullableScMapID): void { + this.view.setPtrs(paramsID, resultsID); + } + + call(): void { + return panic("cannot call init"); + } + + clone(): ScInitFunc { + let o = new ScInitFunc(this.view.hContract, this.view.hFunction); + o.view = this.view.clone(); + return o; + } + + ofContract(hContract: ScHname): ScInitFunc { + let ret = this.clone(); + ret.view.hContract = hContract; + return ret; + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +export class ScFunc { + view: ScView; + delaySeconds: i32 = 0; + transferID: i32 = 0; + + constructor(hContract: ScHname, hFunction: ScHname) { + this.view = new ScView(hContract, hFunction); + } + + setPtrs(paramsID: NullableScMapID, resultsID: NullableScMapID): void { + this.view.setPtrs(paramsID, resultsID); + } + + call(): void { + if (this.delaySeconds != 0) { + return panic("cannot delay a call"); + } + this.view.callWithTransfer(this.transferID); + } + + clone(): ScFunc { + let o = new ScFunc(this.view.hContract, this.view.hFunction); + o.view = this.view.clone(); + o.delaySeconds = this.delaySeconds; + o.transferID = this.transferID; + return o; + } + + delay(seconds: i32): ScFunc { + let ret = this.clone(); + ret.delaySeconds = seconds; + return ret; + } + + ofContract(hContract: ScHname): ScFunc { + let ret = this.clone(); + ret.view.hContract = hContract; + return ret; + } + + post(): void { + return this.postToChain(ROOT.getChainID(keys.KEY_CHAIN_ID).value()); + } + + postToChain(chainID: ScChainID): void { + let encode = new BytesEncoder(); + encode.chainID(chainID); + encode.hname(this.view.hContract); + encode.hname(this.view.hFunction); + encode.int32(this.view.id(this.view.paramsID)); + encode.int32(this.transferID); + encode.int32(this.delaySeconds); + ROOT.getBytes(keys.KEY_POST).setValue(encode.data()); + } + + transfer(transfer: ScTransfers): ScFunc { + let ret = this.clone(); + ret.transferID = transfer.transfers.objID; + return ret; + } + + transferIotas(amount: i64): ScFunc { + return this.transfer(ScTransfers.iotas(amount)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/convert.ts b/packages/vm/wasmlib/ts/wasmlib/convert.ts new file mode 100644 index 0000000000..3e23835597 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/convert.ts @@ -0,0 +1,97 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// converts to and from little endian bytes +import {panic} from "./host"; + +export class Convert { + static equals(lhs: u8[], rhs: u8[]): boolean { + if (lhs.length != rhs.length) { + return false; + } + for (let i = lhs.length - 1; i >= 0; i--) { + if (lhs[i] != rhs[i]) { + return false; + } + } + return true; + } + + static fromI16(val: i16): u8[] { + return [ + val as u8, + (val >> 8) as u8, + ]; + } + + static fromI32(val: i32): u8[] { + return [ + val as u8, + (val >> 8) as u8, + (val >> 16) as u8, + (val >> 24) as u8, + ]; + } + + static fromI64(val: i64): u8[] { + return [ + val as u8, + (val >> 8) as u8, + (val >> 16) as u8, + (val >> 24) as u8, + (val >> 32) as u8, + (val >> 40) as u8, + (val >> 48) as u8, + (val >> 56) as u8, + ]; + } + + static fromString(val: string): u8[] { + let arrayBuffer = String.UTF8.encode(val); + let u8Array = Uint8Array.wrap(arrayBuffer) + let ret: u8[] = new Array(u8Array.length); + for (let i = 0; i < ret.length; i++) { + ret[i] = u8Array[i]; + } + return ret; + } + + static toI16(bytes: u8[]): i16 { + if (bytes.length != 2) { + panic("expected i16 (2 bytes)") + } + + let ret: i16 = bytes[1]; + return (ret << 8) | bytes[0]; + } + + static toI32(bytes: u8[]): i32 { + if (bytes.length != 4) { + panic("expected i32 (4 bytes)") + } + + let ret: i32 = bytes[3]; + ret = (ret << 8) | bytes[2]; + ret = (ret << 8) | bytes[1]; + return (ret << 8) | bytes[0]; + } + + static toI64(bytes: u8[]): i64 { + if (bytes.length != 8) { + panic("expected i64 (8 bytes)") + } + + let ret: i64 = bytes[7]; + ret = (ret << 8) | bytes[6]; + ret = (ret << 8) | bytes[5]; + ret = (ret << 8) | bytes[4]; + ret = (ret << 8) | bytes[3]; + ret = (ret << 8) | bytes[2]; + ret = (ret << 8) | bytes[1]; + return (ret << 8) | bytes[0]; + } + + static toString(bytes: u8[]): string { + return String.UTF8.decodeUnsafe(bytes.dataStart, bytes.length); + } +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/coreaccounts/consts.ts b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/consts.ts new file mode 100644 index 0000000000..82006e4680 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/consts.ts @@ -0,0 +1,34 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "accounts"; +export const ScDescription = "Core chain account ledger contract"; +export const HScName = new wasmlib.ScHname(0x3c4b5e02); + +export const ParamAgentID = "a"; +export const ParamWithdrawAmount = "m"; +export const ParamWithdrawColor = "c"; + +export const ResultAccountNonce = "n"; + +export const FuncDeposit = "deposit"; +export const FuncHarvest = "harvest"; +export const FuncWithdraw = "withdraw"; +export const ViewAccounts = "accounts"; +export const ViewBalance = "balance"; +export const ViewGetAccountNonce = "getAccountNonce"; +export const ViewTotalAssets = "totalAssets"; + +export const HFuncDeposit = new wasmlib.ScHname(0xbdc9102d); +export const HFuncHarvest = new wasmlib.ScHname(0x7b40efbd); +export const HFuncWithdraw = new wasmlib.ScHname(0x9dcc0f41); +export const HViewAccounts = new wasmlib.ScHname(0x3c4b5e02); +export const HViewBalance = new wasmlib.ScHname(0x84168cb4); +export const HViewGetAccountNonce = new wasmlib.ScHname(0x529d7df9); +export const HViewTotalAssets = new wasmlib.ScHname(0xfab0f8d2); diff --git a/packages/vm/wasmlib/ts/wasmlib/coreaccounts/contract.ts b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/contract.ts new file mode 100644 index 0000000000..f969323f7d --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/contract.ts @@ -0,0 +1,89 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class DepositCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncDeposit); + params: sc.MutableDepositParams = new sc.MutableDepositParams(); +} + +export class HarvestCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncHarvest); + params: sc.MutableHarvestParams = new sc.MutableHarvestParams(); +} + +export class WithdrawCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncWithdraw); +} + +export class AccountsCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewAccounts); + results: sc.ImmutableAccountsResults = new sc.ImmutableAccountsResults(); +} + +export class BalanceCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewBalance); + params: sc.MutableBalanceParams = new sc.MutableBalanceParams(); + results: sc.ImmutableBalanceResults = new sc.ImmutableBalanceResults(); +} + +export class GetAccountNonceCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetAccountNonce); + params: sc.MutableGetAccountNonceParams = new sc.MutableGetAccountNonceParams(); + results: sc.ImmutableGetAccountNonceResults = new sc.ImmutableGetAccountNonceResults(); +} + +export class TotalAssetsCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewTotalAssets); + results: sc.ImmutableTotalAssetsResults = new sc.ImmutableTotalAssetsResults(); +} + +export class ScFuncs { + + static deposit(ctx: wasmlib.ScFuncCallContext): DepositCall { + let f = new DepositCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static harvest(ctx: wasmlib.ScFuncCallContext): HarvestCall { + let f = new HarvestCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static withdraw(ctx: wasmlib.ScFuncCallContext): WithdrawCall { + let f = new WithdrawCall(); + return f; + } + + static accounts(ctx: wasmlib.ScViewCallContext): AccountsCall { + let f = new AccountsCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static balance(ctx: wasmlib.ScViewCallContext): BalanceCall { + let f = new BalanceCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getAccountNonce(ctx: wasmlib.ScViewCallContext): GetAccountNonceCall { + let f = new GetAccountNonceCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static totalAssets(ctx: wasmlib.ScViewCallContext): TotalAssetsCall { + let f = new TotalAssetsCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/tokenregistry/results.go b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/index.ts similarity index 62% rename from contracts/wasm/tokenregistry/results.go rename to packages/vm/wasmlib/ts/wasmlib/coreaccounts/index.ts index 8b3b04d0c9..3e162ac6e1 100644 --- a/contracts/wasm/tokenregistry/results.go +++ b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/index.ts @@ -5,4 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -package tokenregistry +export * from "./consts"; +export * from "./contract"; +export * from "./params"; +export * from "./results"; diff --git a/packages/vm/wasmlib/ts/wasmlib/coreaccounts/params.ts b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/params.ts new file mode 100644 index 0000000000..3ab645fd6e --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/params.ts @@ -0,0 +1,73 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableDepositParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamAgentID)); + } +} + +export class MutableDepositParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamAgentID)); + } +} + +export class ImmutableHarvestParams extends wasmlib.ScMapID { + + withdrawAmount(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamWithdrawAmount)); + } + + withdrawColor(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamWithdrawColor)); + } +} + +export class MutableHarvestParams extends wasmlib.ScMapID { + + withdrawAmount(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamWithdrawAmount)); + } + + withdrawColor(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamWithdrawColor)); + } +} + +export class ImmutableBalanceParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamAgentID)); + } +} + +export class MutableBalanceParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamAgentID)); + } +} + +export class ImmutableGetAccountNonceParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamAgentID)); + } +} + +export class MutableGetAccountNonceParams extends wasmlib.ScMapID { + + agentID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamAgentID)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreaccounts/results.ts b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/results.ts new file mode 100644 index 0000000000..e0eb7ca6ea --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/results.ts @@ -0,0 +1,121 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class MapAgentIDToImmutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getBytes(key: wasmlib.ScAgentID): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.objID, key.getKeyID()); + } +} + +export class ImmutableAccountsResults extends wasmlib.ScMapID { + + agents(): sc.MapAgentIDToImmutableBytes { + return new sc.MapAgentIDToImmutableBytes(this.mapID); + } +} + +export class MapAgentIDToMutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getBytes(key: wasmlib.ScAgentID): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.objID, key.getKeyID()); + } +} + +export class MutableAccountsResults extends wasmlib.ScMapID { + + agents(): sc.MapAgentIDToMutableBytes { + return new sc.MapAgentIDToMutableBytes(this.mapID); + } +} + +export class MapColorToImmutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getInt64(key: wasmlib.ScColor): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.objID, key.getKeyID()); + } +} + +export class ImmutableBalanceResults extends wasmlib.ScMapID { + + balances(): sc.MapColorToImmutableInt64 { + return new sc.MapColorToImmutableInt64(this.mapID); + } +} + +export class MapColorToMutableInt64 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getInt64(key: wasmlib.ScColor): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.objID, key.getKeyID()); + } +} + +export class MutableBalanceResults extends wasmlib.ScMapID { + + balances(): sc.MapColorToMutableInt64 { + return new sc.MapColorToMutableInt64(this.mapID); + } +} + +export class ImmutableGetAccountNonceResults extends wasmlib.ScMapID { + + accountNonce(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultAccountNonce)); + } +} + +export class MutableGetAccountNonceResults extends wasmlib.ScMapID { + + accountNonce(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultAccountNonce)); + } +} + +export class ImmutableTotalAssetsResults extends wasmlib.ScMapID { + + balances(): sc.MapColorToImmutableInt64 { + return new sc.MapColorToImmutableInt64(this.mapID); + } +} + +export class MutableTotalAssetsResults extends wasmlib.ScMapID { + + balances(): sc.MapColorToMutableInt64 { + return new sc.MapColorToMutableInt64(this.mapID); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreaccounts/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/tsconfig.json new file mode 100644 index 0000000000..b51e156a90 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreaccounts/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblob/consts.ts b/packages/vm/wasmlib/ts/wasmlib/coreblob/consts.ts new file mode 100644 index 0000000000..0e49977214 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblob/consts.ts @@ -0,0 +1,28 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "blob"; +export const ScDescription = "Core blob contract"; +export const HScName = new wasmlib.ScHname(0xfd91bc63); + +export const ParamField = "field"; +export const ParamHash = "hash"; + +export const ResultBytes = "bytes"; +export const ResultHash = "hash"; + +export const FuncStoreBlob = "storeBlob"; +export const ViewGetBlobField = "getBlobField"; +export const ViewGetBlobInfo = "getBlobInfo"; +export const ViewListBlobs = "listBlobs"; + +export const HFuncStoreBlob = new wasmlib.ScHname(0xddd4c281); +export const HViewGetBlobField = new wasmlib.ScHname(0x1f448130); +export const HViewGetBlobInfo = new wasmlib.ScHname(0xfde4ab46); +export const HViewListBlobs = new wasmlib.ScHname(0x62ca7990); diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblob/contract.ts b/packages/vm/wasmlib/ts/wasmlib/coreblob/contract.ts new file mode 100644 index 0000000000..7fd8422de0 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblob/contract.ts @@ -0,0 +1,59 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class StoreBlobCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncStoreBlob); + params: sc.MutableStoreBlobParams = new sc.MutableStoreBlobParams(); + results: sc.ImmutableStoreBlobResults = new sc.ImmutableStoreBlobResults(); +} + +export class GetBlobFieldCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetBlobField); + params: sc.MutableGetBlobFieldParams = new sc.MutableGetBlobFieldParams(); + results: sc.ImmutableGetBlobFieldResults = new sc.ImmutableGetBlobFieldResults(); +} + +export class GetBlobInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetBlobInfo); + params: sc.MutableGetBlobInfoParams = new sc.MutableGetBlobInfoParams(); + results: sc.ImmutableGetBlobInfoResults = new sc.ImmutableGetBlobInfoResults(); +} + +export class ListBlobsCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewListBlobs); + results: sc.ImmutableListBlobsResults = new sc.ImmutableListBlobsResults(); +} + +export class ScFuncs { + + static storeBlob(ctx: wasmlib.ScFuncCallContext): StoreBlobCall { + let f = new StoreBlobCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getBlobField(ctx: wasmlib.ScViewCallContext): GetBlobFieldCall { + let f = new GetBlobFieldCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getBlobInfo(ctx: wasmlib.ScViewCallContext): GetBlobInfoCall { + let f = new GetBlobInfoCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static listBlobs(ctx: wasmlib.ScViewCallContext): ListBlobsCall { + let f = new ListBlobsCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/helloworld/params.go b/packages/vm/wasmlib/ts/wasmlib/coreblob/index.ts similarity index 62% rename from contracts/wasm/helloworld/params.go rename to packages/vm/wasmlib/ts/wasmlib/coreblob/index.ts index 9346abb4e0..3e162ac6e1 100644 --- a/contracts/wasm/helloworld/params.go +++ b/packages/vm/wasmlib/ts/wasmlib/coreblob/index.ts @@ -5,4 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -package helloworld +export * from "./consts"; +export * from "./contract"; +export * from "./params"; +export * from "./results"; diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblob/params.ts b/packages/vm/wasmlib/ts/wasmlib/coreblob/params.ts new file mode 100644 index 0000000000..0c86f3ab50 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblob/params.ts @@ -0,0 +1,87 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class MapStringToImmutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getBytes(key: string): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class ImmutableStoreBlobParams extends wasmlib.ScMapID { + + blobs(): sc.MapStringToImmutableBytes { + return new sc.MapStringToImmutableBytes(this.mapID); + } +} + +export class MapStringToMutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getBytes(key: string): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class MutableStoreBlobParams extends wasmlib.ScMapID { + + blobs(): sc.MapStringToMutableBytes { + return new sc.MapStringToMutableBytes(this.mapID); + } +} + +export class ImmutableGetBlobFieldParams extends wasmlib.ScMapID { + + field(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamField)); + } + + hash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamHash)); + } +} + +export class MutableGetBlobFieldParams extends wasmlib.ScMapID { + + field(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamField)); + } + + hash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamHash)); + } +} + +export class ImmutableGetBlobInfoParams extends wasmlib.ScMapID { + + hash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamHash)); + } +} + +export class MutableGetBlobInfoParams extends wasmlib.ScMapID { + + hash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamHash)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblob/results.ts b/packages/vm/wasmlib/ts/wasmlib/coreblob/results.ts new file mode 100644 index 0000000000..ec64602e60 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblob/results.ts @@ -0,0 +1,121 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableStoreBlobResults extends wasmlib.ScMapID { + + hash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ResultHash)); + } +} + +export class MutableStoreBlobResults extends wasmlib.ScMapID { + + hash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ResultHash)); + } +} + +export class ImmutableGetBlobFieldResults extends wasmlib.ScMapID { + + bytes(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultBytes)); + } +} + +export class MutableGetBlobFieldResults extends wasmlib.ScMapID { + + bytes(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultBytes)); + } +} + +export class MapStringToImmutableInt32 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getInt32(key: string): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class ImmutableGetBlobInfoResults extends wasmlib.ScMapID { + + blobSizes(): sc.MapStringToImmutableInt32 { + return new sc.MapStringToImmutableInt32(this.mapID); + } +} + +export class MapStringToMutableInt32 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getInt32(key: string): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.objID, wasmlib.Key32.fromString(key).getKeyID()); + } +} + +export class MutableGetBlobInfoResults extends wasmlib.ScMapID { + + blobSizes(): sc.MapStringToMutableInt32 { + return new sc.MapStringToMutableInt32(this.mapID); + } +} + +export class MapHashToImmutableInt32 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getInt32(key: wasmlib.ScHash): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.objID, key.getKeyID()); + } +} + +export class ImmutableListBlobsResults extends wasmlib.ScMapID { + + blobSizes(): sc.MapHashToImmutableInt32 { + return new sc.MapHashToImmutableInt32(this.mapID); + } +} + +export class MapHashToMutableInt32 { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getInt32(key: wasmlib.ScHash): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.objID, key.getKeyID()); + } +} + +export class MutableListBlobsResults extends wasmlib.ScMapID { + + blobSizes(): sc.MapHashToMutableInt32 { + return new sc.MapHashToMutableInt32(this.mapID); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblob/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/coreblob/tsconfig.json new file mode 100644 index 0000000000..b51e156a90 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblob/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblocklog/consts.ts b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/consts.ts new file mode 100644 index 0000000000..11238f332a --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/consts.ts @@ -0,0 +1,50 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "blocklog"; +export const ScDescription = "Core block log contract"; +export const HScName = new wasmlib.ScHname(0xf538ef2b); + +export const ParamBlockIndex = "n"; +export const ParamContractHname = "h"; +export const ParamFromBlock = "f"; +export const ParamRequestID = "u"; +export const ParamToBlock = "t"; + +export const ResultBlockIndex = "n"; +export const ResultBlockInfo = "i"; +export const ResultEvent = "e"; +export const ResultGoverningAddress = "g"; +export const ResultRequestID = "u"; +export const ResultRequestIndex = "r"; +export const ResultRequestProcessed = "p"; +export const ResultRequestRecord = "d"; +export const ResultStateControllerAddress = "s"; + +export const ViewControlAddresses = "controlAddresses"; +export const ViewGetBlockInfo = "getBlockInfo"; +export const ViewGetEventsForBlock = "getEventsForBlock"; +export const ViewGetEventsForContract = "getEventsForContract"; +export const ViewGetEventsForRequest = "getEventsForRequest"; +export const ViewGetLatestBlockInfo = "getLatestBlockInfo"; +export const ViewGetRequestIDsForBlock = "getRequestIDsForBlock"; +export const ViewGetRequestReceipt = "getRequestReceipt"; +export const ViewGetRequestReceiptsForBlock = "getRequestReceiptsForBlock"; +export const ViewIsRequestProcessed = "isRequestProcessed"; + +export const HViewControlAddresses = new wasmlib.ScHname(0x796bd223); +export const HViewGetBlockInfo = new wasmlib.ScHname(0xbe89f9b3); +export const HViewGetEventsForBlock = new wasmlib.ScHname(0x36232798); +export const HViewGetEventsForContract = new wasmlib.ScHname(0x682a1922); +export const HViewGetEventsForRequest = new wasmlib.ScHname(0x4f8d68e4); +export const HViewGetLatestBlockInfo = new wasmlib.ScHname(0x084a1760); +export const HViewGetRequestIDsForBlock = new wasmlib.ScHname(0x5a20327a); +export const HViewGetRequestReceipt = new wasmlib.ScHname(0xb7f9534f); +export const HViewGetRequestReceiptsForBlock = new wasmlib.ScHname(0x77e3beef); +export const HViewIsRequestProcessed = new wasmlib.ScHname(0xd57d50a9); diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblocklog/contract.ts b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/contract.ts new file mode 100644 index 0000000000..ffc38aef7e --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/contract.ts @@ -0,0 +1,130 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ControlAddressesCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewControlAddresses); + results: sc.ImmutableControlAddressesResults = new sc.ImmutableControlAddressesResults(); +} + +export class GetBlockInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetBlockInfo); + params: sc.MutableGetBlockInfoParams = new sc.MutableGetBlockInfoParams(); + results: sc.ImmutableGetBlockInfoResults = new sc.ImmutableGetBlockInfoResults(); +} + +export class GetEventsForBlockCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetEventsForBlock); + params: sc.MutableGetEventsForBlockParams = new sc.MutableGetEventsForBlockParams(); + results: sc.ImmutableGetEventsForBlockResults = new sc.ImmutableGetEventsForBlockResults(); +} + +export class GetEventsForContractCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetEventsForContract); + params: sc.MutableGetEventsForContractParams = new sc.MutableGetEventsForContractParams(); + results: sc.ImmutableGetEventsForContractResults = new sc.ImmutableGetEventsForContractResults(); +} + +export class GetEventsForRequestCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetEventsForRequest); + params: sc.MutableGetEventsForRequestParams = new sc.MutableGetEventsForRequestParams(); + results: sc.ImmutableGetEventsForRequestResults = new sc.ImmutableGetEventsForRequestResults(); +} + +export class GetLatestBlockInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetLatestBlockInfo); + results: sc.ImmutableGetLatestBlockInfoResults = new sc.ImmutableGetLatestBlockInfoResults(); +} + +export class GetRequestIDsForBlockCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetRequestIDsForBlock); + params: sc.MutableGetRequestIDsForBlockParams = new sc.MutableGetRequestIDsForBlockParams(); + results: sc.ImmutableGetRequestIDsForBlockResults = new sc.ImmutableGetRequestIDsForBlockResults(); +} + +export class GetRequestReceiptCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetRequestReceipt); + params: sc.MutableGetRequestReceiptParams = new sc.MutableGetRequestReceiptParams(); + results: sc.ImmutableGetRequestReceiptResults = new sc.ImmutableGetRequestReceiptResults(); +} + +export class GetRequestReceiptsForBlockCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetRequestReceiptsForBlock); + params: sc.MutableGetRequestReceiptsForBlockParams = new sc.MutableGetRequestReceiptsForBlockParams(); + results: sc.ImmutableGetRequestReceiptsForBlockResults = new sc.ImmutableGetRequestReceiptsForBlockResults(); +} + +export class IsRequestProcessedCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewIsRequestProcessed); + params: sc.MutableIsRequestProcessedParams = new sc.MutableIsRequestProcessedParams(); + results: sc.ImmutableIsRequestProcessedResults = new sc.ImmutableIsRequestProcessedResults(); +} + +export class ScFuncs { + + static controlAddresses(ctx: wasmlib.ScViewCallContext): ControlAddressesCall { + let f = new ControlAddressesCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static getBlockInfo(ctx: wasmlib.ScViewCallContext): GetBlockInfoCall { + let f = new GetBlockInfoCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getEventsForBlock(ctx: wasmlib.ScViewCallContext): GetEventsForBlockCall { + let f = new GetEventsForBlockCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getEventsForContract(ctx: wasmlib.ScViewCallContext): GetEventsForContractCall { + let f = new GetEventsForContractCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getEventsForRequest(ctx: wasmlib.ScViewCallContext): GetEventsForRequestCall { + let f = new GetEventsForRequestCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getLatestBlockInfo(ctx: wasmlib.ScViewCallContext): GetLatestBlockInfoCall { + let f = new GetLatestBlockInfoCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static getRequestIDsForBlock(ctx: wasmlib.ScViewCallContext): GetRequestIDsForBlockCall { + let f = new GetRequestIDsForBlockCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getRequestReceipt(ctx: wasmlib.ScViewCallContext): GetRequestReceiptCall { + let f = new GetRequestReceiptCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getRequestReceiptsForBlock(ctx: wasmlib.ScViewCallContext): GetRequestReceiptsForBlockCall { + let f = new GetRequestReceiptsForBlockCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static isRequestProcessed(ctx: wasmlib.ScViewCallContext): IsRequestProcessedCall { + let f = new IsRequestProcessedCall(); + f.func.setPtrs(f.params, f.results); + return f; + } +} diff --git a/contracts/wasm/helloworld/src/params.rs b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/index.ts similarity index 59% rename from contracts/wasm/helloworld/src/params.rs rename to packages/vm/wasmlib/ts/wasmlib/coreblocklog/index.ts index 1437846207..3e162ac6e1 100644 --- a/contracts/wasm/helloworld/src/params.rs +++ b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/index.ts @@ -5,11 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -#![allow(dead_code)] -#![allow(unused_imports)] - -use wasmlib::*; -use wasmlib::host::*; - -use crate::*; -use crate::keys::*; +export * from "./consts"; +export * from "./contract"; +export * from "./params"; +export * from "./results"; diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblocklog/params.ts b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/params.ts new file mode 100644 index 0000000000..a9907bfb27 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/params.ts @@ -0,0 +1,137 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableGetBlockInfoParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class MutableGetBlockInfoParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class ImmutableGetEventsForBlockParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class MutableGetEventsForBlockParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class ImmutableGetEventsForContractParams extends wasmlib.ScMapID { + + contractHname(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamContractHname)); + } + + fromBlock(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamFromBlock)); + } + + toBlock(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamToBlock)); + } +} + +export class MutableGetEventsForContractParams extends wasmlib.ScMapID { + + contractHname(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamContractHname)); + } + + fromBlock(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamFromBlock)); + } + + toBlock(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamToBlock)); + } +} + +export class ImmutableGetEventsForRequestParams extends wasmlib.ScMapID { + + requestID(): wasmlib.ScImmutableRequestID { + return new wasmlib.ScImmutableRequestID(this.mapID, wasmlib.Key32.fromString(sc.ParamRequestID)); + } +} + +export class MutableGetEventsForRequestParams extends wasmlib.ScMapID { + + requestID(): wasmlib.ScMutableRequestID { + return new wasmlib.ScMutableRequestID(this.mapID, wasmlib.Key32.fromString(sc.ParamRequestID)); + } +} + +export class ImmutableGetRequestIDsForBlockParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class MutableGetRequestIDsForBlockParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class ImmutableGetRequestReceiptParams extends wasmlib.ScMapID { + + requestID(): wasmlib.ScImmutableRequestID { + return new wasmlib.ScImmutableRequestID(this.mapID, wasmlib.Key32.fromString(sc.ParamRequestID)); + } +} + +export class MutableGetRequestReceiptParams extends wasmlib.ScMapID { + + requestID(): wasmlib.ScMutableRequestID { + return new wasmlib.ScMutableRequestID(this.mapID, wasmlib.Key32.fromString(sc.ParamRequestID)); + } +} + +export class ImmutableGetRequestReceiptsForBlockParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class MutableGetRequestReceiptsForBlockParams extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); + } +} + +export class ImmutableIsRequestProcessedParams extends wasmlib.ScMapID { + + requestID(): wasmlib.ScImmutableRequestID { + return new wasmlib.ScImmutableRequestID(this.mapID, wasmlib.Key32.fromString(sc.ParamRequestID)); + } +} + +export class MutableIsRequestProcessedParams extends wasmlib.ScMapID { + + requestID(): wasmlib.ScMutableRequestID { + return new wasmlib.ScMutableRequestID(this.mapID, wasmlib.Key32.fromString(sc.ParamRequestID)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblocklog/results.ts b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/results.ts new file mode 100644 index 0000000000..01852ab376 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/results.ts @@ -0,0 +1,271 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableControlAddressesResults extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockIndex)); + } + + governingAddress(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ResultGoverningAddress)); + } + + stateControllerAddress(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ResultStateControllerAddress)); + } +} + +export class MutableControlAddressesResults extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockIndex)); + } + + governingAddress(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ResultGoverningAddress)); + } + + stateControllerAddress(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ResultStateControllerAddress)); + } +} + +export class ImmutableGetBlockInfoResults extends wasmlib.ScMapID { + + blockInfo(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockInfo)); + } +} + +export class MutableGetBlockInfoResults extends wasmlib.ScMapID { + + blockInfo(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockInfo)); + } +} + +export class ArrayOfImmutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBytes(index: i32): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableGetEventsForBlockResults extends wasmlib.ScMapID { + + event(): sc.ArrayOfImmutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultEvent), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBytes(arrID) + } +} + +export class ArrayOfMutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBytes(index: i32): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableGetEventsForBlockResults extends wasmlib.ScMapID { + + event(): sc.ArrayOfMutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultEvent), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBytes(arrID) + } +} + +export class ImmutableGetEventsForContractResults extends wasmlib.ScMapID { + + event(): sc.ArrayOfImmutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultEvent), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBytes(arrID) + } +} + +export class MutableGetEventsForContractResults extends wasmlib.ScMapID { + + event(): sc.ArrayOfMutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultEvent), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBytes(arrID) + } +} + +export class ImmutableGetEventsForRequestResults extends wasmlib.ScMapID { + + event(): sc.ArrayOfImmutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultEvent), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBytes(arrID) + } +} + +export class MutableGetEventsForRequestResults extends wasmlib.ScMapID { + + event(): sc.ArrayOfMutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultEvent), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBytes(arrID) + } +} + +export class ImmutableGetLatestBlockInfoResults extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockIndex)); + } + + blockInfo(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockInfo)); + } +} + +export class MutableGetLatestBlockInfoResults extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockIndex)); + } + + blockInfo(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockInfo)); + } +} + +export class ArrayOfImmutableRequestID { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getRequestID(index: i32): wasmlib.ScImmutableRequestID { + return new wasmlib.ScImmutableRequestID(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableGetRequestIDsForBlockResults extends wasmlib.ScMapID { + + requestID(): sc.ArrayOfImmutableRequestID { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestID), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_REQUEST_ID); + return new sc.ArrayOfImmutableRequestID(arrID) + } +} + +export class ArrayOfMutableRequestID { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getRequestID(index: i32): wasmlib.ScMutableRequestID { + return new wasmlib.ScMutableRequestID(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableGetRequestIDsForBlockResults extends wasmlib.ScMapID { + + requestID(): sc.ArrayOfMutableRequestID { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestID), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_REQUEST_ID); + return new sc.ArrayOfMutableRequestID(arrID) + } +} + +export class ImmutableGetRequestReceiptResults extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockIndex)); + } + + requestIndex(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestIndex)); + } + + requestRecord(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestRecord)); + } +} + +export class MutableGetRequestReceiptResults extends wasmlib.ScMapID { + + blockIndex(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBlockIndex)); + } + + requestIndex(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestIndex)); + } + + requestRecord(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestRecord)); + } +} + +export class ImmutableGetRequestReceiptsForBlockResults extends wasmlib.ScMapID { + + requestRecord(): sc.ArrayOfImmutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestRecord), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBytes(arrID) + } +} + +export class MutableGetRequestReceiptsForBlockResults extends wasmlib.ScMapID { + + requestRecord(): sc.ArrayOfMutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestRecord), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBytes(arrID) + } +} + +export class ImmutableIsRequestProcessedResults extends wasmlib.ScMapID { + + requestProcessed(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestProcessed)); + } +} + +export class MutableIsRequestProcessedResults extends wasmlib.ScMapID { + + requestProcessed(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultRequestProcessed)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreblocklog/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/tsconfig.json new file mode 100644 index 0000000000..b51e156a90 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreblocklog/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/coregovernance/consts.ts b/packages/vm/wasmlib/ts/wasmlib/coregovernance/consts.ts new file mode 100644 index 0000000000..c3ea510454 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coregovernance/consts.ts @@ -0,0 +1,61 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "governance"; +export const ScDescription = "Core governance contract"; +export const HScName = new wasmlib.ScHname(0x17cf909f); + +export const ParamChainOwner = "oi"; +export const ParamFeeColor = "fc"; +export const ParamHname = "hn"; +export const ParamMaxBlobSize = "bs"; +export const ParamMaxEventSize = "es"; +export const ParamMaxEventsPerReq = "ne"; +export const ParamOwnerFee = "of"; +export const ParamStateControllerAddress = "S"; +export const ParamValidatorFee = "vf"; + +export const ResultAllowedStateControllerAddresses = "a"; +export const ResultChainID = "c"; +export const ResultChainOwnerID = "o"; +export const ResultDefaultOwnerFee = "do"; +export const ResultDefaultValidatorFee = "dv"; +export const ResultDescription = "d"; +export const ResultFeeColor = "f"; +export const ResultMaxBlobSize = "mb"; +export const ResultMaxEventSize = "me"; +export const ResultMaxEventsPerReq = "mr"; +export const ResultOwnerFee = "of"; +export const ResultValidatorFee = "vf"; + +export const FuncAddAllowedStateControllerAddress = "addAllowedStateControllerAddress"; +export const FuncClaimChainOwnership = "claimChainOwnership"; +export const FuncDelegateChainOwnership = "delegateChainOwnership"; +export const FuncRemoveAllowedStateControllerAddress = "removeAllowedStateControllerAddress"; +export const FuncRotateStateController = "rotateStateController"; +export const FuncSetChainInfo = "setChainInfo"; +export const FuncSetContractFee = "setContractFee"; +export const FuncSetDefaultFee = "setDefaultFee"; +export const ViewGetAllowedStateControllerAddresses = "getAllowedStateControllerAddresses"; +export const ViewGetChainInfo = "getChainInfo"; +export const ViewGetFeeInfo = "getFeeInfo"; +export const ViewGetMaxBlobSize = "getMaxBlobSize"; + +export const HFuncAddAllowedStateControllerAddress = new wasmlib.ScHname(0x9469d567); +export const HFuncClaimChainOwnership = new wasmlib.ScHname(0x03ff0fc0); +export const HFuncDelegateChainOwnership = new wasmlib.ScHname(0x93ecb6ad); +export const HFuncRemoveAllowedStateControllerAddress = new wasmlib.ScHname(0x31f69447); +export const HFuncRotateStateController = new wasmlib.ScHname(0x244d1038); +export const HFuncSetChainInfo = new wasmlib.ScHname(0x702f5d2b); +export const HFuncSetContractFee = new wasmlib.ScHname(0x8421a42b); +export const HFuncSetDefaultFee = new wasmlib.ScHname(0x3310ecd0); +export const HViewGetAllowedStateControllerAddresses = new wasmlib.ScHname(0xf3505183); +export const HViewGetChainInfo = new wasmlib.ScHname(0x434477e2); +export const HViewGetFeeInfo = new wasmlib.ScHname(0x9fe54b48); +export const HViewGetMaxBlobSize = new wasmlib.ScHname(0xe1db3d28); diff --git a/packages/vm/wasmlib/ts/wasmlib/coregovernance/contract.ts b/packages/vm/wasmlib/ts/wasmlib/coregovernance/contract.ts new file mode 100644 index 0000000000..dfc45e5702 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coregovernance/contract.ts @@ -0,0 +1,143 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class AddAllowedStateControllerAddressCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncAddAllowedStateControllerAddress); + params: sc.MutableAddAllowedStateControllerAddressParams = new sc.MutableAddAllowedStateControllerAddressParams(); +} + +export class ClaimChainOwnershipCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncClaimChainOwnership); +} + +export class DelegateChainOwnershipCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncDelegateChainOwnership); + params: sc.MutableDelegateChainOwnershipParams = new sc.MutableDelegateChainOwnershipParams(); +} + +export class RemoveAllowedStateControllerAddressCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncRemoveAllowedStateControllerAddress); + params: sc.MutableRemoveAllowedStateControllerAddressParams = new sc.MutableRemoveAllowedStateControllerAddressParams(); +} + +export class RotateStateControllerCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncRotateStateController); + params: sc.MutableRotateStateControllerParams = new sc.MutableRotateStateControllerParams(); +} + +export class SetChainInfoCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSetChainInfo); + params: sc.MutableSetChainInfoParams = new sc.MutableSetChainInfoParams(); +} + +export class SetContractFeeCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSetContractFee); + params: sc.MutableSetContractFeeParams = new sc.MutableSetContractFeeParams(); +} + +export class SetDefaultFeeCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncSetDefaultFee); + params: sc.MutableSetDefaultFeeParams = new sc.MutableSetDefaultFeeParams(); +} + +export class GetAllowedStateControllerAddressesCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetAllowedStateControllerAddresses); + results: sc.ImmutableGetAllowedStateControllerAddressesResults = new sc.ImmutableGetAllowedStateControllerAddressesResults(); +} + +export class GetChainInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetChainInfo); + results: sc.ImmutableGetChainInfoResults = new sc.ImmutableGetChainInfoResults(); +} + +export class GetFeeInfoCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetFeeInfo); + params: sc.MutableGetFeeInfoParams = new sc.MutableGetFeeInfoParams(); + results: sc.ImmutableGetFeeInfoResults = new sc.ImmutableGetFeeInfoResults(); +} + +export class GetMaxBlobSizeCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetMaxBlobSize); + results: sc.ImmutableGetMaxBlobSizeResults = new sc.ImmutableGetMaxBlobSizeResults(); +} + +export class ScFuncs { + + static addAllowedStateControllerAddress(ctx: wasmlib.ScFuncCallContext): AddAllowedStateControllerAddressCall { + let f = new AddAllowedStateControllerAddressCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static claimChainOwnership(ctx: wasmlib.ScFuncCallContext): ClaimChainOwnershipCall { + let f = new ClaimChainOwnershipCall(); + return f; + } + + static delegateChainOwnership(ctx: wasmlib.ScFuncCallContext): DelegateChainOwnershipCall { + let f = new DelegateChainOwnershipCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static removeAllowedStateControllerAddress(ctx: wasmlib.ScFuncCallContext): RemoveAllowedStateControllerAddressCall { + let f = new RemoveAllowedStateControllerAddressCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static rotateStateController(ctx: wasmlib.ScFuncCallContext): RotateStateControllerCall { + let f = new RotateStateControllerCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static setChainInfo(ctx: wasmlib.ScFuncCallContext): SetChainInfoCall { + let f = new SetChainInfoCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static setContractFee(ctx: wasmlib.ScFuncCallContext): SetContractFeeCall { + let f = new SetContractFeeCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static setDefaultFee(ctx: wasmlib.ScFuncCallContext): SetDefaultFeeCall { + let f = new SetDefaultFeeCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static getAllowedStateControllerAddresses(ctx: wasmlib.ScViewCallContext): GetAllowedStateControllerAddressesCall { + let f = new GetAllowedStateControllerAddressesCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static getChainInfo(ctx: wasmlib.ScViewCallContext): GetChainInfoCall { + let f = new GetChainInfoCall(); + f.func.setPtrs(null, f.results); + return f; + } + + static getFeeInfo(ctx: wasmlib.ScViewCallContext): GetFeeInfoCall { + let f = new GetFeeInfoCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getMaxBlobSize(ctx: wasmlib.ScViewCallContext): GetMaxBlobSizeCall { + let f = new GetMaxBlobSizeCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/contracts/wasm/tokenregistry/src/results.rs b/packages/vm/wasmlib/ts/wasmlib/coregovernance/index.ts similarity index 55% rename from contracts/wasm/tokenregistry/src/results.rs rename to packages/vm/wasmlib/ts/wasmlib/coregovernance/index.ts index 3a850e1bc9..3e162ac6e1 100644 --- a/contracts/wasm/tokenregistry/src/results.rs +++ b/packages/vm/wasmlib/ts/wasmlib/coregovernance/index.ts @@ -5,12 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -#![allow(dead_code)] -#![allow(unused_imports)] - -use wasmlib::*; -use wasmlib::host::*; - -use crate::*; -use crate::keys::*; -use crate::types::*; +export * from "./consts"; +export * from "./contract"; +export * from "./params"; +export * from "./results"; diff --git a/packages/vm/wasmlib/ts/wasmlib/coregovernance/params.ts b/packages/vm/wasmlib/ts/wasmlib/coregovernance/params.ts new file mode 100644 index 0000000000..7a42ac6c0f --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coregovernance/params.ts @@ -0,0 +1,193 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableAddAllowedStateControllerAddressParams extends wasmlib.ScMapID { + + chainOwner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamChainOwner)); + } + + feeColor(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamFeeColor)); + } + + stateControllerAddress(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ParamStateControllerAddress)); + } +} + +export class MutableAddAllowedStateControllerAddressParams extends wasmlib.ScMapID { + + chainOwner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamChainOwner)); + } + + feeColor(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamFeeColor)); + } + + stateControllerAddress(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ParamStateControllerAddress)); + } +} + +export class ImmutableDelegateChainOwnershipParams extends wasmlib.ScMapID { + + chainOwner(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamChainOwner)); + } +} + +export class MutableDelegateChainOwnershipParams extends wasmlib.ScMapID { + + chainOwner(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamChainOwner)); + } +} + +export class ImmutableRemoveAllowedStateControllerAddressParams extends wasmlib.ScMapID { + + stateControllerAddress(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ParamStateControllerAddress)); + } +} + +export class MutableRemoveAllowedStateControllerAddressParams extends wasmlib.ScMapID { + + stateControllerAddress(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ParamStateControllerAddress)); + } +} + +export class ImmutableRotateStateControllerParams extends wasmlib.ScMapID { + + stateControllerAddress(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ParamStateControllerAddress)); + } +} + +export class MutableRotateStateControllerParams extends wasmlib.ScMapID { + + stateControllerAddress(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, wasmlib.Key32.fromString(sc.ParamStateControllerAddress)); + } +} + +export class ImmutableSetChainInfoParams extends wasmlib.ScMapID { + + maxBlobSize(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamMaxBlobSize)); + } + + maxEventSize(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ParamMaxEventSize)); + } + + maxEventsPerReq(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ParamMaxEventsPerReq)); + } + + ownerFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerFee)); + } + + validatorFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamValidatorFee)); + } +} + +export class MutableSetChainInfoParams extends wasmlib.ScMapID { + + maxBlobSize(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamMaxBlobSize)); + } + + maxEventSize(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ParamMaxEventSize)); + } + + maxEventsPerReq(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ParamMaxEventsPerReq)); + } + + ownerFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerFee)); + } + + validatorFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamValidatorFee)); + } +} + +export class ImmutableSetContractFeeParams extends wasmlib.ScMapID { + + hname(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamHname)); + } + + ownerFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerFee)); + } + + validatorFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamValidatorFee)); + } +} + +export class MutableSetContractFeeParams extends wasmlib.ScMapID { + + hname(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamHname)); + } + + ownerFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerFee)); + } + + validatorFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamValidatorFee)); + } +} + +export class ImmutableSetDefaultFeeParams extends wasmlib.ScMapID { + + ownerFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerFee)); + } + + validatorFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamValidatorFee)); + } +} + +export class MutableSetDefaultFeeParams extends wasmlib.ScMapID { + + ownerFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerFee)); + } + + validatorFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamValidatorFee)); + } +} + +export class ImmutableGetFeeInfoParams extends wasmlib.ScMapID { + + hname(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamHname)); + } +} + +export class MutableGetFeeInfoParams extends wasmlib.ScMapID { + + hname(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamHname)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coregovernance/results.ts b/packages/vm/wasmlib/ts/wasmlib/coregovernance/results.ts new file mode 100644 index 0000000000..8404346f4b --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coregovernance/results.ts @@ -0,0 +1,183 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ArrayOfImmutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBytes(index: i32): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.objID, new wasmlib.Key32(index)); + } +} + +export class ImmutableGetAllowedStateControllerAddressesResults extends wasmlib.ScMapID { + + allowedStateControllerAddresses(): sc.ArrayOfImmutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultAllowedStateControllerAddresses), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfImmutableBytes(arrID) + } +} + +export class ArrayOfMutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + length(): i32 { + return wasmlib.getLength(this.objID); + } + + getBytes(index: i32): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.objID, new wasmlib.Key32(index)); + } +} + +export class MutableGetAllowedStateControllerAddressesResults extends wasmlib.ScMapID { + + allowedStateControllerAddresses(): sc.ArrayOfMutableBytes { + let arrID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultAllowedStateControllerAddresses), wasmlib.TYPE_ARRAY16|wasmlib.TYPE_BYTES); + return new sc.ArrayOfMutableBytes(arrID) + } +} + +export class ImmutableGetChainInfoResults extends wasmlib.ScMapID { + + chainID(): wasmlib.ScImmutableChainID { + return new wasmlib.ScImmutableChainID(this.mapID, wasmlib.Key32.fromString(sc.ResultChainID)); + } + + chainOwnerID(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultChainOwnerID)); + } + + defaultOwnerFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultDefaultOwnerFee)); + } + + defaultValidatorFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultDefaultValidatorFee)); + } + + description(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultDescription)); + } + + feeColor(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ResultFeeColor)); + } + + maxBlobSize(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxBlobSize)); + } + + maxEventSize(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxEventSize)); + } + + maxEventsPerReq(): wasmlib.ScImmutableInt16 { + return new wasmlib.ScImmutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxEventsPerReq)); + } +} + +export class MutableGetChainInfoResults extends wasmlib.ScMapID { + + chainID(): wasmlib.ScMutableChainID { + return new wasmlib.ScMutableChainID(this.mapID, wasmlib.Key32.fromString(sc.ResultChainID)); + } + + chainOwnerID(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultChainOwnerID)); + } + + defaultOwnerFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultDefaultOwnerFee)); + } + + defaultValidatorFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultDefaultValidatorFee)); + } + + description(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultDescription)); + } + + feeColor(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ResultFeeColor)); + } + + maxBlobSize(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxBlobSize)); + } + + maxEventSize(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxEventSize)); + } + + maxEventsPerReq(): wasmlib.ScMutableInt16 { + return new wasmlib.ScMutableInt16(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxEventsPerReq)); + } +} + +export class ImmutableGetFeeInfoResults extends wasmlib.ScMapID { + + feeColor(): wasmlib.ScImmutableColor { + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ResultFeeColor)); + } + + ownerFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultOwnerFee)); + } + + validatorFee(): wasmlib.ScImmutableInt64 { + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultValidatorFee)); + } +} + +export class MutableGetFeeInfoResults extends wasmlib.ScMapID { + + feeColor(): wasmlib.ScMutableColor { + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ResultFeeColor)); + } + + ownerFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultOwnerFee)); + } + + validatorFee(): wasmlib.ScMutableInt64 { + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultValidatorFee)); + } +} + +export class ImmutableGetMaxBlobSizeResults extends wasmlib.ScMapID { + + maxBlobSize(): wasmlib.ScImmutableInt32 { + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxBlobSize)); + } +} + +export class MutableGetMaxBlobSizeResults extends wasmlib.ScMapID { + + maxBlobSize(): wasmlib.ScMutableInt32 { + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultMaxBlobSize)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coregovernance/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/coregovernance/tsconfig.json new file mode 100644 index 0000000000..b51e156a90 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coregovernance/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/coreroot/consts.ts b/packages/vm/wasmlib/ts/wasmlib/coreroot/consts.ts new file mode 100644 index 0000000000..48b60cfd17 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreroot/consts.ts @@ -0,0 +1,34 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" + +export const ScName = "root"; +export const ScDescription = "Core root contract"; +export const HScName = new wasmlib.ScHname(0xcebf5908); + +export const ParamDeployer = "dp"; +export const ParamDescription = "ds"; +export const ParamHname = "hn"; +export const ParamName = "nm"; +export const ParamProgramHash = "ph"; + +export const ResultContractFound = "cf"; +export const ResultContractRecData = "dt"; +export const ResultContractRegistry = "r"; + +export const FuncDeployContract = "deployContract"; +export const FuncGrantDeployPermission = "grantDeployPermission"; +export const FuncRevokeDeployPermission = "revokeDeployPermission"; +export const ViewFindContract = "findContract"; +export const ViewGetContractRecords = "getContractRecords"; + +export const HFuncDeployContract = new wasmlib.ScHname(0x28232c27); +export const HFuncGrantDeployPermission = new wasmlib.ScHname(0xf440263a); +export const HFuncRevokeDeployPermission = new wasmlib.ScHname(0x850744f1); +export const HViewFindContract = new wasmlib.ScHname(0xc145ca00); +export const HViewGetContractRecords = new wasmlib.ScHname(0x078b3ef3); diff --git a/packages/vm/wasmlib/ts/wasmlib/coreroot/contract.ts b/packages/vm/wasmlib/ts/wasmlib/coreroot/contract.ts new file mode 100644 index 0000000000..f4617d16b2 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreroot/contract.ts @@ -0,0 +1,68 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class DeployContractCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncDeployContract); + params: sc.MutableDeployContractParams = new sc.MutableDeployContractParams(); +} + +export class GrantDeployPermissionCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncGrantDeployPermission); + params: sc.MutableGrantDeployPermissionParams = new sc.MutableGrantDeployPermissionParams(); +} + +export class RevokeDeployPermissionCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncRevokeDeployPermission); + params: sc.MutableRevokeDeployPermissionParams = new sc.MutableRevokeDeployPermissionParams(); +} + +export class FindContractCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewFindContract); + params: sc.MutableFindContractParams = new sc.MutableFindContractParams(); + results: sc.ImmutableFindContractResults = new sc.ImmutableFindContractResults(); +} + +export class GetContractRecordsCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewGetContractRecords); + results: sc.ImmutableGetContractRecordsResults = new sc.ImmutableGetContractRecordsResults(); +} + +export class ScFuncs { + + static deployContract(ctx: wasmlib.ScFuncCallContext): DeployContractCall { + let f = new DeployContractCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static grantDeployPermission(ctx: wasmlib.ScFuncCallContext): GrantDeployPermissionCall { + let f = new GrantDeployPermissionCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static revokeDeployPermission(ctx: wasmlib.ScFuncCallContext): RevokeDeployPermissionCall { + let f = new RevokeDeployPermissionCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static findContract(ctx: wasmlib.ScViewCallContext): FindContractCall { + let f = new FindContractCall(); + f.func.setPtrs(f.params, f.results); + return f; + } + + static getContractRecords(ctx: wasmlib.ScViewCallContext): GetContractRecordsCall { + let f = new GetContractRecordsCall(); + f.func.setPtrs(null, f.results); + return f; + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreroot/index.ts b/packages/vm/wasmlib/ts/wasmlib/coreroot/index.ts new file mode 100644 index 0000000000..3e162ac6e1 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreroot/index.ts @@ -0,0 +1,11 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./consts"; +export * from "./contract"; +export * from "./params"; +export * from "./results"; diff --git a/packages/vm/wasmlib/ts/wasmlib/coreroot/params.ts b/packages/vm/wasmlib/ts/wasmlib/coreroot/params.ts new file mode 100644 index 0000000000..065688b604 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreroot/params.ts @@ -0,0 +1,81 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableDeployContractParams extends wasmlib.ScMapID { + + description(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamDescription)); + } + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } + + programHash(): wasmlib.ScImmutableHash { + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamProgramHash)); + } +} + +export class MutableDeployContractParams extends wasmlib.ScMapID { + + description(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamDescription)); + } + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } + + programHash(): wasmlib.ScMutableHash { + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamProgramHash)); + } +} + +export class ImmutableGrantDeployPermissionParams extends wasmlib.ScMapID { + + deployer(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamDeployer)); + } +} + +export class MutableGrantDeployPermissionParams extends wasmlib.ScMapID { + + deployer(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamDeployer)); + } +} + +export class ImmutableRevokeDeployPermissionParams extends wasmlib.ScMapID { + + deployer(): wasmlib.ScImmutableAgentID { + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamDeployer)); + } +} + +export class MutableRevokeDeployPermissionParams extends wasmlib.ScMapID { + + deployer(): wasmlib.ScMutableAgentID { + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamDeployer)); + } +} + +export class ImmutableFindContractParams extends wasmlib.ScMapID { + + hname(): wasmlib.ScImmutableHname { + return new wasmlib.ScImmutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamHname)); + } +} + +export class MutableFindContractParams extends wasmlib.ScMapID { + + hname(): wasmlib.ScMutableHname { + return new wasmlib.ScMutableHname(this.mapID, wasmlib.Key32.fromString(sc.ParamHname)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreroot/results.ts b/packages/vm/wasmlib/ts/wasmlib/coreroot/results.ts new file mode 100644 index 0000000000..5448e3f722 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreroot/results.ts @@ -0,0 +1,75 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib" +import * as sc from "./index"; + +export class ImmutableFindContractResults extends wasmlib.ScMapID { + + contractFound(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultContractFound)); + } + + contractRecData(): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultContractRecData)); + } +} + +export class MutableFindContractResults extends wasmlib.ScMapID { + + contractFound(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultContractFound)); + } + + contractRecData(): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ResultContractRecData)); + } +} + +export class MapHnameToImmutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getBytes(key: wasmlib.ScHname): wasmlib.ScImmutableBytes { + return new wasmlib.ScImmutableBytes(this.objID, key.getKeyID()); + } +} + +export class ImmutableGetContractRecordsResults extends wasmlib.ScMapID { + + contractRegistry(): sc.MapHnameToImmutableBytes { + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultContractRegistry), wasmlib.TYPE_MAP); + return new sc.MapHnameToImmutableBytes(mapID); + } +} + +export class MapHnameToMutableBytes { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID) + } + + getBytes(key: wasmlib.ScHname): wasmlib.ScMutableBytes { + return new wasmlib.ScMutableBytes(this.objID, key.getKeyID()); + } +} + +export class MutableGetContractRecordsResults extends wasmlib.ScMapID { + + contractRegistry(): sc.MapHnameToMutableBytes { + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.ResultContractRegistry), wasmlib.TYPE_MAP); + return new sc.MapHnameToMutableBytes(mapID); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/coreroot/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/coreroot/tsconfig.json new file mode 100644 index 0000000000..b51e156a90 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/coreroot/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/exports.ts b/packages/vm/wasmlib/ts/wasmlib/exports.ts new file mode 100644 index 0000000000..19a08439fc --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/exports.ts @@ -0,0 +1,79 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// Provide host with details about funcs and views in this smart contract + +import {ROOT, ScFuncContext, ScViewContext} from "./context"; +import {KEY_EXPORTS, KEY_PARAMS, KEY_RESULTS, KEY_STATE, KEY_ZZZZZZZ} from "./keys"; +import {ScMutableStringArray} from "./mutable"; +import {getObjectID, OBJ_ID_PARAMS, OBJ_ID_RESULTS, OBJ_ID_ROOT, OBJ_ID_STATE, TYPE_MAP} from "./host"; + +// Note that we do not use the Wasm export symbol table on purpose +// because Wasm does not allow us to determine whether the symbols +// are meant as view or func, or meant as extra public callbacks +// generated by the compilation of the the Wasm code. +// There are only 2 symbols the ISCP host will actually look for +// in the export table: +// on_load (which must be defined by the SC code) and +// on_call (which is defined here as part of WasmLib) + +type ScFuncContextFunc = (f: ScFuncContext) => void; +type ScViewContextFunc = (v: ScViewContext) => void; + +let funcs: Array = []; +let views: Array = []; + +// general entrypoint for the host to call any SC function +// the host will pass the index of one of the entry points +// that was provided by onLoad during SC initialization +export function onCall(index: i32): void { + let ctx = new ScFuncContext(); + ctx.require(getObjectID(OBJ_ID_ROOT, KEY_STATE, TYPE_MAP) == OBJ_ID_STATE, "object id mismatch"); + ctx.require(getObjectID(OBJ_ID_ROOT, KEY_PARAMS, TYPE_MAP) == OBJ_ID_PARAMS, "object id mismatch"); + ctx.require(getObjectID(OBJ_ID_ROOT, KEY_RESULTS, TYPE_MAP) == OBJ_ID_RESULTS, "object id mismatch"); + + if ((index & 0x8000) != 0) { + // immutable view function, invoke with a view context + let view = views[index & 0x7fff]; + view(new ScViewContext()); + return; + } + + // mutable full function, invoke with a func context + let func = funcs[index]; + func(ctx); +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// context for onLoad function to be able to tell host which +// funcs and views are available as entry points to the SC +export class ScExports { + exports: ScMutableStringArray; + + // constructs the symbol export context for the onLoad function + constructor() { + this.exports = ROOT.getStringArray(KEY_EXPORTS); + // tell host what value our special predefined key is + // this helps detect versioning problems between host + // and client versions of WasmLib + this.exports.getString(KEY_ZZZZZZZ.keyID).setValue("TypeScript:KEY_ZZZZZZZ"); + } + + // defines the external name of a smart contract func + // and the entry point function associated with it + addFunc(name: string, f: ScFuncContextFunc): void { + let index = funcs.length; + funcs.push(f); + this.exports.getString(index).setValue(name); + } + + // defines the external name of a smart contract view + // and the entry point function associated with it + addView(name: string, v: ScViewContextFunc): void { + let index = views.length as i32; + views.push(v); + this.exports.getString(index | 0x8000).setValue(name); + } +} + diff --git a/packages/vm/wasmlib/ts/wasmlib/hashtypes.ts b/packages/vm/wasmlib/ts/wasmlib/hashtypes.ts new file mode 100644 index 0000000000..98d65a0324 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/hashtypes.ts @@ -0,0 +1,332 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// standard value types used by the ISCP + +import {base58Encode, ScFuncContext} from "./context"; +import {Convert} from "./convert"; +import {getKeyIDFromBytes, panic} from "./host"; +import {Key32, MapKey} from "./keys"; + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +function zeroes(count: i32): u8[] { + let buf : u8[] = new Array(count); + buf.fill(0); + return buf; +} + +// value object for 33-byte Tangle address ids +export class ScAddress implements MapKey { + id: u8[] = zeroes(33); + + // construct from byte array + static fromBytes(bytes: u8[]): ScAddress { + let o = new ScAddress(); + if (bytes.length != o.id.length) { + panic("invalid address id length"); + } + o.id = bytes.slice(0); + return o; + } + + // returns agent id representation of this Tangle address + asAgentID(): ScAgentID { + return ScAgentID.fromParts(this, ScHname.zero); + } + + equals(other: ScAddress): boolean { + return Convert.equals(this.id, other.id); + } + + // can be used as key in maps + getKeyID(): Key32 { + return getKeyIDFromBytes(this.toBytes()); + } + + // convert to byte array representation + toBytes(): u8[] { + return this.id; + } + + // human-readable string representation + toString(): string { + return base58Encode(this.id); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value object for 37-byte agent ids +export class ScAgentID implements MapKey { + id: u8[] = zeroes(37); + + // construct from byte array + static fromBytes(bytes: u8[]): ScAgentID { + let o = new ScAgentID(); + if (bytes.length != o.id.length) { + panic("invalid agent id length"); + } + o.id = bytes.slice(0); + return o; + } + + // construct from address and contract name hash + static fromParts(address: ScAddress, hname: ScHname): ScAgentID { + let agentID = new ScAgentID(); + agentID.id = address.id.concat(hname.id); + return agentID; + } + + // gets Tangle address from agent id + address(): ScAddress { + let address = new ScAddress(); + address.id = this.id.slice(0, address.id.length); + return address; + } + + equals(other: ScAgentID): boolean { + return Convert.equals(this.id, other.id); + } + + // can be used as key in maps + getKeyID(): Key32 { + return getKeyIDFromBytes(this.toBytes()); + } + + // get contract name hash for this agent + hname(): ScHname { + let hname = new ScHname(0); + hname.id = this.id.slice(this.id.length - hname.id.length); + return hname; + } + + // checks to see if agent id represents a Tangle address + isAddress(): boolean { + return this.hname().equals(ScHname.zero); + } + + // convert to byte array representation + toBytes(): u8[] { + return this.id; + } + + // human-readable string representation + toString(): string { + return base58Encode(this.id); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value object for 33-byte chain ids +export class ScChainID implements MapKey { + id: u8[] = zeroes(33); + + // construct from byte array + static fromBytes(bytes: u8[]): ScChainID { + let o = new ScChainID(); + if (bytes.length != o.id.length) { + panic("invalid chain id length"); + } + o.id = bytes.slice(0); + return o; + } + + // gets Tangle address from chain id + address(): ScAddress { + let address = new ScAddress(); + address.id = this.id.slice(0, address.id.length); + return address; + } + + equals(other: ScChainID): boolean { + return Convert.equals(this.id, other.id); + } + + // can be used as key in maps + getKeyID(): Key32 { + return getKeyIDFromBytes(this.toBytes()); + } + + // convert to byte array representation + toBytes(): u8[] { + return this.id; + } + + // human-readable string representation + toString(): string { + return base58Encode(this.id); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value object for 32-byte token color +export class ScColor implements MapKey { + id: u8[] = new Array(32); + + // predefined colors + static IOTA: ScColor = new ScColor(0x00); + static MINT: ScColor = new ScColor(0xff); + + constructor(fill: u8) { + this.id.fill(fill) + } + + // construct from byte array + static fromBytes(bytes: u8[]): ScColor { + let o = new ScColor(0); + if (bytes.length != o.id.length) { + panic("invalid color id length"); + } + o.id = bytes.slice(0); + return o; + } + + // construct from request id, this will return newly minted color + static fromRequestID(requestID: ScRequestID): ScColor { + let color = new ScColor(0); + color.id = requestID.id.slice(0, color.id.length) + return color; + } + + equals(other: ScColor): boolean { + return Convert.equals(this.id, other.id); + } + + // can be used as key in maps + getKeyID(): Key32 { + return getKeyIDFromBytes(this.toBytes()); + } + + // convert to byte array representation + toBytes(): u8[] { + return this.id; + } + + // human-readable string representation + toString(): string { + return base58Encode(this.id); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value object for 32-byte hash value +export class ScHash implements MapKey { + id: u8[] = zeroes(32); + + // construct from byte array + static fromBytes(bytes: u8[]): ScHash { + let o = new ScHash(); + if (bytes.length != o.id.length) { + panic("invalid hash id length"); + } + o.id = bytes.slice(0); + return o; + } + + equals(other: ScHash): boolean { + return Convert.equals(this.id, other.id); + } + + // can be used as key in maps + getKeyID(): Key32 { + return getKeyIDFromBytes(this.toBytes()); + } + + // convert to byte array representation + toBytes(): u8[] { + return this.id; + } + + // human-readable string representation + toString(): string { + return base58Encode(this.id); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value object for 4-byte name hash +export class ScHname implements MapKey { + id: u8[] = zeroes(4) + + static zero: ScHname = new ScHname(0); + + // construct from name string + constructor(id: u32) { + this.id = Convert.fromI32(id); + } + + static fromName(name: string): ScHname { + return new ScFuncContext().utility().hname(name); + } + + // construct from byte array + static fromBytes(bytes: u8[]): ScHname { + let o = new ScHname(0); + if (bytes.length != o.id.length) { + panic("invalid hname length"); + } + o.id = bytes.slice(0); + return o; + } + + equals(other: ScHname): boolean { + return Convert.equals(this.id, other.id); + } + + // can be used as key in maps + getKeyID(): Key32 { + return getKeyIDFromBytes(this.id); + } + + // convert to byte array representation + toBytes(): u8[] { + return this.id; + } + + // human-readable string representation + toString(): string { + let id = Convert.toI32(this.id); + return id.toString(); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value object for 34-byte transaction request ids +export class ScRequestID implements MapKey { + id: u8[] = zeroes(34); + + // construct from byte array + static fromBytes(bytes: u8[]): ScRequestID { + let o = new ScRequestID(); + if (bytes.length != o.id.length) { + panic("invalid request id length"); + } + o.id = bytes.slice(0); + return o; + } + + equals(other: ScRequestID): boolean { + return Convert.equals(this.id, other.id); + } + + // can be used as key in maps + getKeyID(): Key32 { + return getKeyIDFromBytes(this.toBytes()); + } + + // convert to byte array representation + toBytes(): u8[] { + return this.id; + } + + // human-readable string representation + toString(): string { + return base58Encode(this.id); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/host.ts b/packages/vm/wasmlib/ts/wasmlib/host.ts new file mode 100644 index 0000000000..426741ae3a --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/host.ts @@ -0,0 +1,173 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// interface WasmLib to the VM host + +// all type id values should exactly match their counterpart values on the host! + +import {Key32} from "./keys"; +import * as keys from "./keys"; +import {Convert} from "./convert"; + +export const TYPE_ARRAY: i32 = 0x20; +export const TYPE_ARRAY16: i32 = 0x30; +export const TYPE_CALL: i32 = 0x40; + +export const TYPE_ADDRESS: i32 = 1; +export const TYPE_AGENT_ID: i32 = 2; +export const TYPE_BYTES: i32 = 3; +export const TYPE_CHAIN_ID: i32 = 4; +export const TYPE_COLOR: i32 = 5; +export const TYPE_HASH: i32 = 6; +export const TYPE_HNAME: i32 = 7; +export const TYPE_INT16: i32 = 8; +export const TYPE_INT32: i32 = 9; +export const TYPE_INT64: i32 = 10; +export const TYPE_MAP: i32 = 11; +export const TYPE_REQUEST_ID: i32 = 12; +export const TYPE_STRING: i32 = 13; + +export const OBJ_ID_NULL: i32 = 0; +export const OBJ_ID_ROOT: i32 = 1; +export const OBJ_ID_STATE: i32 = 2; +export const OBJ_ID_PARAMS: i32 = 3; +export const OBJ_ID_RESULTS: i32 = 4; + +// size in bytes of predefined types, indexed by the TYPE_* consts +const TYPE_SIZES: u8[] = [0, 33, 37, 0, 33, 32, 32, 4, 2, 4, 8, 0, 34, 0]; + + +// These 4 external functions are funneling the entire WasmLib functionality +// to their counterparts on the host. + +// Copy the value data bytes of type stored in the host container object , +// under key , into the pre-allocated which can hold len bytes. +// Returns the actual length of the value data bytes on the host. +@external("WasmLib", "hostGetBytes") +export declare function hostGetBytes(objID: i32, keyID: i32, typeID: i32, buffer: usize, size: i32): i32; + +// Retrieve the key id associated with the data bytes of length . +// A negative length indicates a bytes key, positive indicates a string key +// We discern between the two for better readable logging purposes +@external("WasmLib", "hostGetKeyID") +export declare function hostGetKeyID(key: usize, size: i32): i32; + +// Retrieve the id of the container sub-object of type stored in +// the host container object , under key . +@external("WasmLib", "hostGetObjectID") +export declare function hostGetObjectID(objID: i32, keyID: i32, typeID: i32): i32; + +// copy the value data bytes of type from the +// into the host container object , under key . +@external("WasmLib", "hostSetBytes") +export declare function hostSetBytes(objID: i32, keyID: i32, typeID: i32, buffer: usize, size: i32):void; + + +export function callFunc(objID: i32, keyID: Key32, params: u8[]): u8[] { + // variable-sized type, first query expected length of bytes array + // (pass zero-length buffer) + let size = hostGetBytes(objID, keyID.keyID, TYPE_CALL, params.dataStart, params.length); + + // -1 means non-existent, so return default value for type + if (size <= 0) { + return []; + } + + // allocate a sufficient length byte array in Wasm memory + // and let the host copy the actual data bytes into this Wasm byte array + let result:u8[] = new Array(size); + hostGetBytes(objID, keyID.keyID, TYPE_CALL + 1, result.dataStart, size); + return result; +} + +// Clear the entire contents of the specified container object. +// Removes all its sub-objects as well. +export function clear(objID: i32): void { + // special key "length" is used with integer value zero + setBytes(objID, keys.KEY_LENGTH, TYPE_INT32, Convert.fromI32(0)) +} + +// Check if the specified container object contains a value with the specified key and type. +export function exists(objID: i32, keyID: Key32, typeID: i32): boolean { + // negative length (-1) means only test for existence + // returned size -1 indicates keyID not found (or error) + // this removes the need for a separate hostExists function + return hostGetBytes(objID, keyID.keyID, typeID, 0, -1) >= 0; +} + +// Retrieve the bytes stored in the specified container object under the specified key +// and with specified type. Note that if the key does not exist this function will +// return the default value for the specified type. +export function getBytes(objID: i32, keyID: Key32, typeID: i32): u8[] { + let size = TYPE_SIZES[typeID] as i32; + if (size == 0) { + // variable-sized type, first query expected length of bytes array + // (pass zero-length buffer) + size = hostGetBytes(objID, keyID.keyID, typeID, 0, 0); + + // -1 means non-existent, so return default value for type + if (size < 0) { + return []; + } + } + + // allocate a sufficient length byte array in Wasm memory + // and let the host copy the actual data bytes into this Wasm byte array + let result:u8[] = new Array(size); + hostGetBytes(objID, keyID.keyID, typeID, result.dataStart, size); + return result; +} + +// Retrieve the key id that the host has associated with the specified bytes key +export function getKeyID(bytes: u8[], size:i32): Key32 { + // negative size indicates this is a bytes key + return new Key32(hostGetKeyID(bytes.dataStart, size)); +} + +// Retrieve the key id that the host has associated with the specified bytes key +export function getKeyIDFromBytes(bytes: u8[]): Key32 { + // negative size indicates this is a bytes key + return getKeyID(bytes, -bytes.length - 1); +} + +// Retrieve the key id that the host has associated with the specified string key +export function getKeyIDFromString(key: string): Key32 { + let bytes = Convert.fromString(key); + + // non-negative size indicates this is a string key + return getKeyID(bytes, bytes.length); +} + +// Retrieve the length of an array container object on the host +export function getLength(objID: i32): i32 { + // special integer key "length" is used + let bytes = getBytes(objID, keys.KEY_LENGTH, TYPE_INT32); + return Convert.toI32(bytes); +} + +// Retrieve the id of the specified container sub-object +export function getObjectID(objID: i32, keyID: Key32, typeID: i32): i32 { + return hostGetObjectID(objID, keyID.keyID, typeID); +} + +// Direct logging of informational text to host log +export function log(text: string): void { + setBytes(1, keys.KEY_LOG, TYPE_STRING, Convert.fromString(text)) +} + +// Direct logging of error to host log, followed by panicking out of the Wasm code +export function panic(text: string): void { + setBytes(1, keys.KEY_PANIC, TYPE_STRING, Convert.fromString(text)) +} + +// Store the provided value bytes of specified type in the specified container object +// under the specified key. Note that if the key does not exist this function will +// create it first. +export function setBytes(objID: i32, keyID: Key32, typeID: i32, value: u8[]): void { + return hostSetBytes(objID, keyID.keyID, typeID, value.dataStart, value.length); +} + +// Direct logging of debug trace text to host log +export function trace(text: string): void { + setBytes(1, keys.KEY_TRACE, TYPE_STRING, Convert.fromString(text)) +} diff --git a/packages/vm/wasmlib/ts/wasmlib/immutable.ts b/packages/vm/wasmlib/ts/wasmlib/immutable.ts new file mode 100644 index 0000000000..104b77c43f --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/immutable.ts @@ -0,0 +1,784 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// immutable proxies to host objects + +import { base58Encode } from "./context"; +import {Convert} from "./convert"; +import {ScAddress,ScAgentID,ScChainID,ScColor,ScHash,ScHname,ScRequestID} from "./hashtypes"; +import * as host from "./host"; +import {callFunc, exists, getBytes, getLength, getObjectID} from "./host"; +import {Key32,MapKey} from "./keys"; + +// value proxy for immutable ScAddress in host container +export class ScImmutableAddress { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_ADDRESS); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): ScAddress { + return ScAddress.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_ADDRESS)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of ScAddress +export class ScImmutableAddressArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getAddress(index: i32): ScImmutableAddress { + return new ScImmutableAddress(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable ScAgentID in host container +export class ScImmutableAgentID { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_AGENT_ID); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): ScAgentID { + return ScAgentID.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_AGENT_ID)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of ScAgentID +export class ScImmutableAgentIDArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getAgentID(index: i32): ScImmutableAgentID { + return new ScImmutableAgentID(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable bytes array in host container +export class ScImmutableBytes { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_BYTES); + } + + // human-readable string representation + toString(): string { + return base58Encode(this.value()); + } + + // get value from host container + value(): u8[] { + return getBytes(this.objID, this.keyID, host.TYPE_BYTES); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of byte array +export class ScImmutableBytesArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getBytes(index: i32): ScImmutableBytes { + return new ScImmutableBytes(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable ScChainID in host container +export class ScImmutableChainID { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_CHAIN_ID); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): ScChainID { + return ScChainID.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_CHAIN_ID)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of ScChainID +export class ScImmutableChainIDArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getChainID(index: i32): ScImmutableChainID { + return new ScImmutableChainID(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable ScColor in host container +export class ScImmutableColor { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_COLOR); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): ScColor { + return ScColor.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_COLOR)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of ScColor +export class ScImmutableColorArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getColor(index: i32): ScImmutableColor { + return new ScImmutableColor(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable ScHash in host container +export class ScImmutableHash { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_HASH); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): ScHash { + return ScHash.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_HASH)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of ScHash +export class ScImmutableHashArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getHash(index: i32): ScImmutableHash { + return new ScImmutableHash(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable ScHname in host container +export class ScImmutableHname { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_HNAME); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): ScHname { + return ScHname.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_HNAME)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of ScHname +export class ScImmutableHnameArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getHname(index: i32): ScImmutableHname { + return new ScImmutableHname(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable int16 in host container +export class ScImmutableInt16 { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_INT16); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): i16 { + let bytes = getBytes(this.objID, this.keyID, host.TYPE_INT16); + return Convert.toI16(bytes); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of int16 +export class ScImmutableInt16Array { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getInt16(index: i32): ScImmutableInt16 { + return new ScImmutableInt16(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable int32 in host container +export class ScImmutableInt32 { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_INT32); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): i32 { + let bytes = getBytes(this.objID, this.keyID, host.TYPE_INT32); + return Convert.toI32(bytes); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of int32 +export class ScImmutableInt32Array { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getInt32(index: i32): ScImmutableInt32 { + return new ScImmutableInt32(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable int64 in host container +export class ScImmutableInt64 { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_INT64); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): i64 { + let bytes = getBytes(this.objID, this.keyID, host.TYPE_INT64); + return Convert.toI64(bytes); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of int64 +export class ScImmutableInt64Array { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getInt64(index: i32): ScImmutableInt64 { + return new ScImmutableInt64(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// map proxy for immutable map +export class ScImmutableMap { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + callFunc(keyID: Key32, params: u8[]): u8[] { + return callFunc(this.objID, keyID, params); + } + + // get value proxy for immutable ScAddress field specified by key + getAddress(key: MapKey): ScImmutableAddress { + return new ScImmutableAddress(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableAddressArray specified by key + getAddressArray(key: MapKey): ScImmutableAddressArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_ADDRESS | host.TYPE_ARRAY); + return new ScImmutableAddressArray(arrID); + } + + // get value proxy for immutable ScAgentID field specified by key + getAgentID(key: MapKey): ScImmutableAgentID { + return new ScImmutableAgentID(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableAgentIDArray specified by key + getAgentIDArray(key: MapKey): ScImmutableAgentIDArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_AGENT_ID | host.TYPE_ARRAY); + return new ScImmutableAgentIDArray(arrID); + } + + // get value proxy for immutable bytes array field specified by key + getBytes(key: MapKey): ScImmutableBytes { + return new ScImmutableBytes(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableBytesArray specified by key + getBytesArray(key: MapKey): ScImmutableBytesArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_BYTES | host.TYPE_ARRAY); + return new ScImmutableBytesArray(arrID); + } + + // get value proxy for immutable ScChainID field specified by key + getChainID(key: MapKey): ScImmutableChainID { + return new ScImmutableChainID(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableChainIDArray specified by key + getChainIDArray(key: MapKey): ScImmutableChainIDArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_CHAIN_ID | host.TYPE_ARRAY); + return new ScImmutableChainIDArray(arrID); + } + + // get value proxy for immutable ScColor field specified by key + getColor(key: MapKey): ScImmutableColor { + return new ScImmutableColor(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableColorArray specified by key + getColorArray(key: MapKey): ScImmutableColorArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_COLOR | host.TYPE_ARRAY); + return new ScImmutableColorArray(arrID); + } + + // get value proxy for immutable ScHash field specified by key + getHash(key: MapKey): ScImmutableHash { + return new ScImmutableHash(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableHashArray specified by key + getHashArray(key: MapKey): ScImmutableHashArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_HASH | host.TYPE_ARRAY); + return new ScImmutableHashArray(arrID); + } + + // get value proxy for immutable ScHname field specified by key + getHname(key: MapKey): ScImmutableHname { + return new ScImmutableHname(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableHnameArray specified by key + getHnameArray(key: MapKey): ScImmutableHnameArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_HNAME | host.TYPE_ARRAY); + return new ScImmutableHnameArray(arrID); + } + + // get value proxy for immutable int16 field specified by key + getInt16(key: MapKey): ScImmutableInt16 { + return new ScImmutableInt16(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableInt16Array specified by key + getInt16Array(key: MapKey): ScImmutableInt16Array { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_INT16 | host.TYPE_ARRAY); + return new ScImmutableInt16Array(arrID); + } + + // get value proxy for immutable int32 field specified by key + getInt32(key: MapKey): ScImmutableInt32 { + return new ScImmutableInt32(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableInt32Array specified by key + getInt32Array(key: MapKey): ScImmutableInt32Array { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_INT32 | host.TYPE_ARRAY); + return new ScImmutableInt32Array(arrID); + } + + // get value proxy for immutable int64 field specified by key + getInt64(key: MapKey): ScImmutableInt64 { + return new ScImmutableInt64(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableInt64Array specified by key + getInt64Array(key: MapKey): ScImmutableInt64Array { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_INT64 | host.TYPE_ARRAY); + return new ScImmutableInt64Array(arrID); + } + + // get map proxy for ScImmutableMap specified by key + getMap(key: MapKey): ScImmutableMap { + let mapID = getObjectID(this.objID, key.getKeyID(), host.TYPE_MAP); + return new ScImmutableMap(mapID); + } + + // get array proxy for ScImmutableMapArray specified by key + getMapArray(key: MapKey): ScImmutableMapArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_MAP | host.TYPE_ARRAY); + return new ScImmutableMapArray(arrID); + } + + // get value proxy for immutable ScRequestID field specified by key + getRequestID(key: MapKey): ScImmutableRequestID { + return new ScImmutableRequestID(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableRequestIDArray specified by key + getRequestIDArray(key: MapKey): ScImmutableRequestIDArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_REQUEST_ID | host.TYPE_ARRAY); + return new ScImmutableRequestIDArray(arrID); + } + + // get value proxy for immutable UTF-8 text string field specified by key + getString(key: MapKey): ScImmutableString { + return new ScImmutableString(this.objID, key.getKeyID()); + } + + // get array proxy for ScImmutableStringArray specified by key + getStringArray(key: MapKey): ScImmutableStringArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_STRING | host.TYPE_ARRAY); + return new ScImmutableStringArray(arrID); + } + + mapID(): i32 { + return this.objID; + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of maps +export class ScImmutableMapArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getMap(index: i32): ScImmutableMap { + let mapID = getObjectID(this.objID, new Key32(index), host.TYPE_MAP); + return new ScImmutableMap(mapID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// value proxy for immutable ScRequestID in host container +export class ScImmutableRequestID { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_REQUEST_ID); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // get value from host container + value(): ScRequestID { + return ScRequestID.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_REQUEST_ID)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of ScRequestID +export class ScImmutableRequestIDArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getRequestID(index: i32): ScImmutableRequestID { + return new ScImmutableRequestID(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for immutable UTF-8 text string in host container +export class ScImmutableString { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_STRING); + } + + // human-readable string representation + toString(): string { + return this.value(); + } + + // get value from host container + value(): string { + let bytes = getBytes(this.objID, this.keyID, host.TYPE_STRING); + return Convert.toString(bytes); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for immutable array of UTF-8 text string +export class ScImmutableStringArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // get value proxy for item at index, index can be 0..length()-1 + getString(index: i32): ScImmutableString { + return new ScImmutableString(this.objID, new Key32(index)); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/index.ts b/packages/vm/wasmlib/ts/wasmlib/index.ts new file mode 100644 index 0000000000..15311b1cb6 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/index.ts @@ -0,0 +1,10 @@ +export * from "./bytes" +export * from "./context" +export * from "./contract" +export * from "./convert" +export * from "./exports" +export * from "./hashtypes" +export * from "./host" +export * from "./immutable" +export * from "./keys" +export * from "./mutable" diff --git a/packages/vm/wasmlib/ts/wasmlib/keys.ts b/packages/vm/wasmlib/ts/wasmlib/keys.ts new file mode 100644 index 0000000000..7e1b25601c --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/keys.ts @@ -0,0 +1,75 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// conversion of data bytes to key id + +import {getKeyIDFromString} from "./host"; + +export interface MapKey { + getKeyID(): Key32; +} + +export class Key32 implements MapKey { + keyID: i32; + + constructor(keyID: i32) { + this.keyID = keyID; + } + + static fromString(key: string): Key32 { + return getKeyIDFromString(key); + } + + getKeyID(): Key32 { + return this; + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// all predefined key id values should exactly match their counterpart values on the host! +// note that predefined key ids are negative values to distinguish them from indexes + +// @formatter:off +export const KEY_ACCOUNT_ID = new Key32(-1); +export const KEY_ADDRESS = new Key32(-2); +export const KEY_BALANCES = new Key32(-3); +export const KEY_BASE58_DECODE = new Key32(-4); +export const KEY_BASE58_ENCODE = new Key32(-5); +export const KEY_BLS_ADDRESS = new Key32(-6); +export const KEY_BLS_AGGREGATE = new Key32(-7); +export const KEY_BLS_VALID = new Key32(-8); +export const KEY_CALL = new Key32(-9); +export const KEY_CALLER = new Key32(-10); +export const KEY_CHAIN_ID = new Key32(-11); +export const KEY_CHAIN_OWNER_ID = new Key32(-12); +export const KEY_COLOR = new Key32(-13); +export const KEY_CONTRACT = new Key32(-14); +export const KEY_CONTRACT_CREATOR = new Key32(-15); +export const KEY_DEPLOY = new Key32(-16); +export const KEY_ED25519_ADDRESS = new Key32(-17); +export const KEY_ED25519_VALID = new Key32(-18); +export const KEY_EVENT = new Key32(-19); +export const KEY_EXPORTS = new Key32(-20); +export const KEY_HASH_BLAKE2B = new Key32(-21); +export const KEY_HASH_SHA3 = new Key32(-22); +export const KEY_HNAME = new Key32(-23); +export const KEY_INCOMING = new Key32(-24); +export const KEY_LENGTH = new Key32(-25); +export const KEY_LOG = new Key32(-26); +export const KEY_MAPS = new Key32(-27); +export const KEY_MINTED = new Key32(-28); +export const KEY_PANIC = new Key32(-29); +export const KEY_PARAMS = new Key32(-30); +export const KEY_POST = new Key32(-31); +export const KEY_RANDOM = new Key32(-32); +export const KEY_REQUEST_ID = new Key32(-33); +export const KEY_RESULTS = new Key32(-34); +export const KEY_RETURN = new Key32(-35); +export const KEY_STATE = new Key32(-36); +export const KEY_TIMESTAMP = new Key32(-37); +export const KEY_TRACE = new Key32(-38); +export const KEY_TRANSFERS = new Key32(-39); +export const KEY_UTILITY = new Key32(-40); +export const KEY_ZZZZZZZ = new Key32(-41); +// @formatter:on diff --git a/packages/vm/wasmlib/ts/wasmlib/mutable.ts b/packages/vm/wasmlib/ts/wasmlib/mutable.ts new file mode 100644 index 0000000000..0162310e97 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/mutable.ts @@ -0,0 +1,1020 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// mutable proxies to host objects + +import {base58Encode, ROOT} from "./context"; +import {Convert} from "./convert"; +import {ScAddress, ScAgentID, ScChainID, ScColor, ScHash, ScHname, ScRequestID} from "./hashtypes"; +import * as host from "./host"; +import {callFunc, clear, exists, getBytes, getLength, getObjectID, setBytes} from "./host"; +import { + ScImmutableAddressArray, + ScImmutableAgentIDArray, + ScImmutableBytesArray, + ScImmutableChainIDArray, + ScImmutableColorArray, + ScImmutableHashArray, + ScImmutableHnameArray, + ScImmutableInt16Array, + ScImmutableInt32Array, + ScImmutableInt64Array, + ScImmutableMap, + ScImmutableMapArray, + ScImmutableRequestIDArray, + ScImmutableStringArray +} from "./immutable"; +import {Key32, KEY_MAPS, MapKey} from "./keys"; + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable ScAddress in host container +export class ScMutableAddress { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_ADDRESS); + } + + // set value in host container + setValue(val: ScAddress): void { + setBytes(this.objID, this.keyID, host.TYPE_ADDRESS, val.toBytes()); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): ScAddress { + return ScAddress.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_ADDRESS)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of ScAddress +export class ScMutableAddressArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getAddress(index: i32): ScMutableAddress { + return new ScMutableAddress(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableAddressArray { + return new ScImmutableAddressArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable ScAgentID in host container +export class ScMutableAgentID { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_AGENT_ID); + } + + // set value in host container + setValue(val: ScAgentID): void { + setBytes(this.objID, this.keyID, host.TYPE_AGENT_ID, val.toBytes()); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): ScAgentID { + return ScAgentID.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_AGENT_ID)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of ScAgentID +export class ScMutableAgentIDArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getAgentID(index: i32): ScMutableAgentID { + return new ScMutableAgentID(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableAgentIDArray { + return new ScImmutableAgentIDArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable bytes array in host container +export class ScMutableBytes { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_BYTES); + } + + // set value in host container + setValue(val: u8[]): void { + setBytes(this.objID, this.keyID, host.TYPE_BYTES, val); + } + + // human-readable string representation + toString(): string { + return base58Encode(this.value()); + } + + // retrieve value from host container + value(): u8[] { + return getBytes(this.objID, this.keyID, host.TYPE_BYTES); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of byte array +export class ScMutableBytesArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getBytes(index: i32): ScMutableBytes { + return new ScMutableBytes(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableBytesArray { + return new ScImmutableBytesArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable ScChainID in host container +export class ScMutableChainID { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_CHAIN_ID); + } + + // set value in host container + setValue(val: ScChainID): void { + setBytes(this.objID, this.keyID, host.TYPE_CHAIN_ID, val.toBytes()); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): ScChainID { + return ScChainID.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_CHAIN_ID)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of ScChainID +export class ScMutableChainIDArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getChainID(index: i32): ScMutableChainID { + return new ScMutableChainID(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableChainIDArray { + return new ScImmutableChainIDArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable ScColor in host container +export class ScMutableColor { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_COLOR); + } + + // set value in host container + setValue(val: ScColor): void { + setBytes(this.objID, this.keyID, host.TYPE_COLOR, val.toBytes()); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): ScColor { + return ScColor.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_COLOR)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of ScColor +export class ScMutableColorArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getColor(index: i32): ScMutableColor { + return new ScMutableColor(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableColorArray { + return new ScImmutableColorArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable ScHash in host container +export class ScMutableHash { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_HASH); + } + + // set value in host container + setValue(val: ScHash): void { + setBytes(this.objID, this.keyID, host.TYPE_HASH, val.toBytes()); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): ScHash { + return ScHash.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_HASH)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of ScHash +export class ScMutableHashArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getHash(index: i32): ScMutableHash { + return new ScMutableHash(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableHashArray { + return new ScImmutableHashArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable ScHname in host container +export class ScMutableHname { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_HNAME); + } + + // set value in host container + setValue(val: ScHname): void { + setBytes(this.objID, this.keyID, host.TYPE_HNAME, val.toBytes()); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): ScHname { + return ScHname.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_HNAME)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of ScHname +export class ScMutableHnameArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getHname(index: i32): ScMutableHname { + return new ScMutableHname(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableHnameArray { + return new ScImmutableHnameArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable int16 in host container +export class ScMutableInt16 { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_INT16); + } + + // set value in host container + setValue(val: i16): void { + setBytes(this.objID, this.keyID, host.TYPE_INT16, Convert.fromI16(val)); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): i16 { + return Convert.toI16(getBytes(this.objID, this.keyID, host.TYPE_INT16)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of int16 +export class ScMutableInt16Array { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getInt16(index: i32): ScMutableInt16 { + return new ScMutableInt16(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableInt16Array { + return new ScImmutableInt16Array(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable int32 in host container +export class ScMutableInt32 { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_INT32); + } + + // set value in host container + setValue(val: i32): void { + setBytes(this.objID, this.keyID, host.TYPE_INT32, Convert.fromI32(val)); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): i32 { + return Convert.toI32(getBytes(this.objID, this.keyID, host.TYPE_INT32)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of int32 +export class ScMutableInt32Array { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getInt32(index: i32): ScMutableInt32 { + return new ScMutableInt32(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableInt32Array { + return new ScImmutableInt32Array(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable int64 in host container +export class ScMutableInt64 { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_INT64); + } + + // set value in host container + setValue(val: i64): void { + setBytes(this.objID, this.keyID, host.TYPE_INT64, Convert.fromI64(val)); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): i64 { + return Convert.toI64(getBytes(this.objID, this.keyID, host.TYPE_INT64)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of int64 +export class ScMutableInt64Array { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getInt64(index: i32): ScMutableInt64 { + return new ScMutableInt64(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableInt64Array { + return new ScImmutableInt64Array(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// map proxy for mutable map +export class ScMutableMap { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // construct a new map on the host and return a map proxy for it + static create(): ScMutableMap { + let maps = ROOT.getMapArray(KEY_MAPS); + return maps.getMap(maps.length()); + } + + callFunc(keyID: Key32, params: u8[]): u8[] { + return callFunc(this.objID, keyID, params); + } + + // empty the map + clear(): void { + clear(this.objID); + } + + // get value proxy for mutable ScAddress field specified by key + getAddress(key: MapKey): ScMutableAddress { + return new ScMutableAddress(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableAddressArray specified by key + getAddressArray(key: MapKey): ScMutableAddressArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_ADDRESS | host.TYPE_ARRAY); + return new ScMutableAddressArray(arrID); + } + + // get value proxy for mutable ScAgentID field specified by key + getAgentID(key: MapKey): ScMutableAgentID { + return new ScMutableAgentID(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableAgentIDArray specified by key + getAgentIDArray(key: MapKey): ScMutableAgentIDArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_AGENT_ID | host.TYPE_ARRAY); + return new ScMutableAgentIDArray(arrID); + } + + // get value proxy for mutable bytes array field specified by key + getBytes(key: MapKey): ScMutableBytes { + return new ScMutableBytes(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableBytesArray specified by key + getBytesArray(key: MapKey): ScMutableBytesArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_BYTES | host.TYPE_ARRAY); + return new ScMutableBytesArray(arrID); + } + + // get value proxy for mutable ScChainID field specified by key + getChainID(key: MapKey): ScMutableChainID { + return new ScMutableChainID(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableChainIDArray specified by key + getChainIDArray(key: MapKey): ScMutableChainIDArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_CHAIN_ID | host.TYPE_ARRAY); + return new ScMutableChainIDArray(arrID); + } + + // get value proxy for mutable ScColor field specified by key + getColor(key: MapKey): ScMutableColor { + return new ScMutableColor(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableColorArray specified by key + getColorArray(key: MapKey): ScMutableColorArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_COLOR | host.TYPE_ARRAY); + return new ScMutableColorArray(arrID); + } + + // get value proxy for mutable ScHash field specified by key + getHash(key: MapKey): ScMutableHash { + return new ScMutableHash(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableHashArray specified by key + getHashArray(key: MapKey): ScMutableHashArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_HASH | host.TYPE_ARRAY); + return new ScMutableHashArray(arrID); + } + + // get value proxy for mutable ScHname field specified by key + getHname(key: MapKey): ScMutableHname { + return new ScMutableHname(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableHnameArray specified by key + getHnameArray(key: MapKey): ScMutableHnameArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_HNAME | host.TYPE_ARRAY); + return new ScMutableHnameArray(arrID); + } + + // get value proxy for mutable int16 field specified by key + getInt16(key: MapKey): ScMutableInt16 { + return new ScMutableInt16(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableInt16Array specified by key + getInt16Array(key: MapKey): ScMutableInt16Array { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_INT16 | host.TYPE_ARRAY); + return new ScMutableInt16Array(arrID); + } + + // get value proxy for mutable int64 field specified by key + getInt64(key: MapKey): ScMutableInt64 { + return new ScMutableInt64(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableInt32Array specified by key + getInt32Array(key: MapKey): ScMutableInt32Array { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_INT32 | host.TYPE_ARRAY); + return new ScMutableInt32Array(arrID); + } + + // get value proxy for mutable int32 field specified by key + getInt32(key: MapKey): ScMutableInt32 { + return new ScMutableInt32(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableInt64Array specified by key + getInt64Array(key: MapKey): ScMutableInt64Array { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_INT64 | host.TYPE_ARRAY); + return new ScMutableInt64Array(arrID); + } + + // get map proxy for ScMutableMap specified by key + getMap(key: MapKey): ScMutableMap { + let mapID = getObjectID(this.objID, key.getKeyID(), host.TYPE_MAP); + return new ScMutableMap(mapID); + } + + // get array proxy for ScMutableMapArray specified by key + getMapArray(key: MapKey): ScMutableMapArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_MAP | host.TYPE_ARRAY); + return new ScMutableMapArray(arrID); + } + + // get value proxy for mutable ScRequestID field specified by key + getRequestID(key: MapKey): ScMutableRequestID { + return new ScMutableRequestID(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableRequestIDArray specified by key + getRequestIDArray(key: MapKey): ScMutableRequestIDArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_REQUEST_ID | host.TYPE_ARRAY); + return new ScMutableRequestIDArray(arrID); + } + + // get value proxy for mutable UTF-8 text string field specified by key + getString(key: MapKey): ScMutableString { + return new ScMutableString(this.objID, key.getKeyID()); + } + + // get array proxy for ScMutableStringArray specified by key + getStringArray(key: MapKey): ScMutableStringArray { + let arrID = getObjectID(this.objID, key.getKeyID(), host.TYPE_STRING | host.TYPE_ARRAY); + return new ScMutableStringArray(arrID); + } + + // get immutable version of map proxy + immutable(): ScImmutableMap { + return new ScImmutableMap(this.objID); + } + + mapID(): i32 { + return this.objID; + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of maps +export class ScMutableMapArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getMap(index: i32): ScMutableMap { + let mapID = getObjectID(this.objID, new Key32(index), host.TYPE_MAP); + return new ScMutableMap(mapID); + } + + // get immutable version of array proxy + immutable(): ScImmutableMapArray { + return new ScImmutableMapArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable ScRequestID in host container +export class ScMutableRequestID { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_REQUEST_ID); + } + + // set value in host container + setValue(val: ScRequestID): void { + setBytes(this.objID, this.keyID, host.TYPE_REQUEST_ID, val.toBytes()); + } + + // human-readable string representation + toString(): string { + return this.value().toString(); + } + + // retrieve value from host container + value(): ScRequestID { + return ScRequestID.fromBytes(getBytes(this.objID, this.keyID, host.TYPE_REQUEST_ID)); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of ScRequestID +export class ScMutableRequestIDArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getRequestID(index: i32): ScMutableRequestID { + return new ScMutableRequestID(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableRequestIDArray { + return new ScImmutableRequestIDArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// value proxy for mutable UTF-8 text string in host container +export class ScMutableString { + objID: i32; + keyID: Key32; + + constructor(objID: i32, keyID: Key32) { + this.objID = objID; + this.keyID = keyID; + } + + // check if value exists in host container + exists(): boolean { + return exists(this.objID, this.keyID, host.TYPE_STRING); + } + + // set value in host container + setValue(val: string): void { + setBytes(this.objID, this.keyID, host.TYPE_STRING, Convert.fromString(val)); + } + + // human-readable string representation + toString(): string { + return this.value(); + } + + // retrieve value from host container + value(): string { + let bytes = getBytes(this.objID, this.keyID, host.TYPE_STRING); + return Convert.toString(bytes); + } +} + +// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ + +// array proxy for mutable array of UTF-8 text string +export class ScMutableStringArray { + objID: i32; + + constructor(id: i32) { + this.objID = id; + } + + // empty the array + clear(): void { + clear(this.objID); + } + + // get value proxy for item at index, index can be 0..length() + // when index equals length() a new item is appended + getString(index: i32): ScMutableString { + return new ScMutableString(this.objID, new Key32(index)); + } + + // get immutable version of array proxy + immutable(): ScImmutableStringArray { + return new ScImmutableStringArray(this.objID); + } + + // number of items in array + length(): i32 { + return getLength(this.objID); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/package.json b/packages/vm/wasmlib/ts/wasmlib/package.json new file mode 100644 index 0000000000..54a27cd14a --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/package.json @@ -0,0 +1,12 @@ +{ + "name": "wasmlib", + "description": "WasmLib, interface library for ISCP Wasm VM", + "version": "1.0.0", + "author": "Eric Hop", + "dependencies": { + "@assemblyscript/loader": "^0.19.18" + }, + "devDependencies": { + "assemblyscript": "^0.19.18" + } +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/tsconfig.json new file mode 100644 index 0000000000..b51e156a90 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./*.ts" + ] +} \ No newline at end of file diff --git a/packages/vm/wasmproc/wasmcontext.go b/packages/vm/wasmproc/wasmcontext.go index af5653de50..326193746d 100644 --- a/packages/vm/wasmproc/wasmcontext.go +++ b/packages/vm/wasmproc/wasmcontext.go @@ -5,7 +5,7 @@ import ( "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/vm/wasmhost" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) const ( @@ -49,11 +49,11 @@ func NewWasmContext(function string, proc *WasmProcessor) *WasmContext { return wc } -func (wc *WasmContext) AddFunc(f func(ctx wasmlib.ScFuncContext)) []func(ctx wasmlib.ScFuncContext) { +func (wc *WasmContext) AddFunc(f wasmlib.ScFuncContextFunction) []wasmlib.ScFuncContextFunction { return wc.host.AddFunc(f) } -func (wc *WasmContext) AddView(v func(ctx wasmlib.ScViewContext)) []func(ctx wasmlib.ScViewContext) { +func (wc *WasmContext) AddView(v wasmlib.ScViewContextFunction) []wasmlib.ScViewContextFunction { return wc.host.AddView(v) } @@ -62,9 +62,9 @@ func (wc *WasmContext) Call(ctx interface{}) (dict.Dict, error) { panic("Context id is zero") } - wcSaved := wasmlib.ConnectHost(wc) + wcSaved := wasmhost.Connect(wc) defer func() { - wasmlib.ConnectHost(wcSaved) + wasmhost.Connect(wcSaved) // clean up context after use wc.proc.KillContext(wc.id) }() @@ -106,7 +106,7 @@ func (wc *WasmContext) Call(ctx interface{}) (dict.Dict, error) { wc.log().Infof("VM call %s(): error %v", wc.function, err) return nil, err } - resultsID := wc.GetObjectID(wasmlib.OBJ_ID_ROOT, wasmhost.KeyResults, wasmhost.OBJTYPE_MAP) + resultsID := wc.GetObjectID(wasmhost.OBJID_ROOT, wasmhost.KeyResults, wasmhost.OBJTYPE_MAP) results := wc.FindObject(resultsID).(*ScDict).kvStore.(dict.Dict) return results, nil } diff --git a/packages/vm/wasmproc/wasmprocessor.go b/packages/vm/wasmproc/wasmprocessor.go index cd5f9ab889..511d3d53ef 100644 --- a/packages/vm/wasmproc/wasmprocessor.go +++ b/packages/vm/wasmproc/wasmprocessor.go @@ -9,7 +9,6 @@ import ( "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/vm/wasmhost" - "github.com/iotaledger/wasp/packages/vm/wasmlib" ) type WasmProcessor struct { @@ -45,7 +44,7 @@ func GetProcessor(binaryCode []byte, log *logger.Logger) (iscp.VMProcessor, erro // TODO decide if we want be able to examine state directly from tests // proc.SetExport(0x8fff, ViewCopyAllState) proc.scContext = NewWasmContext("", proc) - wasmlib.ConnectHost(proc.scContext) + wasmhost.Connect(proc.scContext) err = proc.LoadWasm(binaryCode) if err != nil { return nil, err diff --git a/packages/vm/wasmsolo/soloagent.go b/packages/vm/wasmsolo/soloagent.go index 2c0ca05a68..a535009f3e 100644 --- a/packages/vm/wasmsolo/soloagent.go +++ b/packages/vm/wasmsolo/soloagent.go @@ -9,7 +9,7 @@ import ( "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/colored" "github.com/iotaledger/wasp/packages/solo" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/stretchr/testify/require" ) diff --git a/packages/vm/wasmsolo/solocontext.go b/packages/vm/wasmsolo/solocontext.go index 401e76c1eb..3cec105f40 100644 --- a/packages/vm/wasmsolo/solocontext.go +++ b/packages/vm/wasmsolo/solocontext.go @@ -18,7 +18,7 @@ import ( "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/util" "github.com/iotaledger/wasp/packages/vm/wasmhost" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmproc" "github.com/stretchr/testify/require" ) @@ -32,6 +32,7 @@ const ( var ( GoDebug = flag.Bool("godebug", false, "debug go smart contract code") GoWasm = flag.Bool("gowasm", false, "prefer go wasm smart contract code") + TsWasm = flag.Bool("tswasm", false, "prefer typescript wasm smart contract code") ) type SoloContext struct { @@ -240,19 +241,19 @@ func (ctx *SoloContext) init(onLoad func()) *SoloContext { ctx.wc.Init(nil) ctx.wc.TrackObject(wasmproc.NewNullObject(&ctx.wc.KvStoreHost)) ctx.wc.TrackObject(NewSoloScContext(ctx)) - ctx.wasmHostOld = wasmlib.ConnectHost(ctx.wc) + ctx.wasmHostOld = wasmhost.Connect(ctx.wc) onLoad() return ctx } // InitFuncCallContext is a function that is required to use SoloContext as an ScFuncCallContext func (ctx *SoloContext) InitFuncCallContext() { - _ = wasmlib.ConnectHost(ctx.wc) + _ = wasmhost.Connect(ctx.wc) } // InitViewCallContext is a function that is required to use SoloContext as an ScViewCallContext func (ctx *SoloContext) InitViewCallContext() { - _ = wasmlib.ConnectHost(ctx.wc) + _ = wasmhost.Connect(ctx.wc) } // Minted returns the color and amount of newly minted tokens @@ -327,14 +328,22 @@ func (ctx *SoloContext) upload(keyPair *ed25519.KeyPair) { wasmFile = "../pkg/" + wasmFile } - rustExists, _ := util.ExistsFilePath("../src/lib.rs") - if *GoWasm || !rustExists { + if *GoWasm { wasmFile = ctx.scName + "_go.wasm" - exists, _ = util.ExistsFilePath("../wasmmain/pkg/" + wasmFile) + exists, _ = util.ExistsFilePath("../go/pkg/" + wasmFile) if exists { - wasmFile = "../wasmmain/pkg/" + wasmFile + wasmFile = "../go/pkg/" + wasmFile } } + + if *TsWasm { + wasmFile = ctx.scName + "_ts.wasm" + exists, _ = util.ExistsFilePath("../ts/pkg/" + wasmFile) + if exists { + wasmFile = "../ts/pkg/" + wasmFile + } + } + ctx.Hprog, ctx.Err = ctx.Chain.UploadWasmFromFile(keyPair, wasmFile) } @@ -343,7 +352,7 @@ func (ctx *SoloContext) upload(keyPair *ed25519.KeyPair) { // The function will wait for maxWait (default 5 seconds) duration before giving up with a timeout. // The function returns the false in case of a timeout. func (ctx *SoloContext) WaitForPendingRequests(expectedRequests int, maxWait ...time.Duration) bool { - _ = wasmlib.ConnectHost(ctx.wasmHostOld) + _ = wasmhost.Connect(ctx.wasmHostOld) if expectedRequests > 0 { info := ctx.Chain.MempoolInfo() expectedRequests += info.OutPoolCounter @@ -352,6 +361,6 @@ func (ctx *SoloContext) WaitForPendingRequests(expectedRequests int, maxWait ... } result := ctx.Chain.WaitForRequestsThrough(expectedRequests, maxWait...) - _ = wasmlib.ConnectHost(ctx.wc) + _ = wasmhost.Connect(ctx.wc) return result } diff --git a/packages/vm/wasmsolo/soloconvertor.go b/packages/vm/wasmsolo/soloconvertor.go index e1bc9988c9..2225226751 100644 --- a/packages/vm/wasmsolo/soloconvertor.go +++ b/packages/vm/wasmsolo/soloconvertor.go @@ -5,7 +5,7 @@ import ( "github.com/iotaledger/wasp/packages/hashing" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/colored" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // SoloConvertor converts ISCP data types to WasmLib data types diff --git a/packages/vm/wasmsolo/solosccontext.go b/packages/vm/wasmsolo/solosccontext.go index 7001e4d41f..a1053a0be8 100644 --- a/packages/vm/wasmsolo/solosccontext.go +++ b/packages/vm/wasmsolo/solosccontext.go @@ -12,7 +12,6 @@ import ( "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/wasmhost" - "github.com/iotaledger/wasp/packages/vm/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmproc" ) @@ -95,15 +94,15 @@ func (o *SoloScContext) processCall(bytes []byte) { } o.Tracef("CALL %s.%s", ctx.scName, funcName) params := o.getParams(paramsID) - _ = wasmlib.ConnectHost(ctx.wasmHostOld) + _ = wasmhost.Connect(ctx.wasmHostOld) res, err := ctx.Chain.CallView(ctx.scName, funcName, params) - _ = wasmlib.ConnectHost(ctx.wc) + _ = wasmhost.Connect(ctx.wc) ctx.Err = err if err != nil { // o.Panic("failed to invoke call: " + err.Error()) return } - returnID := o.GetObjectID(int32(wasmlib.KeyReturn), wasmlib.TYPE_MAP) + returnID := o.GetObjectID(wasmhost.KeyReturn, wasmhost.OBJTYPE_MAP) ctx.wc.FindObject(returnID).(*wasmproc.ScDict).SetKvStore(res) } @@ -212,7 +211,7 @@ func (o *SoloScContext) postSync(contract, function iscp.Hname, paramsID, transf mintAddress := ledgerstate.NewED25519Address(ctx.keyPair.PublicKey) req.WithMint(mintAddress, ctx.mint) } - _ = wasmlib.ConnectHost(ctx.wasmHostOld) + _ = wasmhost.Connect(ctx.wasmHostOld) var res dict.Dict if ctx.offLedger { ctx.offLedger = false @@ -226,10 +225,10 @@ func (o *SoloScContext) postSync(contract, function iscp.Hname, paramsID, transf ctx.Chain.Env.EnqueueRequests(ctx.Tx) } } - _ = wasmlib.ConnectHost(ctx.wc) + _ = wasmhost.Connect(ctx.wc) if ctx.Err != nil { return } - returnID := o.GetObjectID(int32(wasmlib.KeyReturn), wasmlib.TYPE_MAP) + returnID := o.GetObjectID(wasmhost.KeyReturn, wasmhost.OBJTYPE_MAP) ctx.wc.FindObject(returnID).(*wasmproc.ScDict).SetKvStore(res) } diff --git a/packages/wasp/constants.go b/packages/wasp/constants.go index b802c73494..9536098605 100644 --- a/packages/wasp/constants.go +++ b/packages/wasp/constants.go @@ -4,7 +4,7 @@ var VersionHash string const ( // Version version number - Version = "0.2.1" + Version = "0.2.2" // Name app code name Name = "Wasp" diff --git a/packages/webapi/endpoints.go b/packages/webapi/endpoints.go index e5adedc948..24e4af5ea5 100644 --- a/packages/webapi/endpoints.go +++ b/packages/webapi/endpoints.go @@ -51,6 +51,7 @@ func Init( webapiutil.GetAccountBalance, webapiutil.HasRequestBeenProcessed, time.Duration(parameters.GetInt(parameters.OffledgerAPICacheTTL))*time.Second, + log, ) adm := server.Group("admin", "").SetDescription("Admin endpoints") diff --git a/packages/webapi/request/request.go b/packages/webapi/request/request.go index 4b4fb88136..fb9bd3d377 100644 --- a/packages/webapi/request/request.go +++ b/packages/webapi/request/request.go @@ -2,11 +2,12 @@ package request import ( "fmt" - "io/ioutil" + "io" "net/http" "strings" "time" + "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/chains" @@ -32,12 +33,14 @@ func AddEndpoints( getChainBalance getAccountBalanceFn, hasRequestBeenProcessed hasRequestBeenProcessedFn, cacheTTL time.Duration, + log *logger.Logger, ) { instance := &offLedgerReqAPI{ getChain: getChain, getAccountBalance: getChainBalance, hasRequestBeenProcessed: hasRequestBeenProcessed, requestsCache: expiringcache.New(cacheTTL), + log: log, } server.POST(routes.NewRequest(":chainID"), instance.handleNewRequest). SetSummary("New off-ledger request"). @@ -55,6 +58,7 @@ type offLedgerReqAPI struct { getAccountBalance getAccountBalanceFn hasRequestBeenProcessed hasRequestBeenProcessedFn requestsCache *expiringcache.ExpiringCache + log *logger.Logger } func (o *offLedgerReqAPI) handleNewRequest(c echo.Context) error { @@ -82,10 +86,10 @@ func (o *offLedgerReqAPI) handleNewRequest(c echo.Context) error { alreadyProcessed, err := o.hasRequestBeenProcessed(ch, reqID) if err != nil { - return httperrors.BadRequest("internal error") + o.log.Errorf("webapi.offledger - check if already processed: %w", err) + return httperrors.ServerError("internal error") } - o.requestsCache.Set(reqID, true) if alreadyProcessed { return httperrors.BadRequest("request already processed") } @@ -93,9 +97,12 @@ func (o *offLedgerReqAPI) handleNewRequest(c echo.Context) error { // check user has on-chain balance balances, err := o.getAccountBalance(ch, offLedgerReq.SenderAccount()) if err != nil { + o.log.Errorf("webapi.offledger - account balance: %w", err) return httperrors.ServerError("Unable to get account balance") } + o.requestsCache.Set(reqID, true) + if len(balances) == 0 { return httperrors.BadRequest(fmt.Sprintf("No balance on account %s", offLedgerReq.SenderAccount().Base58())) } @@ -118,7 +125,7 @@ func parseParams(c echo.Context) (chainID *iscp.ChainID, req *request.OffLedger, } rGeneric, err := request.FromMarshalUtil(marshalutil.New(r.Request.Bytes())) if err != nil { - return nil, nil, httperrors.BadRequest(fmt.Sprintf("Error constructing off-ledger request from base64 string: \"%s\"", r.Request)) + return nil, nil, httperrors.BadRequest(fmt.Sprintf("Error constructing off-ledger request from base64 string: %q", r.Request)) } var ok bool if req, ok = rGeneric.(*request.OffLedger); !ok { @@ -128,7 +135,7 @@ func parseParams(c echo.Context) (chainID *iscp.ChainID, req *request.OffLedger, } // binary format - reqBytes, err := ioutil.ReadAll(c.Request().Body) + reqBytes, err := io.ReadAll(c.Request().Body) if err != nil { return nil, nil, httperrors.BadRequest("Error parsing request from payload") } diff --git a/packages/webapi/state/callview.go b/packages/webapi/state/callview.go index c7adf36a1a..a5c67d9669 100644 --- a/packages/webapi/state/callview.go +++ b/packages/webapi/state/callview.go @@ -100,7 +100,7 @@ func (s *callViewService) handleStateGet(c echo.Context) error { }) if err != nil { reason := fmt.Sprintf("View call failed: %v", err) - if errors.Is(err, coreutil.ErrStateHasBeenInvalidated) { + if errors.Is(err, coreutil.ErrorStateInvalidated) { return httperrors.Conflict(reason) } return httperrors.BadRequest(reason) diff --git a/packages/webapi/webapiutil/callview.go b/packages/webapi/webapiutil/callview.go index 7439bbf6c2..477d8d916a 100644 --- a/packages/webapi/webapiutil/callview.go +++ b/packages/webapi/webapiutil/callview.go @@ -9,9 +9,9 @@ import ( ) func CallView(ch chain.ChainCore, contractHname, viewHname iscp.Hname, params dict.Dict) (dict.Dict, error) { - vctx := viewcontext.NewFromChain(ch) var ret dict.Dict err := optimism.RetryOnStateInvalidated(func() error { + vctx := viewcontext.NewFromChain(ch) var err error ret, err = vctx.CallView(contractHname, viewHname, params) return err diff --git a/plugins/processors/evm.go b/plugins/processors/evm.go index 1029dba584..f4f1cbb818 100644 --- a/plugins/processors/evm.go +++ b/plugins/processors/evm.go @@ -3,8 +3,16 @@ package processors -import "github.com/iotaledger/wasp/contracts/native/evmchain" +import ( + "github.com/iotaledger/wasp/contracts/native/evm/evmchain" + "github.com/iotaledger/wasp/contracts/native/evm/evmlight" +) func init() { - nativeContracts = append(nativeContracts, evmchain.Processor) + nativeContracts = append( + nativeContracts, + + evmchain.Processor, + evmlight.Processor, + ) } diff --git a/plugins/webapi/plugin.go b/plugins/webapi/plugin.go index d57c07a654..3f29aea4aa 100644 --- a/plugins/webapi/plugin.go +++ b/plugins/webapi/plugin.go @@ -13,6 +13,7 @@ import ( metricspkg "github.com/iotaledger/wasp/packages/metrics" "github.com/iotaledger/wasp/packages/parameters" "github.com/iotaledger/wasp/packages/util/auth" + "github.com/iotaledger/wasp/packages/wasp" "github.com/iotaledger/wasp/packages/webapi" "github.com/iotaledger/wasp/packages/webapi/httperrors" "github.com/iotaledger/wasp/plugins/chains" @@ -47,7 +48,7 @@ func configure(*node.Plugin) { Server = echoswagger.New(echo.New(), "/doc", &echoswagger.Info{ Title: "Wasp API", Description: "REST API for the IOTA Wasp node", - Version: "0.1", + Version: wasp.Version, }) Server.Echo().HideBanner = true diff --git a/teamupdates/update-44-2021.md b/teamupdates/update-44-2021.md new file mode 100644 index 0000000000..cccdf16718 --- /dev/null +++ b/teamupdates/update-44-2021.md @@ -0,0 +1,23 @@ +# Smart Contract Team Updates - Week 44, 2021 + +- Last week we evaluated the release of the Smart Contracts Beta and fixed various issues encountered while people tried it out on the testnet. One of the main conclusions coming from this is that we need to further improve our logging to more easily debug any issues, improve the reliability of our testnet and improve the Wasp node software to better handle any kind of edge-case. Many of these issues have already been improved throughout the week and we started a workgroup within the team to focus on resolving all of these issues in the months to come. + +- The state of smart contract chains has to be recoverable at all times regardless of what happens. With the learnings from the testnet we decided to add additional functionality to the node software and tooling to ensure this is always the case. This is one of the tasks the new workgroup will be focussing on next to chain administration and governance. We will refer to this as the `admin tooling workgroup` from now on. + +- Next to the admin tooling workgroup another part of the team will be forming another workgroup focussing on the migration to Chrysalis with the tokenization framework and all the changes that come with that. We'll refer to this as the `VM workgroup` from now on. Both workgroups will be cancelled again once they finalized their implementations and new workgroups will be formed after that for some of the bigger follow-up tasks. + +- Due to the huge success of the testnet and the first NFT drop on it some changes to the infrastructure of the testnet have been scheduled to handle higher peak loads better which should result in a more reliable experience at these peak moments on the testnet. + +- The team is currently defining and dividing all individual tasks which are deliverables for the next release of IOTA Smart Contracts. Most of these tasks will fall under the workgroups and will be aimed at supporting the Chrysalis network by adding compatibility with Hornet and Bee, together with the tokenization framework integration, improved EVM support and improved reliability and tooling. + +- On the research end we are current looking at simulations for improving the performance of the gossip/dissemination part of off-ledger requests between the chain validator- and access nodes. Next to that we are also looking at other protocols for inspiration and validation of our concepts. Dynamic committees is a longer term ongoing research project that is worked on in parallel to this. + +- The initial content for the IOTA Smart Contracts whitepaper is now complete and is pending a cleanup and review before it's being released to the public. + +- The AssemblyScript (TypeScript) support for our Webassembly based smart contract has been completely integrated including support for it in the schema tool and documented making this the third supported language for our Wasm based smart contracts, you can use this now on our `develop` branch. This should lower the barrier to entry for people without Go or Rust experience by offering a familiar TypeScript interface as well. The schema tool has been improved as well to better structure the generated code for a better overview while developing (each generated language stub now has it's own folder for clarity). + +- The EVM-Light implementation (a more efficient alternative implementation of EVM) has been merged into the `develop` branch as well and can now be tested. You can pick between an EVM-Light and a traditional EVM chain now and we encourage everyone developing using EVM on IOTA Smart Contracts to test this implementation out as well and report back to us if any problems are encountered while using this. Next to this we are still working on implementing predictable block intervals on the EVM chains which is not a very trivial thing to solve elegantly. + +- Additional metrics to Wasp (calculating block sizes) have been added this week for integration into our Promotheus/Grafana based metrics platform. + +- The RFCs for the new Tokenization framework and dust protection have been reviewed internally and will, after some polishing, be released for a public review as well. diff --git a/tools/cluster/config.go b/tools/cluster/config.go index fd69404a28..a9bfd3f57a 100644 --- a/tools/cluster/config.go +++ b/tools/cluster/config.go @@ -3,7 +3,7 @@ package cluster import ( "encoding/json" "fmt" - "io/ioutil" + "os" "path" "strings" @@ -63,7 +63,7 @@ func ConfigExists(dataPath string) (bool, error) { } func LoadConfig(dataPath string) (*ClusterConfig, error) { - b, err := ioutil.ReadFile(configPath(dataPath)) + b, err := os.ReadFile(configPath(dataPath)) if err != nil { return nil, err } @@ -77,7 +77,7 @@ func (c *ClusterConfig) Save(dataPath string) error { if err != nil { return err } - return ioutil.WriteFile(configPath(dataPath), b, 0o600) + return os.WriteFile(configPath(dataPath), b, 0o600) } func configPath(dataPath string) string { diff --git a/tools/cluster/tests/blob_test.go b/tools/cluster/tests/blob_test.go index c566bb9c21..e3d0a56b96 100644 --- a/tools/cluster/tests/blob_test.go +++ b/tools/cluster/tests/blob_test.go @@ -2,7 +2,7 @@ package tests import ( "fmt" - "io/ioutil" + "os" "testing" "time" @@ -121,7 +121,7 @@ func TestBlobStoreManyBlobsNoEncoding(t *testing.T) { fileNames := []string{"blob_test.go", "deploy_test.go", "inccounter_test.go", "account_test.go"} blobs := make([][]byte, len(fileNames)) for i := range fileNames { - blobs[i], err = ioutil.ReadFile(fileNames[i]) + blobs[i], err = os.ReadFile(fileNames[i]) require.NoError(t, err) } blobFieldValues := make(map[string]interface{}) diff --git a/tools/cluster/tests/env.go b/tools/cluster/tests/env.go index 3298adf606..241a4f4c40 100644 --- a/tools/cluster/tests/env.go +++ b/tools/cluster/tests/env.go @@ -2,8 +2,8 @@ package tests import ( "flag" - "io/ioutil" "math/rand" + "os" "testing" "time" @@ -77,7 +77,7 @@ func (e *chainEnv) deployContract(wasmName, scDescription string, initParams map } if !*useWasp { - wasm, err := ioutil.ReadFile(wasmPath) + wasm, err := os.ReadFile(wasmPath) require.NoError(e.t, err) chClient := chainclient.New(e.clu.GoshimmerClient(), e.clu.WaspClient(0), e.chain.ChainID, e.chain.OriginatorKeyPair()) diff --git a/tools/cluster/tests/jsonrpc_test.go b/tools/cluster/tests/jsonrpc_test.go index 171596a5a6..ad6cbc3c2b 100644 --- a/tools/cluster/tests/jsonrpc_test.go +++ b/tools/cluster/tests/jsonrpc_test.go @@ -12,23 +12,31 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" - "github.com/iotaledger/wasp/contracts/native/evmchain" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/packages/evm/evmflavors" "github.com/iotaledger/wasp/packages/evm/evmtest" + "github.com/iotaledger/wasp/packages/evm/evmtypes" "github.com/iotaledger/wasp/packages/evm/jsonrpc" "github.com/iotaledger/wasp/packages/evm/jsonrpc/jsonrpctest" + "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/tools/cluster" "github.com/stretchr/testify/require" ) +func withEVMFlavors(t *testing.T, f func(*testing.T, *coreutil.ContractInfo)) { + for _, evmFlavor := range evmflavors.All { + t.Run(evmFlavor.Name, func(t *testing.T) { f(t, evmFlavor) }) + } +} + type clusterTestEnv struct { jsonrpctest.Env cluster *cluster.Cluster chain *cluster.Chain } -func newClusterTestEnv(t *testing.T) *clusterTestEnv { +func newClusterTestEnv(t *testing.T, evmFlavor *coreutil.ContractInfo) *clusterTestEnv { evmtest.InitGoEthLogger(t) clu := newCluster(t) @@ -39,12 +47,12 @@ func newClusterTestEnv(t *testing.T) *clusterTestEnv { chainID := evm.DefaultChainID _, err = chain.DeployContract( - evmchain.Contract.Name, - evmchain.Contract.ProgramHash.String(), + evmFlavor.Name, + evmFlavor.ProgramHash.String(), "EVM chain on top of ISCP", map[string]interface{}{ - evmchain.FieldChainID: codec.EncodeUint16(uint16(chainID)), - evmchain.FieldGenesisAlloc: evmchain.EncodeGenesisAlloc(core.GenesisAlloc{ + evm.FieldChainID: codec.EncodeUint16(uint16(chainID)), + evm.FieldGenesisAlloc: evmtypes.EncodeGenesisAlloc(core.GenesisAlloc{ evmtest.FaucetAddress: {Balance: evmtest.FaucetSupply}, }), }, @@ -55,7 +63,7 @@ func newClusterTestEnv(t *testing.T) *clusterTestEnv { require.NoError(t, err) backend := jsonrpc.NewWaspClientBackend(chain.Client(signer)) - evmChain := jsonrpc.NewEVMChain(backend, chainID, evmchain.Contract.Name) + evmChain := jsonrpc.NewEVMChain(backend, chainID, evmFlavor.Name) accountManager := jsonrpc.NewAccountManager(evmtest.Accounts) @@ -80,13 +88,19 @@ func newClusterTestEnv(t *testing.T) *clusterTestEnv { } func TestEVMJsonRPCClusterGetLogs(t *testing.T) { - newClusterTestEnv(t).TestRPCGetLogs() + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + newClusterTestEnv(t, evmFlavor).TestRPCGetLogs() + }) } func TestEVMJsonRPCClusterGasLimit(t *testing.T) { - newClusterTestEnv(t).TestRPCGasLimit() + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + newClusterTestEnv(t, evmFlavor).TestRPCGasLimit() + }) } func TestEVMJsonRPCClusterInvalidNonce(t *testing.T) { - newClusterTestEnv(t).TestRPCInvalidNonce() + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + newClusterTestEnv(t, evmFlavor).TestRPCInvalidNonce() + }) } diff --git a/tools/cluster/tests/spam_test.go b/tools/cluster/tests/spam_test.go index 6587931fd6..3c5baa2dce 100644 --- a/tools/cluster/tests/spam_test.go +++ b/tools/cluster/tests/spam_test.go @@ -42,6 +42,10 @@ func TestSpamOnledger(t *testing.T) { println(events) } +// we need to cap the limit of parallel requests, otherwise some reqs will fail due to local tcp limits: `dial tcp 127.0.0.1:9090: socket: too many open files` +const maxParallelRequests = 700 + +// !! WARNING !! - this test should only be run with `database.inMemory` set to `false`. Otherwise it is WAY slower, and will probably time out or take a LONG time func TestSpamOffledger(t *testing.T) { testutil.RunHeavy(t) // single wasp node committee, to test if publishing can break state transitions @@ -61,13 +65,47 @@ func TestSpamOffledger(t *testing.T) { myClient := env.chain.SCClient(iscp.Hn(incCounterSCName), keyPair) + maxChan := make(chan int, maxParallelRequests) + reqSuccessChan := make(chan uint64, numRequests) + reqErrorChan := make(chan error, 1) + for i := 0; i < numRequests; i++ { - _, err = myClient.PostOffLedgerRequest(inccounter.FuncIncCounter.Name, chainclient.PostRequestParams{Nonce: uint64(i + 1)}) - if err != nil { - time.Sleep(5 * time.Second) - fmt.Printf("ERROR sending offledger request, i: %d, err: %v\n", i, err) + maxChan <- i + go func(nonce uint64) { + // send the request + req, er := myClient.PostOffLedgerRequest(inccounter.FuncIncCounter.Name, chainclient.PostRequestParams{Nonce: nonce}) + if er != nil { + reqErrorChan <- er + return + } + + // wait for the request to be processed + err = env.chain.CommitteeMultiClient().WaitUntilRequestProcessed(env.chain.ChainID, req.ID(), 30*time.Second) + + // check receipt + er = env.chain.OriginatorClient().CheckRequestResult(req.ID()) + if er != nil { + reqErrorChan <- er + return + } + reqSuccessChan <- nonce + <-maxChan + }(uint64(i + 1)) + } + + n := 0 + for { + select { + case <-reqSuccessChan: + n++ + case e := <-reqErrorChan: + // no request should fail + fmt.Printf("ERROR sending offledger request, err: %v\n", e) + t.FailNow() + } + if n == numRequests { + break } - require.NoError(t, err) } waitUntil(t, env.counterEquals(int64(numRequests)), []int{0}, 5*time.Minute) diff --git a/tools/cluster/tests/wasm/inccounter_bg.wasm b/tools/cluster/tests/wasm/inccounter_bg.wasm index 37f836ce3c..bb828e396a 100644 Binary files a/tools/cluster/tests/wasm/inccounter_bg.wasm and b/tools/cluster/tests/wasm/inccounter_bg.wasm differ diff --git a/tools/cluster/tests/wasp-cli-evm_test.go b/tools/cluster/tests/wasp-cli-evm_test.go index 132a4b7914..c70b53bd51 100644 --- a/tools/cluster/tests/wasp-cli-evm_test.go +++ b/tools/cluster/tests/wasp-cli-evm_test.go @@ -10,35 +10,38 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" - "github.com/iotaledger/wasp/contracts/native/evmchain" + "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/stretchr/testify/require" ) func TestWaspCLIEVMDeploy(t *testing.T) { - w := newWaspCLITest(t) - w.Run("init") - w.Run("request-funds") - committee, quorum := w.CommitteeConfig() - w.Run("chain", "deploy", "--chain=chain1", committee, quorum) - // for off-ledger requests - w.Run("chain", "deposit", "IOTA:2000") + withEVMFlavors(t, func(t *testing.T, evmFlavor *coreutil.ContractInfo) { + w := newWaspCLITest(t) + w.Run("init") + w.Run("request-funds") + committee, quorum := w.CommitteeConfig() + w.Run("chain", "deploy", "--chain=chain1", committee, quorum) + // for off-ledger requests + w.Run("chain", "deposit", "IOTA:2000") - faucetKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - faucetAddress := crypto.PubkeyToAddress(faucetKey.PublicKey) - faucetSupply := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)) + faucetKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + faucetAddress := crypto.PubkeyToAddress(faucetKey.PublicKey) + faucetSupply := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9)) - // test that the evmchain contract can be deployed using wasp-cli - w.Run("chain", "evm", "deploy", - "--alloc", fmt.Sprintf("%s:%s", faucetAddress.String(), faucetSupply.String()), - ) + // test that the EVM chain can be deployed using wasp-cli + w.Run("chain", "evm", "deploy", + "--alloc", fmt.Sprintf("%s:%s", faucetAddress.String(), faucetSupply.String()), + "--evm-flavor", evmFlavor.Name, + ) - out := w.Run("chain", "list-contracts") - found := false - for _, s := range out { - if strings.Contains(s, evmchain.Contract.Name) { - found = true - break + out := w.Run("chain", "list-contracts") + found := false + for _, s := range out { + if strings.Contains(s, evmFlavor.Name) { + found = true + break + } } - } - require.True(t, found) + require.True(t, found) + }) } diff --git a/tools/evm/evmcli/deploy.go b/tools/evm/evmcli/deploy.go index 438f2f434a..6ddeb9351b 100644 --- a/tools/evm/evmcli/deploy.go +++ b/tools/evm/evmcli/deploy.go @@ -10,28 +10,55 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/iotaledger/wasp/contracts/native/evmchain" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evmchain" + "github.com/iotaledger/wasp/packages/evm/evmflavors" + "github.com/iotaledger/wasp/packages/evm/evmtypes" + "github.com/iotaledger/wasp/packages/iscp/coreutil" "github.com/iotaledger/wasp/tools/wasp-cli/log" "github.com/spf13/cobra" ) type DeployParams struct { + evmFlavor string ChainID int - Name string - Description string + name string + description string alloc []string allocBase64 string GasPerIOTA uint64 } func (d *DeployParams) InitFlags(cmd *cobra.Command) { + cmd.Flags().StringVarP(&d.evmFlavor, "evm-flavor", "", evmchain.Contract.Name, "EVM flavor. One of `evmchain`, `evmlight`") cmd.Flags().IntVarP(&d.ChainID, "chainid", "", evm.DefaultChainID, "ChainID") - cmd.Flags().StringVarP(&d.Name, "name", "", evmchain.Contract.Name, "Contract name") - cmd.Flags().StringVarP(&d.Description, "description", "", evmchain.Contract.Description, "Contract description") + cmd.Flags().StringVarP(&d.name, "name", "", "", "Contract name. Default: same as --evm-flavor") + cmd.Flags().StringVarP(&d.description, "description", "", "", "Contract description") cmd.Flags().StringSliceVarP(&d.alloc, "alloc", "", nil, "Genesis allocation (format:
:,
:,...)") cmd.Flags().StringVarP(&d.allocBase64, "alloc-bytes", "", "", "Genesis allocation (base64-encoded)") - cmd.Flags().Uint64VarP(&d.GasPerIOTA, "gas-per-iota", "", evmchain.DefaultGasPerIota, "Gas per IOTA charged as fee") + cmd.Flags().Uint64VarP(&d.GasPerIOTA, "gas-per-iota", "", evm.DefaultGasPerIota, "Gas per IOTA charged as fee") +} + +func (d *DeployParams) Name() string { + if d.name != "" { + return d.name + } + return d.EVMFlavor().Name +} + +func (d *DeployParams) Description() string { + if d.description != "" { + return d.description + } + return d.EVMFlavor().Description +} + +func (d *DeployParams) EVMFlavor() *coreutil.ContractInfo { + r, ok := evmflavors.All[d.evmFlavor] + if !ok { + log.Fatalf("unknown EVM flavor: %s", d.evmFlavor) + } + return r } func (d *DeployParams) GetGenesis(def core.GenesisAlloc) core.GenesisAlloc { @@ -62,7 +89,7 @@ func (d *DeployParams) GetGenesis(def core.GenesisAlloc) core.GenesisAlloc { // --alloc-bytes provided b, err := base64.StdEncoding.DecodeString(d.allocBase64) log.Check(err) - ret, err := evmchain.DecodeGenesisAlloc(b) + ret, err := evmtypes.DecodeGenesisAlloc(b) log.Check(err) return ret } diff --git a/tools/evm/evmemulator/main.go b/tools/evm/evmemulator/main.go index 954774e9c5..378acf6204 100644 --- a/tools/evm/evmemulator/main.go +++ b/tools/evm/evmemulator/main.go @@ -9,8 +9,9 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" - "github.com/iotaledger/wasp/contracts/native/evmchain" + "github.com/iotaledger/wasp/contracts/native/evm" "github.com/iotaledger/wasp/packages/evm/evmtest" + "github.com/iotaledger/wasp/packages/evm/evmtypes" "github.com/iotaledger/wasp/packages/evm/jsonrpc" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/solo" @@ -29,15 +30,15 @@ func main() { Args: cobra.NoArgs, Run: start, Use: "evmemulator", - Short: "evmemulator runs an instance of the evmchain contract with Solo as backend", - Long: fmt.Sprintf(`evmemulator runs an instance of the evmchain contract with Solo as backend. + Short: "evmemulator runs an instance of the evmchain/evmlight contract with Solo as backend", + Long: fmt.Sprintf(`evmemulator runs an instance of the evmchain/evmlight contract with Solo as backend. evmemulator does the following: - Starts a Solo environment (a framework for running local ISCP chains in-memory) - Deploys an ISCP chain -- Deploys the evmchain ISCP contract (which runs an Ethereum chain on top of the ISCP chain) -- Starts a JSON-RPC server with the evmchain contract as backend +- Deploys the evmchain/evmlight ISCP contract (which runs an Ethereum chain on top of the ISCP chain) +- Starts a JSON-RPC server with the deployed ISCP contract as backend You can connect any Ethereum tool (eg Metamask) to this JSON-RPC server and use it for testing Ethereum contracts running on ISCP. @@ -69,17 +70,17 @@ func start(cmd *cobra.Command, args []string) { chainOwner, _ := env.NewKeyPairWithFunds() chain := env.NewChain(chainOwner, "iscpchain") - err := chain.DeployContract(chainOwner, deployParams.Name, evmchain.Contract.ProgramHash, - evmchain.FieldChainID, codec.EncodeUint16(uint16(deployParams.ChainID)), - evmchain.FieldGenesisAlloc, evmchain.EncodeGenesisAlloc(deployParams.GetGenesis(core.GenesisAlloc{ + err := chain.DeployContract(chainOwner, deployParams.Name(), deployParams.EVMFlavor().ProgramHash, + evm.FieldChainID, codec.EncodeUint16(uint16(deployParams.ChainID)), + evm.FieldGenesisAlloc, evmtypes.EncodeGenesisAlloc(deployParams.GetGenesis(core.GenesisAlloc{ evmtest.FaucetAddress: {Balance: evmtest.FaucetSupply}, })), - evmchain.FieldGasPerIota, deployParams.GasPerIOTA, + evm.FieldGasPerIota, deployParams.GasPerIOTA, ) log.Check(err) signer, _ := env.NewKeyPairWithFunds() backend := jsonrpc.NewSoloBackend(env, chain, signer) - jsonRPCServer.ServeJSONRPC(backend, deployParams.ChainID, deployParams.Name) + jsonRPCServer.ServeJSONRPC(backend, deployParams.ChainID, deployParams.Name()) } diff --git a/tools/schema/generator/field.go b/tools/schema/generator/field.go index cb261a0d1a..2b28ca05af 100644 --- a/tools/schema/generator/field.go +++ b/tools/schema/generator/field.go @@ -8,7 +8,7 @@ import ( "regexp" "strings" - "github.com/iotaledger/wasp/packages/vm/wasmlib" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) var ( diff --git a/tools/schema/generator/generator.go b/tools/schema/generator/generator.go index 88b974e1ab..b6746acdfa 100644 --- a/tools/schema/generator/generator.go +++ b/tools/schema/generator/generator.go @@ -8,72 +8,74 @@ import ( "fmt" "os" "regexp" - "sort" "strings" ) -const ( - AccessChain = "chain" - AccessCreator = "creator" - AccessSelf = "self" - AliasThis = "this" - InitFunc = "Init" - KindFunc = "Func" - KindView = "View" - PropImmutable = "Immutable" - PropMutable = "Mutable" -) +// TODO nested structs +// TODO handle case where owner is type AgentID[] -var ( - ModuleCwd string - ModuleName string - ModulePath string +const ( + AccessChain = "chain" + AccessCreator = "creator" + AccessSelf = "self" + AliasThis = "this" + InitFunc = "Init" + KindFunc = "Func" + KindView = "View" + PropImmutable = "Immutable" + PropMutable = "Mutable" + SpecialFuncInit = "funcInit" + SpecialFuncSetOwner = "setOwner" + SpecialViewGetOwner = "getOwner" ) -//nolint:unused var ( - camelRegExp = regexp.MustCompile(`_[a-z]`) - snakeRegExp = regexp.MustCompile(`[a-z0-9][A-Z]`) - snakeRegExp2 = regexp.MustCompile(`[A-Z][A-Z]+[a-z]`) + ModuleCwd = "???" + ModuleName = "???" + ModulePath = "???" ) -func calculatePadding(fields []*Field, types StringMap, snakeName bool) (nameLen, typeLen int) { - for _, param := range fields { - fldName := param.Name - if snakeName { - fldName = snake(fldName) - } - if nameLen < len(fldName) { - nameLen = len(fldName) - } - fldType := param.Type - if types != nil { - fldType = types[fldType] - } - if typeLen < len(fldType) { - typeLen = len(fldType) - } - } - return +type Generator interface { + funcName(f *Func) string + generateFuncSignature(f *Func) + generateLanguageSpecificFiles() error + generateProxyArray(field *Field, mutability, arrayType, proxyType string) + generateProxyMap(field *Field, mutability, mapType, proxyType string) + generateProxyReference(field *Field, mutability, typeName string) + writeConsts() + writeContract() + writeInitialFuncs() + writeKeys() + writeLib() + writeParams() + writeResults() + writeState() + writeStructs() + writeTypeDefs() } -// convert lowercase snake case to camel case -//nolint:deadcode,unused -func camel(name string) string { - return camelRegExp.ReplaceAllStringFunc(name, func(sub string) string { - return strings.ToUpper(sub[1:]) - }) +type GenBase struct { + extension string + file *os.File + Folder string + funcRegexp *regexp.Regexp + gen Generator + language string + NewTypes map[string]bool + rootFolder string + s *Schema + skipDisclaimer bool } -// capitalize first letter -func capitalize(name string) string { - return upper(name[:1]) + name[1:] +func (g *GenBase) close() { + _ = g.file.Close() } -func copyright(noChange bool) string { +// TODO take copyright from schema? +func (g *GenBase) copyright() string { text := "// Copyright 2020 IOTA Stiftung\n" + "// SPDX-License-Identifier: Apache-2.0\n" - if noChange { + if !g.skipDisclaimer { text += "\n// (Re-)generated by schema tool\n" + "// >>>> DO NOT CHANGE THIS FILE! <<<<\n" + "// Change the json schema instead\n" @@ -81,115 +83,267 @@ func copyright(noChange bool) string { return text } -func formatter(file *os.File, on bool) { +func (g *GenBase) create(path string) (err error) { + g.file, err = os.Create(path) + return err +} + +func (g *GenBase) createSourceFile(name string, generator func()) error { + err := g.create(g.Folder + name + g.extension) + if err != nil { + return err + } + defer g.close() + + // always add copyright to source file + g.println(g.copyright()) + g.skipDisclaimer = false + generator() + return nil +} + +func (g *GenBase) exists(path string) (err error) { + _, err = os.Stat(path) + return err +} + +func (g *GenBase) formatter(on bool) { if on { - fmt.Fprintf(file, "\n// @formatter:%s\n", "on") + g.printf("\n// @formatter:%s\n", "on") return } - fmt.Fprintf(file, "// @formatter:%s\n\n", "off") + g.printf("// @formatter:%s\n\n", "off") } -// convert to lower case -func lower(name string) string { - return strings.ToLower(name) -} +func (g *GenBase) Generate(s *Schema) error { + g.s = s + g.NewTypes = make(map[string]bool) -func FindModulePath() error { - cwd, err := os.Getwd() + g.Folder = g.rootFolder + "/" + if g.rootFolder != "src" { + module := strings.ReplaceAll(ModuleCwd, "\\", "/") + module = module[strings.LastIndex(module, "/")+1:] + g.Folder += module + "/" + } + if g.s.CoreContracts { + g.Folder += g.s.Name + "/" + } + + err := os.MkdirAll(g.Folder, 0o755) if err != nil { return err } - // we're going to walk up the path, make sure to restore - ModuleCwd = cwd - defer func() { - _ = os.Chdir(ModuleCwd) - }() + info, err := os.Stat(g.Folder + "consts" + g.extension) + if err == nil && info.ModTime().After(s.SchemaTime) { + fmt.Printf("skipping %s code generation\n", g.language) + return nil + } - file, err := os.Open("go.mod") - for err != nil { - err = os.Chdir("..") + fmt.Printf("generating %s code\n", g.language) + err = g.generateCode() + if err != nil { + return err + } + if !g.s.CoreContracts { + err = g.generateTests() if err != nil { - return fmt.Errorf("cannot find go.mod in cwd path") + return err + } + } + return nil +} + +func (g *GenBase) generateCode() error { + err := g.createSourceFile("consts", g.gen.writeConsts) + if err != nil { + return err + } + if len(g.s.Structs) != 0 { + err = g.createSourceFile("structs", g.gen.writeStructs) + if err != nil { + return err + } + } + if len(g.s.Typedefs) != 0 { + err = g.createSourceFile("typedefs", g.gen.writeTypeDefs) + if err != nil { + return err } - cwd, err = os.Getwd() + } + if len(g.s.Params) != 0 { + err = g.createSourceFile("params", g.gen.writeParams) if err != nil { return err } - file, err = os.Open("go.mod") + } + if len(g.s.Results) != 0 { + err = g.createSourceFile("results", g.gen.writeResults) + if err != nil { + return err + } + } + err = g.createSourceFile("contract", g.gen.writeContract) + if err != nil { + return err } - // now file is the go.mod and cwd holds the path - defer file.Close() - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "module ") { - ModuleName = strings.TrimSpace(line[len("module"):]) - ModulePath = cwd - return nil + if !g.s.CoreContracts { + err = g.createSourceFile("keys", g.gen.writeKeys) + if err != nil { + return err + } + err = g.createSourceFile("state", g.gen.writeState) + if err != nil { + return err + } + err = g.createSourceFile("lib", g.gen.writeLib) + if err != nil { + return err + } + err = g.generateFuncs() + if err != nil { + return err } } - return fmt.Errorf("cannot find module definition in go.mod") + return g.gen.generateLanguageSpecificFiles() } -// pad to specified size with spaces -func pad(name string, size int) string { - for i := len(name); i < size; i++ { - name += " " +func (g *GenBase) generateFuncs() error { + scFileName := g.Folder + g.s.Name + g.extension + err := g.open(g.Folder + g.s.Name + g.extension) + if err != nil { + // generate initial SC function file + g.skipDisclaimer = true + return g.createSourceFile(g.s.Name, g.gen.writeInitialFuncs) } - return name -} -// convert camel case to lower case snake case -func snake(name string) string { - name = snakeRegExp.ReplaceAllStringFunc(name, func(sub string) string { - return sub[:1] + "_" + sub[1:] - }) - name = snakeRegExp2.ReplaceAllStringFunc(name, func(sub string) string { - n := len(sub) - return sub[:n-2] + "_" + sub[n-2:] - }) - return lower(name) -} + // append missing function signatures to existing code file -// convert to upper case -func upper(name string) string { - return strings.ToUpper(name) -} + // scan existing file for signatures + lines, existing, err := g.scanExistingCode() + if err != nil { + return err + } -func sortedFields(dict FieldMap) []string { - keys := make([]string, 0) - for key := range dict { - keys = append(keys, key) + // save old one from overwrite + scOriginal := g.Folder + g.s.Name + ".bak" + err = os.Rename(scFileName, scOriginal) + if err != nil { + return err + } + err = g.create(scFileName) + if err != nil { + return err } - sort.Strings(keys) - return keys + defer g.close() + + // make copy of file + for _, line := range lines { + g.println(line) + } + + // append any new funcs + for _, f := range g.s.Funcs { + if existing[g.gen.funcName(f)] == "" { + g.gen.generateFuncSignature(f) + } + } + + return os.Remove(scOriginal) } -func sortedKeys(dict StringMap) []string { - keys := make([]string, 0) - for key := range dict { - keys = append(keys, key) +func (g *GenBase) generateProxy(field *Field, mutability string) { + if field.Array { + proxyType := mutability + field.Type + arrayType := "ArrayOf" + proxyType + if !g.NewTypes[arrayType] { + g.NewTypes[arrayType] = true + g.gen.generateProxyArray(field, mutability, arrayType, proxyType) + } + g.gen.generateProxyReference(field, mutability, arrayType) + return + } + + if field.MapKey != "" { + proxyType := mutability + field.Type + mapType := "Map" + field.MapKey + "To" + proxyType + if !g.NewTypes[mapType] { + g.NewTypes[mapType] = true + g.gen.generateProxyMap(field, mutability, mapType, proxyType) + } + g.gen.generateProxyReference(field, mutability, mapType) } - sort.Strings(keys) - return keys } -func sortedFuncDescs(dict FuncDefMap) []string { - keys := make([]string, 0) - for key := range dict { - keys = append(keys, key) +func (g *GenBase) generateTests() error { + err := os.MkdirAll("test", 0o755) + if err != nil { + return err + } + + // do not overwrite existing file + name := strings.ToLower(g.s.Name) + filename := "test/" + name + "_test.go" + err = g.exists(filename) + if err == nil { + return nil + } + + err = g.create(filename) + if err != nil { + return err } - sort.Strings(keys) - return keys + defer g.close() + + module := ModuleName + strings.ReplaceAll(ModuleCwd[len(ModulePath):], "\\", "/") + g.println("package test") + g.println() + g.println("import (") + g.println("\t\"testing\"") + g.println() + g.printf("\t\"%s/go/%s\"\n", module, g.s.Name) + g.println("\t\"github.com/iotaledger/wasp/packages/vm/wasmsolo\"") + g.println("\t\"github.com/stretchr/testify/require\"") + g.println(")") + g.println() + g.println("func TestDeploy(t *testing.T) {") + g.printf("\tctx := wasmsolo.NewSoloContext(t, %s.ScName, %s.OnLoad)\n", name, name) + g.printf("\trequire.NoError(t, ctx.ContractExists(%s.ScName))\n", name) + g.println("}") + + return nil +} + +func (g *GenBase) open(path string) (err error) { + g.file, err = os.Open(path) + return err } -func sortedMaps(dict StringMapMap) []string { - keys := make([]string, 0) - for key := range dict { - keys = append(keys, key) +func (g *GenBase) printf(format string, a ...interface{}) { + _, _ = fmt.Fprintf(g.file, format, a...) +} + +func (g *GenBase) println(a ...interface{}) { + _, _ = fmt.Fprintln(g.file, a...) +} + +func (g *GenBase) scanExistingCode() ([]string, StringMap, error) { + defer g.close() + existing := make(StringMap) + lines := make([]string, 0) + scanner := bufio.NewScanner(g.file) + for scanner.Scan() { + line := scanner.Text() + matches := g.funcRegexp.FindStringSubmatch(line) + if matches != nil { + existing[matches[1]] = line + } + lines = append(lines, line) + } + err := scanner.Err() + if err != nil { + return nil, nil, err } - sort.Strings(keys) - return keys + return lines, existing, nil } diff --git a/tools/schema/generator/generator_go.go b/tools/schema/generator/generator_go.go index dc00d78526..ff7c9c3648 100644 --- a/tools/schema/generator/generator_go.go +++ b/tools/schema/generator/generator_go.go @@ -5,7 +5,6 @@ package generator import ( "fmt" - "os" "regexp" "strconv" "strings" @@ -13,16 +12,10 @@ import ( "github.com/iotaledger/wasp/packages/iscp" ) -const importCoreTypes = `import "github.com/iotaledger/wasp/packages/iscp" -` - -const importWasmLib = `import "github.com/iotaledger/wasp/packages/vm/wasmlib" -` - -const importWasmClient = `import "github.com/iotaledger/wasp/packages/vm/wasmclient" -` - -var goFuncRegexp = regexp.MustCompile(`^func (\w+).+$`) +const ( + goImportWasmLib = "import \"github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib\"" + goImportWasmClient = "import \"github.com/iotaledger/wasp/packages/vm/wasmclient\"" +) var goTypes = StringMap{ "Address": "wasmlib.ScAddress", @@ -46,7 +39,7 @@ var goKeys = StringMap{ "Hash": "key", "Hname": "key", "Int16": "??TODO", - "Int32": "wasmlib.Key32(int32)", + "Int32": "wasmlib.Key32(key)", "Int64": "??TODO", "RequestID": "key", "String": "wasmlib.Key(key)", @@ -71,187 +64,70 @@ const ( goTypeMap = "wasmlib.TYPE_MAP" ) -func (s *Schema) GenerateGo() error { - s.NewTypes = make(map[string]bool) - - err := s.generateGoConsts(false) - if err != nil { - return err - } - err = s.generateGoTypes() - if err != nil { - return err - } - err = s.generateGoSubtypes() - if err != nil { - return err - } - err = s.generateGoParams() - if err != nil { - return err - } - err = s.generateGoResults() - if err != nil { - return err - } - err = s.generateGoContract() - if err != nil { - return err - } - - if !s.CoreContracts { - err = s.generateGoKeys() - if err != nil { - return err - } - err = s.generateGoState() - if err != nil { - return err - } - err = s.generateGoLib() - if err != nil { - return err - } - err = s.generateGoFuncs() - if err != nil { - return err - } - - // go-specific stuff - return s.generateGoWasmMain() - } - - return nil +type GoGenerator struct { + GenBase } -func (s *Schema) generateGoArrayType(varType string) string { - // native core contracts use Array16 instead of our nested array type - if s.CoreContracts { - return "wasmlib.TYPE_ARRAY16|" + varType - } - return "wasmlib.TYPE_ARRAY|" + varType +func NewGoGenerator() *GoGenerator { + g := &GoGenerator{} + g.extension = ".go" + g.funcRegexp = regexp.MustCompile(`^func (\w+).+$`) + g.language = "Go" + g.rootFolder = "go" + g.gen = g + return g } -func (s *Schema) generateGoConsts(test bool) error { - file, err := os.Create("consts.go") - if err != nil { - return err - } - defer file.Close() - - packageName := "test" - importTypes := importCoreTypes - if !test { - packageName = s.Name - importTypes = importWasmLib +func (g *GoGenerator) flushConsts() { + if len(g.s.ConstNames) == 0 { + return } - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n\n", packageName) - fmt.Fprint(file, importTypes) - scName := s.Name - if s.CoreContracts { - // remove 'core' prefix - scName = scName[4:] - } - s.appendConst("ScName", "\""+scName+"\"") - if s.Description != "" { - s.appendConst("ScDescription", "\""+s.Description+"\"") - } - hName := iscp.Hn(scName) - hNameType := "wasmlib.ScHname" - if test { - hNameType = "iscp.Hname" + if len(g.s.ConstNames) == 1 { + name := g.s.ConstNames[0] + value := g.s.ConstValues[0] + g.printf("\nconst %s = %s\n", name, value) + g.s.flushConsts(func(name string, value string, padLen int) {}) + return } - s.appendConst("HScName", hNameType+"(0x"+hName.String()+")") - s.flushGoConsts(file) - s.generateGoConstsFields(file, test, s.Params, "Param") - s.generateGoConstsFields(file, test, s.Results, "Result") - s.generateGoConstsFields(file, test, s.StateVars, "State") + g.printf("\nconst (\n") + g.s.flushConsts(func(name string, value string, padLen int) { + g.printf("\t%s = %s\n", pad(name, padLen), value) + }) + g.printf(")\n") +} - if len(s.Funcs) != 0 { - for _, f := range s.Funcs { - constName := capitalize(f.FuncName) - s.appendConst(constName, "\""+f.String+"\"") - } - s.flushGoConsts(file) +func (g *GoGenerator) funcName(f *Func) string { + return f.FuncName +} - for _, f := range s.Funcs { - constHname := "H" + capitalize(f.FuncName) - s.appendConst(constHname, hNameType+"(0x"+f.Hname.String()+")") - } - s.flushGoConsts(file) +func (g *GoGenerator) generateArrayType(varType string) string { + // native core contracts use Array16 instead of our nested array type + if g.s.CoreContracts { + return "wasmlib.TYPE_ARRAY16|" + varType } - - return nil + return "wasmlib.TYPE_ARRAY|" + varType } -func (s *Schema) generateGoConstsFields(file *os.File, test bool, fields []*Field, prefix string) { +func (g *GoGenerator) generateConstsFields(fields []*Field, prefix string) { if len(fields) != 0 { for _, field := range fields { if field.Alias == AliasThis { continue } name := prefix + capitalize(field.Name) - value := "\"" + field.Alias + "\"" - if !test { - value = "wasmlib.Key(" + value + ")" - } - s.appendConst(name, value) + value := "wasmlib.Key(\"" + field.Alias + "\")" + g.s.appendConst(name, value) } - s.flushGoConsts(file) + g.flushConsts() } } -func (s *Schema) generateGoContract() error { - file, err := os.Create("contract.go") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n\n", s.Name) - fmt.Fprint(file, importWasmLib) - - for _, f := range s.Funcs { - nameLen := f.nameLen(4) - kind := f.Kind - if f.Type == InitFunc { - kind = f.Type + f.Kind - } - fmt.Fprintf(file, "\ntype %sCall struct {\n", f.Type) - fmt.Fprintf(file, "\t%s *wasmlib.Sc%s\n", pad(KindFunc, nameLen), kind) - if len(f.Params) != 0 { - fmt.Fprintf(file, "\t%s Mutable%sParams\n", pad("Params", nameLen), f.Type) - } - if len(f.Results) != 0 { - fmt.Fprintf(file, "\tResults Immutable%sResults\n", f.Type) - } - fmt.Fprintf(file, "}\n") - } - - s.generateGoContractFuncs(file) - - if s.CoreContracts { - fmt.Fprintf(file, "\nfunc OnLoad() {\n") - fmt.Fprintf(file, "\texports := wasmlib.NewScExports()\n") - for _, f := range s.Funcs { - constName := capitalize(f.FuncName) - fmt.Fprintf(file, "\texports.Add%s(%s, wasmlib.%sError)\n", f.Kind, constName, f.Kind) - } - fmt.Fprintf(file, "}\n") - } - return nil -} - -func (s *Schema) generateGoContractFuncs(file *os.File) { - fmt.Fprint(file, "\ntype Funcs struct{}\n") - fmt.Fprint(file, "\nvar ScFuncs Funcs\n") - for _, f := range s.Funcs { +func (g *GoGenerator) generateContractFuncs() { + g.println("\ntype Funcs struct{}") + g.println("\nvar ScFuncs Funcs") + for _, f := range g.s.Funcs { assign := "return" paramsID := "nil" if len(f.Params) != 0 { @@ -269,230 +145,93 @@ func (s *Schema) generateGoContractFuncs(file *os.File) { kind = f.Type + f.Kind keyMap = ", keyMap[:], idxMap[:]" } - fmt.Fprintf(file, "\nfunc (sc Funcs) %s(ctx wasmlib.Sc%sCallContext) *%sCall {\n", f.Type, f.Kind, f.Type) - fmt.Fprintf(file, "\t%s &%sCall{Func: wasmlib.NewSc%s(ctx, HScName, H%s%s%s)}\n", assign, f.Type, kind, f.Kind, f.Type, keyMap) + g.printf("\nfunc (sc Funcs) %s(ctx wasmlib.Sc%sCallContext) *%sCall {\n", f.Type, f.Kind, f.Type) + g.printf("\t%s &%sCall{Func: wasmlib.NewSc%s(ctx, HScName, H%s%s%s)}\n", assign, f.Type, kind, f.Kind, f.Type, keyMap) if len(f.Params) != 0 || len(f.Results) != 0 { - fmt.Fprintf(file, "\tf.Func.SetPtrs(%s, %s)\n", paramsID, resultsID) - fmt.Fprintf(file, "\treturn f\n") + g.printf("\tf.Func.SetPtrs(%s, %s)\n", paramsID, resultsID) + g.printf("\treturn f\n") } - fmt.Fprintf(file, "}\n") + g.printf("}\n") } } -func (s *Schema) generateGoFuncs() error { - scFileName := s.Name + ".go" - file, err := os.Open(scFileName) - if err != nil { - // generate initial code file - return s.generateGoFuncsNew(scFileName) - } - - // append missing function signatures to existing code file - - lines, existing, err := s.scanExistingCode(file, goFuncRegexp) - if err != nil { - return err - } - - // save old one from overwrite - scOriginal := s.Name + ".bak" - err = os.Rename(scFileName, scOriginal) - if err != nil { - return err - } - file, err = os.Create(scFileName) - if err != nil { - return err - } - defer file.Close() - - // make copy of file - for _, line := range lines { - fmt.Fprintln(file, line) - } - - // append any new funcs - for _, f := range s.Funcs { - if existing[f.FuncName] == "" { - s.generateGoFuncSignature(file, f) - } - } - - return os.Remove(scOriginal) -} - -func (s *Schema) generateGoFuncSignature(file *os.File, f *Func) { - fmt.Fprintf(file, "\nfunc %s(ctx wasmlib.Sc%sContext, f *%sContext) {\n", f.FuncName, f.Kind, f.Type) +func (g *GoGenerator) generateFuncSignature(f *Func) { + g.printf("\nfunc %s(ctx wasmlib.Sc%sContext, f *%sContext) {\n", f.FuncName, f.Kind, f.Type) switch f.FuncName { - case "funcInit": - fmt.Fprintf(file, " if f.Params.Owner().Exists() {\n") - fmt.Fprintf(file, " f.State.Owner().SetValue(f.Params.Owner().Value())\n") - fmt.Fprintf(file, " return\n") - fmt.Fprintf(file, " }\n") - fmt.Fprintf(file, " f.State.Owner().SetValue(ctx.ContractCreator())\n") - case "funcSetOwner": - fmt.Fprintf(file, " f.State.Owner().SetValue(f.Params.Owner().Value())\n") - case "viewGetOwner": - fmt.Fprintf(file, " f.Results.Owner().SetValue(f.State.Owner().Value())\n") + case SpecialFuncInit: + g.printf(" if f.Params.Owner().Exists() {\n") + g.printf(" f.State.Owner().SetValue(f.Params.Owner().Value())\n") + g.printf(" return\n") + g.printf(" }\n") + g.printf(" f.State.Owner().SetValue(ctx.ContractCreator())\n") + case SpecialFuncSetOwner: + g.printf(" f.State.Owner().SetValue(f.Params.Owner().Value())\n") + case SpecialViewGetOwner: + g.printf(" f.Results.Owner().SetValue(f.State.Owner().Value())\n") default: } - fmt.Fprintf(file, "}\n") -} - -func (s *Schema) generateGoFuncsNew(scFileName string) error { - file, err := os.Create(scFileName) - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(false)) - fmt.Fprintf(file, "package %s\n\n", s.Name) - fmt.Fprintln(file, importWasmLib) - - for _, f := range s.Funcs { - s.generateGoFuncSignature(file, f) - } - return nil + g.printf("}\n") } -func (s *Schema) generateGoKeys() error { - file, err := os.Create("keys.go") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n\n", s.Name) - fmt.Fprint(file, importWasmLib) - - s.KeyID = 0 - s.generateGoKeysIndexes(s.Params, "Param") - s.generateGoKeysIndexes(s.Results, "Result") - s.generateGoKeysIndexes(s.StateVars, "State") - s.flushGoConsts(file) - - size := s.KeyID - fmt.Fprintf(file, "\nconst keyMapLen = %d\n", size) - fmt.Fprintf(file, "\nvar keyMap = [keyMapLen]wasmlib.Key{\n") - s.generateGoKeysArray(file, s.Params, "Param") - s.generateGoKeysArray(file, s.Results, "Result") - s.generateGoKeysArray(file, s.StateVars, "State") - fmt.Fprintf(file, "}\n") - fmt.Fprintf(file, "\nvar idxMap [keyMapLen]wasmlib.Key32\n") - return nil -} - -func (s *Schema) generateGoKeysArray(file *os.File, fields []*Field, prefix string) { +func (g *GoGenerator) generateKeysArray(fields []*Field, prefix string) { for _, field := range fields { if field.Alias == AliasThis { continue } name := prefix + capitalize(field.Name) - fmt.Fprintf(file, "\t%s,\n", name) - s.KeyID++ + g.printf("\t%s,\n", name) + g.s.KeyID++ } } -func (s *Schema) generateGoKeysIndexes(fields []*Field, prefix string) { +func (g *GoGenerator) generateKeysIndexes(fields []*Field, prefix string) { for _, field := range fields { if field.Alias == AliasThis { continue } name := "Idx" + prefix + capitalize(field.Name) - field.KeyID = s.KeyID + field.KeyID = g.s.KeyID value := strconv.Itoa(field.KeyID) - s.KeyID++ - s.appendConst(name, value) - } -} - -func (s *Schema) generateGoLib() error { - file, err := os.Create("lib.go") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprintln(file, "//nolint:dupl") - fmt.Fprintf(file, "package %s\n\n", s.Name) - fmt.Fprintln(file, importWasmLib) - - fmt.Fprintf(file, "func OnLoad() {\n") - fmt.Fprintf(file, "\texports := wasmlib.NewScExports()\n") - for _, f := range s.Funcs { - constName := capitalize(f.FuncName) - fmt.Fprintf(file, "\texports.Add%s(%s, %sThunk)\n", f.Kind, constName, f.FuncName) - } - - fmt.Fprintf(file, "\n\tfor i, key := range keyMap {\n") - fmt.Fprintf(file, "\t\tidxMap[i] = key.KeyID()\n") - fmt.Fprintf(file, "\t}\n") - - fmt.Fprintf(file, "}\n") - - // generate parameter structs and thunks to set up and check parameters - for _, f := range s.Funcs { - s.generateGoThunk(file, f) + g.s.KeyID++ + g.s.appendConst(name, value) } - return nil } -func (s *Schema) generateGoProxy(file *os.File, field *Field, mutability string) { - if field.Array { - s.generateGoProxyArray(file, field, mutability) - return - } - - if field.MapKey != "" { - s.generateGoProxyMap(file, field, mutability) +func (g *GoGenerator) generateLanguageSpecificFiles() error { + if g.s.CoreContracts { + return nil } + return g.createSourceFile("../main", g.writeSpecialMain) } -func (s *Schema) generateGoProxyArray(file *os.File, field *Field, mutability string) { - proxyType := mutability + field.Type - arrayType := "ArrayOf" + proxyType - if field.Name[0] >= 'A' && field.Name[0] <= 'Z' { - fmt.Fprintf(file, "\ntype %s%s = %s\n", mutability, field.Name, arrayType) - } - if s.NewTypes[arrayType] { - // already generated this array - return - } - s.NewTypes[arrayType] = true - - fmt.Fprintf(file, "\ntype %s struct {\n", arrayType) - fmt.Fprintf(file, "\tobjID int32\n") - fmt.Fprintf(file, "}\n") +func (g *GoGenerator) generateProxyArray(field *Field, mutability, arrayType, proxyType string) { + g.printf("\ntype %s struct {\n", arrayType) + g.printf("\tobjID int32\n") + g.printf("}\n") if mutability == PropMutable { - fmt.Fprintf(file, "\nfunc (a %s) Clear() {\n", arrayType) - fmt.Fprintf(file, "\twasmlib.Clear(a.objID)\n") - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (a %s) Clear() {\n", arrayType) + g.printf("\twasmlib.Clear(a.objID)\n") + g.printf("}\n") } - fmt.Fprintf(file, "\nfunc (a %s) Length() int32 {\n", arrayType) - fmt.Fprintf(file, "\treturn wasmlib.GetLength(a.objID)\n") - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (a %s) Length() int32 {\n", arrayType) + g.printf("\treturn wasmlib.GetLength(a.objID)\n") + g.printf("}\n") if field.TypeID == 0 { - s.generateGoProxyArrayNewType(file, field, proxyType, arrayType) + g.generateProxyArrayNewType(field, proxyType, arrayType) return } // array of predefined type - fmt.Fprintf(file, "\nfunc (a %s) Get%s(index int32) wasmlib.Sc%s {\n", arrayType, field.Type, proxyType) - fmt.Fprintf(file, "\treturn wasmlib.NewSc%s(a.objID, wasmlib.Key32(index))\n", proxyType) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (a %s) Get%s(index int32) wasmlib.Sc%s {\n", arrayType, field.Type, proxyType) + g.printf("\treturn wasmlib.NewSc%s(a.objID, wasmlib.Key32(index))\n", proxyType) + g.printf("}\n") } -func (s *Schema) generateGoProxyArrayNewType(file *os.File, field *Field, proxyType, arrayType string) { - for _, subtype := range s.Typedefs { +func (g *GoGenerator) generateProxyArrayNewType(field *Field, proxyType, arrayType string) { + for _, subtype := range g.s.Typedefs { if subtype.Name != field.Type { continue } @@ -502,58 +241,47 @@ func (s *Schema) generateGoProxyArrayNewType(file *os.File, field *Field, proxyT if varType == "" { varType = goTypeBytes } - varType = s.generateGoArrayType(varType) + varType = g.generateArrayType(varType) } - fmt.Fprintf(file, "\nfunc (a %s) Get%s(index int32) %s {\n", arrayType, field.Type, proxyType) - fmt.Fprintf(file, "\tsubID := wasmlib.GetObjectID(a.objID, wasmlib.Key32(index), %s)\n", varType) - fmt.Fprintf(file, "\treturn %s{objID: subID}\n", proxyType) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (a %s) Get%s(index int32) %s {\n", arrayType, field.Type, proxyType) + g.printf("\tsubID := wasmlib.GetObjectID(a.objID, wasmlib.Key32(index), %s)\n", varType) + g.printf("\treturn %s{objID: subID}\n", proxyType) + g.printf("}\n") return } - fmt.Fprintf(file, "\nfunc (a %s) Get%s(index int32) %s {\n", arrayType, field.Type, proxyType) - fmt.Fprintf(file, "\treturn %s{objID: a.objID, keyID: wasmlib.Key32(index)}\n", proxyType) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (a %s) Get%s(index int32) %s {\n", arrayType, field.Type, proxyType) + g.printf("\treturn %s{objID: a.objID, keyID: wasmlib.Key32(index)}\n", proxyType) + g.printf("}\n") } -func (s *Schema) generateGoProxyMap(file *os.File, field *Field, mutability string) { - proxyType := mutability + field.Type - mapType := "Map" + field.MapKey + "To" + proxyType - if field.Name[0] >= 'A' && field.Name[0] <= 'Z' { - fmt.Fprintf(file, "\ntype %s%s = %s\n", mutability, field.Name, mapType) - } - if s.NewTypes[mapType] { - // already generated this map - return - } - s.NewTypes[mapType] = true - +func (g *GoGenerator) generateProxyMap(field *Field, mutability, mapType, proxyType string) { keyType := goTypes[field.MapKey] keyValue := goKeys[field.MapKey] - fmt.Fprintf(file, "\ntype %s struct {\n", mapType) - fmt.Fprintf(file, "\tobjID int32\n") - fmt.Fprintf(file, "}\n") + g.printf("\ntype %s struct {\n", mapType) + g.printf("\tobjID int32\n") + g.printf("}\n") if mutability == PropMutable { - fmt.Fprintf(file, "\nfunc (m %s) Clear() {\n", mapType) - fmt.Fprintf(file, "\twasmlib.Clear(m.objID)\n") - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (m %s) Clear() {\n", mapType) + g.printf("\twasmlib.Clear(m.objID)\n") + g.printf("}\n") } if field.TypeID == 0 { - s.generateGoProxyMapNewType(file, field, proxyType, mapType, keyType, keyValue) + g.generateProxyMapNewType(field, proxyType, mapType, keyType, keyValue) return } // map of predefined type - fmt.Fprintf(file, "\nfunc (m %s) Get%s(key %s) wasmlib.Sc%s {\n", mapType, field.Type, keyType, proxyType) - fmt.Fprintf(file, "\treturn wasmlib.NewSc%s(m.objID, %s.KeyID())\n", proxyType, keyValue) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (m %s) Get%s(key %s) wasmlib.Sc%s {\n", mapType, field.Type, keyType, proxyType) + g.printf("\treturn wasmlib.NewSc%s(m.objID, %s.KeyID())\n", proxyType, keyValue) + g.printf("}\n") } -func (s *Schema) generateGoProxyMapNewType(file *os.File, field *Field, proxyType, mapType, keyType, keyValue string) { - for _, subtype := range s.Typedefs { +func (g *GoGenerator) generateProxyMapNewType(field *Field, proxyType, mapType, keyType, keyValue string) { + for _, subtype := range g.s.Typedefs { if subtype.Name != field.Type { continue } @@ -563,115 +291,43 @@ func (s *Schema) generateGoProxyMapNewType(file *os.File, field *Field, proxyTyp if varType == "" { varType = goTypeBytes } - varType = s.generateGoArrayType(varType) + varType = g.generateArrayType(varType) } - fmt.Fprintf(file, "\nfunc (m %s) Get%s(key %s) %s {\n", mapType, field.Type, keyType, proxyType) - fmt.Fprintf(file, "\tsubID := wasmlib.GetObjectID(m.objID, %s.KeyID(), %s)\n", keyValue, varType) - fmt.Fprintf(file, "\treturn %s{objID: subID}\n", proxyType) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (m %s) Get%s(key %s) %s {\n", mapType, field.Type, keyType, proxyType) + g.printf("\tsubID := wasmlib.GetObjectID(m.objID, %s.KeyID(), %s)\n", keyValue, varType) + g.printf("\treturn %s{objID: subID}\n", proxyType) + g.printf("}\n") return } - fmt.Fprintf(file, "\nfunc (m %s) Get%s(key %s) %s {\n", mapType, field.Type, keyType, proxyType) - fmt.Fprintf(file, "\treturn %s{objID: m.objID, keyID: %s.KeyID()}\n", proxyType, keyValue) - fmt.Fprintf(file, "}\n") -} - -func (s *Schema) generateGoParams() error { - file, err := os.Create("params.go") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n", s.Name) - - totalParams := 0 - for _, f := range s.Funcs { - totalParams += len(f.Params) - } - if totalParams != 0 { - fmt.Fprintf(file, "\n"+importWasmLib) - } - - for _, f := range s.Funcs { - if len(f.Params) == 0 { - continue - } - s.generateGoStruct(file, f.Params, PropImmutable, f.Type, "Params") - s.generateGoStruct(file, f.Params, PropMutable, f.Type, "Params") - } - - return nil -} - -func (s *Schema) generateGoResults() error { - file, err := os.Create("results.go") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n", s.Name) - - results := 0 - for _, f := range s.Funcs { - results += len(f.Results) - } - if results != 0 { - fmt.Fprintf(file, "\n"+importWasmLib) - } - - for _, f := range s.Funcs { - if len(f.Results) == 0 { - continue - } - s.generateGoStruct(file, f.Results, PropImmutable, f.Type, "Results") - s.generateGoStruct(file, f.Results, PropMutable, f.Type, "Results") - } - return nil + g.printf("\nfunc (m %s) Get%s(key %s) %s {\n", mapType, field.Type, keyType, proxyType) + g.printf("\treturn %s{objID: m.objID, keyID: %s.KeyID()}\n", proxyType, keyValue) + g.printf("}\n") } -func (s *Schema) generateGoState() error { - file, err := os.Create("state.go") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n", s.Name) - if len(s.StateVars) != 0 { - fmt.Fprintf(file, "\n"+importWasmLib) +func (g *GoGenerator) generateProxyReference(field *Field, mutability, typeName string) { + if field.Name[0] >= 'A' && field.Name[0] <= 'Z' { + g.printf("\ntype %s%s = %s\n", mutability, field.Name, typeName) } - - s.generateGoStruct(file, s.StateVars, PropImmutable, s.FullName, "State") - s.generateGoStruct(file, s.StateVars, PropMutable, s.FullName, "State") - return nil } -func (s *Schema) generateGoStruct(file *os.File, fields []*Field, mutability, typeName, kind string) { +func (g *GoGenerator) generateProxyStruct(fields []*Field, mutability, typeName, kind string) { typeName = mutability + typeName + kind kind = strings.TrimSuffix(kind, "s") // first generate necessary array and map types for _, field := range fields { - s.generateGoProxy(file, field, mutability) + g.generateProxy(field, mutability) } - fmt.Fprintf(file, "\ntype %s struct {\n", typeName) - fmt.Fprintf(file, "\tid int32\n") - fmt.Fprintf(file, "}\n") + g.printf("\ntype %s struct {\n", typeName) + g.printf("\tid int32\n") + g.printf("}\n") for _, field := range fields { varName := capitalize(field.Name) varID := "idxMap[Idx" + kind + varName + "]" - if s.CoreContracts { + if g.s.CoreContracts { varID = kind + varName + ".KeyID()" } varType := goTypeIds[field.Type] @@ -679,172 +335,165 @@ func (s *Schema) generateGoStruct(file *os.File, fields []*Field, mutability, ty varType = goTypeBytes } if field.Array { - varType = s.generateGoArrayType(varType) + varType = g.generateArrayType(varType) arrayType := "ArrayOf" + mutability + field.Type - fmt.Fprintf(file, "\nfunc (s %s) %s() %s {\n", typeName, varName, arrayType) - fmt.Fprintf(file, "\tarrID := wasmlib.GetObjectID(s.id, %s, %s)\n", varID, varType) - fmt.Fprintf(file, "\treturn %s{objID: arrID}\n", arrayType) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (s %s) %s() %s {\n", typeName, varName, arrayType) + g.printf("\tarrID := wasmlib.GetObjectID(s.id, %s, %s)\n", varID, varType) + g.printf("\treturn %s{objID: arrID}\n", arrayType) + g.printf("}\n") continue } if field.MapKey != "" { varType = goTypeMap mapType := "Map" + field.MapKey + "To" + mutability + field.Type - fmt.Fprintf(file, "\nfunc (s %s) %s() %s {\n", typeName, varName, mapType) + g.printf("\nfunc (s %s) %s() %s {\n", typeName, varName, mapType) mapID := "s.id" if field.Alias != AliasThis { mapID = "mapID" - fmt.Fprintf(file, "\tmapID := wasmlib.GetObjectID(s.id, %s, %s)\n", varID, varType) + g.printf("\tmapID := wasmlib.GetObjectID(s.id, %s, %s)\n", varID, varType) } - fmt.Fprintf(file, "\treturn %s{objID: %s}\n", mapType, mapID) - fmt.Fprintf(file, "}\n") + g.printf("\treturn %s{objID: %s}\n", mapType, mapID) + g.printf("}\n") continue } proxyType := mutability + field.Type if field.TypeID == 0 { - fmt.Fprintf(file, "\nfunc (s %s) %s() %s {\n", typeName, varName, proxyType) - fmt.Fprintf(file, "\treturn %s{objID: s.id, keyID: %s}\n", proxyType, varID) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (s %s) %s() %s {\n", typeName, varName, proxyType) + g.printf("\treturn %s{objID: s.id, keyID: %s}\n", proxyType, varID) + g.printf("}\n") continue } - fmt.Fprintf(file, "\nfunc (s %s) %s() wasmlib.Sc%s {\n", typeName, varName, proxyType) - fmt.Fprintf(file, "\treturn wasmlib.NewSc%s(s.id, %s)\n", proxyType, varID) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc (s %s) %s() wasmlib.Sc%s {\n", typeName, varName, proxyType) + g.printf("\treturn wasmlib.NewSc%s(s.id, %s)\n", proxyType, varID) + g.printf("}\n") } } -func (s *Schema) generateGoSubtypes() error { - if len(s.Typedefs) == 0 { - return nil - } +func (g *GoGenerator) generateStruct(typeDef *Struct) { + nameLen, typeLen := calculatePadding(typeDef.Fields, goTypes, false) - file, err := os.Create("typedefs.go") - if err != nil { - return err + g.printf("\ntype %s struct {\n", typeDef.Name) + for _, field := range typeDef.Fields { + fldName := pad(capitalize(field.Name), nameLen) + fldType := goTypes[field.Type] + if field.Comment != "" { + fldType = pad(fldType, typeLen) + } + g.printf("\t%s %s%s\n", fldName, fldType, field.Comment) } - defer file.Close() + g.printf("}\n") - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n\n", s.Name) - fmt.Fprint(file, importWasmLib) + // write encoder and decoder for struct + g.printf("\nfunc New%sFromBytes(bytes []byte) *%s {\n", typeDef.Name, typeDef.Name) + g.printf("\tdecode := wasmlib.NewBytesDecoder(bytes)\n") + g.printf("\tdata := &%s{}\n", typeDef.Name) + for _, field := range typeDef.Fields { + name := capitalize(field.Name) + g.printf("\tdata.%s = decode.%s()\n", name, field.Type) + } + g.printf("\tdecode.Close()\n") + g.printf("\treturn data\n}\n") - for _, subtype := range s.Typedefs { - s.generateGoProxy(file, subtype, PropImmutable) - s.generateGoProxy(file, subtype, PropMutable) + g.printf("\nfunc (o *%s) Bytes() []byte {\n", typeDef.Name) + g.printf("\treturn wasmlib.NewBytesEncoder().\n") + for _, field := range typeDef.Fields { + name := capitalize(field.Name) + g.printf("\t\t%s(o.%s).\n", field.Type, name) } + g.printf("\t\tData()\n}\n") - return nil + g.generateStructProxy(typeDef, false) + g.generateStructProxy(typeDef, true) } -func (s *Schema) GenerateGoTests() error { - err := os.MkdirAll("test", 0o755) - if err != nil { - return err - } - err = os.Chdir("test") - if err != nil { - return err +func (g *GoGenerator) generateStructProxy(typeDef *Struct, mutable bool) { + typeName := PropImmutable + typeDef.Name + if mutable { + typeName = PropMutable + typeDef.Name } - defer func() { - _ = os.Chdir("..") - }() - // do not overwrite existing file - name := strings.ToLower(s.Name) - filename := name + "_test.go" - file, err := os.Open(filename) - if err == nil { - file.Close() - return nil - } + g.printf("\ntype %s struct {\n", typeName) + g.printf("\tobjID int32\n") + g.printf("\tkeyID wasmlib.Key32\n") + g.printf("}\n") - file, err = os.Create(filename) - if err != nil { - return err - } - defer file.Close() + g.printf("\nfunc (o %s) Exists() bool {\n", typeName) + g.printf("\treturn wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES)\n") + g.printf("}\n") - module := ModuleName + strings.ReplaceAll(ModuleCwd[len(ModulePath):], "\\", "/") - fmt.Fprintln(file, "package test") - fmt.Fprintln(file) - fmt.Fprintln(file, "import (") - fmt.Fprintln(file, "\t\"testing\"") - fmt.Fprintln(file) - fmt.Fprintf(file, "\t\"%s\"\n", module) - fmt.Fprintln(file, "\t\"github.com/iotaledger/wasp/packages/vm/wasmsolo\"") - fmt.Fprintln(file, "\t\"github.com/stretchr/testify/require\"") - fmt.Fprintln(file, ")") - fmt.Fprintln(file) - fmt.Fprintln(file, "func TestDeploy(t *testing.T) {") - fmt.Fprintf(file, "\tctx := wasmsolo.NewSoloContext(t, %s.ScName, %s.OnLoad)\n", name, name) - fmt.Fprintf(file, "\trequire.NoError(t, ctx.ContractExists(%s.ScName))\n", name) - fmt.Fprintln(file, "}") + if mutable { + g.printf("\nfunc (o %s) SetValue(value *%s) {\n", typeName, typeDef.Name) + g.printf("\twasmlib.SetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES, value.Bytes())\n") + g.printf("}\n") + } - return nil + g.printf("\nfunc (o %s) Value() *%s {\n", typeName, typeDef.Name) + g.printf("\treturn New%sFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES))\n", typeDef.Name) + g.printf("}\n") } -func (s *Schema) generateGoThunk(file *os.File, f *Func) { +func (g *GoGenerator) generateThunk(f *Func) { nameLen := f.nameLen(5) - fmt.Fprintf(file, "\ntype %sContext struct {\n", f.Type) - if len(f.Params) != 0 { - fmt.Fprintf(file, "\t%s Immutable%sParams\n", pad("Params", nameLen), f.Type) - } - if len(f.Results) != 0 { - fmt.Fprintf(file, "\tResults Mutable%sResults\n", f.Type) - } mutability := PropMutable if f.Kind == KindView { mutability = PropImmutable } - fmt.Fprintf(file, "\t%s %s%sState\n", pad("State", nameLen), mutability, s.FullName) - fmt.Fprintf(file, "}\n") + g.printf("\ntype %sContext struct {\n", f.Type) + if len(f.Params) != 0 { + g.printf("\t%s Immutable%sParams\n", pad("Params", nameLen), f.Type) + } + if len(f.Results) != 0 { + g.printf("\tResults Mutable%sResults\n", f.Type) + } + g.printf("\t%s %s%sState\n", pad("State", nameLen), mutability, g.s.FullName) + g.printf("}\n") - fmt.Fprintf(file, "\nfunc %sThunk(ctx wasmlib.Sc%sContext) {\n", f.FuncName, f.Kind) - fmt.Fprintf(file, "\tctx.Log(\"%s.%s\")\n", s.Name, f.FuncName) + g.printf("\nfunc %sThunk(ctx wasmlib.Sc%sContext) {\n", f.FuncName, f.Kind) + g.printf("\tctx.Log(\"%s.%s\")\n", g.s.Name, f.FuncName) if f.Access != "" { - s.generateGoThunkAccessCheck(file, f) + g.generateThunkAccessCheck(f) } - fmt.Fprintf(file, "\tf := &%sContext{\n", f.Type) + g.printf("\tf := &%sContext{\n", f.Type) if len(f.Params) != 0 { - fmt.Fprintf(file, "\t\tParams: Immutable%sParams{\n", f.Type) - fmt.Fprintf(file, "\t\t\tid: wasmlib.OBJ_ID_PARAMS,\n") - fmt.Fprintf(file, "\t\t},\n") + g.printf("\t\tParams: Immutable%sParams{\n", f.Type) + g.printf("\t\t\tid: wasmlib.OBJ_ID_PARAMS,\n") + g.printf("\t\t},\n") } if len(f.Results) != 0 { - fmt.Fprintf(file, "\t\tResults: Mutable%sResults{\n", f.Type) - fmt.Fprintf(file, "\t\t\tid: wasmlib.OBJ_ID_RESULTS,\n") - fmt.Fprintf(file, "\t\t},\n") + g.printf("\t\tResults: Mutable%sResults{\n", f.Type) + g.printf("\t\t\tid: wasmlib.OBJ_ID_RESULTS,\n") + g.printf("\t\t},\n") } - fmt.Fprintf(file, "\t\tState: %s%sState{\n", mutability, s.FullName) - fmt.Fprintf(file, "\t\t\tid: wasmlib.OBJ_ID_STATE,\n") - fmt.Fprintf(file, "\t\t},\n") + g.printf("\t\tState: %s%sState{\n", mutability, g.s.FullName) + g.printf("\t\t\tid: wasmlib.OBJ_ID_STATE,\n") + g.printf("\t\t},\n") - fmt.Fprintf(file, "\t}\n") + g.printf("\t}\n") for _, param := range f.Params { if !param.Optional { name := capitalize(param.Name) - fmt.Fprintf(file, "\tctx.Require(f.Params.%s().Exists(), \"missing mandatory %s\")\n", name, param.Name) + g.printf("\tctx.Require(f.Params.%s().Exists(), \"missing mandatory %s\")\n", name, param.Name) } } - fmt.Fprintf(file, "\t%s(ctx, f)\n", f.FuncName) - fmt.Fprintf(file, "\tctx.Log(\"%s.%s ok\")\n", s.Name, f.FuncName) - fmt.Fprintf(file, "}\n") + g.printf("\t%s(ctx, f)\n", f.FuncName) + g.printf("\tctx.Log(\"%s.%s ok\")\n", g.s.Name, f.FuncName) + g.printf("}\n") } -func (s *Schema) generateGoThunkAccessCheck(file *os.File, f *Func) { +func (g *GoGenerator) generateThunkAccessCheck(f *Func) { grant := f.Access index := strings.Index(grant, "//") if index >= 0 { - fmt.Fprintf(file, "\t%s\n", grant[index:]) + g.printf("\t%s\n", grant[index:]) grant = strings.TrimSpace(grant[:index]) } switch grant { @@ -855,147 +504,211 @@ func (s *Schema) generateGoThunkAccessCheck(file *os.File, f *Func) { case AccessCreator: grant = "ctx.ContractCreator()" default: - fmt.Fprintf(file, "\taccess := ctx.State().GetAgentID(wasmlib.Key(\"%s\"))\n", grant) - fmt.Fprintf(file, "\tctx.Require(access.Exists(), \"access not set: %s\")\n", grant) + g.printf("\taccess := ctx.State().GetAgentID(wasmlib.Key(\"%s\"))\n", grant) + g.printf("\tctx.Require(access.Exists(), \"access not set: %s\")\n", grant) grant = "access.Value()" } - fmt.Fprintf(file, "\tctx.Require(ctx.Caller() == %s, \"no permission\")\n\n", grant) + g.printf("\tctx.Require(ctx.Caller() == %s, \"no permission\")\n\n", grant) } -func (s *Schema) generateGoTypes() error { - if len(s.Structs) == 0 { - return nil - } +func (g *GoGenerator) packageName() string { + return fmt.Sprintf("package %s\n", g.s.Name) +} - file, err := os.Create("types.go") - if err != nil { - return err +func (g *GoGenerator) writeConsts() { + g.println(g.packageName()) + g.println(goImportWasmLib) + + scName := g.s.Name + if g.s.CoreContracts { + // remove 'core' prefix + scName = scName[4:] + } + g.s.appendConst("ScName", "\""+scName+"\"") + if g.s.Description != "" { + g.s.appendConst("ScDescription", "\""+g.s.Description+"\"") } - defer file.Close() + hName := iscp.Hn(scName) + hNameType := "wasmlib.ScHname" + g.s.appendConst("HScName", hNameType+"(0x"+hName.String()+")") + g.flushConsts() - fmt.Fprintln(file, copyright(true)) - fmt.Fprintf(file, "package %s\n\n", s.Name) - fmt.Fprint(file, importWasmLib) + g.generateConstsFields(g.s.Params, "Param") + g.generateConstsFields(g.s.Results, "Result") + g.generateConstsFields(g.s.StateVars, "State") - for _, typeDef := range s.Structs { - s.generateGoType(file, typeDef) - } + if len(g.s.Funcs) != 0 { + for _, f := range g.s.Funcs { + constName := capitalize(f.FuncName) + g.s.appendConst(constName, "\""+f.String+"\"") + } + g.flushConsts() - return nil + for _, f := range g.s.Funcs { + constHname := "H" + capitalize(f.FuncName) + g.s.appendConst(constHname, hNameType+"(0x"+f.Hname.String()+")") + } + g.flushConsts() + } } -func (s *Schema) generateGoType(file *os.File, typeDef *Struct) { - nameLen, typeLen := calculatePadding(typeDef.Fields, goTypes, false) +func (g *GoGenerator) writeContract() { + g.println(g.packageName()) + g.println(goImportWasmLib) - fmt.Fprintf(file, "\ntype %s struct {\n", typeDef.Name) - for _, field := range typeDef.Fields { - fldName := pad(capitalize(field.Name), nameLen) - fldType := goTypes[field.Type] - if field.Comment != "" { - fldType = pad(fldType, typeLen) + for _, f := range g.s.Funcs { + nameLen := f.nameLen(4) + kind := f.Kind + if f.Type == InitFunc { + kind = f.Type + f.Kind + } + g.printf("\ntype %sCall struct {\n", f.Type) + g.printf("\t%s *wasmlib.Sc%s\n", pad(KindFunc, nameLen), kind) + if len(f.Params) != 0 { + g.printf("\t%s Mutable%sParams\n", pad("Params", nameLen), f.Type) + } + if len(f.Results) != 0 { + g.printf("\tResults Immutable%sResults\n", f.Type) } - fmt.Fprintf(file, "\t%s %s%s\n", fldName, fldType, field.Comment) + g.printf("}\n") } - fmt.Fprintf(file, "}\n") - // write encoder and decoder for struct - fmt.Fprintf(file, "\nfunc New%sFromBytes(bytes []byte) *%s {\n", typeDef.Name, typeDef.Name) - fmt.Fprintf(file, "\tdecode := wasmlib.NewBytesDecoder(bytes)\n") - fmt.Fprintf(file, "\tdata := &%s{}\n", typeDef.Name) - for _, field := range typeDef.Fields { - name := capitalize(field.Name) - fmt.Fprintf(file, "\tdata.%s = decode.%s()\n", name, field.Type) + g.generateContractFuncs() + + if g.s.CoreContracts { + g.printf("\nfunc OnLoad() {\n") + g.printf("\texports := wasmlib.NewScExports()\n") + for _, f := range g.s.Funcs { + constName := capitalize(f.FuncName) + g.printf("\texports.Add%s(%s, wasmlib.%sError)\n", f.Kind, constName, f.Kind) + } + g.printf("}\n") } - fmt.Fprintf(file, "\tdecode.Close()\n") - fmt.Fprintf(file, "\treturn data\n}\n") +} - fmt.Fprintf(file, "\nfunc (o *%s) Bytes() []byte {\n", typeDef.Name) - fmt.Fprintf(file, "\treturn wasmlib.NewBytesEncoder().\n") - for _, field := range typeDef.Fields { - name := capitalize(field.Name) - fmt.Fprintf(file, "\t\t%s(o.%s).\n", field.Type, name) +func (g *GoGenerator) writeInitialFuncs() { + g.println(g.packageName()) + g.println(goImportWasmLib) + + for _, f := range g.s.Funcs { + g.generateFuncSignature(f) } - fmt.Fprintf(file, "\t\tData()\n}\n") +} + +func (g *GoGenerator) writeKeys() { + g.println(g.packageName()) + g.println(goImportWasmLib) - s.generateGoTypeProxy(file, typeDef, false) - s.generateGoTypeProxy(file, typeDef, true) + g.s.KeyID = 0 + g.generateKeysIndexes(g.s.Params, "Param") + g.generateKeysIndexes(g.s.Results, "Result") + g.generateKeysIndexes(g.s.StateVars, "State") + g.flushConsts() + + size := g.s.KeyID + g.printf("\nconst keyMapLen = %d\n", size) + g.printf("\nvar keyMap = [keyMapLen]wasmlib.Key{\n") + g.generateKeysArray(g.s.Params, "Param") + g.generateKeysArray(g.s.Results, "Result") + g.generateKeysArray(g.s.StateVars, "State") + g.printf("}\n") + g.printf("\nvar idxMap [keyMapLen]wasmlib.Key32\n") } -func (s *Schema) generateGoTypeProxy(file *os.File, typeDef *Struct, mutable bool) { - typeName := PropImmutable + typeDef.Name - if mutable { - typeName = PropMutable + typeDef.Name +func (g *GoGenerator) writeLib() { + g.println("//nolint:dupl") + g.println(g.packageName()) + g.println(goImportWasmLib) + + g.printf("\nfunc OnLoad() {\n") + g.printf("\texports := wasmlib.NewScExports()\n") + for _, f := range g.s.Funcs { + constName := capitalize(f.FuncName) + g.printf("\texports.Add%s(%s, %sThunk)\n", f.Kind, constName, f.FuncName) } - fmt.Fprintf(file, "\ntype %s struct {\n", typeName) - fmt.Fprintf(file, "\tobjID int32\n") - fmt.Fprintf(file, "\tkeyID wasmlib.Key32\n") - fmt.Fprintf(file, "}\n") + g.printf("\n\tfor i, key := range keyMap {\n") + g.printf("\t\tidxMap[i] = key.KeyID()\n") + g.printf("\t}\n") - fmt.Fprintf(file, "\nfunc (o %s) Exists() bool {\n", typeName) - fmt.Fprintf(file, "\treturn wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES)\n") - fmt.Fprintf(file, "}\n") + g.printf("}\n") - if mutable { - fmt.Fprintf(file, "\nfunc (o %s) SetValue(value *%s) {\n", typeName, typeDef.Name) - fmt.Fprintf(file, "\twasmlib.SetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES, value.Bytes())\n") - fmt.Fprintf(file, "}\n") + // generate parameter structs and thunks to set up and check parameters + for _, f := range g.s.Funcs { + g.generateThunk(f) } - - fmt.Fprintf(file, "\nfunc (o %s) Value() *%s {\n", typeName, typeDef.Name) - fmt.Fprintf(file, "\treturn New%sFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES))\n", typeDef.Name) - fmt.Fprintf(file, "}\n") } -func (s *Schema) generateGoWasmMain() error { - err := os.MkdirAll("wasmmain", 0o755) - if err != nil { - return err - } +func (g *GoGenerator) writeParams() { + g.printf(g.packageName()) + g.println(goImportWasmLib) - file, err := os.Create("wasmmain/main.go") - if err != nil { - return err + for _, f := range g.s.Funcs { + if len(f.Params) == 0 { + continue + } + g.generateProxyStruct(f.Params, PropImmutable, f.Type, "Params") + g.generateProxyStruct(f.Params, PropMutable, f.Type, "Params") } - defer file.Close() +} - importname := ModuleName + strings.Replace(ModuleCwd[len(ModulePath):], "\\", "/", -1) - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprint(file, "// +build wasm\n\n") - fmt.Fprint(file, "package main\n\n") - fmt.Fprint(file, importWasmClient) - fmt.Fprintf(file, "import \"%s\"\n\n", importname) +func (g *GoGenerator) writeResults() { + g.printf(g.packageName()) + g.println(goImportWasmLib) - fmt.Fprintf(file, "func main() {\n") - fmt.Fprintf(file, "}\n\n") + for _, f := range g.s.Funcs { + if len(f.Results) == 0 { + continue + } + g.generateProxyStruct(f.Results, PropImmutable, f.Type, "Results") + g.generateProxyStruct(f.Results, PropMutable, f.Type, "Results") + } +} + +func (g *GoGenerator) writeSpecialMain() { + g.println("// +build wasm") + g.println("\npackage main") + g.println() + g.println(goImportWasmClient) + module := ModuleName + strings.Replace(ModuleCwd[len(ModulePath):], "\\", "/", -1) + g.printf("\nimport \"%s/go/%s\"\n", module, g.s.Name) - fmt.Fprintf(file, "//export on_load\n") - fmt.Fprintf(file, "func OnLoad() {\n") - fmt.Fprintf(file, "\th := &wasmclient.WasmVMHost{}\n") - fmt.Fprintf(file, "\th.ConnectWasmHost()\n") - fmt.Fprintf(file, "\t%s.OnLoad()\n", s.Name) - fmt.Fprintf(file, "}\n") + g.printf("\nfunc main() {\n") + g.printf("}\n") - return nil + g.printf("\n//export on_load\n") + g.printf("func onLoad() {\n") + g.printf("\th := &wasmclient.WasmVMHost{}\n") + g.printf("\th.ConnectWasmHost()\n") + g.printf("\t%s.OnLoad()\n", g.s.Name) + g.printf("}\n") } -func (s *Schema) flushGoConsts(file *os.File) { - if len(s.ConstNames) == 0 { - return +func (g *GoGenerator) writeState() { + g.printf(g.packageName()) + if len(g.s.StateVars) != 0 { + g.println(goImportWasmLib) } - if len(s.ConstNames) == 1 { - name := s.ConstNames[0] - value := s.ConstValues[0] - fmt.Fprintf(file, "\nconst %s = %s\n", name, value) - s.flushConsts(func(name string, value string, padLen int) {}) - return + g.generateProxyStruct(g.s.StateVars, PropImmutable, g.s.FullName, "State") + g.generateProxyStruct(g.s.StateVars, PropMutable, g.s.FullName, "State") +} + +func (g *GoGenerator) writeStructs() { + g.println(g.packageName()) + g.println(goImportWasmLib) + + for _, typeDef := range g.s.Structs { + g.generateStruct(typeDef) } +} - fmt.Fprintf(file, "\nconst (\n") - s.flushConsts(func(name string, value string, padLen int) { - fmt.Fprintf(file, "\t%s = %s\n", pad(name, padLen), value) - }) - fmt.Fprintf(file, ")\n") +func (g *GoGenerator) writeTypeDefs() { + g.println(g.packageName()) + g.println(goImportWasmLib) + + for _, subtype := range g.s.Typedefs { + g.generateProxy(subtype, PropImmutable) + g.generateProxy(subtype, PropMutable) + } } diff --git a/tools/schema/generator/generator_java.go b/tools/schema/generator/generator_java.go index 95ebc2c863..139dab062c 100644 --- a/tools/schema/generator/generator_java.go +++ b/tools/schema/generator/generator_java.go @@ -156,8 +156,7 @@ func (s *Schema) GenerateJavaFuncsNew(scFileName string) error { defer file.Close() // write file header - fmt.Fprintln(file, copyright(false)) - fmt.Fprintln(file, copyright(true)) + // fmt.Fprintln(file, copyright(false)) fmt.Fprintf(file, "package org.iota.wasp.contracts.%s;\n\n", s.Name) fmt.Fprintf(file, "import org.iota.wasp.contracts.%s.lib.*;\n", s.Name) fmt.Fprintf(file, "import org.iota.wasp.wasmlib.context.*;\n") @@ -188,7 +187,7 @@ func (s *Schema) GenerateJavaLib() error { defer file.Close() // write file header - fmt.Fprintln(file, copyright(true)) + // fmt.Fprintln(file, copyright(true)) fmt.Fprintf(file, "package org.iota.wasp.contracts.%s.lib;\n\n", s.Name) fmt.Fprintf(file, "import de.mirkosertic.bytecoder.api.*;\n") fmt.Fprintf(file, "import org.iota.wasp.contracts.%s.*;\n", s.Name) @@ -234,7 +233,7 @@ func (s *Schema) GenerateJavaConsts() error { defer file.Close() // write file header - fmt.Fprintln(file, copyright(true)) + // fmt.Fprintln(file, copyright(true)) fmt.Fprintf(file, "package org.iota.wasp.contracts.%s.lib;\n\n", s.Name) fmt.Fprintf(file, "import org.iota.wasp.wasmlib.hashtypes.*;\n") fmt.Fprintf(file, "import org.iota.wasp.wasmlib.keys.*;\n") @@ -288,7 +287,7 @@ func (s *Schema) GenerateJavaThunk(file, params *os.File, f *Func) { funcName := capitalize(f.FuncName) funcKind := capitalize(f.FuncName[:4]) - fmt.Fprintln(params, copyright(true)) + // fmt.Fprintln(params, copyright(true)) fmt.Fprintf(params, "package org.iota.wasp.contracts.%s.lib;\n", s.Name) if len(f.Params) != 0 { fmt.Fprintf(params, "\nimport org.iota.wasp.wasmlib.immutable.*;\n") @@ -363,14 +362,14 @@ func (s *Schema) GenerateJavaTypes() error { return nil } - err := os.MkdirAll("types", 0o755) + err := os.MkdirAll("structs", 0o755) if err != nil { return err } // write structs for _, typeDef := range s.Structs { - err = typeDef.GenerateJavaType(s.Name) + err = s.GenerateJavaType(typeDef) if err != nil { return err } @@ -379,29 +378,62 @@ func (s *Schema) GenerateJavaTypes() error { return nil } -func (s *Schema) GenerateJavaWasmMain() error { - file, err := os.Create("wasmmain/" + s.Name + ".go") +func (s *Schema) GenerateJavaType(td *Struct) error { + file, err := os.Create("structs/" + td.Name + ".java") if err != nil { return err } defer file.Close() - importname := ModuleName + strings.Replace(ModuleCwd[len(ModulePath):], "\\", "/", -1) + // calculate padding + nameLen, typeLen := calculatePadding(td.Fields, javaTypes, false) + // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprint(file, "// +build wasm\n\n") - fmt.Fprint(file, "package main\n\n") - fmt.Fprint(file, importWasmClient) - fmt.Fprintf(file, "import \"%s\"\n\n", importname) - - fmt.Fprintf(file, "func main() {\n") - fmt.Fprintf(file, "}\n\n") - - fmt.Fprintf(file, "//export on_load\n") - fmt.Fprintf(file, "func OnLoad() {\n") - fmt.Fprintf(file, " wasmclient.ConnectWasmHost()\n") - fmt.Fprintf(file, " %s.OnLoad()\n", s.Name) - fmt.Fprintf(file, "}\n") + // fmt.Fprint(file, copyright(true)) + fmt.Fprintf(file, "\npackage org.iota.wasp.contracts.%s.structs;\n\n", s.Name) + fmt.Fprint(file, "import org.iota.wasp.wasmlib.bytes.*;\n") + fmt.Fprint(file, "import org.iota.wasp.wasmlib.hashtypes.*;\n\n") + fmt.Fprintf(file, "public class %s {\n", td.Name) + + // write struct layout + if len(td.Fields) > 1 { + fmt.Fprint(file, " // @formatter:off\n") + } + for _, field := range td.Fields { + fldName := capitalize(field.Name) + ";" + fldType := pad(javaTypes[field.Type], typeLen) + if field.Comment != "" { + fldName = pad(fldName, nameLen+1) + } + fmt.Fprintf(file, " public %s %s%s\n", fldType, fldName, field.Comment) + } + if len(td.Fields) > 1 { + fmt.Fprint(file, " // @formatter:on\n") + } + + // write default constructor + fmt.Fprintf(file, "\n public %s() {\n }\n", td.Name) + + // write constructor from byte array + fmt.Fprintf(file, "\n public %s(byte[] bytes) {\n", td.Name) + fmt.Fprintf(file, " BytesDecoder decode = new BytesDecoder(bytes);\n") + for _, field := range td.Fields { + name := capitalize(field.Name) + fmt.Fprintf(file, " %s = decode.%s();\n", name, field.Type) + } + fmt.Fprintf(file, " decode.Close();\n") + fmt.Fprintf(file, " }\n") + + // write conversion to byte array + fmt.Fprintf(file, "\n public byte[] toBytes() {\n") + fmt.Fprintf(file, " return new BytesEncoder().\n") + for _, field := range td.Fields { + name := capitalize(field.Name) + fmt.Fprintf(file, " %s(%s).\n", field.Type, name) + } + fmt.Fprintf(file, " Data();\n }\n") + + fmt.Fprintf(file, "}\n") return nil } diff --git a/tools/schema/generator/generator_rust.go b/tools/schema/generator/generator_rust.go index ba689ab99b..0eb330e612 100644 --- a/tools/schema/generator/generator_rust.go +++ b/tools/schema/generator/generator_rust.go @@ -4,8 +4,6 @@ package generator import ( - "fmt" - "os" "regexp" "strconv" "strings" @@ -14,23 +12,21 @@ import ( ) const ( - allowDeadCode = "#![allow(dead_code)]\n" - allowUnusedImports = "#![allow(unused_imports)]\n" - useConsts = "use crate::consts::*;\n" - useCrate = "use crate::*;\n" - useKeys = "use crate::keys::*;\n" - useParams = "use crate::params::*;\n" - useResults = "use crate::results::*;\n" - useState = "use crate::state::*;\n" - useStdPtr = "use std::ptr;\n" - useSubtypes = "use crate::typedefs::*;\n" - useTypes = "use crate::types::*;\n" - useWasmLib = "use wasmlib::*;\n" - useWasmLibHost = "use wasmlib::host::*;\n" + allowDeadCode = "#![allow(dead_code)]" + allowUnusedImports = "#![allow(unused_imports)]" + useConsts = "use crate::consts::*;" + useCrate = "use crate::*;" + useKeys = "use crate::keys::*;" + useParams = "use crate::params::*;" + useResults = "use crate::results::*;" + useState = "use crate::state::*;" + useStdPtr = "use std::ptr;" + useStructs = "use crate::structs::*;" + useTypeDefs = "use crate::typedefs::*;" + useWasmLib = "use wasmlib::*;" + useWasmLibHost = "use wasmlib::host::*;" ) -var rustFuncRegexp = regexp.MustCompile(`^pub fn (\w+).+$`) - var rustTypes = StringMap{ "Address": "ScAddress", "AgentID": "ScAgentID", @@ -92,166 +88,66 @@ const ( rustTypeMap = "TYPE_MAP" ) -func (s *Schema) GenerateRust() error { - s.NewTypes = make(map[string]bool) - - if !s.CoreContracts { - err := os.MkdirAll("src", 0o755) - if err != nil { - return err - } - err = os.Chdir("src") - if err != nil { - return err - } - defer func() { - _ = os.Chdir("..") - }() - } +type RustGenerator struct { + GenBase +} - err := s.generateRustConsts() - if err != nil { - return err - } - err = s.generateRustTypes() - if err != nil { - return err - } - err = s.generateRustSubtypes() - if err != nil { - return err - } - err = s.generateRustParams() - if err != nil { - return err - } - err = s.generateRustResults() - if err != nil { - return err - } - err = s.generateRustContract() - if err != nil { - return err - } +func NewRustGenerator() *RustGenerator { + g := &RustGenerator{} + g.extension = ".rs" + g.funcRegexp = regexp.MustCompile(`^pub fn (\w+).+$`) + g.language = "Rust" + g.rootFolder = "src" + g.gen = g + return g +} - if !s.CoreContracts { - err = s.generateRustKeys() - if err != nil { - return err - } - err = s.generateRustState() - if err != nil { - return err +func (g *RustGenerator) crateOrWasmLib(withContract, withHost bool) string { + if g.s.CoreContracts { + retVal := useCrate + if withContract { + retVal += "\nuse crate::" + g.s.Name + "::*;" } - err = s.generateRustLib() - if err != nil { - return err + if withHost { + retVal += "\nuse crate::host::*;" } - err = s.generateRustFuncs() - if err != nil { - return err - } - - // rust-specific stuff - return s.generateRustCargo() + return retVal } - - return nil -} - -func (s *Schema) generateRustArrayType(varType string) string { - // native core contracts use Array16 instead of our nested array type - if s.CoreContracts { - return "TYPE_ARRAY16 | " + varType + retVal := useWasmLib + if withHost { + retVal += "\n" + useWasmLibHost } - return "TYPE_ARRAY | " + varType + return retVal } -func (s *Schema) generateRustCargo() error { - file, err := os.Open("../Cargo.toml") - if err == nil { - // already exists - file.Close() - return nil +func (g *RustGenerator) flushConsts(crateOnly bool) { + if len(g.s.ConstNames) == 0 { + return } - file, err = os.Create("../Cargo.toml") - if err != nil { - return err + crate := "" + if crateOnly { + crate = "(crate)" } - defer file.Close() - - fmt.Fprintf(file, "[package]\n") - fmt.Fprintf(file, "name = \"%s\"\n", s.Name) - fmt.Fprintf(file, "description = \"%s\"\n", s.Description) - fmt.Fprintf(file, "license = \"Apache-2.0\"\n") - fmt.Fprintf(file, "version = \"0.1.0\"\n") - fmt.Fprintf(file, "authors = [\"Eric Hop \"]\n") - fmt.Fprintf(file, "edition = \"2018\"\n") - fmt.Fprintf(file, "repository = \"https://%s\"\n", ModuleName) - fmt.Fprintf(file, "\n[lib]\n") - fmt.Fprintf(file, "crate-type = [\"cdylib\", \"rlib\"]\n") - fmt.Fprintf(file, "\n[features]\n") - fmt.Fprintf(file, "default = [\"console_error_panic_hook\"]\n") - fmt.Fprintf(file, "\n[dependencies]\n") - fmt.Fprintf(file, "wasmlib = { git = \"https://github.com/iotaledger/wasp\", branch = \"develop\" }\n") - fmt.Fprintf(file, "console_error_panic_hook = { version = \"0.1.6\", optional = true }\n") - fmt.Fprintf(file, "wee_alloc = { version = \"0.4.5\", optional = true }\n") - fmt.Fprintf(file, "\n[dev-dependencies]\n") - fmt.Fprintf(file, "wasm-bindgen-test = \"0.3.13\"\n") - - return nil + g.println() + g.s.flushConsts(func(name string, value string, padLen int) { + g.printf("pub%s const %s %s;\n", crate, pad(name+":", padLen+1), value) + }) } -func (s *Schema) generateRustConsts() error { - file, err := os.Create("consts.rs") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - formatter(file, false) - fmt.Fprintln(file, allowDeadCode) - fmt.Fprint(file, s.crateOrWasmLib(false, false)) - - scName := s.Name - if s.CoreContracts { - // remove 'core' prefix - scName = scName[4:] - } - s.appendConst("SC_NAME", "&str = \""+scName+"\"") - if s.Description != "" { - s.appendConst("SC_DESCRIPTION", "&str = \""+s.Description+"\"") - } - hName := iscp.Hn(scName) - s.appendConst("HSC_NAME", "ScHname = ScHname(0x"+hName.String()+")") - s.flushRustConsts(file, false) - - s.generateRustConstsFields(file, s.Params, "PARAM_") - s.generateRustConstsFields(file, s.Results, "RESULT_") - s.generateRustConstsFields(file, s.StateVars, "STATE_") - - if len(s.Funcs) != 0 { - for _, f := range s.Funcs { - constName := upper(snake(f.FuncName)) - s.appendConst(constName, "&str = \""+f.String+"\"") - } - s.flushRustConsts(file, s.CoreContracts) +func (g *RustGenerator) funcName(f *Func) string { + return snake(f.FuncName) +} - for _, f := range s.Funcs { - constHname := "H" + upper(snake(f.FuncName)) - s.appendConst(constHname, "ScHname = ScHname(0x"+f.Hname.String()+")") - } - s.flushRustConsts(file, s.CoreContracts) +func (g *RustGenerator) generateArrayType(varType string) string { + // native core contracts use Array16 instead of our nested array type + if g.s.CoreContracts { + return "TYPE_ARRAY16 | " + varType } - - formatter(file, true) - return nil + return "TYPE_ARRAY | " + varType } -func (s *Schema) generateRustConstsFields(file *os.File, fields []*Field, prefix string) { +func (g *RustGenerator) generateConstsFields(fields []*Field, prefix string) { if len(fields) != 0 { for _, field := range fields { if field.Alias == AliasThis { @@ -259,61 +155,20 @@ func (s *Schema) generateRustConstsFields(file *os.File, fields []*Field, prefix } name := prefix + upper(snake(field.Name)) value := "&str = \"" + field.Alias + "\"" - s.appendConst(name, value) - } - s.flushRustConsts(file, s.CoreContracts) - } -} - -func (s *Schema) generateRustContract() error { - file, err := os.Create("contract.rs") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - formatter(file, false) - fmt.Fprintln(file, allowDeadCode) - fmt.Fprintln(file, useStdPtr) - fmt.Fprint(file, s.crateOrWasmLib(true, false)) - if !s.CoreContracts { - fmt.Fprint(file, "\n"+useConsts) - fmt.Fprint(file, useParams) - fmt.Fprint(file, useResults) - } - - for _, f := range s.Funcs { - nameLen := f.nameLen(4) + 1 - kind := f.Kind - if f.Type == InitFunc { - kind = f.Type + f.Kind - } - fmt.Fprintf(file, "\npub struct %sCall {\n", f.Type) - fmt.Fprintf(file, " pub %s Sc%s,\n", pad("func:", nameLen), kind) - if len(f.Params) != 0 { - fmt.Fprintf(file, " pub %s Mutable%sParams,\n", pad("params:", nameLen), f.Type) - } - if len(f.Results) != 0 { - fmt.Fprintf(file, " pub results: Immutable%sResults,\n", f.Type) + g.s.appendConst(name, value) } - fmt.Fprintf(file, "}\n") + g.flushConsts(g.s.CoreContracts) } - - s.generateRustContractFuncs(file) - formatter(file, true) - return nil } -func (s *Schema) generateRustContractFuncs(file *os.File) { - fmt.Fprint(file, "\npub struct ScFuncs {\n") - fmt.Fprint(file, "}\n") - fmt.Fprint(file, "\nimpl ScFuncs {\n") +func (g *RustGenerator) generateContractFuncs() { + g.println("\npub struct ScFuncs {") + g.println("}") + g.println("\nimpl ScFuncs {") - for _, f := range s.Funcs { + for _, f := range g.s.Funcs { nameLen := f.nameLen(4) + 1 - funcName := snake(f.FuncName) + funcName := g.funcName(f) constName := upper(funcName) letMut := "" if len(f.Params) != 0 || len(f.Results) != 0 { @@ -323,298 +178,144 @@ func (s *Schema) generateRustContractFuncs(file *os.File) { if f.Type == InitFunc { kind = f.Type + f.Kind } - fmt.Fprintf(file, " pub fn %s(_ctx: & dyn Sc%sCallContext) -> %sCall {\n", funcName[5:], f.Kind, f.Type) - fmt.Fprintf(file, " %s%sCall {\n", letMut, f.Type) - fmt.Fprintf(file, " %s Sc%s::new(HSC_NAME, H%s),\n", pad("func:", nameLen), kind, constName) + g.printf(" pub fn %s(_ctx: & dyn Sc%sCallContext) -> %sCall {\n", funcName[5:], f.Kind, f.Type) + g.printf(" %s%sCall {\n", letMut, f.Type) + g.printf(" %s Sc%s::new(HSC_NAME, H%s),\n", pad("func:", nameLen), kind, constName) paramsID := "ptr::null_mut()" if len(f.Params) != 0 { paramsID = "&mut f.params.id" - fmt.Fprintf(file, " %s Mutable%sParams { id: 0 },\n", pad("params:", nameLen), f.Type) + g.printf(" %s Mutable%sParams { id: 0 },\n", pad("params:", nameLen), f.Type) } resultsID := "ptr::null_mut()" if len(f.Results) != 0 { resultsID = "&mut f.results.id" - fmt.Fprintf(file, " results: Immutable%sResults { id: 0 },\n", f.Type) + g.printf(" results: Immutable%sResults { id: 0 },\n", f.Type) } - fmt.Fprintf(file, " }") + g.printf(" }") if len(f.Params) != 0 || len(f.Results) != 0 { - fmt.Fprintf(file, ";\n") - fmt.Fprintf(file, " f.func.set_ptrs(%s, %s);\n", paramsID, resultsID) - fmt.Fprintf(file, " f") + g.printf(";\n") + g.printf(" f.func.set_ptrs(%s, %s);\n", paramsID, resultsID) + g.printf(" f") } - fmt.Fprintf(file, "\n }\n") + g.printf("\n }\n") } - fmt.Fprintf(file, "}\n") + g.printf("}\n") } -func (s *Schema) generateRustFuncs() error { - scFileName := s.Name + ".rs" - file, err := os.Open(scFileName) - if err != nil { - // generate initial code file - return s.generateRustFuncsNew(scFileName) - } - - // append missing function signatures to existing code file - - lines, existing, err := s.scanExistingCode(file, rustFuncRegexp) - if err != nil { - return err - } - - // save old one from overwrite - scOriginal := s.Name + ".bak" - err = os.Rename(scFileName, scOriginal) - if err != nil { - return err - } - file, err = os.Create(scFileName) - if err != nil { - return err - } - defer file.Close() - - // make copy of file - for _, line := range lines { - fmt.Fprintln(file, line) - } - - // append any new funcs - for _, f := range s.Funcs { - name := snake(f.FuncName) - if existing[name] == "" { - s.generateRustFuncSignature(file, f) - } - } - - return os.Remove(scOriginal) -} - -func (s *Schema) generateRustFuncSignature(file *os.File, f *Func) { +func (g *RustGenerator) generateFuncSignature(f *Func) { switch f.FuncName { - case "funcInit": - fmt.Fprintf(file, "\npub fn %s(ctx: &Sc%sContext, f: &%sContext) {\n", snake(f.FuncName), f.Kind, capitalize(f.Type)) - fmt.Fprintf(file, " if f.params.owner().exists() {\n") - fmt.Fprintf(file, " f.state.owner().set_value(&f.params.owner().value());\n") - fmt.Fprintf(file, " return;\n") - fmt.Fprintf(file, " }\n") - fmt.Fprintf(file, " f.state.owner().set_value(&ctx.contract_creator());\n") - case "funcSetOwner": - fmt.Fprintf(file, "\npub fn %s(_ctx: &Sc%sContext, f: &%sContext) {\n", snake(f.FuncName), f.Kind, capitalize(f.Type)) - fmt.Fprintf(file, " f.state.owner().set_value(&f.params.owner().value());\n") - case "viewGetOwner": - fmt.Fprintf(file, "\npub fn %s(_ctx: &Sc%sContext, f: &%sContext) {\n", snake(f.FuncName), f.Kind, capitalize(f.Type)) - fmt.Fprintf(file, " f.results.owner().set_value(&f.state.owner().value());\n") + case SpecialFuncInit: + g.printf("\npub fn %s(ctx: &Sc%sContext, f: &%sContext) {\n", g.funcName(f), f.Kind, capitalize(f.Type)) + g.printf(" if f.params.owner().exists() {\n") + g.printf(" f.state.owner().set_value(&f.params.owner().value());\n") + g.printf(" return;\n") + g.printf(" }\n") + g.printf(" f.state.owner().set_value(&ctx.contract_creator());\n") + case SpecialFuncSetOwner: + g.printf("\npub fn %s(_ctx: &Sc%sContext, f: &%sContext) {\n", g.funcName(f), f.Kind, capitalize(f.Type)) + g.printf(" f.state.owner().set_value(&f.params.owner().value());\n") + case SpecialViewGetOwner: + g.printf("\npub fn %s(_ctx: &Sc%sContext, f: &%sContext) {\n", g.funcName(f), f.Kind, capitalize(f.Type)) + g.printf(" f.results.owner().set_value(&f.state.owner().value());\n") default: - fmt.Fprintf(file, "\npub fn %s(_ctx: &Sc%sContext, _f: &%sContext) {\n", snake(f.FuncName), f.Kind, capitalize(f.Type)) - } - fmt.Fprintf(file, "}\n") -} - -func (s *Schema) generateRustFuncsNew(scFileName string) error { - file, err := os.Create(scFileName) - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(false)) - fmt.Fprintln(file, useWasmLib) - - fmt.Fprint(file, useCrate) - if len(s.Typedefs) != 0 { - fmt.Fprint(file, useSubtypes) - } - if len(s.Structs) != 0 { - fmt.Fprint(file, useTypes) - } - - for _, f := range s.Funcs { - s.generateRustFuncSignature(file, f) - } - return nil -} - -func (s *Schema) generateRustKeys() error { - file, err := os.Create("keys.rs") - if err != nil { - return err + g.printf("\npub fn %s(_ctx: &Sc%sContext, _f: &%sContext) {\n", g.funcName(f), f.Kind, capitalize(f.Type)) } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - formatter(file, false) - fmt.Fprintln(file, allowDeadCode) - fmt.Fprintln(file, useWasmLib) - fmt.Fprint(file, useCrate) - - s.KeyID = 0 - s.generateRustKeysIndexes(s.Params, "PARAM_") - s.generateRustKeysIndexes(s.Results, "RESULT_") - s.generateRustKeysIndexes(s.StateVars, "STATE_") - s.flushRustConsts(file, true) - - size := s.KeyID - fmt.Fprintf(file, "\npub const KEY_MAP_LEN: usize = %d;\n", size) - fmt.Fprintf(file, "\npub const KEY_MAP: [&str; KEY_MAP_LEN] = [\n") - s.generateRustKeysArray(file, s.Params, "PARAM_") - s.generateRustKeysArray(file, s.Results, "RESULT_") - s.generateRustKeysArray(file, s.StateVars, "STATE_") - fmt.Fprintf(file, "];\n") - - fmt.Fprintf(file, "\npub static mut IDX_MAP: [Key32; KEY_MAP_LEN] = [Key32(0); KEY_MAP_LEN];\n") - - fmt.Fprintf(file, "\npub fn idx_map(idx: usize) -> Key32 {\n") - fmt.Fprintf(file, " unsafe {\n") - fmt.Fprintf(file, " IDX_MAP[idx]\n") - fmt.Fprintf(file, " }\n") - fmt.Fprintf(file, "}\n") - - formatter(file, true) - return nil + g.printf("}\n") } -func (s *Schema) generateRustKeysArray(file *os.File, fields []*Field, prefix string) { +func (g *RustGenerator) generateKeysArray(fields []*Field, prefix string) { for _, field := range fields { if field.Alias == AliasThis { continue } name := prefix + upper(snake(field.Name)) - fmt.Fprintf(file, " %s,\n", name) - s.KeyID++ + g.printf(" %s,\n", name) + g.s.KeyID++ } } -func (s *Schema) generateRustKeysIndexes(fields []*Field, prefix string) { +func (g *RustGenerator) generateKeysIndexes(fields []*Field, prefix string) { for _, field := range fields { if field.Alias == AliasThis { continue } name := "IDX_" + prefix + upper(snake(field.Name)) - field.KeyID = s.KeyID + field.KeyID = g.s.KeyID value := "usize = " + strconv.Itoa(field.KeyID) - s.KeyID++ - s.appendConst(name, value) + g.s.KeyID++ + g.s.appendConst(name, value) } } -func (s *Schema) generateRustLib() error { - file, err := os.Create("lib.rs") - if err != nil { - return err +func (g *RustGenerator) generateLanguageSpecificFiles() error { + if g.s.CoreContracts { + return g.createSourceFile("mod", g.writeSpecialMod) } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - formatter(file, false) - fmt.Fprintln(file, allowDeadCode) - fmt.Fprintln(file, allowUnusedImports) - fmt.Fprintf(file, "use %s::*;\n", s.Name) - fmt.Fprint(file, useWasmLib) - fmt.Fprintln(file, useWasmLibHost) - fmt.Fprint(file, useConsts) - fmt.Fprint(file, useKeys) - fmt.Fprint(file, useParams) - fmt.Fprint(file, useResults) - fmt.Fprintln(file, useState) - - fmt.Fprintf(file, "mod consts;\n") - fmt.Fprintf(file, "mod contract;\n") - fmt.Fprintf(file, "mod keys;\n") - fmt.Fprintf(file, "mod params;\n") - fmt.Fprintf(file, "mod results;\n") - fmt.Fprintf(file, "mod state;\n") - if len(s.Typedefs) != 0 { - fmt.Fprintf(file, "mod typedefs;\n") - } - if len(s.Structs) != 0 { - fmt.Fprintf(file, "mod types;\n") - } - fmt.Fprintf(file, "mod %s;\n", s.Name) - - fmt.Fprintf(file, "\n#[no_mangle]\n") - fmt.Fprintf(file, "fn on_load() {\n") - if len(s.Funcs) != 0 { - fmt.Fprintf(file, " let exports = ScExports::new();\n") - } - for _, f := range s.Funcs { - name := snake(f.FuncName) - fmt.Fprintf(file, " exports.add_%s(%s, %s_thunk);\n", lower(f.Kind), upper(name), name) - } - - fmt.Fprintf(file, "\n unsafe {\n") - fmt.Fprintf(file, " for i in 0..KEY_MAP_LEN {\n") - fmt.Fprintf(file, " IDX_MAP[i] = get_key_id_from_string(KEY_MAP[i]);\n") - fmt.Fprintf(file, " }\n") - fmt.Fprintf(file, " }\n") - - fmt.Fprintf(file, "}\n") - - // generate parameter structs and thunks to set up and check parameters - for _, f := range s.Funcs { - s.generateRustThunk(file, f) - } - - formatter(file, true) - return nil + return g.writeSpecialCargoToml() } -func (s *Schema) generateRustProxy(file *os.File, field *Field, mutability string) { - if field.Array { - s.generateRustProxyArray(file, field, mutability) - return - } +func (g *RustGenerator) generateModLines(format string) { + g.println() - if field.MapKey != "" { - s.generateRustProxyMap(file, field, mutability) + if !g.s.CoreContracts { + g.printf(format, g.s.Name) + g.println() } -} -func (s *Schema) generateRustProxyArray(file *os.File, field *Field, mutability string) { - proxyType := mutability + field.Type - arrayType := "ArrayOf" + proxyType - if field.Name[0] >= 'A' && field.Name[0] <= 'Z' { - fmt.Fprintf(file, "\npub type %s%s = %s;\n", mutability, field.Name, arrayType) + g.printf(format, "consts") + g.printf(format, "contract") + if !g.s.CoreContracts { + g.printf(format, "keys") + g.printf(format, "lib") } - if s.NewTypes[arrayType] { - // already generated this array - return + if len(g.s.Params) != 0 { + g.printf(format, "params") } - s.NewTypes[arrayType] = true + if len(g.s.Results) != 0 { + g.printf(format, "results") + } + if !g.s.CoreContracts { + g.printf(format, "state") + if len(g.s.Structs) != 0 { + g.printf(format, "structs") + } + if len(g.s.Typedefs) != 0 { + g.printf(format, "typedefs") + } + } +} - fmt.Fprintf(file, "\npub struct %s {\n", arrayType) - fmt.Fprintf(file, " pub(crate) obj_id: i32,\n") - fmt.Fprintf(file, "}\n") +func (g *RustGenerator) generateProxyArray(field *Field, mutability, arrayType, proxyType string) { + g.printf("\npub struct %s {\n", arrayType) + g.printf(" pub(crate) obj_id: i32,\n") + g.printf("}\n") - fmt.Fprintf(file, "\nimpl %s {", arrayType) - defer fmt.Fprintf(file, "}\n") + g.printf("\nimpl %s {", arrayType) + defer g.printf("}\n") if mutability == PropMutable { - fmt.Fprintf(file, "\n pub fn clear(&self) {\n") - fmt.Fprintf(file, " clear(self.obj_id);\n") - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn clear(&self) {\n") + g.printf(" clear(self.obj_id);\n") + g.printf(" }\n") } - fmt.Fprintf(file, "\n pub fn length(&self) -> i32 {\n") - fmt.Fprintf(file, " get_length(self.obj_id)\n") - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn length(&self) -> i32 {\n") + g.printf(" get_length(self.obj_id)\n") + g.printf(" }\n") if field.TypeID == 0 { - s.generateRustProxyArrayNewType(file, field, proxyType) + g.generateProxyArrayNewType(field, proxyType) return } // array of predefined type - fmt.Fprintf(file, "\n pub fn get_%s(&self, index: i32) -> Sc%s {\n", snake(field.Type), proxyType) - fmt.Fprintf(file, " Sc%s::new(self.obj_id, Key32(index))\n", proxyType) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn get_%s(&self, index: i32) -> Sc%s {\n", snake(field.Type), proxyType) + g.printf(" Sc%s::new(self.obj_id, Key32(index))\n", proxyType) + g.printf(" }\n") } -func (s *Schema) generateRustProxyArrayNewType(file *os.File, field *Field, proxyType string) { - for _, subtype := range s.Typedefs { +func (g *RustGenerator) generateProxyArrayNewType(field *Field, proxyType string) { + for _, subtype := range g.s.Typedefs { if subtype.Name != field.Type { continue } @@ -624,61 +325,50 @@ func (s *Schema) generateRustProxyArrayNewType(file *os.File, field *Field, prox if varType == "" { varType = rustTypeBytes } - varType = s.generateRustArrayType(varType) + varType = g.generateArrayType(varType) } - fmt.Fprintf(file, "\n pub fn get_%s(&self, index: i32) -> %s {\n", snake(field.Type), proxyType) - fmt.Fprintf(file, " let sub_id = get_object_id(self.obj_id, Key32(index), %s)\n", varType) - fmt.Fprintf(file, " %s { obj_id: sub_id }\n", proxyType) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn get_%s(&self, index: i32) -> %s {\n", snake(field.Type), proxyType) + g.printf(" let sub_id = get_object_id(self.obj_id, Key32(index), %s)\n", varType) + g.printf(" %s { obj_id: sub_id }\n", proxyType) + g.printf(" }\n") return } - fmt.Fprintf(file, "\n pub fn get_%s(&self, index: i32) -> %s {\n", snake(field.Type), proxyType) - fmt.Fprintf(file, " %s { obj_id: self.obj_id, key_id: Key32(index) }\n", proxyType) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn get_%s(&self, index: i32) -> %s {\n", snake(field.Type), proxyType) + g.printf(" %s { obj_id: self.obj_id, key_id: Key32(index) }\n", proxyType) + g.printf(" }\n") } -func (s *Schema) generateRustProxyMap(file *os.File, field *Field, mutability string) { - proxyType := mutability + field.Type - mapType := "Map" + field.MapKey + "To" + proxyType - if field.Name[0] >= 'A' && field.Name[0] <= 'Z' { - fmt.Fprintf(file, "\npub type %s%s = %s;\n", mutability, field.Name, mapType) - } - if s.NewTypes[mapType] { - // already generated this map - return - } - s.NewTypes[mapType] = true - +func (g *RustGenerator) generateProxyMap(field *Field, mutability, mapType, proxyType string) { keyType := rustKeyTypes[field.MapKey] keyValue := rustKeys[field.MapKey] - fmt.Fprintf(file, "\npub struct %s {\n", mapType) - fmt.Fprintf(file, " pub(crate) obj_id: i32,\n") - fmt.Fprintf(file, "}\n") + g.printf("\npub struct %s {\n", mapType) + g.printf(" pub(crate) obj_id: i32,\n") + g.printf("}\n") - fmt.Fprintf(file, "\nimpl %s {", mapType) - defer fmt.Fprintf(file, "}\n") + g.printf("\nimpl %s {", mapType) + defer g.printf("}\n") if mutability == PropMutable { - fmt.Fprintf(file, "\n pub fn clear(&self) {\n") - fmt.Fprintf(file, " clear(self.obj_id)\n") - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn clear(&self) {\n") + g.printf(" clear(self.obj_id)\n") + g.printf(" }\n") } if field.TypeID == 0 { - s.generateRustProxyMapNewType(file, field, proxyType, keyType, keyValue) + g.generateProxyMapNewType(field, proxyType, keyType, keyValue) return } // map of predefined type - fmt.Fprintf(file, "\n pub fn get_%s(&self, key: %s) -> Sc%s {\n", snake(field.Type), keyType, proxyType) - fmt.Fprintf(file, " Sc%s::new(self.obj_id, %s.get_key_id())\n", proxyType, keyValue) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn get_%s(&self, key: %s) -> Sc%s {\n", snake(field.Type), keyType, proxyType) + g.printf(" Sc%s::new(self.obj_id, %s.get_key_id())\n", proxyType, keyValue) + g.printf(" }\n") } -func (s *Schema) generateRustProxyMapNewType(file *os.File, field *Field, proxyType, keyType, keyValue string) { - for _, subtype := range s.Typedefs { +func (g *RustGenerator) generateProxyMapNewType(field *Field, proxyType, keyType, keyValue string) { + for _, subtype := range g.s.Typedefs { if subtype.Name != field.Type { continue } @@ -688,134 +378,50 @@ func (s *Schema) generateRustProxyMapNewType(file *os.File, field *Field, proxyT if varType == "" { varType = rustTypeBytes } - varType = s.generateRustArrayType(varType) + varType = g.generateArrayType(varType) } - fmt.Fprintf(file, "\n pub fn get_%s(&self, key: %s) -> %s {\n", snake(field.Type), keyType, proxyType) - fmt.Fprintf(file, " let sub_id = get_object_id(self.obj_id, %s.get_key_id(), %s);\n", keyValue, varType) - fmt.Fprintf(file, " %s { obj_id: sub_id }\n", proxyType) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn get_%s(&self, key: %s) -> %s {\n", snake(field.Type), keyType, proxyType) + g.printf(" let sub_id = get_object_id(self.obj_id, %s.get_key_id(), %s);\n", keyValue, varType) + g.printf(" %s { obj_id: sub_id }\n", proxyType) + g.printf(" }\n") return } - fmt.Fprintf(file, "\n pub fn get_%s(&self, key: %s) -> %s {\n", snake(field.Type), keyType, proxyType) - fmt.Fprintf(file, " %s { obj_id: self.obj_id, key_id: %s.get_key_id() }\n", proxyType, keyValue) - fmt.Fprintf(file, " }\n") -} - -func (s *Schema) generateRustState() error { - file, err := os.Create("state.rs") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprint(file, allowDeadCode) - fmt.Fprintln(file, allowUnusedImports) - fmt.Fprint(file, useWasmLib) - fmt.Fprintln(file, useWasmLibHost) - fmt.Fprint(file, useCrate) - fmt.Fprint(file, useKeys) - if len(s.Typedefs) != 0 { - fmt.Fprint(file, useSubtypes) - } - if len(s.Structs) != 0 { - fmt.Fprint(file, useTypes) - } - - s.generateRustStruct(file, s.StateVars, PropImmutable, s.FullName, "State") - s.generateRustStruct(file, s.StateVars, PropMutable, s.FullName, "State") - return nil -} - -func (s *Schema) generateRustParams() error { - file, err := os.Create("params.rs") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprint(file, allowDeadCode) - fmt.Fprintln(file, allowUnusedImports) - fmt.Fprint(file, s.crateOrWasmLib(true, true)) - if !s.CoreContracts { - fmt.Fprint(file, "\n"+useCrate) - fmt.Fprint(file, useKeys) - } - - for _, f := range s.Funcs { - params := make([]*Field, 0, len(f.Params)) - for _, param := range f.Params { - if param.Alias != "@" { - params = append(params, param) - } - } - if len(params) == 0 { - continue - } - s.generateRustStruct(file, params, PropImmutable, f.Type, "Params") - s.generateRustStruct(file, params, PropMutable, f.Type, "Params") - } - return nil + g.printf("\n pub fn get_%s(&self, key: %s) -> %s {\n", snake(field.Type), keyType, proxyType) + g.printf(" %s { obj_id: self.obj_id, key_id: %s.get_key_id() }\n", proxyType, keyValue) + g.printf(" }\n") } -func (s *Schema) generateRustResults() error { - file, err := os.Create("results.rs") - if err != nil { - return err - } - defer file.Close() - - // write file header - fmt.Fprintln(file, copyright(true)) - fmt.Fprint(file, allowDeadCode) - fmt.Fprintln(file, allowUnusedImports) - fmt.Fprint(file, s.crateOrWasmLib(true, true)) - if !s.CoreContracts { - fmt.Fprint(file, "\n"+useCrate) - fmt.Fprint(file, useKeys) - if len(s.Structs) != 0 { - fmt.Fprint(file, useTypes) - } - } - - for _, f := range s.Funcs { - if len(f.Results) == 0 { - continue - } - s.generateRustStruct(file, f.Results, PropImmutable, f.Type, "Results") - s.generateRustStruct(file, f.Results, PropMutable, f.Type, "Results") +func (g *RustGenerator) generateProxyReference(field *Field, mutability, typeName string) { + if field.Name[0] >= 'A' && field.Name[0] <= 'Z' { + g.printf("\npub type %s%s = %s;\n", mutability, field.Name, typeName) } - return nil } -func (s *Schema) generateRustStruct(file *os.File, fields []*Field, mutability, typeName, kind string) { +func (g *RustGenerator) generateProxyStruct(fields []*Field, mutability, typeName, kind string) { typeName = mutability + typeName + kind kind = strings.TrimSuffix(kind, "s") kind = upper(kind) + "_" // first generate necessary array and map types for _, field := range fields { - s.generateRustProxy(file, field, mutability) + g.generateProxy(field, mutability) } - fmt.Fprintf(file, "\n#[derive(Clone, Copy)]\n") - fmt.Fprintf(file, "pub struct %s {\n", typeName) - fmt.Fprintf(file, " pub(crate) id: i32,\n") - fmt.Fprintf(file, "}\n") + g.printf("\n#[derive(Clone, Copy)]\n") + g.printf("pub struct %s {\n", typeName) + g.printf(" pub(crate) id: i32,\n") + g.printf("}\n") if len(fields) != 0 { - fmt.Fprintf(file, "\nimpl %s {", typeName) - defer fmt.Fprintf(file, "}\n") + g.printf("\nimpl %s {", typeName) + defer g.printf("}\n") } for _, field := range fields { varName := snake(field.Name) varID := "idx_map(IDX_" + kind + upper(varName) + ")" - if s.CoreContracts { + if g.s.CoreContracts { varID = kind + upper(varName) + ".get_key_id()" } varType := rustTypeIds[field.Type] @@ -823,131 +429,177 @@ func (s *Schema) generateRustStruct(file *os.File, fields []*Field, mutability, varType = rustTypeBytes } if field.Array { - varType = s.generateRustArrayType(varType) + varType = g.generateArrayType(varType) arrayType := "ArrayOf" + mutability + field.Type - fmt.Fprintf(file, "\n pub fn %s(&self) -> %s {\n", varName, arrayType) - fmt.Fprintf(file, " let arr_id = get_object_id(self.id, %s, %s);\n", varID, varType) - fmt.Fprintf(file, " %s { obj_id: arr_id }\n", arrayType) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn %s(&self) -> %s {\n", varName, arrayType) + g.printf(" let arr_id = get_object_id(self.id, %s, %s);\n", varID, varType) + g.printf(" %s { obj_id: arr_id }\n", arrayType) + g.printf(" }\n") continue } if field.MapKey != "" { varType = rustTypeMap mapType := "Map" + field.MapKey + "To" + mutability + field.Type - fmt.Fprintf(file, "\n pub fn %s(&self) -> %s {\n", varName, mapType) + g.printf("\n pub fn %s(&self) -> %s {\n", varName, mapType) mapID := "self.id" if field.Alias != AliasThis { mapID = "map_id" - fmt.Fprintf(file, " let map_id = get_object_id(self.id, %s, %s);\n", varID, varType) + g.printf(" let map_id = get_object_id(self.id, %s, %s);\n", varID, varType) } - fmt.Fprintf(file, " %s { obj_id: %s }\n", mapType, mapID) - fmt.Fprintf(file, " }\n") + g.printf(" %s { obj_id: %s }\n", mapType, mapID) + g.printf(" }\n") continue } proxyType := mutability + field.Type if field.TypeID == 0 { - fmt.Fprintf(file, "\n pub fn %s(&self) -> %s {\n", varName, proxyType) - fmt.Fprintf(file, " %s { obj_id: self.id, key_id: %s }\n", proxyType, varID) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn %s(&self) -> %s {\n", varName, proxyType) + g.printf(" %s { obj_id: self.id, key_id: %s }\n", proxyType, varID) + g.printf(" }\n") continue } - fmt.Fprintf(file, "\n pub fn %s(&self) -> Sc%s {\n", varName, proxyType) - fmt.Fprintf(file, " Sc%s::new(self.id, %s)\n", proxyType, varID) - fmt.Fprintf(file, " }\n") + g.printf("\n pub fn %s(&self) -> Sc%s {\n", varName, proxyType) + g.printf(" Sc%s::new(self.id, %s)\n", proxyType, varID) + g.printf(" }\n") } } -func (s *Schema) generateRustSubtypes() error { - if len(s.Typedefs) == 0 { - return nil - } +func (g *RustGenerator) generateStruct(typeDef *Struct) { + nameLen, typeLen := calculatePadding(typeDef.Fields, rustTypes, true) - file, err := os.Create("typedefs.rs") - if err != nil { - return err + g.printf("\npub struct %s {\n", typeDef.Name) + for _, field := range typeDef.Fields { + fldName := pad(snake(field.Name)+":", nameLen+1) + fldType := rustTypes[field.Type] + "," + if field.Comment != "" { + fldType = pad(fldType, typeLen+1) + } + g.printf(" pub %s %s%s\n", fldName, fldType, field.Comment) } - defer file.Close() + g.printf("}\n") - fmt.Fprintln(file, copyright(true)) - formatter(file, false) - fmt.Fprintln(file, allowDeadCode) - fmt.Fprint(file, useWasmLib) - fmt.Fprint(file, useWasmLibHost) - if len(s.Structs) != 0 { - fmt.Fprint(file, "\n", useTypes) + // write encoder and decoder for struct + g.printf("\nimpl %s {", typeDef.Name) + + g.printf("\n pub fn from_bytes(bytes: &[u8]) -> %s {\n", typeDef.Name) + g.printf(" let mut decode = BytesDecoder::new(bytes);\n") + g.printf(" %s {\n", typeDef.Name) + for _, field := range typeDef.Fields { + name := snake(field.Name) + g.printf(" %s: decode.%s(),\n", name, snake(field.Type)) } + g.printf(" }\n") + g.printf(" }\n") - for _, subtype := range s.Typedefs { - s.generateRustProxy(file, subtype, PropImmutable) - s.generateRustProxy(file, subtype, PropMutable) + g.printf("\n pub fn to_bytes(&self) -> Vec {\n") + g.printf(" let mut encode = BytesEncoder::new();\n") + for _, field := range typeDef.Fields { + name := snake(field.Name) + ref := "&" + if field.Type == "Hname" || field.Type == "Int64" || field.Type == "Int32" || field.Type == "Int16" { + ref = "" + } + g.printf(" encode.%s(%sself.%s);\n", snake(field.Type), ref, name) } + g.printf(" return encode.data();\n") + g.printf(" }\n") + g.printf("}\n") - formatter(file, true) - return nil + g.generateStructProxy(typeDef, false) + g.generateStructProxy(typeDef, true) } -func (s *Schema) generateRustThunk(file *os.File, f *Func) { - nameLen := f.nameLen(5) + 1 - fmt.Fprintf(file, "\npub struct %sContext {\n", f.Type) - if len(f.Params) != 0 { - fmt.Fprintf(file, " %s Immutable%sParams,\n", pad("params:", nameLen), f.Type) +func (g *RustGenerator) generateStructProxy(typeDef *Struct, mutable bool) { + typeName := PropImmutable + typeDef.Name + if mutable { + typeName = PropMutable + typeDef.Name } - if len(f.Results) != 0 { - fmt.Fprintf(file, " results: Mutable%sResults,\n", f.Type) + + g.printf("\npub struct %s {\n", typeName) + g.printf(" pub(crate) obj_id: i32,\n") + g.printf(" pub(crate) key_id: Key32,\n") + g.printf("}\n") + + g.printf("\nimpl %s {", typeName) + + g.printf("\n pub fn exists(&self) -> bool {\n") + g.printf(" exists(self.obj_id, self.key_id, TYPE_BYTES)\n") + g.printf(" }\n") + + if mutable { + g.printf("\n pub fn set_value(&self, value: &%s) {\n", typeDef.Name) + g.printf(" set_bytes(self.obj_id, self.key_id, TYPE_BYTES, &value.to_bytes());\n") + g.printf(" }\n") } + + g.printf("\n pub fn value(&self) -> %s {\n", typeDef.Name) + g.printf(" %s::from_bytes(&get_bytes(self.obj_id, self.key_id, TYPE_BYTES))\n", typeDef.Name) + g.printf(" }\n") + + g.printf("}\n") +} + +func (g *RustGenerator) generateThunk(f *Func) { + nameLen := f.nameLen(5) + 1 mutability := PropMutable if f.Kind == KindView { mutability = PropImmutable } - fmt.Fprintf(file, " %s %s%sState,\n", pad("state:", nameLen), mutability, s.FullName) - fmt.Fprintf(file, "}\n") + g.printf("\npub struct %sContext {\n", f.Type) + if len(f.Params) != 0 { + g.printf(" %s Immutable%sParams,\n", pad("params:", nameLen), f.Type) + } + if len(f.Results) != 0 { + g.printf(" results: Mutable%sResults,\n", f.Type) + } + g.printf(" %s %s%sState,\n", pad("state:", nameLen), mutability, g.s.FullName) + g.printf("}\n") - fmt.Fprintf(file, "\nfn %s_thunk(ctx: &Sc%sContext) {\n", snake(f.FuncName), f.Kind) - fmt.Fprintf(file, " ctx.log(\"%s.%s\");\n", s.Name, f.FuncName) + g.printf("\nfn %s_thunk(ctx: &Sc%sContext) {\n", g.funcName(f), f.Kind) + g.printf(" ctx.log(\"%s.%s\");\n", g.s.Name, f.FuncName) if f.Access != "" { - s.generateRustThunkAccessCheck(file, f) + g.generateThunkAccessCheck(f) } - fmt.Fprintf(file, " let f = %sContext {\n", f.Type) + g.printf(" let f = %sContext {\n", f.Type) if len(f.Params) != 0 { - fmt.Fprintf(file, " params: Immutable%sParams {\n", f.Type) - fmt.Fprintf(file, " id: OBJ_ID_PARAMS,\n") - fmt.Fprintf(file, " },\n") + g.printf(" params: Immutable%sParams {\n", f.Type) + g.printf(" id: OBJ_ID_PARAMS,\n") + g.printf(" },\n") } if len(f.Results) != 0 { - fmt.Fprintf(file, " results: Mutable%sResults {\n", f.Type) - fmt.Fprintf(file, " id: OBJ_ID_RESULTS,\n") - fmt.Fprintf(file, " },\n") + g.printf(" results: Mutable%sResults {\n", f.Type) + g.printf(" id: OBJ_ID_RESULTS,\n") + g.printf(" },\n") } - fmt.Fprintf(file, " state: %s%sState {\n", mutability, s.FullName) - fmt.Fprintf(file, " id: OBJ_ID_STATE,\n") - fmt.Fprintf(file, " },\n") + g.printf(" state: %s%sState {\n", mutability, g.s.FullName) + g.printf(" id: OBJ_ID_STATE,\n") + g.printf(" },\n") - fmt.Fprintf(file, " };\n") + g.printf(" };\n") for _, param := range f.Params { if !param.Optional { name := snake(param.Name) - fmt.Fprintf(file, " ctx.require(f.params.%s().exists(), \"missing mandatory %s\");\n", name, param.Name) + g.printf(" ctx.require(f.params.%s().exists(), \"missing mandatory %s\");\n", name, param.Name) } } - fmt.Fprintf(file, " %s(ctx, &f);\n", snake(f.FuncName)) - fmt.Fprintf(file, " ctx.log(\"%s.%s ok\");\n", s.Name, f.FuncName) - fmt.Fprintf(file, "}\n") + g.printf(" %s(ctx, &f);\n", g.funcName(f)) + g.printf(" ctx.log(\"%s.%s ok\");\n", g.s.Name, f.FuncName) + g.printf("}\n") } -func (s *Schema) generateRustThunkAccessCheck(file *os.File, f *Func) { +func (g *RustGenerator) generateThunkAccessCheck(f *Func) { grant := f.Access index := strings.Index(grant, "//") if index >= 0 { - fmt.Fprintf(file, " %s\n", grant[index:]) + g.printf(" %s\n", grant[index:]) grant = strings.TrimSpace(grant[:index]) } switch grant { @@ -958,126 +610,338 @@ func (s *Schema) generateRustThunkAccessCheck(file *os.File, f *Func) { case AccessCreator: grant = "ctx.contract_creator()" default: - fmt.Fprintf(file, " let access = ctx.state().get_agent_id(\"%s\");\n", grant) - fmt.Fprintf(file, " ctx.require(access.exists(), \"access not set: %s\");\n", grant) + g.printf(" let access = ctx.state().get_agent_id(\"%s\");\n", grant) + g.printf(" ctx.require(access.exists(), \"access not set: %s\");\n", grant) grant = "access.value()" } - fmt.Fprintf(file, " ctx.require(ctx.caller() == %s, \"no permission\");\n\n", grant) + g.printf(" ctx.require(ctx.caller() == %s, \"no permission\");\n\n", grant) } -func (s *Schema) generateRustTypes() error { - if len(s.Structs) == 0 { - return nil - } +func (g *RustGenerator) writeConsts() { + g.formatter(false) + g.println(allowDeadCode) + g.println() + g.println(g.crateOrWasmLib(false, false)) - file, err := os.Create("types.rs") - if err != nil { - return err + scName := g.s.Name + if g.s.CoreContracts { + // remove 'core' prefix + scName = scName[4:] + } + g.s.appendConst("SC_NAME", "&str = \""+scName+"\"") + if g.s.Description != "" { + g.s.appendConst("SC_DESCRIPTION", "&str = \""+g.s.Description+"\"") } - defer file.Close() + hName := iscp.Hn(scName) + g.s.appendConst("HSC_NAME", "ScHname = ScHname(0x"+hName.String()+")") + g.flushConsts(false) - // write file header - fmt.Fprintln(file, copyright(true)) - formatter(file, false) - fmt.Fprintln(file, allowDeadCode) - fmt.Fprint(file, useWasmLib) - fmt.Fprint(file, useWasmLibHost) + g.generateConstsFields(g.s.Params, "PARAM_") + g.generateConstsFields(g.s.Results, "RESULT_") + g.generateConstsFields(g.s.StateVars, "STATE_") + + if len(g.s.Funcs) != 0 { + for _, f := range g.s.Funcs { + constName := upper(g.funcName(f)) + g.s.appendConst(constName, "&str = \""+f.String+"\"") + } + g.flushConsts(g.s.CoreContracts) - // write structs - for _, typeDef := range s.Structs { - s.generateRustType(file, typeDef) + for _, f := range g.s.Funcs { + constHname := "H" + upper(g.funcName(f)) + g.s.appendConst(constHname, "ScHname = ScHname(0x"+f.Hname.String()+")") + } + g.flushConsts(g.s.CoreContracts) } - formatter(file, true) - return nil + g.formatter(true) } -func (s *Schema) generateRustType(file *os.File, typeDef *Struct) { - nameLen, typeLen := calculatePadding(typeDef.Fields, rustTypes, true) +func (g *RustGenerator) writeContract() { + g.formatter(false) + g.println(allowDeadCode) + g.println() + g.println(useStdPtr) + g.println() + g.println(g.crateOrWasmLib(true, false)) + if !g.s.CoreContracts { + g.println() + g.println(useConsts) + if len(g.s.Params) != 0 { + g.println(useParams) + } + if len(g.s.Results) != 0 { + g.println(useResults) + } + } - fmt.Fprintf(file, "\npub struct %s {\n", typeDef.Name) - for _, field := range typeDef.Fields { - fldName := pad(snake(field.Name)+":", nameLen+1) - fldType := rustTypes[field.Type] + "," - if field.Comment != "" { - fldType = pad(fldType, typeLen+1) + for _, f := range g.s.Funcs { + nameLen := f.nameLen(4) + 1 + kind := f.Kind + if f.Type == InitFunc { + kind = f.Type + f.Kind } - fmt.Fprintf(file, " pub %s %s%s\n", fldName, fldType, field.Comment) + g.printf("\npub struct %sCall {\n", f.Type) + g.printf(" pub %s Sc%s,\n", pad("func:", nameLen), kind) + if len(f.Params) != 0 { + g.printf(" pub %s Mutable%sParams,\n", pad("params:", nameLen), f.Type) + } + if len(f.Results) != 0 { + g.printf(" pub results: Immutable%sResults,\n", f.Type) + } + g.printf("}\n") } - fmt.Fprintf(file, "}\n") - // write encoder and decoder for struct - fmt.Fprintf(file, "\nimpl %s {", typeDef.Name) + g.generateContractFuncs() + g.formatter(true) +} - fmt.Fprintf(file, "\n pub fn from_bytes(bytes: &[u8]) -> %s {\n", typeDef.Name) - fmt.Fprintf(file, " let mut decode = BytesDecoder::new(bytes);\n") - fmt.Fprintf(file, " %s {\n", typeDef.Name) - for _, field := range typeDef.Fields { - name := snake(field.Name) - fmt.Fprintf(file, " %s: decode.%s(),\n", name, snake(field.Type)) +func (g *RustGenerator) writeInitialFuncs() { + g.println(useWasmLib) + g.println() + g.println(useCrate) + if len(g.s.Structs) != 0 { + g.println(useStructs) + } + if len(g.s.Typedefs) != 0 { + g.println(useTypeDefs) } - fmt.Fprintf(file, " }\n") - fmt.Fprintf(file, " }\n") - fmt.Fprintf(file, "\n pub fn to_bytes(&self) -> Vec {\n") - fmt.Fprintf(file, " let mut encode = BytesEncoder::new();\n") - for _, field := range typeDef.Fields { - name := snake(field.Name) - ref := "&" - if field.Type == "Hname" || field.Type == "Int64" || field.Type == "Int32" || field.Type == "Int16" { - ref = "" + for _, f := range g.s.Funcs { + g.generateFuncSignature(f) + } +} + +func (g *RustGenerator) writeKeys() { + g.formatter(false) + g.println(allowDeadCode) + g.println() + g.println(useWasmLib) + g.println() + g.println(useCrate) + + g.s.KeyID = 0 + g.generateKeysIndexes(g.s.Params, "PARAM_") + g.generateKeysIndexes(g.s.Results, "RESULT_") + g.generateKeysIndexes(g.s.StateVars, "STATE_") + g.flushConsts(true) + + size := g.s.KeyID + g.printf("\npub const KEY_MAP_LEN: usize = %d;\n", size) + g.printf("\npub const KEY_MAP: [&str; KEY_MAP_LEN] = [\n") + g.generateKeysArray(g.s.Params, "PARAM_") + g.generateKeysArray(g.s.Results, "RESULT_") + g.generateKeysArray(g.s.StateVars, "STATE_") + g.printf("];\n") + + g.printf("\npub static mut IDX_MAP: [Key32; KEY_MAP_LEN] = [Key32(0); KEY_MAP_LEN];\n") + + g.printf("\npub fn idx_map(idx: usize) -> Key32 {\n") + g.printf(" unsafe {\n") + g.printf(" IDX_MAP[idx]\n") + g.printf(" }\n") + g.printf("}\n") + + g.formatter(true) +} + +func (g *RustGenerator) writeLib() { + g.formatter(false) + g.println(allowDeadCode) + g.println(allowUnusedImports) + g.println() + g.printf("use %s::*;\n", g.s.Name) + g.println(useWasmLib) + g.println(useWasmLibHost) + g.println() + g.println(useConsts) + g.println(useKeys) + if len(g.s.Params) != 0 { + g.println(useParams) + } + if len(g.s.Results) != 0 { + g.println(useResults) + } + g.println(useState) + g.println() + + g.println("mod consts;") + g.println("mod contract;") + g.println("mod keys;") + if len(g.s.Params) != 0 { + g.println("mod params;") + } + if len(g.s.Results) != 0 { + g.println("mod results;") + } + g.println("mod state;") + if len(g.s.Structs) != 0 { + g.println("mod structs;") + } + if len(g.s.Typedefs) != 0 { + g.println("mod typedefs;") + } + g.printf("mod %s;\n", g.s.Name) + + g.println("\n#[no_mangle]") + g.println("fn on_load() {") + if len(g.s.Funcs) != 0 { + g.printf(" let exports = ScExports::new();\n") + } + for _, f := range g.s.Funcs { + name := g.funcName(f) + g.printf(" exports.add_%s(%s, %s_thunk);\n", lower(f.Kind), upper(name), name) + } + + g.printf("\n unsafe {\n") + g.printf(" for i in 0..KEY_MAP_LEN {\n") + g.printf(" IDX_MAP[i] = get_key_id_from_string(KEY_MAP[i]);\n") + g.printf(" }\n") + g.printf(" }\n") + + g.printf("}\n") + + // generate parameter structs and thunks to set up and check parameters + for _, f := range g.s.Funcs { + g.generateThunk(f) + } + + g.formatter(true) +} + +func (g *RustGenerator) writeParams() { + g.println(allowDeadCode) + g.println(allowUnusedImports) + g.println() + g.println(g.crateOrWasmLib(true, true)) + if !g.s.CoreContracts { + g.println() + g.println(useCrate) + g.println(useKeys) + } + + for _, f := range g.s.Funcs { + if len(f.Params) == 0 { + continue } - fmt.Fprintf(file, " encode.%s(%sself.%s);\n", snake(field.Type), ref, name) + g.generateProxyStruct(f.Params, PropImmutable, f.Type, "Params") + g.generateProxyStruct(f.Params, PropMutable, f.Type, "Params") } - fmt.Fprintf(file, " return encode.data();\n") - fmt.Fprintf(file, " }\n") - fmt.Fprintf(file, "}\n") +} - s.generateRustTypeProxy(file, typeDef, false) - s.generateRustTypeProxy(file, typeDef, true) +func (g *RustGenerator) writeResults() { + g.println(allowDeadCode) + g.println(allowUnusedImports) + g.println() + g.println(g.crateOrWasmLib(true, true)) + if !g.s.CoreContracts { + g.println() + g.println(useCrate) + g.println(useKeys) + if len(g.s.Structs) != 0 { + g.println(useStructs) + } + } + + for _, f := range g.s.Funcs { + if len(f.Results) == 0 { + continue + } + g.generateProxyStruct(f.Results, PropImmutable, f.Type, "Results") + g.generateProxyStruct(f.Results, PropMutable, f.Type, "Results") + } } -func (s *Schema) generateRustTypeProxy(file *os.File, typeDef *Struct, mutable bool) { - typeName := PropImmutable + typeDef.Name - if mutable { - typeName = PropMutable + typeDef.Name +func (g *RustGenerator) writeSpecialCargoToml() error { + err := g.exists("Cargo.toml") + if err == nil { + // already exists + return nil } - fmt.Fprintf(file, "\npub struct %s {\n", typeName) - fmt.Fprintf(file, " pub(crate) obj_id: i32,\n") - fmt.Fprintf(file, " pub(crate) key_id: Key32,\n") - fmt.Fprintf(file, "}\n") + err = g.create("Cargo.toml") + if err != nil { + return err + } + defer g.close() + + g.printf("[package]\n") + g.printf("name = \"%s\"\n", g.s.Name) + g.printf("description = \"%s\"\n", g.s.Description) + g.printf("license = \"Apache-2.0\"\n") + g.printf("version = \"0.1.0\"\n") + g.printf("authors = [\"John Doe \"]\n") + g.printf("edition = \"2018\"\n") + g.printf("repository = \"https://%s\"\n", ModuleName) + g.printf("\n[lib]\n") + g.printf("crate-type = [\"cdylib\", \"rlib\"]\n") + g.printf("\n[features]\n") + g.printf("default = [\"console_error_panic_hook\"]\n") + g.printf("\n[dependencies]\n") + g.printf("wasmlib = { git = \"https://github.com/iotaledger/wasp\", branch = \"develop\" }\n") + g.printf("console_error_panic_hook = { version = \"0.1.6\", optional = true }\n") + g.printf("wee_alloc = { version = \"0.4.5\", optional = true }\n") + g.printf("\n[dev-dependencies]\n") + g.printf("wasm-bindgen-test = \"0.3.13\"\n") - fmt.Fprintf(file, "\nimpl %s {", typeName) + return nil +} - fmt.Fprintf(file, "\n pub fn exists(&self) -> bool {\n") - fmt.Fprintf(file, " exists(self.obj_id, self.key_id, TYPE_BYTES)\n") - fmt.Fprintf(file, " }\n") +func (g *RustGenerator) writeSpecialMod() { + g.println(allowUnusedImports) + g.generateModLines("pub use %s::*;\n") + g.generateModLines("pub mod %s;\n") +} - if mutable { - fmt.Fprintf(file, "\n pub fn set_value(&self, value: &%s) {\n", typeDef.Name) - fmt.Fprintf(file, " set_bytes(self.obj_id, self.key_id, TYPE_BYTES, &value.to_bytes());\n") - fmt.Fprintf(file, " }\n") +func (g *RustGenerator) writeState() { + g.println(allowDeadCode) + g.println(allowUnusedImports) + g.println() + g.println(useWasmLib) + g.println(useWasmLibHost) + g.println() + g.println(useCrate) + g.println(useKeys) + if len(g.s.Structs) != 0 { + g.println(useStructs) + } + if len(g.s.Typedefs) != 0 { + g.println(useTypeDefs) } - fmt.Fprintf(file, "\n pub fn value(&self) -> %s {\n", typeDef.Name) - fmt.Fprintf(file, " %s::from_bytes(&get_bytes(self.obj_id, self.key_id, TYPE_BYTES))\n", typeDef.Name) - fmt.Fprintf(file, " }\n") + g.generateProxyStruct(g.s.StateVars, PropImmutable, g.s.FullName, "State") + g.generateProxyStruct(g.s.StateVars, PropMutable, g.s.FullName, "State") +} + +func (g *RustGenerator) writeStructs() { + g.formatter(false) + g.println(allowDeadCode) + g.println() + g.println(useWasmLib) + g.println(useWasmLibHost) - fmt.Fprintf(file, "}\n") + for _, typeDef := range g.s.Structs { + g.generateStruct(typeDef) + } + + g.formatter(true) } -func (s *Schema) flushRustConsts(file *os.File, crateOnly bool) { - if len(s.ConstNames) == 0 { - return +func (g *RustGenerator) writeTypeDefs() { + g.formatter(false) + g.println(allowDeadCode) + g.println() + g.println(useWasmLib) + g.println(useWasmLibHost) + if len(g.s.Structs) != 0 { + g.println() + g.println(useStructs) } - crate := "" - if crateOnly { - crate = "(crate)" + for _, subtype := range g.s.Typedefs { + g.generateProxy(subtype, PropImmutable) + g.generateProxy(subtype, PropMutable) } - fmt.Fprintln(file) - s.flushConsts(func(name string, value string, padLen int) { - fmt.Fprintf(file, "pub%s const %s %s;\n", crate, pad(name+":", padLen+1), value) - }) + + g.formatter(true) } diff --git a/tools/schema/generator/generator_ts.go b/tools/schema/generator/generator_ts.go new file mode 100644 index 0000000000..361fac2732 --- /dev/null +++ b/tools/schema/generator/generator_ts.go @@ -0,0 +1,734 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "regexp" + "strconv" + "strings" + + "github.com/iotaledger/wasp/packages/iscp" +) + +const ( + tsImportSelf = "import * as sc from \"./index\";" + tsImportWasmLib = "import * as wasmlib from \"wasmlib\"" +) + +var tsTypes = StringMap{ + "Address": "wasmlib.ScAddress", + "AgentID": "wasmlib.ScAgentID", + "ChainID": "wasmlib.ScChainID", + "Color": "wasmlib.ScColor", + "Hash": "wasmlib.ScHash", + "Hname": "wasmlib.ScHname", + "Int16": "i16", + "Int32": "i32", + "Int64": "i64", + "RequestID": "wasmlib.ScRequestID", + "String": "string", +} + +var tsInits = StringMap{ + "Address": "new wasmlib.ScAddress()", + "AgentID": "new wasmlib.ScAgentID()", + "ChainID": "new wasmlib.ScChainID()", + "Color": "new wasmlib.ScColor(0)", + "Hash": "new wasmlib.ScHash()", + "Hname": "new wasmlib.ScHname(0)", + "Int16": "0", + "Int32": "0", + "Int64": "0", + "RequestID": "new wasmlib.ScRequestID()", + "String": "\"\"", +} + +var tsKeys = StringMap{ + "Address": "key", + "AgentID": "key", + "ChainID": "key", + "Color": "key", + "Hash": "key", + "Hname": "key", + "Int16": "??TODO", + "Int32": "new wasmlib.Key32(key)", + "Int64": "??TODO", + "RequestID": "key", + "String": "wasmlib.Key32.fromString(key)", +} + +var tsTypeIds = StringMap{ + "Address": "wasmlib.TYPE_ADDRESS", + "AgentID": "wasmlib.TYPE_AGENT_ID", + "ChainID": "wasmlib.TYPE_CHAIN_ID", + "Color": "wasmlib.TYPE_COLOR", + "Hash": "wasmlib.TYPE_HASH", + "Hname": "wasmlib.TYPE_HNAME", + "Int16": "wasmlib.TYPE_INT16", + "Int32": "wasmlib.TYPE_INT32", + "Int64": "wasmlib.TYPE_INT64", + "RequestID": "wasmlib.TYPE_REQUEST_ID", + "String": "wasmlib.TYPE_STRING", +} + +const ( + tsTypeBytes = "wasmlib.TYPE_BYTES" + tsTypeMap = "wasmlib.TYPE_MAP" +) + +type TypeScriptGenerator struct { + GenBase +} + +func NewTypeScriptGenerator() *TypeScriptGenerator { + g := &TypeScriptGenerator{} + g.extension = ".ts" + g.funcRegexp = regexp.MustCompile(`^export function (\w+).+$`) + g.language = "TypeScript" + g.rootFolder = "ts" + g.gen = g + return g +} + +func (g *TypeScriptGenerator) flushConsts() { + if len(g.s.ConstNames) == 0 { + return + } + + g.println() + g.s.flushConsts(func(name string, value string, padLen int) { + g.printf("export const %s = %s;\n", pad(name, padLen), value) + }) +} + +func (g *TypeScriptGenerator) funcName(f *Func) string { + return f.FuncName +} + +func (g *TypeScriptGenerator) generateArrayType(varType string) string { + // native core contracts use Array16 instead of our nested array type + if g.s.CoreContracts { + return "wasmlib.TYPE_ARRAY16|" + varType + } + return "wasmlib.TYPE_ARRAY|" + varType +} + +func (g *TypeScriptGenerator) generateConstsFields(fields []*Field, prefix string) { + if len(fields) != 0 { + for _, field := range fields { + if field.Alias == AliasThis { + continue + } + name := prefix + capitalize(field.Name) + value := "\"" + field.Alias + "\"" + g.s.appendConst(name, value) + } + g.flushConsts() + } +} + +func (g *TypeScriptGenerator) generateContractFuncs() { + g.println("\nexport class ScFuncs {") + for _, f := range g.s.Funcs { + g.printf("\n static %s(ctx: wasmlib.Sc%sCallContext): %sCall {\n", uncapitalize(f.Type), f.Kind, f.Type) + g.printf(" let f = new %sCall();\n", f.Type) + + paramsID := "null" + if len(f.Params) != 0 { + paramsID = "f.params" + } + resultsID := "null" + if len(f.Results) != 0 { + resultsID = "f.results" + } + if len(f.Params) != 0 || len(f.Results) != 0 { + g.printf(" f.func.setPtrs(%s, %s);\n", paramsID, resultsID) + } + g.printf(" return f;\n") + g.printf(" }\n") + } + g.printf("}\n") +} + +func (g *TypeScriptGenerator) generateFuncSignature(f *Func) { + g.printf("\nexport function %s(ctx: wasmlib.Sc%sContext, f: sc.%sContext): void {\n", f.FuncName, f.Kind, f.Type) + switch f.FuncName { + case SpecialFuncInit: + g.printf(" if (f.params.owner().exists()) {\n") + g.printf(" f.state.owner().setValue(f.params.owner().value());\n") + g.printf(" return;\n") + g.printf(" }\n") + g.printf(" f.state.owner().setValue(ctx.contractCreator());\n") + case SpecialFuncSetOwner: + g.printf(" f.state.owner().setValue(f.params.owner().value());\n") + case SpecialViewGetOwner: + g.printf(" f.results.owner().setValue(f.state.owner().value());\n") + default: + } + g.printf("}\n") +} + +func (g *TypeScriptGenerator) generateKeysArray(fields []*Field, prefix string) { + for _, field := range fields { + if field.Alias == AliasThis { + continue + } + name := prefix + capitalize(field.Name) + g.printf(" sc.%s,\n", name) + g.s.KeyID++ + } +} + +func (g *TypeScriptGenerator) generateKeysIndexes(fields []*Field, prefix string) { + for _, field := range fields { + if field.Alias == AliasThis { + continue + } + name := "Idx" + prefix + capitalize(field.Name) + field.KeyID = g.s.KeyID + value := strconv.Itoa(field.KeyID) + g.s.KeyID++ + g.s.appendConst(name, value) + } +} + +func (g *TypeScriptGenerator) generateLanguageSpecificFiles() error { + err := g.createSourceFile("index", g.writeSpecialIndex) + if err != nil { + return err + } + return g.writeSpecialConfigJSON() +} + +func (g *TypeScriptGenerator) generateProxyArray(field *Field, mutability, arrayType, proxyType string) { + g.printf("\nexport class %s {\n", arrayType) + g.printf(" objID: i32;\n") + + g.printf("\n constructor(objID: i32) {\n") + g.printf(" this.objID = objID;\n") + g.printf(" }\n") + + if mutability == PropMutable { + g.printf("\n clear(): void {\n") + g.printf(" wasmlib.clear(this.objID);\n") + g.printf(" }\n") + } + + g.printf("\n length(): i32 {\n") + g.printf(" return wasmlib.getLength(this.objID);\n") + g.printf(" }\n") + + if field.TypeID == 0 { + g.generateProxyArrayNewType(field, proxyType) + g.printf("}\n") + return + } + + // array of predefined type + g.printf("\n get%s(index: i32): wasmlib.Sc%s {\n", field.Type, proxyType) + g.printf(" return new wasmlib.Sc%s(this.objID, new wasmlib.Key32(index));\n", proxyType) + g.printf(" }\n") + + g.printf("}\n") +} + +func (g *TypeScriptGenerator) generateProxyArrayNewType(field *Field, proxyType string) { + for _, subtype := range g.s.Typedefs { + if subtype.Name != field.Type { + continue + } + varType := tsTypeMap + if subtype.Array { + varType = tsTypeIds[subtype.Type] + if varType == "" { + varType = tsTypeBytes + } + varType = g.generateArrayType(varType) + } + g.printf("\n get%s(index: i32): sc.%s {\n", field.Type, proxyType) + g.printf(" let subID = wasmlib.getObjectID(this.objID, new wasmlib.Key32(index), %s);\n", varType) + g.printf(" return new sc.%s(subID);\n", proxyType) + g.printf(" }\n") + return + } + + g.printf("\n get%s(index: i32): sc.%s {\n", field.Type, proxyType) + g.printf(" return new sc.%s(this.objID, new wasmlib.Key32(index));\n", proxyType) + g.printf(" }\n") +} + +func (g *TypeScriptGenerator) generateProxyMap(field *Field, mutability, mapType, proxyType string) { + keyType := tsTypes[field.MapKey] + keyValue := tsKeys[field.MapKey] + + g.printf("\nexport class %s {\n", mapType) + g.printf(" objID: i32;\n") + + g.printf("\n constructor(objID: i32) {\n") + g.printf(" this.objID = objID;\n") + g.printf(" }\n") + + if mutability == PropMutable { + g.printf("\n clear(): void {\n") + g.printf(" wasmlib.clear(this.objID)\n") + g.printf(" }\n") + } + + if field.TypeID == 0 { + g.generateProxyMapNewType(field, proxyType, keyType, keyValue) + g.printf("}\n") + return + } + + // map of predefined type + g.printf("\n get%s(key: %s): wasmlib.Sc%s {\n", field.Type, keyType, proxyType) + g.printf(" return new wasmlib.Sc%s(this.objID, %s.getKeyID());\n", proxyType, keyValue) + g.printf(" }\n") + + g.printf("}\n") +} + +func (g *TypeScriptGenerator) generateProxyMapNewType(field *Field, proxyType, keyType, keyValue string) { + for _, subtype := range g.s.Typedefs { + if subtype.Name != field.Type { + continue + } + varType := tsTypeMap + if subtype.Array { + varType = tsTypeIds[subtype.Type] + if varType == "" { + varType = tsTypeBytes + } + varType = g.generateArrayType(varType) + } + g.printf("\n get%s(key: %s): sc.%s {\n", field.Type, keyType, proxyType) + g.printf(" let subID = wasmlib.getObjectID(this.objID, %s.getKeyID(), %s);\n", keyValue, varType) + g.printf(" return new sc.%s(subID);\n", proxyType) + g.printf(" }\n") + return + } + + g.printf("\n get%s(key: %s): sc.%s {\n", field.Type, keyType, proxyType) + g.printf(" return new sc.%s(this.objID, %s.getKeyID());\n", proxyType, keyValue) + g.printf(" }\n") +} + +func (g *TypeScriptGenerator) generateProxyReference(field *Field, mutability, typeName string) { + if field.Name[0] >= 'A' && field.Name[0] <= 'Z' { + g.printf("\nexport class %s%s extends %s {\n};\n", mutability, field.Name, typeName) + } +} + +func (g *TypeScriptGenerator) generateProxyStruct(fields []*Field, mutability, typeName, kind string) { + typeName = mutability + typeName + kind + kind = strings.TrimSuffix(kind, "s") + + // first generate necessary array and map types + for _, field := range fields { + g.generateProxy(field, mutability) + } + + g.printf("\nexport class %s extends wasmlib.ScMapID {\n", typeName) + + for _, field := range fields { + varName := field.Name + varID := "sc.idxMap[sc.Idx" + kind + capitalize(varName) + "]" + if g.s.CoreContracts { + varID = "wasmlib.Key32.fromString(sc." + kind + capitalize(varName) + ")" + } + varType := tsTypeIds[field.Type] + if varType == "" { + varType = tsTypeBytes + } + if field.Array { + varType = g.generateArrayType(varType) + arrayType := "ArrayOf" + mutability + field.Type + g.printf("\n %s(): sc.%s {\n", varName, arrayType) + g.printf(" let arrID = wasmlib.getObjectID(this.mapID, %s, %s);\n", varID, varType) + g.printf(" return new sc.%s(arrID)\n", arrayType) + g.printf(" }\n") + continue + } + if field.MapKey != "" { + varType = tsTypeMap + mapType := "Map" + field.MapKey + "To" + mutability + field.Type + g.printf("\n %s(): sc.%s {\n", varName, mapType) + mapID := "this.mapID" + if field.Alias != AliasThis { + mapID = "mapID" + g.printf(" let mapID = wasmlib.getObjectID(this.mapID, %s, %s);\n", varID, varType) + } + g.printf(" return new sc.%s(%s);\n", mapType, mapID) + g.printf(" }\n") + continue + } + + proxyType := mutability + field.Type + if field.TypeID == 0 { + g.printf("\n %s(): sc.%s {\n", varName, proxyType) + g.printf(" return new sc.%s(this.mapID, %s);\n", proxyType, varID) + g.printf(" }\n") + continue + } + + g.printf("\n %s(): wasmlib.Sc%s {\n", varName, proxyType) + g.printf(" return new wasmlib.Sc%s(this.mapID, %s);\n", proxyType, varID) + g.printf(" }\n") + } + g.printf("}\n") +} + +func (g *TypeScriptGenerator) generateStruct(typeDef *Struct) { + nameLen, typeLen := calculatePadding(typeDef.Fields, tsTypes, false) + + g.printf("\nexport class %s {\n", typeDef.Name) + for _, field := range typeDef.Fields { + fldName := pad(field.Name, nameLen) + fldType := tsTypes[field.Type] + " = " + tsInits[field.Type] + ";" + if field.Comment != "" { + fldType = pad(fldType, typeLen) + } + g.printf(" %s: %s%s\n", fldName, fldType, field.Comment) + } + + // write encoder and decoder for struct + g.printf("\n static fromBytes(bytes: u8[]): %s {\n", typeDef.Name) + g.printf(" let decode = new wasmlib.BytesDecoder(bytes);\n") + g.printf(" let data = new %s();\n", typeDef.Name) + for _, field := range typeDef.Fields { + name := field.Name + g.printf(" data.%s = decode.%s();\n", name, uncapitalize(field.Type)) + } + g.printf(" decode.close();\n") + g.printf(" return data;\n }\n") + + g.printf("\n bytes(): u8[] {\n") + g.printf(" return new wasmlib.BytesEncoder().\n") + for _, field := range typeDef.Fields { + name := field.Name + g.printf(" %s(this.%s).\n", uncapitalize(field.Type), name) + } + g.printf(" data();\n }\n") + + g.printf("}\n") + + g.generateStructProxy(typeDef, false) + g.generateStructProxy(typeDef, true) +} + +func (g *TypeScriptGenerator) generateStructProxy(typeDef *Struct, mutable bool) { + typeName := PropImmutable + typeDef.Name + if mutable { + typeName = PropMutable + typeDef.Name + } + + g.printf("\nexport class %s {\n", typeName) + g.printf(" objID: i32;\n") + g.printf(" keyID: wasmlib.Key32;\n") + + g.printf("\n constructor(objID: i32, keyID: wasmlib.Key32) {\n") + g.printf(" this.objID = objID;\n") + g.printf(" this.keyID = keyID;\n") + g.printf(" }\n") + + g.printf("\n exists(): boolean {\n") + g.printf(" return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES);\n") + g.printf(" }\n") + + if mutable { + g.printf("\n setValue(value: %s): void {\n", typeDef.Name) + g.printf(" wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes());\n") + g.printf(" }\n") + } + + g.printf("\n value(): %s {\n", typeDef.Name) + g.printf(" return %s.fromBytes(wasmlib.getBytes(this.objID, this.keyID,wasmlib. TYPE_BYTES));\n", typeDef.Name) + g.printf(" }\n") + + g.printf("}\n") +} + +func (g *TypeScriptGenerator) generateThunk(f *Func) { + g.printf("\nfunction %sThunk(ctx: wasmlib.Sc%sContext): void {\n", f.FuncName, f.Kind) + g.printf(" ctx.log(\"%s.%s\");\n", g.s.Name, f.FuncName) + + if f.Access != "" { + g.generateThunkAccessCheck(f) + } + + g.printf(" let f = new sc.%sContext();\n", f.Type) + + if len(f.Params) != 0 { + g.printf(" f.params.mapID = wasmlib.OBJ_ID_PARAMS;\n") + } + + if len(f.Results) != 0 { + g.printf(" f.results.mapID = wasmlib.OBJ_ID_RESULTS;\n") + } + + g.printf(" f.state.mapID = wasmlib.OBJ_ID_STATE;\n") + + for _, param := range f.Params { + if !param.Optional { + name := param.Name + g.printf(" ctx.require(f.params.%s().exists(), \"missing mandatory %s\")\n", name, param.Name) + } + } + + g.printf(" sc.%s(ctx, f);\n", f.FuncName) + g.printf(" ctx.log(\"%s.%s ok\");\n", g.s.Name, f.FuncName) + g.printf("}\n") +} + +func (g *TypeScriptGenerator) generateThunkAccessCheck(f *Func) { + grant := f.Access + index := strings.Index(grant, "//") + if index >= 0 { + g.printf(" %s\n", grant[index:]) + grant = strings.TrimSpace(grant[:index]) + } + switch grant { + case AccessSelf: + grant = "ctx.accountID()" + case AccessChain: + grant = "ctx.chainOwnerID()" + case AccessCreator: + grant = "ctx.contractCreator()" + default: + g.printf(" let access = ctx.state().getAgentID(wasmlib.Key32.fromString(\"%s\"));\n", grant) + g.printf(" ctx.require(access.exists(), \"access not set: %s\");\n", grant) + grant = "access.value()" + } + g.printf(" ctx.require(ctx.caller().equals(%s), \"no permission\");\n\n", grant) +} + +func (g *TypeScriptGenerator) writeConsts() { + g.println(tsImportWasmLib) + + scName := g.s.Name + if g.s.CoreContracts { + // remove 'core' prefix + scName = scName[4:] + } + g.s.appendConst("ScName", "\""+scName+"\"") + if g.s.Description != "" { + g.s.appendConst("ScDescription", "\""+g.s.Description+"\"") + } + hName := iscp.Hn(scName) + hNameType := "new wasmlib.ScHname" + g.s.appendConst("HScName", hNameType+"(0x"+hName.String()+")") + g.flushConsts() + + g.generateConstsFields(g.s.Params, "Param") + g.generateConstsFields(g.s.Results, "Result") + g.generateConstsFields(g.s.StateVars, "State") + + if len(g.s.Funcs) != 0 { + for _, f := range g.s.Funcs { + constName := capitalize(f.FuncName) + g.s.appendConst(constName, "\""+f.String+"\"") + } + g.flushConsts() + + for _, f := range g.s.Funcs { + constHname := "H" + capitalize(f.FuncName) + g.s.appendConst(constHname, hNameType+"(0x"+f.Hname.String()+")") + } + g.flushConsts() + } +} + +func (g *TypeScriptGenerator) writeContract() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + for _, f := range g.s.Funcs { + kind := f.Kind + if f.Type == InitFunc { + kind = f.Type + f.Kind + } + g.printf("\nexport class %sCall {\n", f.Type) + g.printf(" func: wasmlib.Sc%s = new wasmlib.Sc%s(sc.HScName, sc.H%s%s);\n", kind, kind, f.Kind, f.Type) + if len(f.Params) != 0 { + g.printf(" params: sc.Mutable%sParams = new sc.Mutable%sParams();\n", f.Type, f.Type) + } + if len(f.Results) != 0 { + g.printf(" results: sc.Immutable%sResults = new sc.Immutable%sResults();\n", f.Type, f.Type) + } + g.printf("}\n") + + if !g.s.CoreContracts { + mutability := PropMutable + if f.Kind == KindView { + mutability = PropImmutable + } + g.printf("\nexport class %sContext {\n", f.Type) + if len(f.Params) != 0 { + g.printf(" params: sc.Immutable%sParams = new sc.Immutable%sParams();\n", f.Type, f.Type) + } + if len(f.Results) != 0 { + g.printf(" results: sc.Mutable%sResults = new sc.Mutable%sResults();\n", f.Type, f.Type) + } + g.printf(" state: sc.%s%sState = new sc.%s%sState();\n", mutability, g.s.FullName, mutability, g.s.FullName) + g.printf("}\n") + } + } + + g.generateContractFuncs() +} + +func (g *TypeScriptGenerator) writeInitialFuncs() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + for _, f := range g.s.Funcs { + g.generateFuncSignature(f) + } +} + +func (g *TypeScriptGenerator) writeKeys() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + g.s.KeyID = 0 + g.generateKeysIndexes(g.s.Params, "Param") + g.generateKeysIndexes(g.s.Results, "Result") + g.generateKeysIndexes(g.s.StateVars, "State") + g.flushConsts() + + g.printf("\nexport let keyMap: string[] = [\n") + g.generateKeysArray(g.s.Params, "Param") + g.generateKeysArray(g.s.Results, "Result") + g.generateKeysArray(g.s.StateVars, "State") + g.printf("];\n") + g.printf("\nexport let idxMap: wasmlib.Key32[] = new Array(keyMap.length);\n") +} + +func (g *TypeScriptGenerator) writeLib() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + g.printf("\nexport function on_call(index: i32): void {\n") + g.printf(" return wasmlib.onCall(index);\n") + g.printf("}\n") + + g.printf("\nexport function on_load(): void {\n") + g.printf(" let exports = new wasmlib.ScExports();\n") + for _, f := range g.s.Funcs { + constName := capitalize(f.FuncName) + g.printf(" exports.add%s(sc.%s, %sThunk);\n", f.Kind, constName, f.FuncName) + } + + g.printf("\n for (let i = 0; i < sc.keyMap.length; i++) {\n") + g.printf(" sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]);\n") + g.printf(" }\n") + + g.printf("}\n") + + // generate parameter structs and thunks to set up and check parameters + for _, f := range g.s.Funcs { + g.generateThunk(f) + } +} + +func (g *TypeScriptGenerator) writeParams() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + for _, f := range g.s.Funcs { + if len(f.Params) == 0 { + continue + } + g.generateProxyStruct(f.Params, PropImmutable, f.Type, "Params") + g.generateProxyStruct(f.Params, PropMutable, f.Type, "Params") + } +} + +func (g *TypeScriptGenerator) writeResults() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + for _, f := range g.s.Funcs { + if len(f.Results) == 0 { + continue + } + g.generateProxyStruct(f.Results, PropImmutable, f.Type, "Results") + g.generateProxyStruct(f.Results, PropMutable, f.Type, "Results") + } +} + +func (g *TypeScriptGenerator) writeSpecialConfigJSON() error { + err := g.exists(g.Folder + "tsconfig.json") + if err == nil { + // already exists + return nil + } + + err = g.create(g.Folder + "tsconfig.json") + if err != nil { + return err + } + defer g.close() + + g.println("{") + g.println(" \"extends\": \"assemblyscript/std/assembly.json\",") + g.println(" \"include\": [\"./*.ts\"]") + g.println("}") + + return nil +} + +func (g *TypeScriptGenerator) writeSpecialIndex() { + if !g.s.CoreContracts { + g.printf("export * from \"./%s\";\n\n", g.s.Name) + } + + g.println("export * from \"./consts\";") + g.println("export * from \"./contract\";") + if !g.s.CoreContracts { + g.println("export * from \"./keys\";") + g.println("export * from \"./lib\";") + } + if len(g.s.Params) != 0 { + g.println("export * from \"./params\";") + } + if len(g.s.Results) != 0 { + g.println("export * from \"./results\";") + } + if !g.s.CoreContracts { + g.println("export * from \"./state\";") + if len(g.s.Structs) != 0 { + g.println("export * from \"./structs\";") + } + if len(g.s.Typedefs) != 0 { + g.println("export * from \"./typedefs\";") + } + } +} + +func (g *TypeScriptGenerator) writeState() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + g.generateProxyStruct(g.s.StateVars, PropImmutable, g.s.FullName, "State") + g.generateProxyStruct(g.s.StateVars, PropMutable, g.s.FullName, "State") +} + +func (g *TypeScriptGenerator) writeStructs() { + g.println(tsImportWasmLib) + + for _, typeDef := range g.s.Structs { + g.generateStruct(typeDef) + } +} + +func (g *TypeScriptGenerator) writeTypeDefs() { + g.println(tsImportWasmLib) + g.println(tsImportSelf) + + for _, subtype := range g.s.Typedefs { + g.generateProxy(subtype, PropImmutable) + g.generateProxy(subtype, PropMutable) + } +} diff --git a/tools/schema/generator/schema.go b/tools/schema/generator/schema.go index fbfe2a607b..07ad36c581 100644 --- a/tools/schema/generator/schema.go +++ b/tools/schema/generator/schema.go @@ -4,15 +4,14 @@ package generator import ( - "bufio" "fmt" - "os" - "regexp" "strings" + "time" "github.com/iotaledger/wasp/packages/iscp" ) +// TODO describe schema details in docs type ( FieldMap map[string]*Field FieldMapMap map[string]FieldMap @@ -58,6 +57,11 @@ func (f *Func) nameLen(smallest int) int { return smallest } +type Struct struct { + Name string + Fields []*Field +} + type Schema struct { Name string FullName string @@ -67,8 +71,8 @@ type Schema struct { ConstNames []string ConstValues []string CoreContracts bool + SchemaTime time.Time Funcs []*Func - NewTypes map[string]bool Params []*Field Results []*Field StateVars []*Field @@ -81,6 +85,14 @@ func NewSchema() *Schema { return &Schema{} } +func (s *Schema) appendConst(name, value string) { + if s.ConstLen < len(name) { + s.ConstLen = len(name) + } + s.ConstNames = append(s.ConstNames, name) + s.ConstValues = append(s.ConstValues, value) +} + func (s *Schema) Compile(schemaDef *SchemaDef) error { s.FullName = strings.TrimSpace(schemaDef.Name) if s.FullName == "" { @@ -89,11 +101,11 @@ func (s *Schema) Compile(schemaDef *SchemaDef) error { s.Name = lower(s.FullName) s.Description = strings.TrimSpace(schemaDef.Description) - err := s.compileTypes(schemaDef) + err := s.compileStructs(schemaDef) if err != nil { return err } - err = s.compileSubtypes(schemaDef) + err = s.compileTypeDefs(schemaDef) if err != nil { return err } @@ -116,7 +128,7 @@ func (s *Schema) Compile(schemaDef *SchemaDef) error { return s.compileStateVars(schemaDef) } -func (s *Schema) CompileField(fldName, fldType string) (*Field, error) { +func (s *Schema) compileField(fldName, fldType string) (*Field, error) { field := &Field{} err := field.Compile(s, fldName, fldType) if err != nil { @@ -170,7 +182,7 @@ func (s *Schema) compileFuncFields(fieldMap StringMap, allFieldMap *FieldMap, wh fieldAliases := make(StringMap) for _, fldName := range sortedKeys(fieldMap) { fldType := fieldMap[fldName] - field, err := s.CompileField(fldName, fldType) + field, err := s.compileField(fldName, fldType) if err != nil { return nil, err } @@ -203,7 +215,7 @@ func (s *Schema) compileStateVars(schemaDef *SchemaDef) error { varAliases := make(StringMap) for _, varName := range sortedKeys(schemaDef.State) { varType := schemaDef.State[varName] - varDef, err := s.CompileField(varName, varType) + varDef, err := s.compileField(varName, varType) if err != nil { return err } @@ -220,29 +232,7 @@ func (s *Schema) compileStateVars(schemaDef *SchemaDef) error { return nil } -func (s *Schema) compileSubtypes(schemaDef *SchemaDef) error { - varNames := make(StringMap) - varAliases := make(StringMap) - for _, varName := range sortedKeys(schemaDef.Typedefs) { - varType := schemaDef.Typedefs[varName] - varDef, err := s.CompileField(varName, varType) - if err != nil { - return err - } - if _, ok := varNames[varDef.Name]; ok { - return fmt.Errorf("duplicate sybtype name") - } - varNames[varDef.Name] = varDef.Name - if _, ok := varAliases[varDef.Alias]; ok { - return fmt.Errorf("duplicate subtype alias") - } - varAliases[varDef.Alias] = varDef.Alias - s.Typedefs = append(s.Typedefs, varDef) - } - return nil -} - -func (s *Schema) compileTypes(schemaDef *SchemaDef) error { +func (s *Schema) compileStructs(schemaDef *SchemaDef) error { for _, typeName := range sortedMaps(schemaDef.Structs) { fieldMap := schemaDef.Structs[typeName] typeDef := &Struct{} @@ -251,7 +241,7 @@ func (s *Schema) compileTypes(schemaDef *SchemaDef) error { fieldAliases := make(StringMap) for _, fldName := range sortedKeys(fieldMap) { fldType := fieldMap[fldName] - field, err := s.CompileField(fldName, fldType) + field, err := s.compileField(fldName, fldType) if err != nil { return err } @@ -273,32 +263,26 @@ func (s *Schema) compileTypes(schemaDef *SchemaDef) error { return nil } -func (s *Schema) scanExistingCode(file *os.File, funcRegexp *regexp.Regexp) ([]string, StringMap, error) { - defer file.Close() - existing := make(StringMap) - lines := make([]string, 0) - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - matches := funcRegexp.FindStringSubmatch(line) - if matches != nil { - existing[matches[1]] = line +func (s *Schema) compileTypeDefs(schemaDef *SchemaDef) error { + varNames := make(StringMap) + varAliases := make(StringMap) + for _, varName := range sortedKeys(schemaDef.Typedefs) { + varType := schemaDef.Typedefs[varName] + varDef, err := s.compileField(varName, varType) + if err != nil { + return err } - lines = append(lines, line) - } - err := scanner.Err() - if err != nil { - return nil, nil, err - } - return lines, existing, nil -} - -func (s *Schema) appendConst(name, value string) { - if s.ConstLen < len(name) { - s.ConstLen = len(name) + if _, ok := varNames[varDef.Name]; ok { + return fmt.Errorf("duplicate sybtype name") + } + varNames[varDef.Name] = varDef.Name + if _, ok := varAliases[varDef.Alias]; ok { + return fmt.Errorf("duplicate subtype alias") + } + varAliases[varDef.Alias] = varDef.Alias + s.Typedefs = append(s.Typedefs, varDef) } - s.ConstNames = append(s.ConstNames, name) - s.ConstValues = append(s.ConstValues, value) + return nil } func (s *Schema) flushConsts(printer func(name string, value string, padLen int)) { @@ -309,21 +293,3 @@ func (s *Schema) flushConsts(printer func(name string, value string, padLen int) s.ConstNames = nil s.ConstValues = nil } - -func (s *Schema) crateOrWasmLib(withContract, withHost bool) string { - if s.CoreContracts { - retVal := useCrate - if withContract { - retVal += "use crate::corecontracts::" + s.Name + "::*;\n" - } - if withHost { - retVal += "use crate::host::*;\n" - } - return retVal - } - retVal := useWasmLib - if withHost { - retVal += useWasmLibHost - } - return retVal -} diff --git a/tools/schema/generator/struct.go b/tools/schema/generator/struct.go deleted file mode 100644 index ea5c0b2760..0000000000 --- a/tools/schema/generator/struct.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package generator - -import ( - "fmt" - "os" -) - -type Struct struct { - Name string - Fields []*Field -} - -func (td *Struct) GenerateJavaType(contract string) error { - file, err := os.Create("types/" + td.Name + ".java") - if err != nil { - return err - } - defer file.Close() - - // calculate padding - nameLen, typeLen := calculatePadding(td.Fields, javaTypes, false) - - // write file header - fmt.Fprint(file, copyright(true)) - fmt.Fprintf(file, "\npackage org.iota.wasp.contracts.%s.types;\n\n", contract) - fmt.Fprint(file, "import org.iota.wasp.wasmlib.bytes.*;\n") - fmt.Fprint(file, "import org.iota.wasp.wasmlib.hashtypes.*;\n\n") - - fmt.Fprintf(file, "public class %s {\n", td.Name) - - // write struct layout - if len(td.Fields) > 1 { - fmt.Fprint(file, " // @formatter:off\n") - } - for _, field := range td.Fields { - fldName := capitalize(field.Name) + ";" - fldType := pad(javaTypes[field.Type], typeLen) - if field.Comment != "" { - fldName = pad(fldName, nameLen+1) - } - fmt.Fprintf(file, " public %s %s%s\n", fldType, fldName, field.Comment) - } - if len(td.Fields) > 1 { - fmt.Fprint(file, " // @formatter:on\n") - } - - // write default constructor - fmt.Fprintf(file, "\n public %s() {\n }\n", td.Name) - - // write constructor from byte array - fmt.Fprintf(file, "\n public %s(byte[] bytes) {\n", td.Name) - fmt.Fprintf(file, " BytesDecoder decode = new BytesDecoder(bytes);\n") - for _, field := range td.Fields { - name := capitalize(field.Name) - fmt.Fprintf(file, " %s = decode.%s();\n", name, field.Type) - } - fmt.Fprintf(file, " decode.Close();\n") - fmt.Fprintf(file, " }\n") - - // write conversion to byte array - fmt.Fprintf(file, "\n public byte[] toBytes() {\n") - fmt.Fprintf(file, " return new BytesEncoder().\n") - for _, field := range td.Fields { - name := capitalize(field.Name) - fmt.Fprintf(file, " %s(%s).\n", field.Type, name) - } - fmt.Fprintf(file, " Data();\n }\n") - - fmt.Fprintf(file, "}\n") - return nil -} diff --git a/tools/schema/generator/utils.go b/tools/schema/generator/utils.go new file mode 100644 index 0000000000..9687ca39a0 --- /dev/null +++ b/tools/schema/generator/utils.go @@ -0,0 +1,174 @@ +package generator + +import ( + "bufio" + "fmt" + "os" + "regexp" + "sort" + "strings" +) + +var ( + //nolint:unused + snakePart = regexp.MustCompile(`_[a-z]`) + camelPart = regexp.MustCompile(`[a-z0-9][A-Z]`) + camelPartWithID = regexp.MustCompile(`[A-Z][A-Z]+[a-z]`) +) + +func calculatePadding(fields []*Field, types StringMap, snakeName bool) (nameLen, typeLen int) { + for _, param := range fields { + fldName := param.Name + if snakeName { + fldName = snake(fldName) + } + if nameLen < len(fldName) { + nameLen = len(fldName) + } + fldType := param.Type + if types != nil { + fldType = types[fldType] + } + if typeLen < len(fldType) { + typeLen = len(fldType) + } + } + return +} + +// convert lowercase snake case to camel case +//nolint:deadcode,unused +func camel(name string) string { + // replace each underscore followed by [a-z] with [A-Z] + return snakePart.ReplaceAllStringFunc(name, func(sub string) string { + return strings.ToUpper(sub[1:]) + }) +} + +// capitalize first letter +func capitalize(name string) string { + return upper(name[:1]) + name[1:] +} + +// convert to lower case +func lower(name string) string { + return strings.ToLower(name) +} + +func FindModulePath() error { + cwd, err := os.Getwd() + if err != nil { + return err + } + // we're going to walk up the path, make sure to restore + ModuleCwd = cwd + defer func() { + _ = os.Chdir(ModuleCwd) + }() + + file, err := os.Open("go.mod") + for err != nil { + err = os.Chdir("..") + if err != nil { + return fmt.Errorf("cannot find go.mod in cwd path") + } + prev := cwd + cwd, err = os.Getwd() + if err != nil { + return err + } + if cwd == prev { + // e.g. Chdir("..") gets us in a loop at Linux root + return fmt.Errorf("cannot find go.mod in cwd path") + } + file, err = os.Open("go.mod") + } + + // now file is the go.mod and cwd holds the path + defer func() { + _ = file.Close() + }() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "module ") { + ModuleName = strings.TrimSpace(line[len("module"):]) + ModulePath = cwd + return nil + } + } + + return fmt.Errorf("cannot find module definition in go.mod") +} + +// pad to specified size with spaces +func pad(name string, size int) string { + for i := len(name); i < size; i++ { + name += " " + } + return name +} + +// convert camel case to lower case snake case +func snake(name string) string { + // insert underscores between [a-z0-9] followed by [A-Z] + name = camelPart.ReplaceAllStringFunc(name, func(sub string) string { + return sub[:1] + "_" + sub[1:] + }) + + // insert underscores between double [A-Z] followed by [a-z] + name = camelPartWithID.ReplaceAllStringFunc(name, func(sub string) string { + n := len(sub) + return sub[:n-2] + "_" + sub[n-2:] + }) + + // lowercase the entire final result + return lower(name) +} + +// uncapitalize first letter +func uncapitalize(name string) string { + return lower(name[:1]) + name[1:] +} + +// convert to upper case +func upper(name string) string { + return strings.ToUpper(name) +} + +func sortedFields(dict FieldMap) []string { + keys := make([]string, 0) + for key := range dict { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +func sortedFuncDescs(dict FuncDefMap) []string { + keys := make([]string, 0) + for key := range dict { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +func sortedKeys(dict StringMap) []string { + keys := make([]string, 0) + for key := range dict { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +func sortedMaps(dict StringMapMap) []string { + keys := make([]string, 0) + for key := range dict { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} diff --git a/tools/schema/main.go b/tools/schema/main.go index e4aa50d9c0..74bdc229f7 100644 --- a/tools/schema/main.go +++ b/tools/schema/main.go @@ -9,24 +9,25 @@ import ( "errors" "flag" "fmt" - "io/ioutil" + "io" + "io/fs" "os" "path/filepath" "strings" + "time" "github.com/iotaledger/wasp/tools/schema/generator" "gopkg.in/yaml.v2" ) var ( - disabledFlag = false - flagCore = flag.Bool("core", false, "generate core contract interface") - flagForce = flag.Bool("force", false, "force code generation") - flagGo = flag.Bool("go", false, "generate Go code") - flagInit = flag.String("init", "", "generate new schema file for smart contract named ") - flagJava = &disabledFlag // flag.Bool("java", false, "generate Java code ") - flagRust = flag.Bool("rust", false, "generate Rust code ") - flagType = flag.String("type", "yaml", "type of schema file that will be generated. Values(yaml,json)") + flagCore = flag.Bool("core", false, "generate core contract interface") + flagForce = flag.Bool("force", false, "force code generation") + flagGo = flag.Bool("go", false, "generate Go code") + flagInit = flag.String("init", "", "generate new schema file for smart contract named ") + flagRust = flag.Bool("rust", false, "generate Rust code") + flagTs = flag.Bool("ts", false, "generate TypScript code") + flagType = flag.String("type", "yaml", "type of schema file that will be generated. Values(yaml,json)") ) func init() { @@ -35,11 +36,16 @@ func init() { func main() { err := generator.FindModulePath() - if err != nil { + if err != nil && *flagGo { fmt.Println(err) return } + if *flagCore { + generateCoreInterfaces() + return + } + file, err := os.Open("schema.yaml") if err != nil { file, err = os.Open("schema.json") @@ -68,62 +74,66 @@ func main() { flag.Usage() } +func generateCoreInterfaces() { + err := filepath.WalkDir("interfaces", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if !strings.HasSuffix(path, ".yaml") { + return nil + } + + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + return generateSchema(file) + }) + if err != nil { + fmt.Println(err) + } +} + func generateSchema(file *os.File) error { info, err := file.Stat() if err != nil { return err } - schemaTime := info.ModTime() s, err := loadSchema(file) if err != nil { return err } - s.CoreContracts = *flagCore - if *flagGo { - info, err = os.Stat("consts.go") - if err == nil && info.ModTime().After(schemaTime) && !*flagForce { - fmt.Println("skipping Go code generation") - } else { - fmt.Println("generating Go code") - err = s.GenerateGo() - if err != nil { - return err - } - if !s.CoreContracts { - err = s.GenerateGoTests() - if err != nil { - return err - } - } + + s.SchemaTime = info.ModTime() + if *flagForce { + // make as if it has just been updated + s.SchemaTime = time.Now() + } + + if *flagTs { + g := generator.NewTypeScriptGenerator() + err = g.Generate(s) + if err != nil { + return err } } - if *flagJava { - fmt.Println("generating Java code") - err = s.GenerateJava() + if *flagGo { + g := generator.NewGoGenerator() + err = g.Generate(s) if err != nil { return err } } if *flagRust { - info, err = os.Stat("src/consts.rs") - if err == nil && info.ModTime().After(schemaTime) && !*flagForce { - fmt.Println("skipping Rust code generation") - } else { - fmt.Println("generating Rust code") - err = s.GenerateRust() - if err != nil { - return err - } - if !s.CoreContracts { - err = s.GenerateGoTests() - if err != nil { - return err - } - } + g := generator.NewRustGenerator() + err = g.Generate(s) + if err != nil { + return err } } return nil @@ -177,6 +187,36 @@ func generateSchemaNew() error { return errors.New("invalid schema type: " + *flagType) } +func loadSchema(file *os.File) (s *generator.Schema, err error) { + fmt.Println("loading " + file.Name()) + schemaDef := &generator.SchemaDef{} + switch filepath.Ext(file.Name()) { + case ".json": + err = json.NewDecoder(file).Decode(schemaDef) + if err == nil && *flagType == "convert" { + err = WriteYAMLSchema(schemaDef) + } + case ".yaml": + fileByteArray, _ := io.ReadAll(file) + err = yaml.Unmarshal(fileByteArray, schemaDef) + if err == nil && *flagType == "convert" { + err = WriteJSONSchema(schemaDef) + } + default: + err = errors.New("unexpected file type: " + file.Name()) + } + if err != nil { + return nil, err + } + + s = generator.NewSchema() + err = s.Compile(schemaDef) + if err != nil { + return nil, err + } + return s, nil +} + func WriteJSONSchema(schemaDef *generator.SchemaDef) error { file, err := os.Create("schema.json") if err != nil { @@ -213,33 +253,3 @@ func WriteYAMLSchema(schemaDef *generator.SchemaDef) error { _, err = file.Write(b) return err } - -func loadSchema(file *os.File) (s *generator.Schema, err error) { - fmt.Println("loading " + file.Name()) - schemaDef := &generator.SchemaDef{} - switch filepath.Ext(file.Name()) { - case ".json": - err = json.NewDecoder(file).Decode(schemaDef) - if err == nil && *flagType == "convert" { - err = WriteYAMLSchema(schemaDef) - } - case ".yaml": - fileByteArray, _ := ioutil.ReadAll(file) - err = yaml.Unmarshal(fileByteArray, schemaDef) - if err == nil && *flagType == "convert" { - err = WriteJSONSchema(schemaDef) - } - default: - err = errors.New("unexpected file type: " + file.Name()) - } - if err != nil { - return nil, err - } - - s = generator.NewSchema() - err = s.Compile(schemaDef) - if err != nil { - return nil, err - } - return s, nil -} diff --git a/tools/wasp-cli/chain/evm.go b/tools/wasp-cli/chain/evm.go index 8284f2201c..2145a3c817 100644 --- a/tools/wasp-cli/chain/evm.go +++ b/tools/wasp-cli/chain/evm.go @@ -4,8 +4,9 @@ package chain import ( - "github.com/iotaledger/wasp/contracts/native/evmchain" - "github.com/iotaledger/wasp/packages/evm" + "github.com/iotaledger/wasp/contracts/native/evm" + "github.com/iotaledger/wasp/contracts/native/evm/evmchain" + "github.com/iotaledger/wasp/packages/evm/evmtypes" "github.com/iotaledger/wasp/packages/evm/jsonrpc" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/kv/dict" @@ -35,13 +36,13 @@ func initEVMDeploy(evmCmd *cobra.Command) { var deployParams evmcli.DeployParams evmDeployCmd := &cobra.Command{ Use: "deploy", - Short: "Deploy the evmchain contract (i.e. create a new EVM chain)", + Short: "Deploy the evmchain/evmlight contract (i.e. create a new EVM chain)", Run: func(cmd *cobra.Command, args []string) { - deployContract(deployParams.Name, deployParams.Description, evmchain.Contract.ProgramHash, dict.Dict{ - evmchain.FieldChainID: codec.EncodeUint16(uint16(deployParams.ChainID)), - evmchain.FieldGenesisAlloc: evmchain.EncodeGenesisAlloc(deployParams.GetGenesis(nil)), + deployContract(deployParams.Name(), deployParams.Description(), deployParams.EVMFlavor().ProgramHash, dict.Dict{ + evm.FieldChainID: codec.EncodeUint16(uint16(deployParams.ChainID)), + evm.FieldGenesisAlloc: evmtypes.EncodeGenesisAlloc(deployParams.GetGenesis(nil)), }) - log.Printf("%s contract successfully deployed.\n", evmchain.Contract.Name) + log.Printf("%s contract successfully deployed.\n", deployParams.Name()) }, } evmCmd.AddCommand(evmDeployCmd) @@ -72,6 +73,6 @@ By default the server has no unlocked accounts. To send transactions, either: jsonRPCServer.InitFlags(jsonRPCCmd) jsonRPCCmd.Flags().IntVarP(&chainID, "chainid", "", evm.DefaultChainID, "ChainID (used for signing transactions)") - jsonRPCCmd.Flags().StringVarP(&contractName, "name", "", evmchain.Contract.Name, "evmchain contract name") + jsonRPCCmd.Flags().StringVarP(&contractName, "name", "", evmchain.Contract.Name, "evmchain/evmlight contract name") evmCmd.AddCommand(jsonRPCCmd) } diff --git a/tools/wasp-cli/util/file.go b/tools/wasp-cli/util/file.go index 7d873b0d84..03ec86bd2a 100644 --- a/tools/wasp-cli/util/file.go +++ b/tools/wasp-cli/util/file.go @@ -1,13 +1,13 @@ package util import ( - "io/ioutil" + "os" "github.com/iotaledger/wasp/tools/wasp-cli/log" ) func ReadFile(fname string) []byte { - b, err := ioutil.ReadFile(fname) + b, err := os.ReadFile(fname) log.Check(err) return b }