From 897be9b4df3798d1aca7223d3b89dcd251b84c41 Mon Sep 17 00:00:00 2001 From: William Law Date: Wed, 24 Apr 2024 14:50:16 -0400 Subject: [PATCH 1/2] Remove Warp Integration (#856) * remove warp * fix type_parser tests * fix chain/transaction * add back ShouldVerifyWithContext * fix rpc * mock gen works * morpheusvm integration passes * fix load & e2e for morpheus * tokenvm integration passes * fix tokenvm e2e and load * fix tokenvm * fix morpheus * fix lint * fix x/programs * remove StatelessBlock.bctx * remove block functions * update readme * remove unused outputs * fix Result.Size() * inline size into NewWriter * keep vm.cacheAuth * storage remove unnecessary alloc * check no bytes left to read * update tokenvm readme * reduce alloc size of v * remove vm.buildBlock blockCtx * add todo to remove cachAuth * unify verify and Verify * unify buildBlock and BuildBlock --- README.md | 131 +--- chain/block.go | 189 +---- chain/builder.go | 57 +- chain/consts.go | 23 +- chain/dependencies.go | 23 +- chain/errors.go | 10 - chain/mock_action.go | 28 +- chain/mock_rules.go | 58 -- chain/processor.go | 12 +- chain/result.go | 27 +- chain/transaction.go | 199 +---- chain/warp_signature.go | 13 - cli/spam.go | 8 +- codec/type_parser.go | 24 +- codec/type_parser_test.go | 19 +- examples/morpheusvm/actions/transfer.go | 18 +- examples/morpheusvm/auth/bls.go | 3 +- examples/morpheusvm/auth/ed25519.go | 3 +- examples/morpheusvm/auth/secp256r1.go | 3 +- .../morpheusvm/cmd/morpheus-cli/cmd/action.go | 2 +- .../cmd/morpheus-cli/cmd/resolutions.go | 5 +- .../morpheusvm/cmd/morpheus-cli/cmd/spam.go | 2 +- examples/morpheusvm/consts/consts.go | 5 +- examples/morpheusvm/genesis/genesis.go | 8 +- examples/morpheusvm/genesis/rules.go | 16 - examples/morpheusvm/registry/registry.go | 5 +- examples/morpheusvm/storage/state_manager.go | 9 - examples/morpheusvm/storage/storage.go | 27 +- examples/morpheusvm/tests/e2e/e2e_test.go | 3 - .../tests/integration/integration_test.go | 44 +- examples/morpheusvm/tests/load/load_test.go | 1 - examples/tokenvm/README.md | 45 +- examples/tokenvm/actions/burn_asset.go | 28 +- examples/tokenvm/actions/close_order.go | 24 +- examples/tokenvm/actions/create_asset.go | 26 +- examples/tokenvm/actions/create_order.go | 26 +- examples/tokenvm/actions/export_asset.go | 284 ------- examples/tokenvm/actions/fill_order.go | 44 +- examples/tokenvm/actions/import_asset.go | 292 ------- examples/tokenvm/actions/mint_asset.go | 35 +- examples/tokenvm/actions/outputs.go | 61 +- examples/tokenvm/actions/transfer.go | 20 +- examples/tokenvm/actions/warp_transfer.go | 156 ---- examples/tokenvm/auth/ed25519.go | 3 +- examples/tokenvm/cmd/token-cli/cmd/action.go | 326 +------- examples/tokenvm/cmd/token-cli/cmd/handler.go | 29 +- .../tokenvm/cmd/token-cli/cmd/resolutions.go | 76 +- examples/tokenvm/cmd/token-cli/cmd/root.go | 3 - examples/tokenvm/cmd/token-cli/cmd/spam.go | 3 +- .../cmd/token-faucet/manager/manager.go | 2 +- .../cmd/token-wallet/backend/backend.go | 52 +- examples/tokenvm/consts/consts.go | 5 +- examples/tokenvm/controller/controller.go | 4 - examples/tokenvm/controller/resolutions.go | 2 +- examples/tokenvm/controller/state_manager.go | 8 - examples/tokenvm/genesis/genesis.go | 9 +- examples/tokenvm/genesis/rules.go | 20 - examples/tokenvm/registry/registry.go | 8 +- examples/tokenvm/rpc/dependencies.go | 2 +- examples/tokenvm/rpc/jsonrpc_client.go | 12 +- examples/tokenvm/rpc/jsonrpc_server.go | 4 +- examples/tokenvm/storage/storage.go | 56 +- examples/tokenvm/tests/e2e/e2e_test.go | 741 ------------------ .../tests/integration/integration_test.go | 359 +-------- examples/tokenvm/tests/load/load_test.go | 1 - go.mod | 2 +- rpc/dependencies.go | 4 - rpc/jsonrpc_client.go | 156 +--- rpc/jsonrpc_server.go | 83 -- vm/network_warp.go | 71 -- vm/resolutions.go | 45 +- vm/storage.go | 102 --- vm/vm.go | 49 +- vm/warp_manager.go | 360 --------- x/programs/cmd/simulator/cmd/program.go | 4 +- .../simulator/vm/actions/program_create.go | 16 +- .../simulator/vm/actions/program_execute.go | 34 +- x/programs/cmd/simulator/vm/consts/consts.go | 5 +- .../cmd/simulator/vm/genesis/genesis.go | 8 +- x/programs/cmd/simulator/vm/genesis/rules.go | 16 - .../cmd/simulator/vm/registry/registry.go | 5 +- .../cmd/simulator/vm/storage/state_manager.go | 9 - .../cmd/simulator/vm/storage/storage.go | 25 +- 83 files changed, 367 insertions(+), 4368 deletions(-) delete mode 100644 chain/warp_signature.go delete mode 100644 examples/tokenvm/actions/export_asset.go delete mode 100644 examples/tokenvm/actions/import_asset.go delete mode 100644 examples/tokenvm/actions/warp_transfer.go delete mode 100644 vm/network_warp.go delete mode 100644 vm/warp_manager.go diff --git a/README.md b/README.md index 9f5b8efbb3..b4f614cf5a 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,6 @@ type StatefulBlock struct { Txs []*Transaction `json:"txs"` StateRoot ids.ID `json:"stateRoot"` - WarpResults set.Bits64 `json:"warpResults"` } ``` @@ -276,9 +275,6 @@ GetWindowTargetUnits() Dimensions GetMaxBlockUnits() Dimensions GetBaseComputeUnits() uint64 -GetBaseWarpComputeUnits() uint64 -GetWarpComputeUnitsPerSigner() uint64 -GetOutgoingWarpComputeUnits() uint64 GetStorageKeyReadUnits() uint64 GetStorageValueReadUnits() uint64 // per chunk @@ -296,9 +292,6 @@ WindowTargetUnits: chain.Dimensions{20_000_000, 1_000, 1_000, 1_000, 1_ MaxBlockUnits: chain.Dimensions{1_800_000, 2_000, 2_000, 2_000, 2_000}, BaseComputeUnits: 1, -BaseWarpComputeUnits: 1_024, -WarpComputeUnitsPerSigner: 128, -OutgoingWarpComputeUnits: 1_024, StorageKeyReadUnits: 5, StorageValueReadUnits: 2, @@ -399,36 +392,6 @@ for a single account and ensure they are ordered) and makes the network layer more efficient (we can gossip any valid transaction to any node instead of just the transactions for each account that can be executed at the moment). -### Avalanche Warp Messaging Support -`hypersdk` provides support for Avalanche Warp Messaging (AWM) out-of-the-box. AWM enables any -Avalanche Subnet to send arbitrary messages to any other Avalanche Subnet in just a few -seconds (or less) without relying on a trusted relayer or bridge (just the validators of the Subnet sending the message). -You can learn more about AWM and how it works -[here](https://docs.google.com/presentation/d/1eV4IGMB7qNV7Fc4hp7NplWxK_1cFycwCMhjrcnsE9mU/edit). - -

- warp -

- -AWM is a primitive provided by the Avalanche Network used to verify that -a particular [BLS Multi-Signatures](https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html) -is valid and signed by some % of the stake weight of a particular Avalanche -Subnet (typically the Subnet where the message originated). Specifying when an -Avalanche Custom VM produces a Warp Message for signing, defining the format -of Warp Messages sent between Subnets, implementing some mechanism to gather -individual signatures from validators (to aggregate into a BLS -Multi-Signature) over this user-defined message, articulating how an imported -Warp Message from another Subnet is handled on a destination (if the -destination chooses to even accept the message), and enabling retries in the -case that a message is dropped or the BLS Multi-Signature expires are just a few of the items -left to the implementer. - -The `hypersdk` handles all of the above items for you except for defining when -you should emit a Warp Message to send to another Subnet (i.e. what an export looks like on-chain), -what this Warp Message should look like (i.e. what do you want to send to another Subnet), and -what you should do if you receive a Warp Message (i.e. mint assets if you -receive an import). - ### Easy Functionality Upgrades Every object that can appear on-chain (i.e. `Actions` and/or `Auth`) and every chain parameter (i.e. `Unit Price`) is scoped by block timestamp. This makes it @@ -630,8 +593,8 @@ You can view what this looks like in the `tokenvm` by clicking this #### Registry ```golang -ActionRegistry *codec.TypeParser[Action, *warp.Message, bool] -AuthRegistry *codec.TypeParser[Auth, *warp.Message, bool] +ActionRegistry *codec.TypeParser[Action, bool] +AuthRegistry *codec.TypeParser[Auth, bool] ``` The `ActionRegistry` and `AuthRegistry` inform the `hypersdk` how to @@ -702,12 +665,7 @@ type Action interface { timestamp int64, actor codec.Address, txID ids.ID, - warpVerified bool, - ) (success bool, computeUnits uint64, output []byte, warpMessage *warp.UnsignedMessage, err error) - - // OutputsWarpMessage indicates whether an [Action] will produce a warp message. The max size - // of any warp message is [MaxOutgoingWarpChunks]. - OutputsWarpMessage() bool + ) (success bool, computeUnits uint64, output []byte, err error) } ``` @@ -727,16 +685,13 @@ type Result struct { Consumed Dimensions Fee uint64 - - WarpMessage *warp.UnsignedMessage } ``` `Actions` emit a `Result` at the end of their execution. This `Result` indicates if the execution was a `Success` (if not, all effects are rolled back), how many `Units` were used (failed execution may not use all units an -`Action` requested), an `Output` (arbitrary bytes specific to the `hypervm`), -and optionally a `WarpMessage` (which Subnet Validators will sign). +`Action` requested), an `Output` (arbitrary bytes specific to the `hypervm`). ### Auth ```golang @@ -795,9 +750,6 @@ type Rules interface { GetMaxBlockUnits() Dimensions GetBaseComputeUnits() uint64 - GetBaseWarpComputeUnits() uint64 - GetWarpComputeUnitsPerSigner() uint64 - GetOutgoingWarpComputeUnits() uint64 // Invariants: // * Controllers must manage the max key length and max value length (max network @@ -819,8 +771,6 @@ type Rules interface { GetStorageKeyWriteUnits() uint64 GetStorageValueWriteUnits() uint64 // per chunk - GetWarpConfig(sourceChainID ids.ID) (bool, uint64, uint64) - FetchCustom(string) (any, bool) } ``` @@ -836,79 +786,6 @@ You can view what this looks like in the `indexvm` by clicking case of the `indexvm`, the custom rule support is used to set the cost for adding anything to state (which is a very `hypervm-specific` value). -### Avalanche Warp Messaging -To add AWM support to a `hypervm`, an implementer first specifies whether a -particular `Action`/`Auth` item expects a `*warp.Message` when registering -them with their corresponding registry (`false` if no expected and `true` if -so): -```golang -ActionRegistry.Register(&actions.Transfer{}, actions.UnmarshalTransfer, false) -ActionRegistry.Register(&actions.ImportAsset{}, actions.UnmarshalImportAsset, true) -``` - -You can view what this looks like in the `tokenvm` by clicking -[here](./examples/tokenvm/controller/registry.go). The `hypersdk` uses this -boolean to enforce the existence/non-existence of a `*warp.Message` on the -`chain.Transaction` that wraps the `Action` (marking a block as invalid if there is -something unexpected). - -`Actions` can use the provided `*warp.Message` in their registered unmarshaler -(in this case, the provided `*warp.Message` is parsed into a format specified -by the `tokenvm`): -```golang -func UnmarshalImportAsset(p *codec.Packer, wm *warp.Message) (chain.Action, error) { - var ( - imp ImportAsset - err error - ) - imp.Fill = p.UnpackBool() - if err := p.Err(); err != nil { - return nil, err - } - imp.warpMessage = wm - imp.warpTransfer, err = UnmarshalWarpTransfer(imp.warpMessage.Payload) - if err != nil { - return nil, err - } - // Ensure we can fill the swap if it exists - if imp.Fill && imp.warpTransfer.SwapIn == 0 { - return nil, ErrNoSwapToFill - } - return &imp, nil -} -``` - -This `WarpTransfer` object looks like: -```golang -type WarpTransfer struct { - To crypto.PublicKey `json:"to"` - Asset ids.ID `json:"asset"` - Value uint64 `json:"value"` - - // Return is set to true when a warp message is sending funds back to the - // chain where they were created. - Return bool `json:"return"` - - // Reward is the amount of [Asset] to send the [Actor] that submits this - // transaction. - Reward uint64 `json:"reward"` - - // SwapIn is the amount of [Asset] we are willing to swap for [AssetOut]. - SwapIn uint64 `json:"swapIn"` - // AssetOut is the asset we are seeking to get for [SwapIn]. - AssetOut ids.ID `json:"assetOut"` - // SwapOut is the amount of [AssetOut] we are seeking. - SwapOut uint64 `json:"swapOut"` - // SwapExpiry is the unix timestamp at which the swap becomes invalid (and - // the message can be processed without a swap. - SwapExpiry int64 `json:"swapExpiry"` - - // TxID is the transaction that created this message. This is used to ensure - // there is WarpID uniqueness. - TxID ids.ID `json:"txID"` -} -``` - You can view what the import `Action` associated with the above examples looks like [here](./examples/tokenvm/actions/import_asset.go) diff --git a/chain/block.go b/chain/block.go index 7be9c7ec6f..fd97d86f1d 100644 --- a/chain/block.go +++ b/chain/block.go @@ -13,9 +13,7 @@ import ( "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" - "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/x/merkledb" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -31,9 +29,8 @@ import ( ) var ( - _ snowman.Block = &StatelessBlock{} - _ block.WithVerifyContext = &StatelessBlock{} - _ block.StateSummary = &SyncableBlock{} + _ snowman.Block = &StatelessBlock{} + _ block.StateSummary = &SyncableBlock{} ) type StatefulBlock struct { @@ -51,8 +48,7 @@ type StatefulBlock struct { // or [Verify], which reduces the amount of time we are // blocking the consensus engine from voting on the block, // starting the verification of another block, etc. - StateRoot ids.ID `json:"stateRoot"` - WarpResults set.Bits64 `json:"warpResults"` + StateRoot ids.ID `json:"stateRoot"` size int @@ -73,16 +69,6 @@ func (b *StatefulBlock) ID() (ids.ID, error) { return utils.ToID(blk), nil } -// warpJob is used to signal to a listner that a *warp.Message has been -// verified. -type warpJob struct { - msg *warp.Message - signers int - verifiedChan chan bool - verified bool - warpNum int -} - func NewGenesisBlock(root ids.ID) *StatefulBlock { return &StatefulBlock{ // We set the genesis block timestamp to be after the ProposerVM fork activation. @@ -112,11 +98,6 @@ type StatelessBlock struct { bytes []byte txsSet set.Set[ids.ID] - warpMessages map[ids.ID]*warpJob - containsWarp bool // this allows us to avoid allocating a map when we build - bctx *block.Context - vdrState validators.State - results []*Result feeManager *fees.Manager @@ -179,7 +160,6 @@ func (b *StatelessBlock) populateTxs(ctx context.Context) error { // Confirm no transaction duplicates and setup // AWM processing b.txsSet = set.NewSet[ids.ID](len(b.Txs)) - b.warpMessages = map[ids.ID]*warpJob{} for _, tx := range b.Txs { // Ensure there are no duplicate transactions if b.txsSet.Contains(tx.ID()) { @@ -195,29 +175,6 @@ func (b *StatelessBlock) populateTxs(ctx context.Context) error { } batchVerifier.Add(txDigest, tx.Auth) } - - // Check if we need the block context to verify the block (which contains - // an Avalanche Warp Message) - // - // Instead of erroring out if a warp message is invalid, we mark the - // verification as skipped and include it in the verification result so - // that a fee can still be deducted. - if tx.WarpMessage != nil { - if len(b.warpMessages) == MaxWarpMessages { - return ErrTooManyWarpMessages - } - signers, err := tx.WarpMessage.Signature.NumSigners() - if err != nil { - return err - } - b.warpMessages[tx.ID()] = &warpJob{ - msg: tx.WarpMessage, - signers: signers, - verifiedChan: make(chan bool, 1), - warpNum: len(b.warpMessages), - } - b.containsWarp = true - } } return nil } @@ -287,9 +244,6 @@ func (b *StatelessBlock) initializeBuilt( b.txsSet = set.NewSet[ids.ID](len(b.Txs)) for _, tx := range b.Txs { b.txsSet.Add(tx.ID()) - if tx.WarpMessage != nil { - b.containsWarp = true - } } return nil } @@ -297,38 +251,6 @@ func (b *StatelessBlock) initializeBuilt( // implements "snowman.Block.choices.Decidable" func (b *StatelessBlock) ID() ids.ID { return b.id } -// implements "block.WithVerifyContext" -func (b *StatelessBlock) ShouldVerifyWithContext(context.Context) (bool, error) { - return b.containsWarp, nil -} - -// implements "block.WithVerifyContext" -func (b *StatelessBlock) VerifyWithContext(ctx context.Context, bctx *block.Context) error { - start := time.Now() - defer func() { - b.vm.RecordBlockVerify(time.Since(start)) - }() - - stateReady := b.vm.StateReady() - ctx, span := b.vm.Tracer().Start( - ctx, "StatelessBlock.VerifyWithContext", - trace.WithAttributes( - attribute.Int("txs", len(b.Txs)), - attribute.Int64("height", int64(b.Hght)), - attribute.Bool("stateReady", stateReady), - attribute.Int64("pchainHeight", int64(bctx.PChainHeight)), - attribute.Bool("built", b.Processed()), - ), - ) - defer span.End() - - // Persist the context in case we need it during Accept - b.bctx = bctx - - // Proceed with normal verification - return b.verify(ctx, stateReady) -} - // implements "snowman.Block" func (b *StatelessBlock) Verify(ctx context.Context) error { start := time.Now() @@ -348,10 +270,6 @@ func (b *StatelessBlock) Verify(ctx context.Context) error { ) defer span.End() - return b.verify(ctx, stateReady) -} - -func (b *StatelessBlock) verify(ctx context.Context, stateReady bool) error { log := b.vm.Logger() switch { case !stateReady: @@ -407,33 +325,6 @@ func (b *StatelessBlock) verify(ctx context.Context, stateReady bool) error { return nil } -// verifyWarpMessage will attempt to verify a given warp message provided by an -// Action. -func (b *StatelessBlock) verifyWarpMessage(ctx context.Context, r Rules, msg *warp.Message) bool { - // We do not check the validity of [SourceChainID] because a VM could send - // itself a message to trigger a chain upgrade. - allowed, num, denom := r.GetWarpConfig(msg.SourceChainID) - if !allowed { - b.vm.Logger(). - Warn("unable to verify warp message", zap.Stringer("warpID", msg.ID()), zap.Error(ErrDisabledChainID)) - return false - } - if err := msg.Signature.Verify( - ctx, - &msg.UnsignedMessage, - r.NetworkID(), - b.vdrState, - b.bctx.PChainHeight, - num, - denom, - ); err != nil { - b.vm.Logger(). - Warn("unable to verify warp message", zap.Stringer("warpID", msg.ID()), zap.Error(err)) - return false - } - return true -} - // innerVerify executes the block on top of the provided [VerifyContext]. // // Invariants: @@ -515,64 +406,11 @@ func (b *StatelessBlock) innerVerify(ctx context.Context, vctx VerifyContext) er } } - // Start validating warp messages, if they exist - var invalidWarpResult bool - if b.containsWarp { - if b.bctx == nil { - log.Error( - "missing verify block context", - zap.Uint64("height", b.Hght), - zap.Stringer("id", b.ID()), - ) - return ErrMissingBlockContext - } - _, warpVerifySpan := b.vm.Tracer().Start(ctx, "StatelessBlock.verifyWarpMessages") //nolint:spancheck - b.vdrState = b.vm.ValidatorState() - go func() { - defer warpVerifySpan.End() - // We don't use [b.vm.Workers] here because we need the warp verification - // results during normal execution. If we added a job to the workers queue, - // it would get executed after all signatures. Additionally, BLS - // Multi-Signature verification is already parallelized so we should just - // do one at a time to avoid overwhelming the CPU. - for txID, msg := range b.warpMessages { - if ctx.Err() != nil { - return - } - blockVerified := b.WarpResults.Contains(uint(msg.warpNum)) - if b.vm.IsBootstrapped() && !invalidWarpResult { - start := time.Now() - verified := b.verifyWarpMessage(ctx, r, msg.msg) - msg.verifiedChan <- verified - msg.verified = verified - log.Info( - "processed warp message", - zap.Stringer("txID", txID), - zap.Bool("verified", verified), - zap.Int("signers", msg.signers), - zap.Duration("t", time.Since(start)), - ) - if blockVerified != verified { - invalidWarpResult = true - } - } else { - // When we are bootstrapping, we just use the result in the block. - // - // We also use the result in the block when we have found - // a verification mismatch (our verify result is different than the - // block) to avoid doing extra work. - msg.verifiedChan <- blockVerified - msg.verified = blockVerified - } - } - }() - } - // Compute next unit prices to use feeKey := FeeKey(b.vm.StateManager().FeeKey()) feeRaw, err := parentView.GetValue(ctx, feeKey) if err != nil { - return err //nolint:spancheck + return err } parentFeeManager := fees.NewManager(feeRaw) feeManager, err := parentFeeManager.ComputeNext(parentTimestamp, b.Tmstmp, r) @@ -589,23 +427,6 @@ func (b *StatelessBlock) innerVerify(ctx context.Context, vctx VerifyContext) er b.results = results b.feeManager = feeManager - // Ensure warp results are correct - if invalidWarpResult { - return ErrWarpResultMismatch - } - numWarp := len(b.warpMessages) - if numWarp > MaxWarpMessages { - return ErrTooManyWarpMessages - } - var warpResultsLimit set.Bits64 - warpResultsLimit.Add(uint(numWarp)) - if b.WarpResults >= warpResultsLimit { - // If the value of [WarpResults] is greater than the value of uint64 with - // a 1-bit shifted [numWarp] times, then there are unused bits set to - // 1 (which should is not allowed). - return ErrWarpResultMismatch - } - // Update chain metadata heightKeyStr := string(heightKey) timestampKeyStr := string(timestampKey) @@ -999,7 +820,6 @@ func (b *StatefulBlock) Marshal() ([]byte, error) { } p.PackID(b.StateRoot) - p.PackUint64(uint64(b.WarpResults)) bytes := p.Bytes() if err := p.Err(); err != nil { return nil, err @@ -1034,7 +854,6 @@ func UnmarshalBlock(raw []byte, parser Parser) (*StatefulBlock, error) { } p.UnpackID(false, &b.StateRoot) - b.WarpResults = set.Bits64(p.UnpackUint64(false)) // Ensure no leftover bytes if !p.Empty() { diff --git a/chain/builder.go b/chain/builder.go index 71ec95575b..c6122563b1 100644 --- a/chain/builder.go +++ b/chain/builder.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" "go.opentelemetry.io/otel/attribute" @@ -65,7 +64,6 @@ func BuildBlock( ctx context.Context, vm VM, parent *StatelessBlock, - blockContext *block.Context, ) (*StatelessBlock, error) { ctx, span := vm.Tracer().Start(ctx, "chain.BuildBlock") defer span.End() @@ -125,13 +123,11 @@ func BuildBlock( cache = map[string]*fetchData{} blockLock sync.RWMutex - warpAdded = uint(0) start = time.Now() txsAttempted = 0 results = []*Result{} - vdrState = vm.ValidatorState() - sm = vm.StateManager() + sm = vm.StateManager() // prepareStreamLock ensures we don't overwrite stream prefetching spawned // asynchronously. @@ -174,18 +170,6 @@ func BuildBlock( continue } - // Ensure we can process if transaction includes a warp message - if tx.WarpMessage != nil && blockContext == nil { - log.Debug( - "dropping pending warp message because no context provided", - zap.Stringer("txID", tx.ID()), - ) - restorableLock.Lock() - restorable = append(restorable, tx) - restorableLock.Unlock() - continue - } - stateKeys, err := tx.StateKeys(sm) if err != nil { // Drop bad transaction and continue @@ -287,35 +271,6 @@ func BuildBlock( return nil } - // Verify warp message, if it exists - // - // We don't drop invalid warp messages because we must collect fees for - // the work the sender made us do (otherwise this would be a DoS). - // - // We wait as long as possible to verify the signature to ensure we don't - // spend unnecessary time on an invalid tx. - var warpErr error - if tx.WarpMessage != nil { - // We do not check the validity of [SourceChainID] because a VM could send - // itself a message to trigger a chain upgrade. - allowed, num, denom := r.GetWarpConfig(tx.WarpMessage.SourceChainID) - if allowed { - warpErr = tx.WarpMessage.Signature.Verify( - ctx, &tx.WarpMessage.UnsignedMessage, r.NetworkID(), - vdrState, blockContext.PChainHeight, num, denom, - ) - } else { - warpErr = ErrDisabledChainID - } - if warpErr != nil { - log.Warn( - "warp verification failed", - zap.Stringer("txID", tx.ID()), - zap.Error(warpErr), - ) - } - } - // If execution works, keep moving forward with new state // // Note, these calculations must match block verification exactly @@ -347,7 +302,6 @@ func BuildBlock( r, tsv, nextTime, - tx.WarpMessage != nil && warpErr == nil, ) if err != nil { // Returning an error here should be avoided at all costs (can be a DoS). Rather, @@ -357,7 +311,6 @@ func BuildBlock( return err } - // Need to atomically check there aren't too many warp messages and add to block blockLock.Lock() defer blockLock.Unlock() @@ -385,13 +338,6 @@ func BuildBlock( tsv.Commit() b.Txs = append(b.Txs, tx) results = append(results, result) - if tx.WarpMessage != nil { - if warpErr == nil { - // Add a bit if the warp message was verified - b.WarpResults.Add(warpAdded) - } - warpAdded++ - } return nil }) } @@ -509,7 +455,6 @@ func BuildBlock( log.Info( "built block", - zap.Bool("context", blockContext != nil), zap.Uint64("hght", b.Hght), zap.Int("attempted", txsAttempted), zap.Int("added", len(b.Txs)), diff --git a/chain/consts.go b/chain/consts.go index 66d7315b10..568f2a3f14 100644 --- a/chain/consts.go +++ b/chain/consts.go @@ -6,8 +6,6 @@ package chain import ( "time" - "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/hypersdk/keys" ) @@ -18,23 +16,10 @@ const ( // This value should be (much) less than the value of [ProposerWindow], otherwise honest // nodes may not build during their allocated window to avoid increasing the skew of the // chain time. - FutureBound = 1 * time.Second - // MaxWarpMessageSize is the maximum size of a warp message. - MaxWarpMessageSize = 256 * units.KiB - // MaxWarpMessages is the maximum number of warp messages allows in a single - // block. - MaxWarpMessages = 64 - // MaxIncomingWarpChunks is the number of chunks stored for an incoming warp message. - MaxIncomingWarpChunks = 0 - // MaxOutgoingWarpChunks is the max number of chunks that can be stored for an outgoing warp message. - // - // This is defined as a constant because storage of warp messages is handled by the hypersdk, - // not the [Controller]. In this mechanism, we frequently query warp messages by TxID across - // ranges (so, we can't expose a way to modify this over time). - MaxOutgoingWarpChunks = 4 - HeightKeyChunks = 1 - TimestampKeyChunks = 1 - FeeKeyChunks = 8 // 96 (per dimension) * 5 (num dimensions) + FutureBound = 1 * time.Second + HeightKeyChunks = 1 + TimestampKeyChunks = 1 + FeeKeyChunks = 8 // 96 (per dimension) * 5 (num dimensions) // MaxKeyDependencies must be greater than the maximum number of key dependencies // any single task could have when executing a task. diff --git a/chain/dependencies.go b/chain/dependencies.go index 6ceddda353..6ca1057feb 100644 --- a/chain/dependencies.go +++ b/chain/dependencies.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/x/merkledb" "github.com/ava-labs/hypersdk/codec" @@ -24,8 +23,8 @@ import ( ) type ( - ActionRegistry *codec.TypeParser[Action, *warp.Message, bool] - AuthRegistry *codec.TypeParser[Auth, *warp.Message, bool] + ActionRegistry *codec.TypeParser[Action, bool] + AuthRegistry *codec.TypeParser[Auth, bool] ) type Parser interface { @@ -131,9 +130,6 @@ type Rules interface { GetMaxBlockUnits() fees.Dimensions GetBaseComputeUnits() uint64 - GetBaseWarpComputeUnits() uint64 - GetWarpComputeUnitsPerSigner() uint64 - GetOutgoingWarpComputeUnits() uint64 // Invariants: // * Controllers must manage the max key length and max value length (max network @@ -155,8 +151,6 @@ type Rules interface { GetStorageKeyWriteUnits() uint64 GetStorageValueWriteUnits() uint64 // per chunk - GetWarpConfig(sourceChainID ids.ID) (bool, uint64, uint64) - FetchCustom(string) (any, bool) } @@ -166,11 +160,6 @@ type MetadataManager interface { FeeKey() []byte } -type WarpManager interface { - IncomingWarpKeyPrefix(sourceChainID ids.ID, msgID ids.ID) []byte - OutgoingWarpKeyPrefix(txID ids.ID) []byte -} - type FeeHandler interface { // StateKeys is a full enumeration of all database keys that could be touched during fee payment // by [addr]. This is used to prefetch state and will be used to parallelize execution (making @@ -205,7 +194,6 @@ type FeeHandler interface { type StateManager interface { FeeHandler MetadataManager - WarpManager } type Object interface { @@ -267,12 +255,7 @@ type Action interface { timestamp int64, actor codec.Address, txID ids.ID, - warpVerified bool, - ) (success bool, computeUnits uint64, output []byte, warpMessage *warp.UnsignedMessage, err error) - - // OutputsWarpMessage indicates whether an [Action] will produce a warp message. The max size - // of any warp message is [MaxOutgoingWarpChunks]. - OutputsWarpMessage() bool + ) (success bool, computeUnits uint64, output []byte, err error) } type Auth interface { diff --git a/chain/errors.go b/chain/errors.go index fc520a41dd..a88d8ccb37 100644 --- a/chain/errors.go +++ b/chain/errors.go @@ -56,16 +56,6 @@ var ( ErrBlockTooBig = errors.New("block too big") ErrKeyNotSpecified = errors.New("key not specified") - // Warp - ErrDisabledChainID = errors.New("cannot import from chain ID") - ErrMissingBlockContext = errors.New("cannot verify warp messages without block context") - ErrUnexpectedWarpMessage = errors.New("unexpected warp message") - ErrExpectedWarpMessage = errors.New("expected warp message") - ErrWarpMessageNotInitialized = errors.New("warp message not initialized") - ErrEmptyWarpPayload = errors.New("empty warp payload") - ErrTooManyWarpMessages = errors.New("too many warp messages") - ErrWarpResultMismatch = errors.New("warp result mismatch") - // Misc ErrNotImplemented = errors.New("not implemented") ErrBlockNotProcessed = errors.New("block is not processed") diff --git a/chain/mock_action.go b/chain/mock_action.go index 6b75860ab4..920b763dbd 100644 --- a/chain/mock_action.go +++ b/chain/mock_action.go @@ -17,7 +17,6 @@ import ( reflect "reflect" ids "github.com/ava-labs/avalanchego/ids" - warp "github.com/ava-labs/avalanchego/vms/platformvm/warp" codec "github.com/ava-labs/hypersdk/codec" state "github.com/ava-labs/hypersdk/state" gomock "go.uber.org/mock/gomock" @@ -47,21 +46,20 @@ func (m *MockAction) EXPECT() *MockActionMockRecorder { } // Execute mocks base method. -func (m *MockAction) Execute(arg0 context.Context, arg1 Rules, arg2 state.Mutable, arg3 int64, arg4 codec.Address, arg5 ids.ID, arg6 bool) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +func (m *MockAction) Execute(arg0 context.Context, arg1 Rules, arg2 state.Mutable, arg3 int64, arg4 codec.Address, arg5 ids.ID) (bool, uint64, []byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Execute", arg0, arg1, arg2, arg3, arg4, arg5, arg6) + ret := m.ctrl.Call(m, "Execute", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(uint64) ret2, _ := ret[2].([]byte) - ret3, _ := ret[3].(*warp.UnsignedMessage) - ret4, _ := ret[4].(error) - return ret0, ret1, ret2, ret3, ret4 + ret3, _ := ret[3].(error) + return ret0, ret1, ret2, ret3 } // Execute indicates an expected call of Execute. -func (mr *MockActionMockRecorder) Execute(arg0, arg1, arg2, arg3, arg4, arg5, arg6 any) *gomock.Call { +func (mr *MockActionMockRecorder) Execute(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockAction)(nil).Execute), arg0, arg1, arg2, arg3, arg4, arg5, arg6) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockAction)(nil).Execute), arg0, arg1, arg2, arg3, arg4, arg5) } // GetTypeID mocks base method. @@ -104,20 +102,6 @@ func (mr *MockActionMockRecorder) MaxComputeUnits(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxComputeUnits", reflect.TypeOf((*MockAction)(nil).MaxComputeUnits), arg0) } -// OutputsWarpMessage mocks base method. -func (m *MockAction) OutputsWarpMessage() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OutputsWarpMessage") - ret0, _ := ret[0].(bool) - return ret0 -} - -// OutputsWarpMessage indicates an expected call of OutputsWarpMessage. -func (mr *MockActionMockRecorder) OutputsWarpMessage() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OutputsWarpMessage", reflect.TypeOf((*MockAction)(nil).OutputsWarpMessage)) -} - // Size mocks base method. func (m *MockAction) Size() int { m.ctrl.T.Helper() diff --git a/chain/mock_rules.go b/chain/mock_rules.go index c0ac1ba3bc..feabddc7a7 100644 --- a/chain/mock_rules.go +++ b/chain/mock_rules.go @@ -86,20 +86,6 @@ func (mr *MockRulesMockRecorder) GetBaseComputeUnits() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseComputeUnits", reflect.TypeOf((*MockRules)(nil).GetBaseComputeUnits)) } -// GetBaseWarpComputeUnits mocks base method. -func (m *MockRules) GetBaseWarpComputeUnits() uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBaseWarpComputeUnits") - ret0, _ := ret[0].(uint64) - return ret0 -} - -// GetBaseWarpComputeUnits indicates an expected call of GetBaseWarpComputeUnits. -func (mr *MockRulesMockRecorder) GetBaseWarpComputeUnits() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseWarpComputeUnits", reflect.TypeOf((*MockRules)(nil).GetBaseWarpComputeUnits)) -} - // GetMaxBlockUnits mocks base method. func (m *MockRules) GetMaxBlockUnits() fees.Dimensions { m.ctrl.T.Helper() @@ -156,20 +142,6 @@ func (mr *MockRulesMockRecorder) GetMinUnitPrice() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMinUnitPrice", reflect.TypeOf((*MockRules)(nil).GetMinUnitPrice)) } -// GetOutgoingWarpComputeUnits mocks base method. -func (m *MockRules) GetOutgoingWarpComputeUnits() uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOutgoingWarpComputeUnits") - ret0, _ := ret[0].(uint64) - return ret0 -} - -// GetOutgoingWarpComputeUnits indicates an expected call of GetOutgoingWarpComputeUnits. -func (mr *MockRulesMockRecorder) GetOutgoingWarpComputeUnits() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOutgoingWarpComputeUnits", reflect.TypeOf((*MockRules)(nil).GetOutgoingWarpComputeUnits)) -} - // GetSponsorStateKeysMaxChunks mocks base method. func (m *MockRules) GetSponsorStateKeysMaxChunks() []uint16 { m.ctrl.T.Helper() @@ -296,36 +268,6 @@ func (mr *MockRulesMockRecorder) GetValidityWindow() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidityWindow", reflect.TypeOf((*MockRules)(nil).GetValidityWindow)) } -// GetWarpComputeUnitsPerSigner mocks base method. -func (m *MockRules) GetWarpComputeUnitsPerSigner() uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetWarpComputeUnitsPerSigner") - ret0, _ := ret[0].(uint64) - return ret0 -} - -// GetWarpComputeUnitsPerSigner indicates an expected call of GetWarpComputeUnitsPerSigner. -func (mr *MockRulesMockRecorder) GetWarpComputeUnitsPerSigner() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWarpComputeUnitsPerSigner", reflect.TypeOf((*MockRules)(nil).GetWarpComputeUnitsPerSigner)) -} - -// GetWarpConfig mocks base method. -func (m *MockRules) GetWarpConfig(arg0 ids.ID) (bool, uint64, uint64) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetWarpConfig", arg0) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(uint64) - ret2, _ := ret[2].(uint64) - return ret0, ret1, ret2 -} - -// GetWarpConfig indicates an expected call of GetWarpConfig. -func (mr *MockRulesMockRecorder) GetWarpConfig(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWarpConfig", reflect.TypeOf((*MockRules)(nil).GetWarpConfig), arg0) -} - // GetWindowTargetUnits mocks base method. func (m *MockRules) GetWindowTargetUnits() fees.Dimensions { m.ctrl.T.Helper() diff --git a/chain/processor.go b/chain/processor.go index 3aef2c87a0..0b44215923 100644 --- a/chain/processor.go +++ b/chain/processor.go @@ -79,17 +79,7 @@ func (b *StatelessBlock) Execute( return err } - // Wait to execute transaction until we have the warp result processed. - var warpVerified bool - warpMsg, ok := b.warpMessages[tx.ID()] - if ok { - select { - case warpVerified = <-warpMsg.verifiedChan: - case <-ctx.Done(): - return ctx.Err() - } - } - result, err := tx.Execute(ctx, feeManager, reads, sm, r, tsv, t, ok && warpVerified) + result, err := tx.Execute(ctx, feeManager, reads, sm, r, tsv, t) if err != nil { return err } diff --git a/chain/result.go b/chain/result.go index 40b1604796..9e9964608d 100644 --- a/chain/result.go +++ b/chain/result.go @@ -4,8 +4,6 @@ package chain import ( - "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/fees" @@ -17,18 +15,10 @@ type Result struct { Consumed fees.Dimensions Fee uint64 - - WarpMessage *warp.UnsignedMessage } func (r *Result) Size() int { - size := consts.BoolLen + codec.BytesLen(r.Output) + fees.DimensionsLen + consts.Uint64Len - if r.WarpMessage != nil { - size += codec.BytesLen(r.WarpMessage.Bytes()) - } else { - size += codec.BytesLen(nil) - } - return size + return consts.BoolLen + codec.BytesLen(r.Output) + fees.DimensionsLen + consts.Uint64Len } func (r *Result) Marshal(p *codec.Packer) error { @@ -36,11 +26,6 @@ func (r *Result) Marshal(p *codec.Packer) error { p.PackBytes(r.Output) p.PackFixedBytes(r.Consumed.Bytes()) p.PackUint64(r.Fee) - var warpBytes []byte - if r.WarpMessage != nil { - warpBytes = r.WarpMessage.Bytes() - } - p.PackBytes(warpBytes) return nil } @@ -73,14 +58,8 @@ func UnmarshalResult(p *codec.Packer) (*Result, error) { } result.Consumed = consumed result.Fee = p.UnpackUint64(false) - var warpMessage []byte - p.UnpackBytes(MaxWarpMessageSize, false, &warpMessage) - if len(warpMessage) > 0 { - msg, err := warp.ParseUnsignedMessage(warpMessage) - if err != nil { - return nil, err - } - result.WarpMessage = msg + if !p.Empty() { + return nil, p.Err() } return result, p.Err() } diff --git a/chain/transaction.go b/chain/transaction.go index 2623a724fa..5357692cea 100644 --- a/chain/transaction.go +++ b/chain/transaction.go @@ -5,12 +5,9 @@ package chain import ( "context" - "errors" "fmt" - "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -30,36 +27,23 @@ var ( ) type Transaction struct { - Base *Base `json:"base"` - WarpMessage *warp.Message `json:"warpMessage"` + Base *Base `json:"base"` // TODO: turn [Action] into an array (#335) Action Action `json:"action"` Auth Auth `json:"auth"` - digest []byte - bytes []byte - size int - id ids.ID - numWarpSigners int - // warpID is just the hash of the *warp.Message.Payload. We assumed that - // all warp messages from a single source have some unique field that - // prevents duplicates (like txID). We will not allow 2 instances of the same - // warpID from the same sourceChainID to be accepted. - warpID ids.ID + digest []byte + bytes []byte + size int + id ids.ID stateKeys state.Keys } -type WarpResult struct { - Message *warp.Message - VerifyErr error -} - -func NewTx(base *Base, wm *warp.Message, act Action) *Transaction { +func NewTx(base *Base, act Action) *Transaction { return &Transaction{ - Base: base, - WarpMessage: wm, - Action: act, + Base: base, + Action: act, } } @@ -68,16 +52,8 @@ func (t *Transaction) Digest() ([]byte, error) { return t.digest, nil } actionID := t.Action.GetTypeID() - var warpBytes []byte - if t.WarpMessage != nil { - warpBytes = t.WarpMessage.Bytes() - } - size := t.Base.Size() + - codec.BytesLen(warpBytes) + - consts.ByteLen + t.Action.Size() - p := codec.NewWriter(size, consts.NetworkSizeLimit) + p := codec.NewWriter(t.Base.Size()+consts.ByteLen+t.Action.Size(), consts.NetworkSizeLimit) t.Base.Marshal(p) - p.PackBytes(warpBytes) p.PackByte(actionID) t.Action.Marshal(p) return p.Bytes(), p.Err() @@ -140,18 +116,6 @@ func (t *Transaction) StateKeys(sm StateManager) (state.Keys, error) { } } - // Add keys used to manage warp operations - if t.WarpMessage != nil { - p := sm.IncomingWarpKeyPrefix(t.WarpMessage.SourceChainID, t.warpID) - k := keys.EncodeChunks(p, MaxIncomingWarpChunks) - stateKeys.Add(string(k), state.All) - } - if t.Action.OutputsWarpMessage() { - p := sm.OutgoingWarpKeyPrefix(t.id) - k := keys.EncodeChunks(p, MaxOutgoingWarpChunks) - stateKeys.Add(string(k), state.Allocate|state.Write) - } - // Cache keys if called again t.stateKeys = stateKeys return stateKeys, nil @@ -167,14 +131,6 @@ func (t *Transaction) MaxUnits(sm StateManager, r Rules) (fees.Dimensions, error maxComputeUnitsOp := math.NewUint64Operator(r.GetBaseComputeUnits()) maxComputeUnitsOp.Add(t.Action.MaxComputeUnits(r)) maxComputeUnitsOp.Add(t.Auth.ComputeUnits(r)) - if t.WarpMessage != nil { - maxComputeUnitsOp.Add(r.GetBaseWarpComputeUnits()) - maxComputeUnitsOp.MulAdd(uint64(t.numWarpSigners), r.GetWarpComputeUnitsPerSigner()) - } - if t.Action.OutputsWarpMessage() { - // Chunks later accounted for by call to [StateKeys] - maxComputeUnitsOp.Add(r.GetOutgoingWarpComputeUnits()) - } maxComputeUnits, err := maxComputeUnitsOp.Value() if err != nil { return fees.Dimensions{}, err @@ -223,7 +179,7 @@ func (t *Transaction) MaxUnits(sm StateManager, r Rules) (fees.Dimensions, error // EstimateMaxUnits provides a pessimistic estimate of the cost to execute a transaction. This is // typically used during transaction construction. -func EstimateMaxUnits(r Rules, action Action, authFactory AuthFactory, warpMessage *warp.Message) (fees.Dimensions, error) { +func EstimateMaxUnits(r Rules, action Action, authFactory AuthFactory) (fees.Dimensions, error) { authBandwidth, authCompute := authFactory.MaxUnits() bandwidth := BaseSize + consts.ByteLen + uint64(action.Size()) + consts.ByteLen + authBandwidth actionStateKeysMaxChunks := action.StateKeysMaxChunks() @@ -236,20 +192,6 @@ func EstimateMaxUnits(r Rules, action Action, authFactory AuthFactory, warpMessa computeUnitsOp := math.NewUint64Operator(r.GetBaseComputeUnits()) computeUnitsOp.Add(authCompute) computeUnitsOp.Add(action.MaxComputeUnits(r)) - if warpMessage != nil { - bandwidth += uint64(codec.BytesLen(warpMessage.Bytes())) - stateKeysMaxChunks = append(stateKeysMaxChunks, MaxIncomingWarpChunks) - computeUnitsOp.Add(r.GetBaseWarpComputeUnits()) - numSigners, err := warpMessage.Signature.NumSigners() - if err != nil { - return fees.Dimensions{}, err - } - computeUnitsOp.MulAdd(uint64(numSigners), r.GetWarpComputeUnitsPerSigner()) - } - if action.OutputsWarpMessage() { - stateKeysMaxChunks = append(stateKeysMaxChunks, MaxOutgoingWarpChunks) - computeUnitsOp.Add(r.GetOutgoingWarpComputeUnits()) - } computeUnits, err := computeUnitsOp.Value() if err != nil { return fees.Dimensions{}, err @@ -338,7 +280,6 @@ func (t *Transaction) Execute( r Rules, ts *tstate.TStateView, timestamp int64, - warpVerified bool, ) (*Result, error) { // Always charge fee first (in case [Action] moves funds) maxUnits, err := t.MaxUnits(s, r) @@ -357,24 +298,6 @@ func (t *Transaction) Execute( return nil, err } - // Check warp message is not duplicate - if t.WarpMessage != nil { - p := s.IncomingWarpKeyPrefix(t.WarpMessage.SourceChainID, t.warpID) - k := keys.EncodeChunks(p, MaxIncomingWarpChunks) - _, err := ts.GetValue(ctx, k) - switch { - case err == nil: - // Override all errors because warp message is a duplicate - warpVerified = false - case errors.Is(err, database.ErrNotFound): - // This means there are no conflicts - case err != nil: - // An error here can indicate there is an issue with the database or that - // the key was not properly specified. - return &Result{false, utils.ErrBytes(err), maxUnits, maxFee, nil}, nil - } - } - // We create a temp state checkpoint to ensure we don't commit failed actions to state. actionStart := ts.OpIndex() handleRevert := func(rerr error) (*Result, error) { @@ -382,9 +305,9 @@ func (t *Transaction) Execute( // are set when this function is defined. If any of them are // modified later, they will not be used here. ts.Rollback(ctx, actionStart) - return &Result{false, utils.ErrBytes(rerr), maxUnits, maxFee, nil}, nil + return &Result{false, utils.ErrBytes(rerr), maxUnits, maxFee}, nil } - success, actionCUs, output, warpMessage, err := t.Action.Execute(ctx, r, ts, timestamp, t.Auth.Actor(), t.id, warpVerified) + success, actionCUs, output, err := t.Action.Execute(ctx, r, ts, timestamp, t.Auth.Actor(), t.id) if err != nil { return handleRevert(err) } @@ -393,56 +316,14 @@ func (t *Transaction) Execute( // fast) return handleRevert(ErrInvalidObject) } - outputsWarp := t.Action.OutputsWarpMessage() if !success { ts.Rollback(ctx, actionStart) - warpMessage = nil // warp messages can only be emitted on success - } else { - // Ensure constraints hold if successful - if (warpMessage == nil && outputsWarp) || (warpMessage != nil && !outputsWarp) { - return handleRevert(ErrInvalidObject) - } - - // Store incoming warp messages in state by their ID to prevent replays - if t.WarpMessage != nil { - p := s.IncomingWarpKeyPrefix(t.WarpMessage.SourceChainID, t.warpID) - k := keys.EncodeChunks(p, MaxIncomingWarpChunks) - if err := ts.Insert(ctx, k, nil); err != nil { - return handleRevert(err) - } - } - - // Store newly created warp messages in state by their txID to ensure we can - // always sign for a message - if warpMessage != nil { - // Enforce we are the source of our own messages - warpMessage.NetworkID = r.NetworkID() - warpMessage.SourceChainID = r.ChainID() - // Initialize message (compute bytes) now that everything is populated - if err := warpMessage.Initialize(); err != nil { - return handleRevert(err) - } - // We use txID here because did not know the warpID before execution (and - // we pre-reserve this key for the processor). - p := s.OutgoingWarpKeyPrefix(t.id) - k := keys.EncodeChunks(p, MaxOutgoingWarpChunks) - if err := ts.Insert(ctx, k, warpMessage.Bytes()); err != nil { - return handleRevert(err) - } - } } // Calculate units used computeUnitsOp := math.NewUint64Operator(r.GetBaseComputeUnits()) computeUnitsOp.Add(t.Auth.ComputeUnits(r)) computeUnitsOp.Add(actionCUs) - if t.WarpMessage != nil { - computeUnitsOp.Add(r.GetBaseWarpComputeUnits()) - computeUnitsOp.MulAdd(uint64(t.numWarpSigners), r.GetWarpComputeUnitsPerSigner()) - } - if success && outputsWarp { - computeUnitsOp.Add(r.GetOutgoingWarpComputeUnits()) - } computeUnits, err := computeUnitsOp.Value() if err != nil { return handleRevert(err) @@ -524,8 +405,6 @@ func (t *Transaction) Execute( Consumed: used, Fee: feeRequired, - - WarpMessage: warpMessage, }, nil } @@ -538,14 +417,6 @@ func (t *Transaction) Marshal(p *codec.Packer) error { actionID := t.Action.GetTypeID() authID := t.Auth.GetTypeID() t.Base.Marshal(p) - var warpBytes []byte - if t.WarpMessage != nil { - warpBytes = t.WarpMessage.Bytes() - if len(warpBytes) == 0 { - return ErrWarpMessageNotInitialized - } - } - p.PackBytes(warpBytes) p.PackByte(actionID) t.Action.Marshal(p) p.PackByte(authID) @@ -595,55 +466,30 @@ func UnmarshalTxs( func UnmarshalTx( p *codec.Packer, - actionRegistry *codec.TypeParser[Action, *warp.Message, bool], - authRegistry *codec.TypeParser[Auth, *warp.Message, bool], + actionRegistry *codec.TypeParser[Action, bool], + authRegistry *codec.TypeParser[Auth, bool], ) (*Transaction, error) { start := p.Offset() base, err := UnmarshalBase(p) if err != nil { return nil, fmt.Errorf("%w: could not unmarshal base", err) } - var warpBytes []byte - p.UnpackBytes(MaxWarpMessageSize, false, &warpBytes) - var warpMessage *warp.Message - var numWarpSigners int - if len(warpBytes) > 0 { - msg, err := warp.ParseMessage(warpBytes) - if err != nil { - return nil, fmt.Errorf("%w: could not unmarshal warp message", err) - } - if len(msg.Payload) == 0 { - return nil, ErrEmptyWarpPayload - } - warpMessage = msg - numSigners, err := msg.Signature.NumSigners() - if err != nil { - return nil, fmt.Errorf("%w: could not calculate number of warp signers", err) - } - numWarpSigners = numSigners - } actionType := p.UnpackByte() - unmarshalAction, actionWarp, ok := actionRegistry.LookupIndex(actionType) + unmarshalAction, ok := actionRegistry.LookupIndex(actionType) if !ok { return nil, fmt.Errorf("%w: %d is unknown action type", ErrInvalidObject, actionType) } - if actionWarp && warpMessage == nil { - return nil, fmt.Errorf("%w: action %d", ErrExpectedWarpMessage, actionType) - } - action, err := unmarshalAction(p, warpMessage) + action, err := unmarshalAction(p) if err != nil { return nil, fmt.Errorf("%w: could not unmarshal action", err) } digest := p.Offset() authType := p.UnpackByte() - unmarshalAuth, authWarp, ok := authRegistry.LookupIndex(authType) + unmarshalAuth, ok := authRegistry.LookupIndex(authType) if !ok { return nil, fmt.Errorf("%w: %d is unknown auth type", ErrInvalidObject, authType) } - if authWarp && warpMessage == nil { - return nil, fmt.Errorf("%w: auth %d", ErrExpectedWarpMessage, authType) - } - auth, err := unmarshalAuth(p, warpMessage) + auth, err := unmarshalAuth(p) if err != nil { return nil, fmt.Errorf("%w: could not unmarshal auth", err) } @@ -653,15 +499,10 @@ func UnmarshalTx( if sponsorType := auth.Sponsor()[0]; sponsorType != authType { return nil, fmt.Errorf("%w: sponsorType (%d) did not match authType (%d)", ErrInvalidSponsor, sponsorType, authType) } - warpExpected := actionWarp || authWarp - if !warpExpected && warpMessage != nil { - return nil, ErrUnexpectedWarpMessage - } var tx Transaction tx.Base = base tx.Action = action - tx.WarpMessage = warpMessage tx.Auth = auth if err := p.Err(); err != nil { return nil, p.Err() @@ -671,9 +512,5 @@ func UnmarshalTx( tx.bytes = codecBytes[start:p.Offset()] // ensure errors handled before grabbing memory tx.size = len(tx.bytes) tx.id = utils.ToID(tx.bytes) - if tx.WarpMessage != nil { - tx.numWarpSigners = numWarpSigners - tx.warpID = tx.WarpMessage.ID() - } return &tx, nil } diff --git a/chain/warp_signature.go b/chain/warp_signature.go deleted file mode 100644 index b499f70046..0000000000 --- a/chain/warp_signature.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package chain - -type WarpSignature struct { - PublicKey []byte `json:"publicKey"` - Signature []byte `json:"signature"` -} - -func NewWarpSignature(pk []byte, sig []byte) *WarpSignature { - return &WarpSignature{pk, sig} -} diff --git a/cli/spam.go b/cli/spam.go index 90afdbba82..16978d538f 100644 --- a/cli/spam.go +++ b/cli/spam.go @@ -111,7 +111,7 @@ func (h *Handler) Spam( return err } action := getTransfer(keys[0].Address, 0) - maxUnits, err := chain.EstimateMaxUnits(parser.Rules(time.Now().UnixMilli()), action, factory, nil) + maxUnits, err := chain.EstimateMaxUnits(parser.Rules(time.Now().UnixMilli()), action, factory) if err != nil { return err } @@ -167,7 +167,7 @@ func (h *Handler) Spam( accounts[i] = pk // Send funds - _, tx, err := cli.GenerateTransactionManual(parser, nil, getTransfer(pk.Address, distAmount), factory, feePerTx) + _, tx, err := cli.GenerateTransactionManual(parser, getTransfer(pk.Address, distAmount), factory, feePerTx) if err != nil { return err } @@ -316,7 +316,7 @@ func (h *Handler) Spam( if maxFee != nil { fee = *maxFee } - _, tx, err := issuer.c.GenerateTransactionManual(parser, nil, action, factory, fee, tm) + _, tx, err := issuer.c.GenerateTransactionManual(parser, action, factory, fee, tm) if err != nil { utils.Outf("{{orange}}failed to generate tx:{{/}} %v\n", err) continue @@ -424,7 +424,7 @@ func (h *Handler) Spam( if err != nil { return err } - _, tx, err := cli.GenerateTransactionManual(parser, nil, getTransfer(key.Address, returnAmt), f, feePerTx) + _, tx, err := cli.GenerateTransactionManual(parser, getTransfer(key.Address, returnAmt), f, feePerTx) if err != nil { return err } diff --git a/codec/type_parser.go b/codec/type_parser.go index 0614c7b151..dddf099281 100644 --- a/codec/type_parser.go +++ b/codec/type_parser.go @@ -7,45 +7,45 @@ import ( "github.com/ava-labs/hypersdk/consts" ) -type decoder[T any, X any, Y any] struct { - f func(*Packer, X) (T, error) +type decoder[T any, Y any] struct { + f func(*Packer) (T, error) y Y } // The number of types is limited to 255. -type TypeParser[T any, X any, Y any] struct { +type TypeParser[T any, Y any] struct { typeToIndex map[string]uint8 - indexToDecoder map[uint8]*decoder[T, X, Y] + indexToDecoder map[uint8]*decoder[T, Y] } // NewTypeParser returns an instance of a Typeparser with generic type [T]. -func NewTypeParser[T any, X any, Y bool]() *TypeParser[T, X, Y] { - return &TypeParser[T, X, Y]{ +func NewTypeParser[T any, Y bool]() *TypeParser[T, Y] { + return &TypeParser[T, Y]{ typeToIndex: map[string]uint8{}, - indexToDecoder: map[uint8]*decoder[T, X, Y]{}, + indexToDecoder: map[uint8]*decoder[T, Y]{}, } } // Register registers a new type into TypeParser [p]. Registers the type by using // the string representation of [o], and sets the decoder of that index to [f]. // Returns an error if [o] has already been registered or the TypeParser is full. -func (p *TypeParser[T, X, Y]) Register(id uint8, f func(*Packer, X) (T, error), y Y) error { +func (p *TypeParser[T, Y]) Register(id uint8, f func(*Packer) (T, error), y Y) error { if len(p.indexToDecoder) == int(consts.MaxUint8)+1 { return ErrTooManyItems } if _, ok := p.indexToDecoder[id]; ok { return ErrDuplicateItem } - p.indexToDecoder[id] = &decoder[T, X, Y]{f, y} + p.indexToDecoder[id] = &decoder[T, Y]{f, y} return nil } // LookupIndex returns the decoder function and success of lookup of [index] // from Typeparser [p]. -func (p *TypeParser[T, X, Y]) LookupIndex(index uint8) (func(*Packer, X) (T, error), Y, bool) { +func (p *TypeParser[T, Y]) LookupIndex(index uint8) (func(*Packer) (T, error), bool) { d, ok := p.indexToDecoder[index] if ok { - return d.f, d.y, true + return d.f, true } - return nil, *new(Y), false + return nil, false } diff --git a/codec/type_parser_test.go b/codec/type_parser_test.go index 76f6b04ad7..a59a965a50 100644 --- a/codec/type_parser_test.go +++ b/codec/type_parser_test.go @@ -34,14 +34,13 @@ func (*Blah3) Bark() string { return "blah3" } func (*Blah3) GetTypeID() uint8 { return 2 } func TestTypeParser(t *testing.T) { - tp := NewTypeParser[Blah, any, bool]() + tp := NewTypeParser[Blah, bool]() t.Run("empty parser", func(t *testing.T) { require := require.New(t) - f, b, ok := tp.LookupIndex(0) + f, ok := tp.LookupIndex(0) require.Nil(f) require.False(ok) - require.False(b) }) t.Run("populated parser", func(t *testing.T) { @@ -54,29 +53,27 @@ func TestTypeParser(t *testing.T) { require.NoError( tp.Register( blah1.GetTypeID(), - func(*Packer, any) (Blah, error) { return nil, errBlah1 }, + func(*Packer) (Blah, error) { return nil, errBlah1 }, true, ), ) require.NoError( tp.Register( blah2.GetTypeID(), - func(*Packer, any) (Blah, error) { return nil, errBlah2 }, + func(*Packer) (Blah, error) { return nil, errBlah2 }, false, ), ) - f, b, ok := tp.LookupIndex(blah1.GetTypeID()) + f, ok := tp.LookupIndex(blah1.GetTypeID()) require.True(ok) - require.True(b) - res, err := f(nil, nil) + res, err := f(nil) require.Nil(res) require.ErrorIs(err, errBlah1) - f, b, ok = tp.LookupIndex(blah2.GetTypeID()) + f, ok = tp.LookupIndex(blah2.GetTypeID()) require.True(ok) - require.False(b) - res, err = f(nil, nil) + res, err = f(nil) require.Nil(res) require.ErrorIs(err, errBlah2) }) diff --git a/examples/morpheusvm/actions/transfer.go b/examples/morpheusvm/actions/transfer.go index 24e00514bc..d478e698a6 100644 --- a/examples/morpheusvm/actions/transfer.go +++ b/examples/morpheusvm/actions/transfer.go @@ -7,7 +7,6 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -42,10 +41,6 @@ func (*Transfer) StateKeysMaxChunks() []uint16 { return []uint16{storage.BalanceChunks, storage.BalanceChunks} } -func (*Transfer) OutputsWarpMessage() bool { - return false -} - func (t *Transfer) Execute( ctx context.Context, _ chain.Rules, @@ -53,18 +48,17 @@ func (t *Transfer) Execute( _ int64, actor codec.Address, _ ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { if t.Value == 0 { - return false, 1, OutputValueZero, nil, nil + return false, 1, OutputValueZero, nil } if err := storage.SubBalance(ctx, mu, actor, t.Value); err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } if err := storage.AddBalance(ctx, mu, t.To, t.Value, true); err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } - return true, 1, nil, nil, nil + return true, 1, nil, nil } func (*Transfer) MaxComputeUnits(chain.Rules) uint64 { @@ -80,7 +74,7 @@ func (t *Transfer) Marshal(p *codec.Packer) { p.PackUint64(t.Value) } -func UnmarshalTransfer(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalTransfer(p *codec.Packer) (chain.Action, error) { var transfer Transfer p.UnpackAddress(&transfer.To) // we do not verify the typeID is valid transfer.Value = p.UnpackUint64(true) diff --git a/examples/morpheusvm/auth/bls.go b/examples/morpheusvm/auth/bls.go index e612109294..f56f59bf8b 100644 --- a/examples/morpheusvm/auth/bls.go +++ b/examples/morpheusvm/auth/bls.go @@ -6,7 +6,6 @@ package auth import ( "context" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto" @@ -72,7 +71,7 @@ func (b *BLS) Marshal(p *codec.Packer) { p.PackFixedBytes(bls.SignatureToBytes(b.Signature)) } -func UnmarshalBLS(p *codec.Packer, _ *warp.Message) (chain.Auth, error) { +func UnmarshalBLS(p *codec.Packer) (chain.Auth, error) { var b BLS signer := make([]byte, bls.PublicKeyLen) diff --git a/examples/morpheusvm/auth/ed25519.go b/examples/morpheusvm/auth/ed25519.go index 3c58182dbd..e8dd4ac7fe 100644 --- a/examples/morpheusvm/auth/ed25519.go +++ b/examples/morpheusvm/auth/ed25519.go @@ -6,7 +6,6 @@ package auth import ( "context" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto" @@ -72,7 +71,7 @@ func (d *ED25519) Marshal(p *codec.Packer) { p.PackFixedBytes(d.Signature[:]) } -func UnmarshalED25519(p *codec.Packer, _ *warp.Message) (chain.Auth, error) { +func UnmarshalED25519(p *codec.Packer) (chain.Auth, error) { var d ED25519 signer := d.Signer[:] // avoid allocating additional memory p.UnpackFixedBytes(ed25519.PublicKeyLen, &signer) diff --git a/examples/morpheusvm/auth/secp256r1.go b/examples/morpheusvm/auth/secp256r1.go index a0e6869482..41f9adde6c 100644 --- a/examples/morpheusvm/auth/secp256r1.go +++ b/examples/morpheusvm/auth/secp256r1.go @@ -6,7 +6,6 @@ package auth import ( "context" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto" @@ -72,7 +71,7 @@ func (d *SECP256R1) Marshal(p *codec.Packer) { p.PackFixedBytes(d.Signature[:]) } -func UnmarshalSECP256R1(p *codec.Packer, _ *warp.Message) (chain.Auth, error) { +func UnmarshalSECP256R1(p *codec.Packer) (chain.Auth, error) { var d SECP256R1 signer := d.Signer[:] // avoid allocating additional memory p.UnpackFixedBytes(secp256r1.PublicKeyLen, &signer) diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go index 51f6e9a1dd..9fd10c3d28 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go @@ -52,7 +52,7 @@ var transferCmd = &cobra.Command{ } // Generate transaction - _, _, err = sendAndWait(ctx, nil, &actions.Transfer{ + _, _, err = sendAndWait(ctx, &actions.Transfer{ To: recipient, Value: amount, }, cli, bcli, ws, factory, true) diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go index b1931cac64..675fc47e19 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go @@ -9,7 +9,6 @@ import ( "reflect" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/cli" "github.com/ava-labs/hypersdk/codec" @@ -22,14 +21,14 @@ import ( // sendAndWait may not be used concurrently func sendAndWait( - ctx context.Context, warpMsg *warp.Message, action chain.Action, cli *rpc.JSONRPCClient, + ctx context.Context, action chain.Action, cli *rpc.JSONRPCClient, bcli *brpc.JSONRPCClient, ws *rpc.WebSocketClient, factory chain.AuthFactory, printStatus bool, ) (bool, ids.ID, error) { //nolint:unparam parser, err := bcli.Parser(ctx) if err != nil { return false, ids.Empty, err } - _, tx, _, err := cli.GenerateTransaction(ctx, parser, warpMsg, action, factory) + _, tx, _, err := cli.GenerateTransaction(ctx, parser, action, factory) if err != nil { return false, ids.Empty, err } diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go index 43482b51a1..1ac060733c 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go @@ -106,7 +106,7 @@ var runSpamCmd = &cobra.Command{ if err != nil { return err } - _, _, err = sendAndWait(ictx, nil, &actions.Transfer{ + _, _, err = sendAndWait(ictx, &actions.Transfer{ To: priv.Address, Value: count, // prevent duplicate txs }, cli, bclient, wclient, factory, false) diff --git a/examples/morpheusvm/consts/consts.go b/examples/morpheusvm/consts/consts.go index 5a6d79d2fa..4223730a4d 100644 --- a/examples/morpheusvm/consts/consts.go +++ b/examples/morpheusvm/consts/consts.go @@ -5,7 +5,6 @@ package consts import ( "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -33,6 +32,6 @@ func init() { // Instantiate registry here so it can be imported by any package. We set these // values in [controller/registry]. var ( - ActionRegistry *codec.TypeParser[chain.Action, *warp.Message, bool] - AuthRegistry *codec.TypeParser[chain.Auth, *warp.Message, bool] + ActionRegistry *codec.TypeParser[chain.Action, bool] + AuthRegistry *codec.TypeParser[chain.Auth, bool] ) diff --git a/examples/morpheusvm/genesis/genesis.go b/examples/morpheusvm/genesis/genesis.go index 74962b5c96..bfe536e32b 100644 --- a/examples/morpheusvm/genesis/genesis.go +++ b/examples/morpheusvm/genesis/genesis.go @@ -47,9 +47,6 @@ type Genesis struct { // Tx Fee Parameters BaseComputeUnits uint64 `json:"baseUnits"` - BaseWarpComputeUnits uint64 `json:"baseWarpUnits"` - WarpComputeUnitsPerSigner uint64 `json:"warpUnitsPerSigner"` - OutgoingWarpComputeUnits uint64 `json:"outgoingWarpComputeUnits"` StorageKeyReadUnits uint64 `json:"storageKeyReadUnits"` StorageValueReadUnits uint64 `json:"storageValueReadUnits"` // per chunk StorageKeyAllocateUnits uint64 `json:"storageKeyAllocateUnits"` @@ -80,10 +77,7 @@ func Default() *Genesis { ValidityWindow: 60 * hconsts.MillisecondsPerSecond, // ms // Tx Fee Compute Parameters - BaseComputeUnits: 1, - BaseWarpComputeUnits: 1_024, - WarpComputeUnitsPerSigner: 128, - OutgoingWarpComputeUnits: 1_024, + BaseComputeUnits: 1, // Tx Fee Storage Parameters // diff --git a/examples/morpheusvm/genesis/rules.go b/examples/morpheusvm/genesis/rules.go index 0a37046203..bb375af7d3 100644 --- a/examples/morpheusvm/genesis/rules.go +++ b/examples/morpheusvm/genesis/rules.go @@ -24,10 +24,6 @@ func (g *Genesis) Rules(_ int64, networkID uint32, chainID ids.ID) *Rules { return &Rules{g, networkID, chainID} } -func (*Rules) GetWarpConfig(ids.ID) (bool, uint64, uint64) { - return false, 0, 0 -} - func (r *Rules) NetworkID() uint32 { return r.networkID } @@ -56,18 +52,6 @@ func (r *Rules) GetBaseComputeUnits() uint64 { return r.g.BaseComputeUnits } -func (r *Rules) GetBaseWarpComputeUnits() uint64 { - return r.g.BaseWarpComputeUnits -} - -func (r *Rules) GetWarpComputeUnitsPerSigner() uint64 { - return r.g.WarpComputeUnitsPerSigner -} - -func (r *Rules) GetOutgoingWarpComputeUnits() uint64 { - return r.g.OutgoingWarpComputeUnits -} - func (*Rules) GetSponsorStateKeysMaxChunks() []uint16 { return []uint16{storage.BalanceChunks} } diff --git a/examples/morpheusvm/registry/registry.go b/examples/morpheusvm/registry/registry.go index 47d4279a38..1a1cf13e93 100644 --- a/examples/morpheusvm/registry/registry.go +++ b/examples/morpheusvm/registry/registry.go @@ -5,7 +5,6 @@ package registry import ( "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" @@ -16,8 +15,8 @@ import ( // Setup types func init() { - consts.ActionRegistry = codec.NewTypeParser[chain.Action, *warp.Message]() - consts.AuthRegistry = codec.NewTypeParser[chain.Auth, *warp.Message]() + consts.ActionRegistry = codec.NewTypeParser[chain.Action]() + consts.AuthRegistry = codec.NewTypeParser[chain.Auth]() errs := &wrappers.Errs{} errs.Add( diff --git a/examples/morpheusvm/storage/state_manager.go b/examples/morpheusvm/storage/state_manager.go index 6372517181..9e27c216fc 100644 --- a/examples/morpheusvm/storage/state_manager.go +++ b/examples/morpheusvm/storage/state_manager.go @@ -6,7 +6,6 @@ package storage import ( "context" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/state" @@ -28,14 +27,6 @@ func (*StateManager) FeeKey() []byte { return FeeKey() } -func (*StateManager) IncomingWarpKeyPrefix(sourceChainID ids.ID, msgID ids.ID) []byte { - return IncomingWarpKeyPrefix(sourceChainID, msgID) -} - -func (*StateManager) OutgoingWarpKeyPrefix(txID ids.ID) []byte { - return OutgoingWarpKeyPrefix(txID) -} - func (*StateManager) SponsorStateKeys(addr codec.Address) state.Keys { return state.Keys{ string(BalanceKey(addr)): state.Read | state.Write, diff --git a/examples/morpheusvm/storage/storage.go b/examples/morpheusvm/storage/storage.go index 01e0bb28aa..7db1a7eee6 100644 --- a/examples/morpheusvm/storage/storage.go +++ b/examples/morpheusvm/storage/storage.go @@ -34,20 +34,16 @@ type ReadState func(context.Context, [][]byte) ([][]byte, []error) // 0x1/ (hypersdk-height) // 0x2/ (hypersdk-timestamp) // 0x3/ (hypersdk-fee) -// 0x4/ (hypersdk-incoming warp) -// 0x5/ (hypersdk-outgoing warp) const ( // metaDB txPrefix = 0x0 // stateDB - balancePrefix = 0x0 - heightPrefix = 0x1 - timestampPrefix = 0x2 - feePrefix = 0x3 - incomingWarpPrefix = 0x4 - outgoingWarpPrefix = 0x5 + balancePrefix = 0x0 + heightPrefix = 0x1 + timestampPrefix = 0x2 + feePrefix = 0x3 ) const BalanceChunks uint16 = 1 @@ -257,18 +253,3 @@ func TimestampKey() (k []byte) { func FeeKey() (k []byte) { return feeKey } - -func IncomingWarpKeyPrefix(sourceChainID ids.ID, msgID ids.ID) (k []byte) { - k = make([]byte, 1+consts.IDLen*2) - k[0] = incomingWarpPrefix - copy(k[1:], sourceChainID[:]) - copy(k[1+consts.IDLen:], msgID[:]) - return k -} - -func OutgoingWarpKeyPrefix(txID ids.ID) (k []byte) { - k = make([]byte, 1+consts.IDLen) - k[0] = outgoingWarpPrefix - copy(k[1:], txID[:]) - return k -} diff --git a/examples/morpheusvm/tests/e2e/e2e_test.go b/examples/morpheusvm/tests/e2e/e2e_test.go index 039b69c984..bf352848bc 100644 --- a/examples/morpheusvm/tests/e2e/e2e_test.go +++ b/examples/morpheusvm/tests/e2e/e2e_test.go @@ -424,7 +424,6 @@ var _ = ginkgo.Describe("[Test]", func() { submit, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: aother, Value: sendAmount, @@ -717,7 +716,6 @@ func generateBlocks( submit, _, _, err := instances[cumulativeTxs%len(instances)].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: aother, Value: 1, @@ -786,7 +784,6 @@ func acceptTransaction(cli *rpc.JSONRPCClient, lcli *lrpc.JSONRPCClient) { submit, tx, maxFee, err := cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: aother, Value: 1, diff --git a/examples/morpheusvm/tests/integration/integration_test.go b/examples/morpheusvm/tests/integration/integration_test.go index a4f4dc9857..0955c9c2c2 100644 --- a/examples/morpheusvm/tests/integration/integration_test.go +++ b/examples/morpheusvm/tests/integration/integration_test.go @@ -26,7 +26,6 @@ import ( "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/fatih/color" ginkgo "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -211,7 +210,6 @@ var _ = ginkgo.BeforeSuite(func() { ChainDataDir: dname, Metrics: metrics.NewOptionalGatherer(), PublicKey: bls.PublicFromSecretKey(sk), - WarpSigner: warp.NewSigner(sk, networkID, chainID), ValidatorState: &validators.TestState{}, } @@ -336,7 +334,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, transferTx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr2, Value: 100_000, // must be more than StateLockup @@ -369,7 +366,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: 0, MaxFee: 1000, }, - nil, &actions.Transfer{ To: addr2, Value: 110, @@ -436,19 +432,19 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // read: 2 keys reads, 1 had 0 chunks // allocate: 1 key created with 1 chunk // write: 2 keys modified (new + old) - transferTxConsumed := fees.Dimensions{191, 7, 12, 25, 26} + transferTxConsumed := fees.Dimensions{187, 7, 12, 25, 26} gomega.Ω(results[0].Consumed).Should(gomega.Equal(transferTxConsumed)) // Fee explanation // // Multiply all unit consumption by 1 and sum - gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(261))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(257))) }) ginkgo.By("ensure balance is updated", func() { balance, err := instances[1].lcli.Balance(context.Background(), addrStr) gomega.Ω(err).To(gomega.BeNil()) - gomega.Ω(balance).To(gomega.Equal(uint64(9899739))) + gomega.Ω(balance).To(gomega.Equal(uint64(9899743))) balance2, err := instances[1].lcli.Balance(context.Background(), addrStr2) gomega.Ω(err).To(gomega.BeNil()) gomega.Ω(balance2).To(gomega.Equal(uint64(100000))) @@ -462,7 +458,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr2, Value: 101, @@ -483,13 +478,13 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // read: 2 keys reads, 1 chunk each // allocate: 0 key created // write: 2 key modified - transferTxConsumed := fees.Dimensions{191, 7, 14, 0, 26} + transferTxConsumed := fees.Dimensions{187, 7, 14, 0, 26} gomega.Ω(results[0].Consumed).Should(gomega.Equal(transferTxConsumed)) // Fee explanation // // Multiply all unit consumption by 1 and sum - gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(238))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(234))) balance2, err := instances[1].lcli.Balance(context.Background(), addrStr2) gomega.Ω(err).To(gomega.BeNil()) @@ -503,7 +498,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr2, Value: 102, @@ -515,7 +509,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err = instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr2, Value: 103, @@ -527,7 +520,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err = instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr3, Value: 104, @@ -539,7 +531,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err = instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr3, Value: 105, @@ -568,12 +559,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // allocate: 0 key created // write: 2 key modified gomega.Ω(results[0].Success).Should(gomega.BeTrue()) - transferTxConsumed := fees.Dimensions{191, 7, 14, 0, 26} + transferTxConsumed := fees.Dimensions{187, 7, 14, 0, 26} gomega.Ω(results[0].Consumed).Should(gomega.Equal(transferTxConsumed)) // Fee explanation // // Multiply all unit consumption by 1 and sum - gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(238))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(234))) // Unit explanation // @@ -583,12 +574,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // allocate: 0 key created // write: 2 keys modified gomega.Ω(results[1].Success).Should(gomega.BeTrue()) - transferTxConsumed = fees.Dimensions{191, 7, 14, 0, 26} + transferTxConsumed = fees.Dimensions{187, 7, 14, 0, 26} gomega.Ω(results[1].Consumed).Should(gomega.Equal(transferTxConsumed)) // Fee explanation // // Multiply all unit consumption by 1 and sum - gomega.Ω(results[1].Fee).Should(gomega.Equal(uint64(238))) + gomega.Ω(results[1].Fee).Should(gomega.Equal(uint64(234))) // Unit explanation // @@ -598,12 +589,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // allocate: 1 key created (1 chunk) // write: 2 key modified (1 chunk), both previously modified gomega.Ω(results[2].Success).Should(gomega.BeTrue()) - transferTxConsumed = fees.Dimensions{191, 7, 12, 25, 26} + transferTxConsumed = fees.Dimensions{187, 7, 12, 25, 26} gomega.Ω(results[2].Consumed).Should(gomega.Equal(transferTxConsumed)) // Fee explanation // // Multiply all unit consumption by 1 and sum - gomega.Ω(results[2].Fee).Should(gomega.Equal(uint64(261))) + gomega.Ω(results[2].Fee).Should(gomega.Equal(uint64(257))) // Unit explanation // @@ -613,12 +604,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // allocate: 0 key created // write: 2 keys modified (1 chunk) gomega.Ω(results[3].Success).Should(gomega.BeTrue()) - transferTxConsumed = fees.Dimensions{191, 7, 12, 0, 26} + transferTxConsumed = fees.Dimensions{187, 7, 12, 0, 26} gomega.Ω(results[3].Consumed).Should(gomega.Equal(transferTxConsumed)) // Fee explanation // // Multiply all unit consumption by 1 and sum - gomega.Ω(results[3].Fee).Should(gomega.Equal(uint64(236))) + gomega.Ω(results[3].Fee).Should(gomega.Equal(uint64(232))) // Check end balance balance2, err := instances[1].lcli.Balance(context.Background(), addrStr2) @@ -639,7 +630,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr2, Value: 200, @@ -653,7 +643,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err = instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr2, Value: 201, @@ -682,7 +671,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr2, Value: 203, @@ -773,7 +761,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, transfer, factory, ) @@ -821,7 +808,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { _, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, transfer, factory, ) @@ -870,7 +856,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: r1addr, Value: 2000, @@ -895,7 +880,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr, Value: 100, @@ -924,7 +908,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: r1addr, Value: 2000, @@ -949,7 +932,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: addr, Value: 100, diff --git a/examples/morpheusvm/tests/load/load_test.go b/examples/morpheusvm/tests/load/load_test.go index 5f98fd5b68..634826ccd1 100644 --- a/examples/morpheusvm/tests/load/load_test.go +++ b/examples/morpheusvm/tests/load/load_test.go @@ -542,7 +542,6 @@ func issueSimpleTx( ChainID: i.chainID, MaxFee: maxFee, }, - nil, &actions.Transfer{ To: to, Value: amount, diff --git a/examples/tokenvm/README.md b/examples/tokenvm/README.md index 52bc0ff5a6..3210c6208b 100644 --- a/examples/tokenvm/README.md +++ b/examples/tokenvm/README.md @@ -105,41 +105,6 @@ be valid only until a particular time. This enables you to go for orders as you see fit at the time and not have to worry about your fill sitting around until you explicitly cancel it/replace it. -### Avalanche Warp Support -We take advantage of the Avalanche Warp Messaging (AWM) support provided by the -`hypersdk` to enable any `tokenvm` to send assets to any other `tokenvm` without -relying on a trusted relayer or bridge (just the validators of the `tokenvm` -sending the message). - -By default, a `tokenvm` will accept a message from another `tokenvm` if 80% of -the stake weight of the source has signed it. Because each imported asset is -given a unique `AssetID` (hash of `sourceChainID + sourceAssetID`), it is not -possible for a malicious/rogue Subnet to corrupt token balances imported from -other Subnets with this default import setting. `tokenvms` also track the -amount of assets exported to all other `tokenvms` and ensure that more assets -can't be brought back from a `tokenvm` than were exported to it (prevents -infinite minting). - -To limit "contagion" in the case of a `tokenvm` failure, we ONLY allow the -export of natively minted assets to another `tokenvm`. This means you can -transfer an asset between two `tokenvms` A and B but you can't export from -`tokenvm` A to `tokenvm` B to `tokenvm` C. This ensures that the import policy -for an external `tokenvm` is always transparent and is never inherited -implicitly by the transfers between other `tokenvms`. The ability to impose -this restriction (without massively driving up the cost of each transfer) is -possible because AWM does not impose an additional overhead per Subnet -connection (no "per connection" state to maintain). This means it is just as -cheap/scalable to communicate with every other `tokenvm` as it is to only -communicate with one. - -Lastly, the `tokenvm` allows users to both tip relayers (whoever sends -a transaction that imports their message) and to swap for another asset when -their message is imported (so they can acquire fee-paying tokens right when -they arrive). - -You can see how this works by checking out the [E2E test suite](./tests/e2e/e2e_test.go) that -runs through these flows. - ## Demos Someone: "Seems cool but I need to see it to really get it." Me: "Look no further." @@ -237,7 +202,7 @@ database: .token-cli address: token1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsjzf3yp chainID: Em2pZtHr7rDCzii43an2bBi1M2mTFyLN33QP1Xfjy7BcWtaH9 assetID (use TKN for native token): 27grFs9vE2YP9kwLM5hQJGLDvqEY9ii71zzdoRHNGC4Appavug -metadata: MarioCoin supply: 10000 warp: false +metadata: MarioCoin supply: 10000 balance: 10000 27grFs9vE2YP9kwLM5hQJGLDvqEY9ii71zzdoRHNGC4Appavug ``` @@ -257,7 +222,7 @@ chainID: Em2pZtHr7rDCzii43an2bBi1M2mTFyLN33QP1Xfjy7BcWtaH9 in assetID (use TKN for native token): TKN ✔ in tick: 1█ out assetID (use TKN for native token): 27grFs9vE2YP9kwLM5hQJGLDvqEY9ii71zzdoRHNGC4Appavug -metadata: MarioCoin supply: 10000 warp: false +metadata: MarioCoin supply: 10000 balance: 10000 27grFs9vE2YP9kwLM5hQJGLDvqEY9ii71zzdoRHNGC4Appavug out tick: 10 supply (must be multiple of out tick): 100 @@ -287,7 +252,7 @@ chainID: Em2pZtHr7rDCzii43an2bBi1M2mTFyLN33QP1Xfjy7BcWtaH9 in assetID (use TKN for native token): TKN balance: 997.999993843 TKN out assetID (use TKN for native token): 27grFs9vE2YP9kwLM5hQJGLDvqEY9ii71zzdoRHNGC4Appavug -metadata: MarioCoin supply: 10000 warp: false +metadata: MarioCoin supply: 10000 available orders: 1 0) Rate(in/out): 100000000.0000 InTick: 1.000000000 TKN OutTick: 10 27grFs9vE2YP9kwLM5hQJGLDvqEY9ii71zzdoRHNGC4Appavug Remaining: 100 27grFs9vE2YP9kwLM5hQJGLDvqEY9ii71zzdoRHNGC4Appavug select order: 0 @@ -484,10 +449,6 @@ out on the Avalanche Discord._ * Add expiring order support (can't fill an order after some point in time but still need to explicitly close it to get your funds back -> async cleanup is not a good idea) -* Add lockup fee for creating a Warp Message and ability to reclaim the lockup - with a refund action (this will allow for "user-driven" acks on - messages, which will remain signable and in state until a refund action is - issued)

diff --git a/examples/tokenvm/actions/burn_asset.go b/examples/tokenvm/actions/burn_asset.go index cbbda3ad83..b72b49a577 100644 --- a/examples/tokenvm/actions/burn_asset.go +++ b/examples/tokenvm/actions/burn_asset.go @@ -8,7 +8,6 @@ import ( "github.com/ava-labs/avalanchego/ids" smath "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" @@ -43,10 +42,6 @@ func (*BurnAsset) StateKeysMaxChunks() []uint16 { return []uint16{storage.AssetChunks, storage.BalanceChunks} } -func (*BurnAsset) OutputsWarpMessage() bool { - return false -} - func (b *BurnAsset) Execute( ctx context.Context, _ chain.Rules, @@ -54,29 +49,28 @@ func (b *BurnAsset) Execute( _ int64, actor codec.Address, _ ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { if b.Value == 0 { - return false, BurnComputeUnits, OutputValueZero, nil, nil + return false, BurnComputeUnits, OutputValueZero, nil } if err := storage.SubBalance(ctx, mu, actor, b.Asset, b.Value); err != nil { - return false, BurnComputeUnits, utils.ErrBytes(err), nil, nil + return false, BurnComputeUnits, utils.ErrBytes(err), nil } - exists, symbol, decimals, metadata, supply, owner, warp, err := storage.GetAsset(ctx, mu, b.Asset) + exists, symbol, decimals, metadata, supply, owner, err := storage.GetAsset(ctx, mu, b.Asset) if err != nil { - return false, BurnComputeUnits, utils.ErrBytes(err), nil, nil + return false, BurnComputeUnits, utils.ErrBytes(err), nil } if !exists { - return false, BurnComputeUnits, OutputAssetMissing, nil, nil + return false, BurnComputeUnits, OutputAssetMissing, nil } newSupply, err := smath.Sub(supply, b.Value) if err != nil { - return false, BurnComputeUnits, utils.ErrBytes(err), nil, nil + return false, BurnComputeUnits, utils.ErrBytes(err), nil } - if err := storage.SetAsset(ctx, mu, b.Asset, symbol, decimals, metadata, newSupply, owner, warp); err != nil { - return false, BurnComputeUnits, utils.ErrBytes(err), nil, nil + if err := storage.SetAsset(ctx, mu, b.Asset, symbol, decimals, metadata, newSupply, owner); err != nil { + return false, BurnComputeUnits, utils.ErrBytes(err), nil } - return true, BurnComputeUnits, nil, nil, nil + return true, BurnComputeUnits, nil, nil } func (*BurnAsset) MaxComputeUnits(chain.Rules) uint64 { @@ -92,7 +86,7 @@ func (b *BurnAsset) Marshal(p *codec.Packer) { p.PackUint64(b.Value) } -func UnmarshalBurnAsset(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalBurnAsset(p *codec.Packer) (chain.Action, error) { var burn BurnAsset p.UnpackID(false, &burn.Asset) // can burn native asset burn.Value = p.UnpackUint64(true) diff --git a/examples/tokenvm/actions/close_order.go b/examples/tokenvm/actions/close_order.go index aa499b7eac..862d142752 100644 --- a/examples/tokenvm/actions/close_order.go +++ b/examples/tokenvm/actions/close_order.go @@ -7,7 +7,6 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -42,10 +41,6 @@ func (*CloseOrder) StateKeysMaxChunks() []uint16 { return []uint16{storage.OrderChunks, storage.BalanceChunks} } -func (*CloseOrder) OutputsWarpMessage() bool { - return false -} - func (c *CloseOrder) Execute( ctx context.Context, _ chain.Rules, @@ -53,28 +48,27 @@ func (c *CloseOrder) Execute( _ int64, actor codec.Address, _ ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { exists, _, _, out, _, remaining, owner, err := storage.GetOrder(ctx, mu, c.Order) if err != nil { - return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil } if !exists { - return false, CloseOrderComputeUnits, OutputOrderMissing, nil, nil + return false, CloseOrderComputeUnits, OutputOrderMissing, nil } if owner != actor { - return false, CloseOrderComputeUnits, OutputUnauthorized, nil, nil + return false, CloseOrderComputeUnits, OutputUnauthorized, nil } if out != c.Out { - return false, CloseOrderComputeUnits, OutputWrongOut, nil, nil + return false, CloseOrderComputeUnits, OutputWrongOut, nil } if err := storage.DeleteOrder(ctx, mu, c.Order); err != nil { - return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil } if err := storage.AddBalance(ctx, mu, actor, c.Out, remaining, true); err != nil { - return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil } - return true, CloseOrderComputeUnits, nil, nil, nil + return true, CloseOrderComputeUnits, nil, nil } func (*CloseOrder) MaxComputeUnits(chain.Rules) uint64 { @@ -90,7 +84,7 @@ func (c *CloseOrder) Marshal(p *codec.Packer) { p.PackID(c.Out) } -func UnmarshalCloseOrder(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalCloseOrder(p *codec.Packer) (chain.Action, error) { var cl CloseOrder p.UnpackID(true, &cl.Order) p.UnpackID(false, &cl.Out) // empty ID is the native asset diff --git a/examples/tokenvm/actions/create_asset.go b/examples/tokenvm/actions/create_asset.go index 7cc44ea9c7..a044e3bacd 100644 --- a/examples/tokenvm/actions/create_asset.go +++ b/examples/tokenvm/actions/create_asset.go @@ -7,7 +7,6 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -38,10 +37,6 @@ func (*CreateAsset) StateKeysMaxChunks() []uint16 { return []uint16{storage.AssetChunks} } -func (*CreateAsset) OutputsWarpMessage() bool { - return false -} - func (c *CreateAsset) Execute( ctx context.Context, _ chain.Rules, @@ -49,29 +44,28 @@ func (c *CreateAsset) Execute( _ int64, actor codec.Address, txID ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { if len(c.Symbol) == 0 { - return false, CreateAssetComputeUnits, OutputSymbolEmpty, nil, nil + return false, CreateAssetComputeUnits, OutputSymbolEmpty, nil } if len(c.Symbol) > MaxSymbolSize { - return false, CreateAssetComputeUnits, OutputSymbolTooLarge, nil, nil + return false, CreateAssetComputeUnits, OutputSymbolTooLarge, nil } if c.Decimals > MaxDecimals { - return false, CreateAssetComputeUnits, OutputDecimalsTooLarge, nil, nil + return false, CreateAssetComputeUnits, OutputDecimalsTooLarge, nil } if len(c.Metadata) == 0 { - return false, CreateAssetComputeUnits, OutputMetadataEmpty, nil, nil + return false, CreateAssetComputeUnits, OutputMetadataEmpty, nil } if len(c.Metadata) > MaxMetadataSize { - return false, CreateAssetComputeUnits, OutputMetadataTooLarge, nil, nil + return false, CreateAssetComputeUnits, OutputMetadataTooLarge, nil } // It should only be possible to overwrite an existing asset if there is // a hash collision. - if err := storage.SetAsset(ctx, mu, txID, c.Symbol, c.Decimals, c.Metadata, 0, actor, false); err != nil { - return false, CreateAssetComputeUnits, utils.ErrBytes(err), nil, nil + if err := storage.SetAsset(ctx, mu, txID, c.Symbol, c.Decimals, c.Metadata, 0, actor); err != nil { + return false, CreateAssetComputeUnits, utils.ErrBytes(err), nil } - return true, CreateAssetComputeUnits, nil, nil, nil + return true, CreateAssetComputeUnits, nil, nil } func (*CreateAsset) MaxComputeUnits(chain.Rules) uint64 { @@ -89,7 +83,7 @@ func (c *CreateAsset) Marshal(p *codec.Packer) { p.PackBytes(c.Metadata) } -func UnmarshalCreateAsset(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalCreateAsset(p *codec.Packer) (chain.Action, error) { var create CreateAsset p.UnpackBytes(MaxSymbolSize, true, &create.Symbol) create.Decimals = p.UnpackByte() diff --git a/examples/tokenvm/actions/create_order.go b/examples/tokenvm/actions/create_order.go index c8dd51e9d1..243e4ad9fb 100644 --- a/examples/tokenvm/actions/create_order.go +++ b/examples/tokenvm/actions/create_order.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -62,10 +61,6 @@ func (*CreateOrder) StateKeysMaxChunks() []uint16 { return []uint16{storage.BalanceChunks, storage.OrderChunks} } -func (*CreateOrder) OutputsWarpMessage() bool { - return false -} - func (c *CreateOrder) Execute( ctx context.Context, _ chain.Rules, @@ -73,30 +68,29 @@ func (c *CreateOrder) Execute( _ int64, actor codec.Address, txID ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { if c.In == c.Out { - return false, CreateOrderComputeUnits, OutputSameInOut, nil, nil + return false, CreateOrderComputeUnits, OutputSameInOut, nil } if c.InTick == 0 { - return false, CreateOrderComputeUnits, OutputInTickZero, nil, nil + return false, CreateOrderComputeUnits, OutputInTickZero, nil } if c.OutTick == 0 { - return false, CreateOrderComputeUnits, OutputOutTickZero, nil, nil + return false, CreateOrderComputeUnits, OutputOutTickZero, nil } if c.Supply == 0 { - return false, CreateOrderComputeUnits, OutputSupplyZero, nil, nil + return false, CreateOrderComputeUnits, OutputSupplyZero, nil } if c.Supply%c.OutTick != 0 { - return false, CreateOrderComputeUnits, OutputSupplyMisaligned, nil, nil + return false, CreateOrderComputeUnits, OutputSupplyMisaligned, nil } if err := storage.SubBalance(ctx, mu, actor, c.Out, c.Supply); err != nil { - return false, CreateOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, CreateOrderComputeUnits, utils.ErrBytes(err), nil } if err := storage.SetOrder(ctx, mu, txID, c.In, c.InTick, c.Out, c.OutTick, c.Supply, actor); err != nil { - return false, CreateOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, CreateOrderComputeUnits, utils.ErrBytes(err), nil } - return true, CreateOrderComputeUnits, nil, nil, nil + return true, CreateOrderComputeUnits, nil, nil } func (*CreateOrder) MaxComputeUnits(chain.Rules) uint64 { @@ -115,7 +109,7 @@ func (c *CreateOrder) Marshal(p *codec.Packer) { p.PackUint64(c.Supply) } -func UnmarshalCreateOrder(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalCreateOrder(p *codec.Packer) (chain.Action, error) { var create CreateOrder p.UnpackID(false, &create.In) // empty ID is the native asset create.InTick = p.UnpackUint64(true) diff --git a/examples/tokenvm/actions/export_asset.go b/examples/tokenvm/actions/export_asset.go deleted file mode 100644 index 6811698aef..0000000000 --- a/examples/tokenvm/actions/export_asset.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package actions - -import ( - "context" - - "github.com/ava-labs/avalanchego/ids" - smath "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" - - "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/codec" - "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/examples/tokenvm/storage" - "github.com/ava-labs/hypersdk/state" - "github.com/ava-labs/hypersdk/utils" -) - -var _ chain.Action = (*ExportAsset)(nil) - -type ExportAsset struct { - To codec.Address `json:"to"` - Asset ids.ID `json:"asset"` - Value uint64 `json:"value"` - Return bool `json:"return"` - Reward uint64 `json:"reward"` - SwapIn uint64 `json:"swapIn"` - AssetOut ids.ID `json:"assetOut"` - SwapOut uint64 `json:"swapOut"` - SwapExpiry int64 `json:"swapExpiry"` - Destination ids.ID `json:"destination"` -} - -func (*ExportAsset) GetTypeID() uint8 { - return exportAssetID -} - -func (e *ExportAsset) StateKeys(actor codec.Address, _ ids.ID) state.Keys { - if e.Return { - return state.Keys{ - string(storage.AssetKey(e.Asset)): state.Read | state.Write, - string(storage.BalanceKey(actor, e.Asset)): state.Read | state.Write, - } - } - return state.Keys{ - string(storage.AssetKey(e.Asset)): state.Read | state.Write, - string(storage.LoanKey(e.Asset, e.Destination)): state.All, - string(storage.BalanceKey(actor, e.Asset)): state.Read | state.Write, - } -} - -func (e *ExportAsset) StateKeysMaxChunks() []uint16 { - if e.Return { - return []uint16{storage.AssetChunks, storage.BalanceChunks} - } - return []uint16{storage.AssetChunks, storage.LoanChunks, storage.BalanceChunks} -} - -func (*ExportAsset) OutputsWarpMessage() bool { - return true -} - -func (e *ExportAsset) executeReturn( - ctx context.Context, - mu state.Mutable, - actor codec.Address, - txID ids.ID, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - exists, symbol, decimals, metadata, supply, _, isWarp, err := storage.GetAsset(ctx, mu, e.Asset) - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if !exists { - return false, ExportAssetComputeUnits, OutputAssetMissing, nil, nil - } - if !isWarp { - return false, ExportAssetComputeUnits, OutputNotWarpAsset, nil, nil - } - allowedDestination, err := ids.ToID(metadata[consts.IDLen:]) - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if allowedDestination != e.Destination { - return false, ExportAssetComputeUnits, OutputWrongDestination, nil, nil - } - newSupply, err := smath.Sub(supply, e.Value) - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - newSupply, err = smath.Sub(newSupply, e.Reward) - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if newSupply > 0 { - if err := storage.SetAsset(ctx, mu, e.Asset, symbol, decimals, metadata, newSupply, codec.EmptyAddress, true); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - } else { - if err := storage.DeleteAsset(ctx, mu, e.Asset); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - } - if err := storage.SubBalance(ctx, mu, actor, e.Asset, e.Value); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if e.Reward > 0 { - if err := storage.SubBalance(ctx, mu, actor, e.Asset, e.Reward); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - } - originalAsset, err := ids.ToID(metadata[:consts.IDLen]) - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - wt := &WarpTransfer{ - To: e.To, - Symbol: symbol, - Decimals: decimals, - Asset: originalAsset, - Value: e.Value, - Return: e.Return, - Reward: e.Reward, - SwapIn: e.SwapIn, - AssetOut: e.AssetOut, - SwapOut: e.SwapOut, - SwapExpiry: e.SwapExpiry, - TxID: txID, - DestinationChainID: e.Destination, - } - payload, err := wt.Marshal() - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - wm := &warp.UnsignedMessage{ - // NetworkID + SourceChainID is populated by hypersdk - Payload: payload, - } - return true, ExportAssetComputeUnits, nil, wm, nil -} - -func (e *ExportAsset) executeLoan( - ctx context.Context, - mu state.Mutable, - actor codec.Address, - txID ids.ID, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - exists, symbol, decimals, _, _, _, isWarp, err := storage.GetAsset(ctx, mu, e.Asset) - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if !exists { - return false, ExportAssetComputeUnits, OutputAssetMissing, nil, nil - } - if isWarp { - // Cannot export an asset if it was warped in and not returning - return false, ExportAssetComputeUnits, OutputWarpAsset, nil, nil - } - if err := storage.AddLoan(ctx, mu, e.Asset, e.Destination, e.Value); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if err := storage.SubBalance(ctx, mu, actor, e.Asset, e.Value); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if e.Reward > 0 { - if err := storage.AddLoan(ctx, mu, e.Asset, e.Destination, e.Reward); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if err := storage.SubBalance(ctx, mu, actor, e.Asset, e.Reward); err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - } - wt := &WarpTransfer{ - To: e.To, - Symbol: symbol, - Decimals: decimals, - Asset: e.Asset, - Value: e.Value, - Return: e.Return, - Reward: e.Reward, - SwapIn: e.SwapIn, - AssetOut: e.AssetOut, - SwapOut: e.SwapOut, - SwapExpiry: e.SwapExpiry, - TxID: txID, - DestinationChainID: e.Destination, - } - payload, err := wt.Marshal() - if err != nil { - return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - wm := &warp.UnsignedMessage{ - // NetworkID + SourceChainID is populated by hypersdk - Payload: payload, - } - return true, ExportAssetComputeUnits, nil, wm, nil -} - -func (e *ExportAsset) Execute( - ctx context.Context, - _ chain.Rules, - mu state.Mutable, - _ int64, - actor codec.Address, - txID ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - if e.Value == 0 { - return false, ExportAssetComputeUnits, OutputValueZero, nil, nil - } - if e.Destination == ids.Empty { - // This would result in multiplying balance export by whoever imports the - // transaction. - return false, ExportAssetComputeUnits, OutputAnycast, nil, nil - } - // TODO: check if destination is ourselves - if e.Return { - return e.executeReturn(ctx, mu, actor, txID) - } - return e.executeLoan(ctx, mu, actor, txID) -} - -func (*ExportAsset) MaxComputeUnits(chain.Rules) uint64 { - return ExportAssetComputeUnits -} - -func (*ExportAsset) Size() int { - return codec.AddressLen + consts.IDLen + - consts.Uint64Len + consts.BoolLen + - consts.Uint64Len + /* op bits */ - consts.Uint64Len + consts.Uint64Len + consts.IDLen + consts.Uint64Len + - consts.Int64Len + consts.IDLen -} - -func (e *ExportAsset) Marshal(p *codec.Packer) { - p.PackAddress(e.To) - p.PackID(e.Asset) - p.PackUint64(e.Value) - p.PackBool(e.Return) - op := codec.NewOptionalWriter(consts.Uint64Len*3 + consts.Int64Len + consts.IDLen) - op.PackUint64(e.Reward) - op.PackUint64(e.SwapIn) - op.PackID(e.AssetOut) - op.PackUint64(e.SwapOut) - op.PackInt64(e.SwapExpiry) - p.PackOptional(op) - p.PackID(e.Destination) -} - -func UnmarshalExportAsset(p *codec.Packer, _ *warp.Message) (chain.Action, error) { - var export ExportAsset - p.UnpackAddress(&export.To) - p.UnpackID(false, &export.Asset) // may export native - export.Value = p.UnpackUint64(true) - export.Return = p.UnpackBool() - op := p.NewOptionalReader() - export.Reward = op.UnpackUint64() // reward not required - export.SwapIn = op.UnpackUint64() // optional - op.UnpackID(&export.AssetOut) - export.SwapOut = op.UnpackUint64() - export.SwapExpiry = op.UnpackInt64() - op.Done() - p.UnpackID(true, &export.Destination) - if err := p.Err(); err != nil { - return nil, err - } - // Handle swap checks - if !ValidSwapParams( - export.Value, - export.SwapIn, - export.AssetOut, - export.SwapOut, - export.SwapExpiry, - ) { - return nil, chain.ErrInvalidObject - } - return &export, nil -} - -func (*ExportAsset) ValidRange(chain.Rules) (int64, int64) { - // Returning -1, -1 means that the action is always valid. - return -1, -1 -} diff --git a/examples/tokenvm/actions/fill_order.go b/examples/tokenvm/actions/fill_order.go index bb216e77e1..0c16454642 100644 --- a/examples/tokenvm/actions/fill_order.go +++ b/examples/tokenvm/actions/fill_order.go @@ -8,7 +8,6 @@ import ( "github.com/ava-labs/avalanchego/ids" smath "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" @@ -57,10 +56,6 @@ func (*FillOrder) StateKeysMaxChunks() []uint16 { return []uint16{storage.OrderChunks, storage.BalanceChunks, storage.BalanceChunks, storage.BalanceChunks} } -func (*FillOrder) OutputsWarpMessage() bool { - return false -} - func (f *FillOrder) Execute( ctx context.Context, _ chain.Rules, @@ -68,40 +63,39 @@ func (f *FillOrder) Execute( _ int64, actor codec.Address, _ ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { exists, in, inTick, out, outTick, remaining, owner, err := storage.GetOrder(ctx, mu, f.Order) if err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } if !exists { - return false, NoFillOrderComputeUnits, OutputOrderMissing, nil, nil + return false, NoFillOrderComputeUnits, OutputOrderMissing, nil } if owner != f.Owner { - return false, NoFillOrderComputeUnits, OutputWrongOwner, nil, nil + return false, NoFillOrderComputeUnits, OutputWrongOwner, nil } if in != f.In { - return false, NoFillOrderComputeUnits, OutputWrongIn, nil, nil + return false, NoFillOrderComputeUnits, OutputWrongIn, nil } if out != f.Out { - return false, NoFillOrderComputeUnits, OutputWrongOut, nil, nil + return false, NoFillOrderComputeUnits, OutputWrongOut, nil } if f.Value == 0 { // This should be guarded via [Unmarshal] but we check anyways. - return false, NoFillOrderComputeUnits, OutputValueZero, nil, nil + return false, NoFillOrderComputeUnits, OutputValueZero, nil } if f.Value%inTick != 0 { - return false, NoFillOrderComputeUnits, OutputValueMisaligned, nil, nil + return false, NoFillOrderComputeUnits, OutputValueMisaligned, nil } // Determine amount of [Out] counterparty will receive if the trade is // successful. outputAmount, err := smath.Mul64(outTick, f.Value/inTick) if err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } if outputAmount == 0 { // This should never happen because [f.Value] > 0 - return false, NoFillOrderComputeUnits, OutputInsufficientOutput, nil, nil + return false, NoFillOrderComputeUnits, OutputInsufficientOutput, nil } var ( inputAmount = f.Value @@ -127,32 +121,32 @@ func (f *FillOrder) Execute( } if inputAmount == 0 { // Don't allow free trades (can happen due to refund rounding) - return false, NoFillOrderComputeUnits, OutputInsufficientInput, nil, nil + return false, NoFillOrderComputeUnits, OutputInsufficientInput, nil } if err := storage.SubBalance(ctx, mu, actor, f.In, inputAmount); err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } if err := storage.AddBalance(ctx, mu, f.Owner, f.In, inputAmount, true); err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } if err := storage.AddBalance(ctx, mu, actor, f.Out, outputAmount, true); err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } if shouldDelete { if err := storage.DeleteOrder(ctx, mu, f.Order); err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } } else { if err := storage.SetOrder(ctx, mu, f.Order, in, inTick, out, outTick, orderRemaining, owner); err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } } or := &OrderResult{In: inputAmount, Out: outputAmount, Remaining: orderRemaining} output, err := or.Marshal() if err != nil { - return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil + return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil } - return true, FillOrderComputeUnits, output, nil, nil + return true, FillOrderComputeUnits, output, nil } func (*FillOrder) MaxComputeUnits(chain.Rules) uint64 { @@ -171,7 +165,7 @@ func (f *FillOrder) Marshal(p *codec.Packer) { p.PackUint64(f.Value) } -func UnmarshalFillOrder(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalFillOrder(p *codec.Packer) (chain.Action, error) { var fill FillOrder p.UnpackID(true, &fill.Order) p.UnpackAddress(&fill.Owner) diff --git a/examples/tokenvm/actions/import_asset.go b/examples/tokenvm/actions/import_asset.go deleted file mode 100644 index 9af545778c..0000000000 --- a/examples/tokenvm/actions/import_asset.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package actions - -import ( - "bytes" - "context" - - "github.com/ava-labs/avalanchego/ids" - smath "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" - - "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/codec" - "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/examples/tokenvm/storage" - "github.com/ava-labs/hypersdk/state" - "github.com/ava-labs/hypersdk/utils" -) - -var _ chain.Action = (*ImportAsset)(nil) - -type ImportAsset struct { - // Fill indicates if the actor wishes to fill the order request in the warp - // message. This must be true if the warp message is in a block with - // a timestamp < [SwapExpiry]. - Fill bool `json:"fill"` - - // warpTransfer is parsed from the inner *warp.Message - warpTransfer *WarpTransfer - - // warpMessage is the full *warp.Message parsed from [chain.Transaction] - warpMessage *warp.Message -} - -func (*ImportAsset) GetTypeID() uint8 { - return importAssetID -} - -func (i *ImportAsset) StateKeys(actor codec.Address, _ ids.ID) state.Keys { - var ( - keys state.Keys - assetID ids.ID - ) - if i.warpTransfer.Return { - assetID = i.warpTransfer.Asset - keys = state.Keys{ - string(storage.AssetKey(i.warpTransfer.Asset)): state.Read | state.Write, - string(storage.LoanKey(i.warpTransfer.Asset, i.warpMessage.SourceChainID)): state.Read | state.Write, - string(storage.BalanceKey(i.warpTransfer.To, i.warpTransfer.Asset)): state.All, - } - } else { - assetID = ImportedAssetID(i.warpTransfer.Asset, i.warpMessage.SourceChainID) - keys = state.Keys{ - string(storage.AssetKey(assetID)): state.All, - string(storage.BalanceKey(i.warpTransfer.To, assetID)): state.All, - } - } - - // If the [warpTransfer] specified a reward, we add the state key to make - // sure it is paid. - if i.warpTransfer.Reward > 0 { - keys.Add(string(storage.BalanceKey(actor, assetID)), state.Read|state.Write) - } - - // If the [warpTransfer] requests a swap, we add the state keys to transfer - // the required balances. - if i.Fill && i.warpTransfer.SwapIn > 0 { - keys.Add(string(storage.BalanceKey(actor, i.warpTransfer.AssetOut)), state.All) - keys.Add(string(storage.BalanceKey(actor, assetID)), state.All) - keys.Add(string(storage.BalanceKey(i.warpTransfer.To, i.warpTransfer.AssetOut)), state.All) - } - return keys -} - -func (i *ImportAsset) StateKeysMaxChunks() []uint16 { - // Can't use [warpTransfer] because it may not be populated yet - chunks := []uint16{} - chunks = append(chunks, storage.LoanChunks) - chunks = append(chunks, storage.AssetChunks) - chunks = append(chunks, storage.BalanceChunks) - - // If the [warpTransfer] specified a reward, we add the state key to make - // sure it is paid. - chunks = append(chunks, storage.BalanceChunks) - - // If the [warpTransfer] requests a swap, we add the state keys to transfer - // the required balances. - if i.Fill { - chunks = append(chunks, storage.BalanceChunks) - chunks = append(chunks, storage.BalanceChunks) - chunks = append(chunks, storage.BalanceChunks) - } - return chunks -} - -func (*ImportAsset) OutputsWarpMessage() bool { - return false -} - -func (i *ImportAsset) executeMint( - ctx context.Context, - mu state.Mutable, - actor codec.Address, -) []byte { - asset := ImportedAssetID(i.warpTransfer.Asset, i.warpMessage.SourceChainID) - exists, symbol, decimals, metadata, supply, _, warp, err := storage.GetAsset(ctx, mu, asset) - if err != nil { - return utils.ErrBytes(err) - } - if exists && !warp { - // Should not be possible - return OutputConflictingAsset - } - if !exists { - symbol = i.warpTransfer.Symbol - decimals = i.warpTransfer.Decimals - metadata = ImportedAssetMetadata(i.warpTransfer.Asset, i.warpMessage.SourceChainID) - } - newSupply, err := smath.Add64(supply, i.warpTransfer.Value) - if err != nil { - return utils.ErrBytes(err) - } - newSupply, err = smath.Add64(newSupply, i.warpTransfer.Reward) - if err != nil { - return utils.ErrBytes(err) - } - if err := storage.SetAsset(ctx, mu, asset, symbol, decimals, metadata, newSupply, codec.EmptyAddress, true); err != nil { - return utils.ErrBytes(err) - } - if err := storage.AddBalance(ctx, mu, i.warpTransfer.To, asset, i.warpTransfer.Value, true); err != nil { - return utils.ErrBytes(err) - } - if i.warpTransfer.Reward > 0 { - if err := storage.AddBalance(ctx, mu, actor, asset, i.warpTransfer.Reward, true); err != nil { - return utils.ErrBytes(err) - } - } - return nil -} - -func (i *ImportAsset) executeReturn( - ctx context.Context, - mu state.Mutable, - actor codec.Address, -) []byte { - exists, symbol, decimals, _, _, _, warp, err := storage.GetAsset(ctx, mu, i.warpTransfer.Asset) - if err != nil { - return utils.ErrBytes(err) - } - if !exists { - return OutputAssetMissing - } - if !bytes.Equal(i.warpTransfer.Symbol, symbol) { - return OutputSymbolIncorrect - } - if i.warpTransfer.Decimals != decimals { - return OutputDecimalsIncorrect - } - if warp { - return OutputWarpAsset - } - if err := storage.SubLoan( - ctx, mu, i.warpTransfer.Asset, - i.warpMessage.SourceChainID, i.warpTransfer.Value, - ); err != nil { - return utils.ErrBytes(err) - } - if err := storage.AddBalance( - ctx, mu, i.warpTransfer.To, - i.warpTransfer.Asset, i.warpTransfer.Value, - true, - ); err != nil { - return utils.ErrBytes(err) - } - if i.warpTransfer.Reward > 0 { - if err := storage.SubLoan( - ctx, mu, i.warpTransfer.Asset, - i.warpMessage.SourceChainID, i.warpTransfer.Reward, - ); err != nil { - return utils.ErrBytes(err) - } - if err := storage.AddBalance( - ctx, mu, actor, - i.warpTransfer.Asset, i.warpTransfer.Reward, - true, - ); err != nil { - return utils.ErrBytes(err) - } - } - return nil -} - -func (i *ImportAsset) Execute( - ctx context.Context, - r chain.Rules, - mu state.Mutable, - t int64, - actor codec.Address, - _ ids.ID, - warpVerified bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - if !warpVerified { - return false, ImportAssetComputeUnits, OutputWarpVerificationFailed, nil, nil - } - if i.warpTransfer.DestinationChainID != r.ChainID() { - return false, ImportAssetComputeUnits, OutputInvalidDestination, nil, nil - } - if i.warpTransfer.Value == 0 { - return false, ImportAssetComputeUnits, OutputValueZero, nil, nil - } - var output []byte - if i.warpTransfer.Return { - output = i.executeReturn(ctx, mu, actor) - } else { - output = i.executeMint(ctx, mu, actor) - } - if len(output) > 0 { - return false, ImportAssetComputeUnits, output, nil, nil - } - if i.warpTransfer.SwapIn == 0 { - // We are ensured that [i.Fill] is false here because of logic in unmarshal - return true, ImportAssetComputeUnits, nil, nil, nil - } - if !i.Fill { - if i.warpTransfer.SwapExpiry > t { - return false, ImportAssetComputeUnits, OutputMustFill, nil, nil - } - return true, ImportAssetComputeUnits, nil, nil, nil - } - // TODO: charge more if swap is performed - var assetIn ids.ID - if i.warpTransfer.Return { - assetIn = i.warpTransfer.Asset - } else { - assetIn = ImportedAssetID(i.warpTransfer.Asset, i.warpMessage.SourceChainID) - } - if err := storage.SubBalance(ctx, mu, i.warpTransfer.To, assetIn, i.warpTransfer.SwapIn); err != nil { - return false, ImportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if err := storage.AddBalance(ctx, mu, actor, assetIn, i.warpTransfer.SwapIn, true); err != nil { - return false, ImportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if err := storage.SubBalance(ctx, mu, actor, i.warpTransfer.AssetOut, i.warpTransfer.SwapOut); err != nil { - return false, ImportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - if err := storage.AddBalance(ctx, mu, i.warpTransfer.To, i.warpTransfer.AssetOut, i.warpTransfer.SwapOut, true); err != nil { - return false, ImportAssetComputeUnits, utils.ErrBytes(err), nil, nil - } - return true, ImportAssetComputeUnits, nil, nil, nil -} - -func (*ImportAsset) MaxComputeUnits(chain.Rules) uint64 { - return ImportAssetComputeUnits -} - -func (*ImportAsset) Size() int { - return consts.BoolLen -} - -// All we encode that is action specific for now is the type byte from the -// registry. -func (i *ImportAsset) Marshal(p *codec.Packer) { - p.PackBool(i.Fill) -} - -func UnmarshalImportAsset(p *codec.Packer, wm *warp.Message) (chain.Action, error) { - var ( - imp ImportAsset - err error - ) - imp.Fill = p.UnpackBool() - if err := p.Err(); err != nil { - return nil, err - } - imp.warpMessage = wm - imp.warpTransfer, err = UnmarshalWarpTransfer(imp.warpMessage.Payload) - if err != nil { - return nil, err - } - // Ensure we can fill the swap if it exists - if imp.Fill && imp.warpTransfer.SwapIn == 0 { - return nil, ErrNoSwapToFill - } - return &imp, nil -} - -func (*ImportAsset) ValidRange(chain.Rules) (int64, int64) { - // Returning -1, -1 means that the action is always valid. - return -1, -1 -} diff --git a/examples/tokenvm/actions/mint_asset.go b/examples/tokenvm/actions/mint_asset.go index a8eea1919e..52e05ca576 100644 --- a/examples/tokenvm/actions/mint_asset.go +++ b/examples/tokenvm/actions/mint_asset.go @@ -8,7 +8,6 @@ import ( "github.com/ava-labs/avalanchego/ids" smath "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" @@ -46,10 +45,6 @@ func (*MintAsset) StateKeysMaxChunks() []uint16 { return []uint16{storage.AssetChunks, storage.BalanceChunks} } -func (*MintAsset) OutputsWarpMessage() bool { - return false -} - func (m *MintAsset) Execute( ctx context.Context, _ chain.Rules, @@ -57,38 +52,34 @@ func (m *MintAsset) Execute( _ int64, actor codec.Address, _ ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { if m.Asset == ids.Empty { - return false, MintAssetComputeUnits, OutputAssetIsNative, nil, nil + return false, MintAssetComputeUnits, OutputAssetIsNative, nil } if m.Value == 0 { - return false, MintAssetComputeUnits, OutputValueZero, nil, nil + return false, MintAssetComputeUnits, OutputValueZero, nil } - exists, symbol, decimals, metadata, supply, owner, isWarp, err := storage.GetAsset(ctx, mu, m.Asset) + exists, symbol, decimals, metadata, supply, owner, err := storage.GetAsset(ctx, mu, m.Asset) if err != nil { - return false, MintAssetComputeUnits, utils.ErrBytes(err), nil, nil + return false, MintAssetComputeUnits, utils.ErrBytes(err), nil } if !exists { - return false, MintAssetComputeUnits, OutputAssetMissing, nil, nil - } - if isWarp { - return false, MintAssetComputeUnits, OutputWarpAsset, nil, nil + return false, MintAssetComputeUnits, OutputAssetMissing, nil } if owner != actor { - return false, MintAssetComputeUnits, OutputWrongOwner, nil, nil + return false, MintAssetComputeUnits, OutputWrongOwner, nil } newSupply, err := smath.Add64(supply, m.Value) if err != nil { - return false, MintAssetComputeUnits, utils.ErrBytes(err), nil, nil + return false, MintAssetComputeUnits, utils.ErrBytes(err), nil } - if err := storage.SetAsset(ctx, mu, m.Asset, symbol, decimals, metadata, newSupply, actor, isWarp); err != nil { - return false, MintAssetComputeUnits, utils.ErrBytes(err), nil, nil + if err := storage.SetAsset(ctx, mu, m.Asset, symbol, decimals, metadata, newSupply, actor); err != nil { + return false, MintAssetComputeUnits, utils.ErrBytes(err), nil } if err := storage.AddBalance(ctx, mu, m.To, m.Asset, m.Value, true); err != nil { - return false, MintAssetComputeUnits, utils.ErrBytes(err), nil, nil + return false, MintAssetComputeUnits, utils.ErrBytes(err), nil } - return true, MintAssetComputeUnits, nil, nil, nil + return true, MintAssetComputeUnits, nil, nil } func (*MintAsset) MaxComputeUnits(chain.Rules) uint64 { @@ -105,7 +96,7 @@ func (m *MintAsset) Marshal(p *codec.Packer) { p.PackUint64(m.Value) } -func UnmarshalMintAsset(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalMintAsset(p *codec.Packer) (chain.Action, error) { var mint MintAsset p.UnpackAddress(&mint.To) p.UnpackID(true, &mint.Asset) // empty ID is the native asset diff --git a/examples/tokenvm/actions/outputs.go b/examples/tokenvm/actions/outputs.go index 5a976e8b89..c9535fc315 100644 --- a/examples/tokenvm/actions/outputs.go +++ b/examples/tokenvm/actions/outputs.go @@ -4,37 +4,32 @@ package actions var ( - OutputValueZero = []byte("value is zero") - OutputMemoTooLarge = []byte("memo is too large") - OutputAssetIsNative = []byte("cannot mint native asset") - OutputAssetAlreadyExists = []byte("asset already exists") - OutputAssetMissing = []byte("asset missing") - OutputInTickZero = []byte("in rate is zero") - OutputOutTickZero = []byte("out rate is zero") - OutputSupplyZero = []byte("supply is zero") - OutputSupplyMisaligned = []byte("supply is misaligned") - OutputOrderMissing = []byte("order is missing") - OutputUnauthorized = []byte("unauthorized") - OutputWrongIn = []byte("wrong in asset") - OutputWrongOut = []byte("wrong out asset") - OutputWrongOwner = []byte("wrong owner") - OutputInsufficientInput = []byte("insufficient input") - OutputInsufficientOutput = []byte("insufficient output") - OutputValueMisaligned = []byte("value is misaligned") - OutputSymbolEmpty = []byte("symbol is empty") - OutputSymbolIncorrect = []byte("symbol is incorrect") - OutputSymbolTooLarge = []byte("symbol is too large") - OutputDecimalsIncorrect = []byte("decimal is incorrect") - OutputDecimalsTooLarge = []byte("decimal is too large") - OutputMetadataEmpty = []byte("metadata is empty") - OutputMetadataTooLarge = []byte("metadata is too large") - OutputSameInOut = []byte("same asset used for in and out") - OutputConflictingAsset = []byte("warp has same asset as another") - OutputAnycast = []byte("anycast output") - OutputNotWarpAsset = []byte("not warp asset") - OutputWarpAsset = []byte("warp asset") - OutputWrongDestination = []byte("wrong destination") - OutputMustFill = []byte("must fill request") - OutputWarpVerificationFailed = []byte("warp verification failed") - OutputInvalidDestination = []byte("invalid destination") + OutputValueZero = []byte("value is zero") + OutputMemoTooLarge = []byte("memo is too large") + OutputAssetIsNative = []byte("cannot mint native asset") + OutputAssetAlreadyExists = []byte("asset already exists") + OutputAssetMissing = []byte("asset missing") + OutputInTickZero = []byte("in rate is zero") + OutputOutTickZero = []byte("out rate is zero") + OutputSupplyZero = []byte("supply is zero") + OutputSupplyMisaligned = []byte("supply is misaligned") + OutputOrderMissing = []byte("order is missing") + OutputUnauthorized = []byte("unauthorized") + OutputWrongIn = []byte("wrong in asset") + OutputWrongOut = []byte("wrong out asset") + OutputWrongOwner = []byte("wrong owner") + OutputInsufficientInput = []byte("insufficient input") + OutputInsufficientOutput = []byte("insufficient output") + OutputValueMisaligned = []byte("value is misaligned") + OutputSymbolEmpty = []byte("symbol is empty") + OutputSymbolIncorrect = []byte("symbol is incorrect") + OutputSymbolTooLarge = []byte("symbol is too large") + OutputDecimalsIncorrect = []byte("decimal is incorrect") + OutputDecimalsTooLarge = []byte("decimal is too large") + OutputMetadataEmpty = []byte("metadata is empty") + OutputMetadataTooLarge = []byte("metadata is too large") + OutputSameInOut = []byte("same asset used for in and out") + OutputWrongDestination = []byte("wrong destination") + OutputMustFill = []byte("must fill request") + OutputInvalidDestination = []byte("invalid destination") ) diff --git a/examples/tokenvm/actions/transfer.go b/examples/tokenvm/actions/transfer.go index e1b52d3995..f1630f10c0 100644 --- a/examples/tokenvm/actions/transfer.go +++ b/examples/tokenvm/actions/transfer.go @@ -7,7 +7,6 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -47,10 +46,6 @@ func (*Transfer) StateKeysMaxChunks() []uint16 { return []uint16{storage.BalanceChunks, storage.BalanceChunks} } -func (*Transfer) OutputsWarpMessage() bool { - return false -} - func (t *Transfer) Execute( ctx context.Context, _ chain.Rules, @@ -58,22 +53,21 @@ func (t *Transfer) Execute( _ int64, actor codec.Address, _ ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { if t.Value == 0 { - return false, TransferComputeUnits, OutputValueZero, nil, nil + return false, TransferComputeUnits, OutputValueZero, nil } if len(t.Memo) > MaxMemoSize { - return false, CreateAssetComputeUnits, OutputMemoTooLarge, nil, nil + return false, CreateAssetComputeUnits, OutputMemoTooLarge, nil } if err := storage.SubBalance(ctx, mu, actor, t.Asset, t.Value); err != nil { - return false, TransferComputeUnits, utils.ErrBytes(err), nil, nil + return false, TransferComputeUnits, utils.ErrBytes(err), nil } // TODO: allow sender to configure whether they will pay to create if err := storage.AddBalance(ctx, mu, t.To, t.Asset, t.Value, true); err != nil { - return false, TransferComputeUnits, utils.ErrBytes(err), nil, nil + return false, TransferComputeUnits, utils.ErrBytes(err), nil } - return true, TransferComputeUnits, nil, nil, nil + return true, TransferComputeUnits, nil, nil } func (*Transfer) MaxComputeUnits(chain.Rules) uint64 { @@ -91,7 +85,7 @@ func (t *Transfer) Marshal(p *codec.Packer) { p.PackBytes(t.Memo) } -func UnmarshalTransfer(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalTransfer(p *codec.Packer) (chain.Action, error) { var transfer Transfer p.UnpackAddress(&transfer.To) p.UnpackID(false, &transfer.Asset) // empty ID is the native asset diff --git a/examples/tokenvm/actions/warp_transfer.go b/examples/tokenvm/actions/warp_transfer.go deleted file mode 100644 index 03c448df21..0000000000 --- a/examples/tokenvm/actions/warp_transfer.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package actions - -import ( - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/codec" - "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/utils" -) - -type WarpTransfer struct { - To codec.Address `json:"to"` - Symbol []byte `json:"symbol"` - Decimals uint8 `json:"decimals"` - Asset ids.ID `json:"asset"` - Value uint64 `json:"value"` - - // Return is set to true when a warp message is sending funds back to the - // chain where they were created. - Return bool `json:"return"` - - // Reward is the amount of [Asset] to send the [Actor] that submits this - // transaction. - Reward uint64 `json:"reward"` - - // SwapIn is the amount of [Asset] we are willing to swap for [AssetOut]. - SwapIn uint64 `json:"swapIn"` - // AssetOut is the asset we are seeking to get for [SwapIn]. - AssetOut ids.ID `json:"assetOut"` - // SwapOut is the amount of [AssetOut] we are seeking. - SwapOut uint64 `json:"swapOut"` - // SwapExpiry is the unix timestamp at which the swap becomes invalid (and - // the message can be processed without a swap. - SwapExpiry int64 `json:"swapExpiry"` - - // TxID is the transaction that created this message. This is used to ensure - // there is WarpID uniqueness. - TxID ids.ID `json:"txID"` - - // DestinationChainID is the destination of this transfer. We assume this - // must be populated (not anycast). - DestinationChainID ids.ID `json:"destinationChainID"` -} - -func (w *WarpTransfer) size() int { - return codec.AddressLen + codec.BytesLen(w.Symbol) + consts.Uint8Len + consts.IDLen + - consts.Uint64Len + consts.BoolLen + - consts.Uint64Len + /* op bits */ - consts.Uint64Len + consts.Uint64Len + consts.IDLen + consts.Uint64Len + consts.Int64Len + - consts.IDLen + consts.IDLen -} - -func (w *WarpTransfer) Marshal() ([]byte, error) { - p := codec.NewWriter(w.size(), w.size()) - p.PackAddress(w.To) - p.PackBytes(w.Symbol) - p.PackByte(w.Decimals) - p.PackID(w.Asset) - p.PackUint64(w.Value) - p.PackBool(w.Return) - op := codec.NewOptionalWriter(consts.Uint64Len*3 + consts.IDLen + consts.Int64Len) - op.PackUint64(w.Reward) - op.PackUint64(w.SwapIn) - op.PackID(w.AssetOut) - op.PackUint64(w.SwapOut) - op.PackInt64(w.SwapExpiry) - p.PackOptional(op) - p.PackID(w.TxID) - p.PackID(w.DestinationChainID) - return p.Bytes(), p.Err() -} - -func ImportedAssetID(assetID ids.ID, sourceChainID ids.ID) ids.ID { - return utils.ToID(ImportedAssetMetadata(assetID, sourceChainID)) -} - -func ImportedAssetMetadata(assetID ids.ID, sourceChainID ids.ID) []byte { - k := make([]byte, consts.IDLen*2) - copy(k, assetID[:]) - copy(k[consts.IDLen:], sourceChainID[:]) - return k -} - -func UnmarshalWarpTransfer(b []byte) (*WarpTransfer, error) { - maxWarpTransferSize := codec.AddressLen + codec.BytesLenSize(MaxSymbolSize) + consts.Uint8Len + consts.IDLen + - consts.Uint64Len + consts.BoolLen + - consts.Uint64Len + /* op bits */ - consts.Uint64Len + consts.Uint64Len + consts.IDLen + consts.Uint64Len + consts.Int64Len + - consts.IDLen + consts.IDLen - - var transfer WarpTransfer - p := codec.NewReader(b, maxWarpTransferSize) - p.UnpackAddress(&transfer.To) - p.UnpackBytes(MaxSymbolSize, true, &transfer.Symbol) - transfer.Decimals = p.UnpackByte() - p.UnpackID(false, &transfer.Asset) - transfer.Value = p.UnpackUint64(true) - transfer.Return = p.UnpackBool() - op := p.NewOptionalReader() - transfer.Reward = op.UnpackUint64() // reward not required - transfer.SwapIn = op.UnpackUint64() // optional - op.UnpackID(&transfer.AssetOut) - transfer.SwapOut = op.UnpackUint64() - transfer.SwapExpiry = op.UnpackInt64() - op.Done() - p.UnpackID(true, &transfer.TxID) - p.UnpackID(true, &transfer.DestinationChainID) - if err := p.Err(); err != nil { - return nil, err - } - if !p.Empty() { - return nil, chain.ErrInvalidObject - } - // Handle swap checks - if !ValidSwapParams( - transfer.Value, - transfer.SwapIn, - transfer.AssetOut, - transfer.SwapOut, - transfer.SwapExpiry, - ) { - return nil, chain.ErrInvalidObject - } - return &transfer, nil -} - -func ValidSwapParams( - value uint64, - swapIn uint64, - assetOut ids.ID, - swapOut uint64, - swapExpiry int64, -) bool { - if swapExpiry < 0 { - return false - } - if swapIn > value { - return false - } - if swapIn > 0 { - return swapOut != 0 - } - if assetOut != ids.Empty { - return false - } - if swapOut != 0 { - return false - } - if swapExpiry != 0 { - return false - } - return true -} diff --git a/examples/tokenvm/auth/ed25519.go b/examples/tokenvm/auth/ed25519.go index fc5a088b39..ee78b9a0bc 100644 --- a/examples/tokenvm/auth/ed25519.go +++ b/examples/tokenvm/auth/ed25519.go @@ -6,7 +6,6 @@ package auth import ( "context" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto" @@ -71,7 +70,7 @@ func (d *ED25519) Marshal(p *codec.Packer) { p.PackFixedBytes(d.Signature[:]) } -func UnmarshalED25519(p *codec.Packer, _ *warp.Message) (chain.Auth, error) { +func UnmarshalED25519(p *codec.Packer) (chain.Auth, error) { var d ED25519 signer := d.Signer[:] // avoid allocating additional memory p.UnpackFixedBytes(ed25519.PublicKeyLen, &signer) diff --git a/examples/tokenvm/cmd/token-cli/cmd/action.go b/examples/tokenvm/cmd/token-cli/cmd/action.go index b5e5cfa614..d8cf34fa2c 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/action.go +++ b/examples/tokenvm/cmd/token-cli/cmd/action.go @@ -6,21 +6,13 @@ package cmd import ( "context" - "errors" - "time" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" frpc "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-faucet/rpc" tconsts "github.com/ava-labs/hypersdk/examples/tokenvm/consts" - trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - "github.com/ava-labs/hypersdk/pubsub" - "github.com/ava-labs/hypersdk/rpc" hutils "github.com/ava-labs/hypersdk/utils" "github.com/spf13/cobra" ) @@ -77,7 +69,7 @@ var fundFaucetCmd = &cobra.Command{ if err != nil { return err } - if _, _, err = sendAndWait(ctx, nil, &actions.Transfer{ + if err = sendAndWait(ctx, &actions.Transfer{ To: addr, Asset: ids.Empty, Value: amount, @@ -127,7 +119,7 @@ var transferCmd = &cobra.Command{ } // Generate transaction - _, _, err = sendAndWait(ctx, nil, &actions.Transfer{ + err = sendAndWait(ctx, &actions.Transfer{ To: recipient, Asset: assetID, Value: amount, @@ -170,7 +162,7 @@ var createAssetCmd = &cobra.Command{ } // Generate transaction - _, _, err = sendAndWait(ctx, nil, &actions.CreateAsset{ + err = sendAndWait(ctx, &actions.CreateAsset{ Symbol: []byte(symbol), Decimals: uint8(decimals), // already constrain above to prevent overflow Metadata: []byte(metadata), @@ -193,7 +185,7 @@ var mintAssetCmd = &cobra.Command{ if err != nil { return err } - exists, symbol, decimals, metadata, supply, owner, warp, err := tcli.Asset(ctx, assetID, false) + exists, symbol, decimals, metadata, supply, owner, err := tcli.Asset(ctx, assetID, false) if err != nil { return err } @@ -202,11 +194,6 @@ var mintAssetCmd = &cobra.Command{ hutils.Outf("{{red}}exiting...{{/}}\n") return nil } - if warp { - hutils.Outf("{{red}}cannot mint a warped asset{{/}}\n", assetID) - hutils.Outf("{{red}}exiting...{{/}}\n") - return nil - } if owner != codec.MustAddressBech32(tconsts.HRP, priv.Address) { hutils.Outf("{{red}}%s is the owner of %s, you are not{{/}}\n", owner, assetID) hutils.Outf("{{red}}exiting...{{/}}\n") @@ -239,7 +226,7 @@ var mintAssetCmd = &cobra.Command{ } // Generate transaction - _, _, err = sendAndWait(ctx, nil, &actions.MintAsset{ + err = sendAndWait(ctx, &actions.MintAsset{ Asset: assetID, To: recipient, Value: amount, @@ -276,7 +263,7 @@ var closeOrderCmd = &cobra.Command{ } // Generate transaction - _, _, err = sendAndWait(ctx, nil, &actions.CloseOrder{ + err = sendAndWait(ctx, &actions.CloseOrder{ Order: orderID, Out: outAssetID, }, cli, scli, tcli, factory, true) @@ -298,7 +285,7 @@ var createOrderCmd = &cobra.Command{ if err != nil { return err } - exists, symbol, decimals, metadata, supply, _, warp, err := tcli.Asset(ctx, inAssetID, false) + exists, symbol, decimals, metadata, supply, _, err := tcli.Asset(ctx, inAssetID, false) if err != nil { return err } @@ -309,12 +296,11 @@ var createOrderCmd = &cobra.Command{ return nil } hutils.Outf( - "{{yellow}}symbol:{{/}} %s {{yellow}}decimals:{{/}} %d {{yellow}}metadata:{{/}} %s {{yellow}}supply:{{/}} %d {{yellow}}warp:{{/}} %t\n", + "{{yellow}}symbol:{{/}} %s {{yellow}}decimals:{{/}} %d {{yellow}}metadata:{{/}} %s {{yellow}}supply:{{/}} %d\n", string(symbol), decimals, string(metadata), supply, - warp, ) } @@ -363,7 +349,7 @@ var createOrderCmd = &cobra.Command{ } // Generate transaction - _, _, err = sendAndWait(ctx, nil, &actions.CreateOrder{ + err = sendAndWait(ctx, &actions.CreateOrder{ In: inAssetID, InTick: inTick, Out: outAssetID, @@ -480,7 +466,7 @@ var fillOrderCmd = &cobra.Command{ if err != nil { return err } - _, _, err = sendAndWait(ctx, nil, &actions.FillOrder{ + err = sendAndWait(ctx, &actions.FillOrder{ Order: order.ID, Owner: owner, In: inAssetID, @@ -490,295 +476,3 @@ var fillOrderCmd = &cobra.Command{ return err }, } - -func performImport( - ctx context.Context, - scli *rpc.JSONRPCClient, - dcli *rpc.JSONRPCClient, - dscli *rpc.WebSocketClient, - dtcli *trpc.JSONRPCClient, - exportTxID ids.ID, - factory chain.AuthFactory, -) error { - // Select TxID (if not provided) - var err error - if exportTxID == ids.Empty { - exportTxID, err = handler.Root().PromptID("export txID") - if err != nil { - return err - } - } - - // Generate warp signature (as long as >= 80% stake) - var ( - msg *warp.Message - subnetWeight, sigWeight uint64 - ) - for ctx.Err() == nil { - msg, subnetWeight, sigWeight, err = scli.GenerateAggregateWarpSignature(ctx, exportTxID) - if sigWeight >= (subnetWeight*4)/5 && err == nil { - break - } - if err == nil { - hutils.Outf( - "{{yellow}}waiting for signature weight:{{/}} %d {{yellow}}observed:{{/}} %d\n", - subnetWeight, - sigWeight, - ) - } else { - hutils.Outf("{{red}}encountered error:{{/}} %v\n", err) - } - cont, err := handler.Root().PromptBool("try again") - if err != nil { - return err - } - if !cont { - hutils.Outf("{{red}}exiting...{{/}}\n") - return nil - } - } - if ctx.Err() != nil { - return ctx.Err() - } - wt, err := actions.UnmarshalWarpTransfer(msg.UnsignedMessage.Payload) - if err != nil { - return err - } - outputAssetID := wt.Asset - if !wt.Return { - outputAssetID = actions.ImportedAssetID(wt.Asset, msg.SourceChainID) - } - hutils.Outf( - "%s {{yellow}}to:{{/}} %s {{yellow}}source assetID:{{/}} %s {{yellow}}source symbol:{{/}} %s {{yellow}}output assetID:{{/}} %s {{yellow}}value:{{/}} %s {{yellow}}reward:{{/}} %s {{yellow}}return:{{/}} %t\n", - hutils.ToID( - msg.UnsignedMessage.Payload, - ), - codec.MustAddressBech32(tconsts.HRP, wt.To), - wt.Asset, - wt.Symbol, - outputAssetID, - hutils.FormatBalance(wt.Value, wt.Decimals), - hutils.FormatBalance(wt.Reward, wt.Decimals), - wt.Return, - ) - if wt.SwapIn > 0 { - _, outSymbol, outDecimals, _, _, _, _, err := dtcli.Asset(ctx, wt.AssetOut, false) - if err != nil { - return err - } - hutils.Outf( - "{{yellow}}asset in:{{/}} %s {{yellow}}swap in:{{/}} %s {{yellow}}asset out:{{/}} %s {{yellow}}symbol out:{{/}} %s {{yellow}}swap out:{{/}} %s {{yellow}}swap expiry:{{/}} %d\n", - outputAssetID, - hutils.FormatBalance(wt.SwapIn, wt.Decimals), - wt.AssetOut, - outSymbol, - hutils.FormatBalance(wt.SwapOut, outDecimals), - wt.SwapExpiry, - ) - } - hutils.Outf( - "{{yellow}}signature weight:{{/}} %d {{yellow}}total weight:{{/}} %d\n", - sigWeight, - subnetWeight, - ) - - // Select fill - var fill bool - if wt.SwapIn > 0 { - fill, err = handler.Root().PromptBool("fill") - if err != nil { - return err - } - } - if !fill && wt.SwapExpiry > time.Now().UnixMilli() { - return ErrMustFill - } - - // Generate transaction - _, _, err = sendAndWait(ctx, msg, &actions.ImportAsset{ - Fill: fill, - }, dcli, dscli, dtcli, factory, true) - return err -} - -var importAssetCmd = &cobra.Command{ - Use: "import-asset", - RunE: func(*cobra.Command, []string) error { - ctx := context.Background() - currentChainID, _, factory, dcli, dscli, dtcli, err := handler.DefaultActor() - if err != nil { - return err - } - - // Select source - _, uris, err := handler.Root().PromptChain("sourceChainID", set.Of(currentChainID)) - if err != nil { - return err - } - scli := rpc.NewJSONRPCClient(uris[0]) - - // Perform import - return performImport(ctx, scli, dcli, dscli, dtcli, ids.Empty, factory) - }, -} - -var exportAssetCmd = &cobra.Command{ - Use: "export-asset", - RunE: func(*cobra.Command, []string) error { - ctx := context.Background() - currentChainID, priv, factory, cli, scli, tcli, err := handler.DefaultActor() - if err != nil { - return err - } - - // Select token to send - assetID, err := handler.Root().PromptAsset("assetID", true) - if err != nil { - return err - } - _, decimals, balance, sourceChainID, err := handler.GetAssetInfo(ctx, tcli, priv.Address, assetID, true) - if balance == 0 || err != nil { - return err - } - - // Select recipient - recipient, err := handler.Root().PromptAddress("recipient") - if err != nil { - return err - } - - // Select amount - amount, err := handler.Root().PromptAmount("amount", decimals, balance, nil) - if err != nil { - return err - } - - // Determine return - var ret bool - if sourceChainID != ids.Empty { - ret = true - } - - // Select reward - reward, err := handler.Root().PromptAmount("reward", decimals, balance-amount, nil) - if err != nil { - return err - } - - // Determine destination - destination := sourceChainID - if !ret { - destination, _, err = handler.Root().PromptChain("destination", set.Of(currentChainID)) - if err != nil { - return err - } - } - - // Determine if swap in - swap, err := handler.Root().PromptBool("swap on import") - if err != nil { - return err - } - var ( - swapIn uint64 - assetOut ids.ID - swapOut uint64 - swapExpiry int64 - ) - if swap { - swapIn, err = handler.Root().PromptAmount("swap in", decimals, amount, nil) - if err != nil { - return err - } - assetOut, err = handler.Root().PromptAsset("asset out (on destination)", true) - if err != nil { - return err - } - uris, err := handler.Root().GetChain(destination) - if err != nil { - return err - } - networkID, _, _, err := cli.Network(ctx) - if err != nil { - return err - } - dcli := trpc.NewJSONRPCClient(uris[0], networkID, destination) - _, decimals, _, _, err := handler.GetAssetInfo(ctx, dcli, priv.Address, assetOut, false) - if err != nil { - return err - } - swapOut, err = handler.Root().PromptAmount( - "swap out (on destination, no decimals)", - decimals, - consts.MaxUint64, - nil, - ) - if err != nil { - return err - } - swapExpiry, err = handler.Root().PromptTime("swap expiry") - if err != nil { - return err - } - } - - // Confirm action - cont, err := handler.Root().PromptContinue() - if !cont || err != nil { - return err - } - - // Generate transaction - success, txID, err := sendAndWait(ctx, nil, &actions.ExportAsset{ - To: recipient, - Asset: assetID, - Value: amount, - Return: ret, - Reward: reward, - SwapIn: swapIn, - AssetOut: assetOut, - SwapOut: swapOut, - SwapExpiry: swapExpiry, - Destination: destination, - }, cli, scli, tcli, factory, true) - if err != nil { - return err - } - if !success { - return errors.New("not successful") - } - - // Perform import - imp, err := handler.Root().PromptBool("perform import on destination") - if err != nil { - return err - } - if imp { - uris, err := handler.Root().GetChain(destination) - if err != nil { - return err - } - networkID, _, _, err := cli.Network(ctx) - if err != nil { - return err - } - dscli, err := rpc.NewWebSocketClient(uris[0], rpc.DefaultHandshakeTimeout, pubsub.MaxPendingMessages, pubsub.MaxReadMessageSize) - if err != nil { - return err - } - if err := performImport(ctx, cli, rpc.NewJSONRPCClient(uris[0]), dscli, trpc.NewJSONRPCClient(uris[0], networkID, destination), txID, factory); err != nil { - return err - } - } - - // Ask if user would like to switch to destination chain - sw, err := handler.Root().PromptBool("switch default chain to destination") - if err != nil { - return err - } - if !sw { - return nil - } - return handler.Root().StoreDefaultChain(destination) - }, -} diff --git a/examples/tokenvm/cmd/token-cli/cmd/handler.go b/examples/tokenvm/cmd/token-cli/cmd/handler.go index c3fee8c16c..697189d4be 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/handler.go +++ b/examples/tokenvm/cmd/token-cli/cmd/handler.go @@ -10,7 +10,6 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/cli" "github.com/ava-labs/hypersdk/codec" - hconsts "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/examples/tokenvm/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/consts" @@ -42,7 +41,7 @@ func (*Handler) GetAssetInfo( checkBalance bool, ) ([]byte, uint8, uint64, ids.ID, error) { var sourceChainID ids.ID - exists, symbol, decimals, metadata, supply, _, warp, err := cli.Asset(ctx, assetID, false) + exists, symbol, decimals, metadata, supply, _, err := cli.Asset(ctx, assetID, false) if err != nil { return nil, 0, 0, ids.Empty, err } @@ -52,25 +51,13 @@ func (*Handler) GetAssetInfo( hutils.Outf("{{red}}exiting...{{/}}\n") return nil, 0, 0, ids.Empty, nil } - if warp { - sourceChainID = ids.ID(metadata[hconsts.IDLen:]) - sourceAssetID := ids.ID(metadata[:hconsts.IDLen]) - hutils.Outf( - "{{yellow}}sourceChainID:{{/}} %s {{yellow}}sourceAssetID:{{/}} %s {{yellow}}supply:{{/}} %d\n", - sourceChainID, - sourceAssetID, - supply, - ) - } else { - hutils.Outf( - "{{yellow}}symbol:{{/}} %s {{yellow}}decimals:{{/}} %d {{yellow}}metadata:{{/}} %s {{yellow}}supply:{{/}} %d {{yellow}}warp:{{/}} %t\n", - symbol, - decimals, - metadata, - supply, - warp, - ) - } + hutils.Outf( + "{{yellow}}symbol:{{/}} %s {{yellow}}decimals:{{/}} %d {{yellow}}metadata:{{/}} %s {{yellow}}supply:{{/}} %d\n", + symbol, + decimals, + metadata, + supply, + ) } if !checkBalance { return symbol, decimals, 0, sourceChainID, nil diff --git a/examples/tokenvm/cmd/token-cli/cmd/resolutions.go b/examples/tokenvm/cmd/token-cli/cmd/resolutions.go index eb62abfbb2..598151c7c9 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/resolutions.go +++ b/examples/tokenvm/cmd/token-cli/cmd/resolutions.go @@ -8,8 +8,6 @@ import ( "fmt" "reflect" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/cli" "github.com/ava-labs/hypersdk/codec" @@ -22,29 +20,29 @@ import ( // sendAndWait may not be used concurrently func sendAndWait( - ctx context.Context, warpMsg *warp.Message, action chain.Action, cli *rpc.JSONRPCClient, + ctx context.Context, action chain.Action, cli *rpc.JSONRPCClient, scli *rpc.WebSocketClient, tcli *trpc.JSONRPCClient, factory chain.AuthFactory, printStatus bool, -) (bool, ids.ID, error) { +) error { parser, err := tcli.Parser(ctx) if err != nil { - return false, ids.Empty, err + return err } - _, tx, _, err := cli.GenerateTransaction(ctx, parser, warpMsg, action, factory) + _, tx, _, err := cli.GenerateTransaction(ctx, parser, action, factory) if err != nil { - return false, ids.Empty, err + return err } if err := scli.RegisterTx(tx); err != nil { - return false, ids.Empty, err + return err } var res *chain.Result for { txID, dErr, result, err := scli.ListenTx(ctx) if dErr != nil { - return false, ids.Empty, dErr + return dErr } if err != nil { - return false, ids.Empty, err + return err } if txID == tx.ID() { res = result @@ -55,7 +53,7 @@ func sendAndWait( if printStatus { handler.Root().PrintStatus(tx.ID(), res.Success) } - return res.Success, tx.ID(), nil + return nil } func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result) { @@ -68,7 +66,7 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result case *actions.CreateAsset: summaryStr = fmt.Sprintf("assetID: %s symbol: %s decimals: %d metadata: %s", tx.ID(), action.Symbol, action.Decimals, action.Metadata) case *actions.MintAsset: - _, symbol, decimals, _, _, _, _, err := c.Asset(context.TODO(), action.Asset, true) + _, symbol, decimals, _, _, _, err := c.Asset(context.TODO(), action.Asset, true) if err != nil { utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) return @@ -79,7 +77,7 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result summaryStr = fmt.Sprintf("%d %s -> 🔥", action.Value, action.Asset) case *actions.Transfer: - _, symbol, decimals, _, _, _, _, err := c.Asset(context.TODO(), action.Asset, true) + _, symbol, decimals, _, _, _, err := c.Asset(context.TODO(), action.Asset, true) if err != nil { utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) return @@ -91,13 +89,13 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result } case *actions.CreateOrder: - _, inSymbol, inDecimals, _, _, _, _, err := c.Asset(context.TODO(), action.In, true) + _, inSymbol, inDecimals, _, _, _, err := c.Asset(context.TODO(), action.In, true) if err != nil { utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) return } inTickStr := utils.FormatBalance(action.InTick, inDecimals) - _, outSymbol, outDecimals, _, _, _, _, err := c.Asset(context.TODO(), action.Out, true) + _, outSymbol, outDecimals, _, _, _, err := c.Asset(context.TODO(), action.Out, true) if err != nil { utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) return @@ -107,13 +105,13 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result summaryStr = fmt.Sprintf("%s %s -> %s %s (supply: %s %s)", inTickStr, inSymbol, outTickStr, outSymbol, supplyStr, outSymbol) case *actions.FillOrder: or, _ := actions.UnmarshalOrderResult(result.Output) - _, inSymbol, inDecimals, _, _, _, _, err := c.Asset(context.TODO(), action.In, true) + _, inSymbol, inDecimals, _, _, _, err := c.Asset(context.TODO(), action.In, true) if err != nil { utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) return } inAmtStr := utils.FormatBalance(or.In, inDecimals) - _, outSymbol, outDecimals, _, _, _, _, err := c.Asset(context.TODO(), action.Out, true) + _, outSymbol, outDecimals, _, _, _, err := c.Asset(context.TODO(), action.Out, true) if err != nil { utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) return @@ -126,50 +124,6 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result ) case *actions.CloseOrder: summaryStr = fmt.Sprintf("orderID: %s", action.Order) - - case *actions.ImportAsset: - wm := tx.WarpMessage - signers, _ := wm.Signature.NumSigners() - wt, _ := actions.UnmarshalWarpTransfer(wm.Payload) - summaryStr = fmt.Sprintf("source: %s signers: %d | ", wm.SourceChainID, signers) - if wt.Return { - summaryStr += fmt.Sprintf("%s %s -> %s (return: %t)", utils.FormatBalance(wt.Value, wt.Decimals), wt.Symbol, codec.MustAddressBech32(tconsts.HRP, wt.To), wt.Return) - } else { - summaryStr += fmt.Sprintf("%s %s (new: %s, original: %s) -> %s (return: %t)", utils.FormatBalance(wt.Value, wt.Decimals), wt.Symbol, actions.ImportedAssetID(wt.Asset, wm.SourceChainID), wt.Asset, codec.MustAddressBech32(tconsts.HRP, wt.To), wt.Return) - } - if wt.Reward > 0 { - summaryStr += fmt.Sprintf(" | reward: %s", utils.FormatBalance(wt.Reward, wt.Decimals)) - } - if wt.SwapIn > 0 { - _, outSymbol, outDecimals, _, _, _, _, err := c.Asset(context.TODO(), wt.AssetOut, true) - if err != nil { - utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) - return - } - summaryStr += fmt.Sprintf(" | swap in: %s %s swap out: %s %s expiry: %d fill: %t", utils.FormatBalance(wt.SwapIn, wt.Decimals), wt.Symbol, utils.FormatBalance(wt.SwapOut, outDecimals), outSymbol, wt.SwapExpiry, action.Fill) - } - case *actions.ExportAsset: - wt, _ := actions.UnmarshalWarpTransfer(result.WarpMessage.Payload) - summaryStr = fmt.Sprintf("destination: %s | ", action.Destination) - var outputAssetID ids.ID - if !action.Return { - outputAssetID = actions.ImportedAssetID(action.Asset, result.WarpMessage.SourceChainID) - summaryStr += fmt.Sprintf("%s %s (%s) -> %s (return: %t)", utils.FormatBalance(action.Value, wt.Decimals), wt.Symbol, action.Asset, codec.MustAddressBech32(tconsts.HRP, action.To), action.Return) - } else { - outputAssetID = wt.Asset - summaryStr += fmt.Sprintf("%s %s (current: %s, original: %s) -> %s (return: %t)", utils.FormatBalance(action.Value, wt.Decimals), wt.Symbol, action.Asset, wt.Asset, codec.MustAddressBech32(tconsts.HRP, action.To), action.Return) - } - if wt.Reward > 0 { - summaryStr += fmt.Sprintf(" | reward: %s", utils.FormatBalance(wt.Reward, wt.Decimals)) - } - if wt.SwapIn > 0 { - _, outSymbol, outDecimals, _, _, _, _, err := c.Asset(context.TODO(), wt.AssetOut, true) - if err != nil { - utils.Outf("{{red}}could not fetch asset info:{{/}} %v", err) - return - } - summaryStr += fmt.Sprintf(" | swap in: %s %s (%s) swap out: %s %s expiry: %d", utils.FormatBalance(wt.SwapIn, wt.Decimals), wt.Symbol, outputAssetID, utils.FormatBalance(wt.SwapOut, outDecimals), outSymbol, wt.SwapExpiry) - } } } utils.Outf( diff --git a/examples/tokenvm/cmd/token-cli/cmd/root.go b/examples/tokenvm/cmd/token-cli/cmd/root.go index e49a5e3822..e55fd77b53 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/root.go +++ b/examples/tokenvm/cmd/token-cli/cmd/root.go @@ -162,9 +162,6 @@ func init() { createOrderCmd, fillOrderCmd, closeOrderCmd, - - importAssetCmd, - exportAssetCmd, ) // spam diff --git a/examples/tokenvm/cmd/token-cli/cmd/spam.go b/examples/tokenvm/cmd/token-cli/cmd/spam.go index 3360ecd99a..f3bf3b6660 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/spam.go +++ b/examples/tokenvm/cmd/token-cli/cmd/spam.go @@ -87,11 +87,10 @@ var runSpamCmd = &cobra.Command{ }, func(cli *rpc.JSONRPCClient, priv *cli.PrivateKey) func(context.Context, uint64) error { // submitDummy return func(ictx context.Context, count uint64) error { - _, _, err := sendAndWait(ictx, nil, &actions.Transfer{ + return sendAndWait(ictx, &actions.Transfer{ To: priv.Address, Value: count, // prevent duplicate txs }, cli, sclient, tclient, auth.NewED25519Factory(ed25519.PrivateKey(priv.Bytes)), false) - return err } }, ) diff --git a/examples/tokenvm/cmd/token-faucet/manager/manager.go b/examples/tokenvm/cmd/token-faucet/manager/manager.go index b445faad52..0c6586f518 100644 --- a/examples/tokenvm/cmd/token-faucet/manager/manager.go +++ b/examples/tokenvm/cmd/token-faucet/manager/manager.go @@ -122,7 +122,7 @@ func (m *Manager) sendFunds(ctx context.Context, destination codec.Address, amou if err != nil { return ids.Empty, 0, err } - submit, tx, maxFee, err := m.cli.GenerateTransaction(ctx, parser, nil, &actions.Transfer{ + submit, tx, maxFee, err := m.cli.GenerateTransaction(ctx, parser, &actions.Transfer{ To: destination, Asset: ids.Empty, Value: amount, diff --git a/examples/tokenvm/cmd/token-wallet/backend/backend.go b/examples/tokenvm/cmd/token-wallet/backend/backend.go index 2b8e8596f1..61c1fa3608 100644 --- a/examples/tokenvm/cmd/token-wallet/backend/backend.go +++ b/examples/tokenvm/cmd/token-wallet/backend/backend.go @@ -226,7 +226,7 @@ func (b *Backend) collectBlocks() { continue } - _, symbol, decimals, _, _, owner, _, err := b.tcli.Asset(b.ctx, action.Asset, true) + _, symbol, decimals, _, _, owner, err := b.tcli.Asset(b.ctx, action.Asset, true) if err != nil { b.fatal(err) return @@ -309,7 +309,7 @@ func (b *Backend) collectBlocks() { continue } - _, symbol, decimals, _, _, owner, _, err := b.tcli.Asset(b.ctx, action.Asset, true) + _, symbol, decimals, _, _, owner, err := b.tcli.Asset(b.ctx, action.Asset, true) if err != nil { b.fatal(err) return @@ -361,12 +361,12 @@ func (b *Backend) collectBlocks() { continue } - _, inSymbol, inDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, action.In, true) + _, inSymbol, inDecimals, _, _, _, err := b.tcli.Asset(b.ctx, action.In, true) if err != nil { b.fatal(err) return } - _, outSymbol, outDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, action.Out, true) + _, outSymbol, outDecimals, _, _, _, err := b.tcli.Asset(b.ctx, action.Out, true) if err != nil { b.fatal(err) return @@ -402,12 +402,12 @@ func (b *Backend) collectBlocks() { continue } - _, inSymbol, inDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, action.In, true) + _, inSymbol, inDecimals, _, _, _, err := b.tcli.Asset(b.ctx, action.In, true) if err != nil { b.fatal(err) return } - _, outSymbol, outDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, action.Out, true) + _, outSymbol, outDecimals, _, _, _, err := b.tcli.Asset(b.ctx, action.Out, true) if err != nil { b.fatal(err) return @@ -608,7 +608,7 @@ func (b *Backend) GetMyAssets() []*AssetInfo { if !owned[i] { continue } - _, symbol, decimals, metadata, supply, owner, _, err := b.tcli.Asset(b.ctx, asset, false) + _, symbol, decimals, metadata, supply, owner, err := b.tcli.Asset(b.ctx, asset, false) if err != nil { b.fatal(err) return nil @@ -639,7 +639,7 @@ func (b *Backend) CreateAsset(symbol string, decimals string, metadata string) e if err != nil { return err } - _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, nil, &actions.CreateAsset{ + _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, &actions.CreateAsset{ Symbol: []byte(symbol), Decimals: uint8(udecimals), Metadata: []byte(metadata), @@ -674,7 +674,7 @@ func (b *Backend) MintAsset(asset string, address string, amount string) error { if err != nil { return err } - _, _, decimals, _, _, _, _, err := b.tcli.Asset(b.ctx, assetID, true) + _, _, decimals, _, _, _, err := b.tcli.Asset(b.ctx, assetID, true) if err != nil { return err } @@ -694,7 +694,7 @@ func (b *Backend) MintAsset(asset string, address string, amount string) error { } // Generate transaction - _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, nil, &actions.MintAsset{ + _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, &actions.MintAsset{ To: to, Asset: assetID, Value: value, @@ -729,7 +729,7 @@ func (b *Backend) Transfer(asset string, address string, amount string, memo str if err != nil { return err } - _, symbol, decimals, _, _, _, _, err := b.tcli.Asset(b.ctx, assetID, true) + _, symbol, decimals, _, _, _, err := b.tcli.Asset(b.ctx, assetID, true) if err != nil { return err } @@ -758,7 +758,7 @@ func (b *Backend) Transfer(asset string, address string, amount string, memo str } // Generate transaction - _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, nil, &actions.Transfer{ + _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, &actions.Transfer{ To: to, Asset: assetID, Value: value, @@ -805,7 +805,7 @@ func (b *Backend) GetBalance() ([]*BalanceInfo, error) { } balances := []*BalanceInfo{} for _, asset := range assets { - _, symbol, decimals, _, _, _, _, err := b.tcli.Asset(b.ctx, asset, true) + _, symbol, decimals, _, _, _, err := b.tcli.Asset(b.ctx, asset, true) if err != nil { return nil, err } @@ -937,7 +937,7 @@ func (b *Backend) GetAllAssets() []*AssetInfo { } assets := []*AssetInfo{} for _, asset := range arr { - _, symbol, decimals, metadata, supply, owner, _, err := b.tcli.Asset(b.ctx, asset, false) + _, symbol, decimals, metadata, supply, owner, err := b.tcli.Asset(b.ctx, asset, false) if err != nil { b.fatal(err) return nil @@ -968,7 +968,7 @@ func (b *Backend) AddAsset(asset string) error { if hasAsset { return nil } - exists, _, _, _, _, owner, _, err := b.tcli.Asset(b.ctx, assetID, true) + exists, _, _, _, _, owner, err := b.tcli.Asset(b.ctx, assetID, true) if err != nil { return err } @@ -993,12 +993,12 @@ func (b *Backend) GetMyOrders() ([]*Order, error) { continue } inID := order.InAsset - _, inSymbol, inDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) + _, inSymbol, inDecimals, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) if err != nil { return nil, err } outID := order.OutAsset - _, outSymbol, outDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, outID, true) + _, outSymbol, outDecimals, _, _, _, err := b.tcli.Asset(b.ctx, outID, true) if err != nil { return nil, err } @@ -1035,7 +1035,7 @@ func (b *Backend) GetOrders(pair string) ([]*Order, error) { if err != nil { return nil, err } - _, inSymbol, inDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) + _, inSymbol, inDecimals, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) if err != nil { return nil, err } @@ -1044,7 +1044,7 @@ func (b *Backend) GetOrders(pair string) ([]*Order, error) { if err != nil { return nil, err } - _, outSymbol, outDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, outID, true) + _, outSymbol, outDecimals, _, _, _, err := b.tcli.Asset(b.ctx, outID, true) if err != nil { return nil, err } @@ -1080,11 +1080,11 @@ func (b *Backend) CreateOrder(assetIn string, inTick string, assetOut string, ou if err != nil { return err } - _, _, inDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) + _, _, inDecimals, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) if err != nil { return err } - _, outSymbol, outDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, outID, true) + _, outSymbol, outDecimals, _, _, _, err := b.tcli.Asset(b.ctx, outID, true) if err != nil { return err } @@ -1112,7 +1112,7 @@ func (b *Backend) CreateOrder(assetIn string, inTick string, assetOut string, ou } // Generate transaction - _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, nil, &actions.CreateOrder{ + _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, &actions.CreateOrder{ In: inID, InTick: iTick, Out: outID, @@ -1171,7 +1171,7 @@ func (b *Backend) FillOrder(orderID string, orderOwner string, assetIn string, i if err != nil { return err } - _, inSymbol, inDecimals, _, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) + _, inSymbol, inDecimals, _, _, _, err := b.tcli.Asset(b.ctx, inID, true) if err != nil { return err } @@ -1198,7 +1198,7 @@ func (b *Backend) FillOrder(orderID string, orderOwner string, assetIn string, i } // Generate transaction - _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, nil, &actions.FillOrder{ + _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, &actions.FillOrder{ Order: oID, Owner: owner, In: inID, @@ -1255,7 +1255,7 @@ func (b *Backend) CloseOrder(orderID string, assetOut string) error { } // Generate transaction - _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, nil, &actions.CloseOrder{ + _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, &actions.CloseOrder{ Order: oID, Out: outID, }, b.factory) @@ -1395,7 +1395,7 @@ func (b *Backend) Message(message string, url string) error { } // Generate transaction - _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, nil, &actions.Transfer{ + _, tx, maxFee, err := b.cli.GenerateTransaction(b.ctx, b.parser, &actions.Transfer{ To: recipientAddr, Asset: ids.Empty, Value: fee, diff --git a/examples/tokenvm/consts/consts.go b/examples/tokenvm/consts/consts.go index e188cad88d..b221ebddfc 100644 --- a/examples/tokenvm/consts/consts.go +++ b/examples/tokenvm/consts/consts.go @@ -5,7 +5,6 @@ package consts import ( "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -33,6 +32,6 @@ func init() { // Instantiate registry here so it can be imported by any package. We set these // values in [controller/registry]. var ( - ActionRegistry *codec.TypeParser[chain.Action, *warp.Message, bool] - AuthRegistry *codec.TypeParser[chain.Auth, *warp.Message, bool] + ActionRegistry *codec.TypeParser[chain.Action, bool] + AuthRegistry *codec.TypeParser[chain.Auth, bool] ) diff --git a/examples/tokenvm/controller/controller.go b/examples/tokenvm/controller/controller.go index e5eecb4196..a84dcdc5d4 100644 --- a/examples/tokenvm/controller/controller.go +++ b/examples/tokenvm/controller/controller.go @@ -206,10 +206,6 @@ func (c *Controller) Accepted(ctx context.Context, blk *chain.StatelessBlock) er case *actions.CloseOrder: c.metrics.closeOrder.Inc() c.orderBook.Remove(action.Order) - case *actions.ImportAsset: - c.metrics.importAsset.Inc() - case *actions.ExportAsset: - c.metrics.exportAsset.Inc() } } } diff --git a/examples/tokenvm/controller/resolutions.go b/examples/tokenvm/controller/resolutions.go index a20510b9bc..ccdd47882e 100644 --- a/examples/tokenvm/controller/resolutions.go +++ b/examples/tokenvm/controller/resolutions.go @@ -38,7 +38,7 @@ func (c *Controller) GetTransaction( func (c *Controller) GetAssetFromState( ctx context.Context, asset ids.ID, -) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, error) { return storage.GetAssetFromState(ctx, c.inner.ReadState, asset) } diff --git a/examples/tokenvm/controller/state_manager.go b/examples/tokenvm/controller/state_manager.go index c3cfb34993..3a1fe82dce 100644 --- a/examples/tokenvm/controller/state_manager.go +++ b/examples/tokenvm/controller/state_manager.go @@ -29,14 +29,6 @@ func (*StateManager) FeeKey() []byte { return storage.FeeKey() } -func (*StateManager) IncomingWarpKeyPrefix(sourceChainID ids.ID, msgID ids.ID) []byte { - return storage.IncomingWarpKeyPrefix(sourceChainID, msgID) -} - -func (*StateManager) OutgoingWarpKeyPrefix(txID ids.ID) []byte { - return storage.OutgoingWarpKeyPrefix(txID) -} - func (*StateManager) SponsorStateKeys(addr codec.Address) state.Keys { return state.Keys{ string(storage.BalanceKey(addr, ids.Empty)): state.Read | state.Write, diff --git a/examples/tokenvm/genesis/genesis.go b/examples/tokenvm/genesis/genesis.go index 23f45e91c8..4632bd332f 100644 --- a/examples/tokenvm/genesis/genesis.go +++ b/examples/tokenvm/genesis/genesis.go @@ -48,9 +48,6 @@ type Genesis struct { // Tx Fee Parameters BaseComputeUnits uint64 `json:"baseUnits"` - BaseWarpComputeUnits uint64 `json:"baseWarpUnits"` - WarpComputeUnitsPerSigner uint64 `json:"warpUnitsPerSigner"` - OutgoingWarpComputeUnits uint64 `json:"outgoingWarpComputeUnits"` StorageKeyReadUnits uint64 `json:"storageKeyReadUnits"` StorageValueReadUnits uint64 `json:"storageValueReadUnits"` // per chunk StorageKeyAllocateUnits uint64 `json:"storageKeyAllocateUnits"` @@ -81,10 +78,7 @@ func Default() *Genesis { ValidityWindow: 60 * hconsts.MillisecondsPerSecond, // ms // Tx Fee Compute Parameters - BaseComputeUnits: 1, - BaseWarpComputeUnits: 1_024, - WarpComputeUnitsPerSigner: 128, - OutgoingWarpComputeUnits: 1_024, + BaseComputeUnits: 1, // Tx Fee Storage Parameters // @@ -139,7 +133,6 @@ func (g *Genesis) Load(ctx context.Context, tracer trace.Tracer, mu state.Mutabl []byte(consts.Name), supply, codec.EmptyAddress, - false, ) } diff --git a/examples/tokenvm/genesis/rules.go b/examples/tokenvm/genesis/rules.go index b2fac9c3f5..c787c5ac6f 100644 --- a/examples/tokenvm/genesis/rules.go +++ b/examples/tokenvm/genesis/rules.go @@ -24,14 +24,6 @@ func (g *Genesis) Rules(_ int64, networkID uint32, chainID ids.ID) *Rules { return &Rules{g, networkID, chainID} } -func (*Rules) GetWarpConfig(ids.ID) (bool, uint64, uint64) { - // We allow inbound transfers from all sources as long as 80% of stake has - // signed a message. - // - // This is safe because the tokenvm scopes all assets by their source chain. - return true, 4, 5 -} - func (r *Rules) NetworkID() uint32 { return r.networkID } @@ -60,18 +52,6 @@ func (r *Rules) GetBaseComputeUnits() uint64 { return r.g.BaseComputeUnits } -func (r *Rules) GetBaseWarpComputeUnits() uint64 { - return r.g.BaseWarpComputeUnits -} - -func (r *Rules) GetWarpComputeUnitsPerSigner() uint64 { - return r.g.WarpComputeUnitsPerSigner -} - -func (r *Rules) GetOutgoingWarpComputeUnits() uint64 { - return r.g.OutgoingWarpComputeUnits -} - func (*Rules) GetSponsorStateKeysMaxChunks() []uint16 { return []uint16{storage.BalanceChunks} } diff --git a/examples/tokenvm/registry/registry.go b/examples/tokenvm/registry/registry.go index be08862f60..b330518a5d 100644 --- a/examples/tokenvm/registry/registry.go +++ b/examples/tokenvm/registry/registry.go @@ -5,7 +5,6 @@ package registry import ( "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" @@ -16,8 +15,8 @@ import ( // Setup types func init() { - consts.ActionRegistry = codec.NewTypeParser[chain.Action, *warp.Message]() - consts.AuthRegistry = codec.NewTypeParser[chain.Auth, *warp.Message]() + consts.ActionRegistry = codec.NewTypeParser[chain.Action]() + consts.AuthRegistry = codec.NewTypeParser[chain.Auth]() errs := &wrappers.Errs{} errs.Add( @@ -32,9 +31,6 @@ func init() { consts.ActionRegistry.Register((&actions.FillOrder{}).GetTypeID(), actions.UnmarshalFillOrder, false), consts.ActionRegistry.Register((&actions.CloseOrder{}).GetTypeID(), actions.UnmarshalCloseOrder, false), - consts.ActionRegistry.Register((&actions.ImportAsset{}).GetTypeID(), actions.UnmarshalImportAsset, true), - consts.ActionRegistry.Register((&actions.ExportAsset{}).GetTypeID(), actions.UnmarshalExportAsset, false), - // When registering new auth, ALWAYS make sure to append at the end. consts.AuthRegistry.Register((&auth.ED25519{}).GetTypeID(), auth.UnmarshalED25519, false), ) diff --git a/examples/tokenvm/rpc/dependencies.go b/examples/tokenvm/rpc/dependencies.go index dd07ad7c5c..fd43a854fa 100644 --- a/examples/tokenvm/rpc/dependencies.go +++ b/examples/tokenvm/rpc/dependencies.go @@ -18,7 +18,7 @@ type Controller interface { Genesis() *genesis.Genesis Tracer() trace.Tracer GetTransaction(context.Context, ids.ID) (bool, int64, bool, fees.Dimensions, uint64, error) - GetAssetFromState(context.Context, ids.ID) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) + GetAssetFromState(context.Context, ids.ID) (bool, []byte, uint8, []byte, uint64, codec.Address, error) GetBalanceFromState(context.Context, codec.Address, ids.ID) (uint64, error) Orders(pair string, limit int) []*orderbook.Order GetOrderFromState(context.Context, ids.ID) ( diff --git a/examples/tokenvm/rpc/jsonrpc_client.go b/examples/tokenvm/rpc/jsonrpc_client.go index a4916c3a03..934ef3700f 100644 --- a/examples/tokenvm/rpc/jsonrpc_client.go +++ b/examples/tokenvm/rpc/jsonrpc_client.go @@ -86,12 +86,12 @@ func (cli *JSONRPCClient) Asset( ctx context.Context, asset ids.ID, useCache bool, -) (bool, []byte, uint8, []byte, uint64, string, bool, error) { +) (bool, []byte, uint8, []byte, uint64, string, error) { cli.assetsL.Lock() r, ok := cli.assets[asset] cli.assetsL.Unlock() if ok && useCache { - return true, r.Symbol, r.Decimals, r.Metadata, r.Supply, r.Owner, r.Warp, nil + return true, r.Symbol, r.Decimals, r.Metadata, r.Supply, r.Owner, nil } resp := new(AssetReply) err := cli.requester.SendRequest( @@ -106,14 +106,14 @@ func (cli *JSONRPCClient) Asset( // We use string parsing here because the JSON-RPC library we use may not // allows us to perform errors.Is. case err != nil && strings.Contains(err.Error(), ErrAssetNotFound.Error()): - return false, nil, 0, nil, 0, "", false, nil + return false, nil, 0, nil, 0, "", nil case err != nil: - return false, nil, 0, nil, 0, "", false, err + return false, nil, 0, nil, 0, "", err } cli.assetsL.Lock() cli.assets[asset] = resp cli.assetsL.Unlock() - return true, resp.Symbol, resp.Decimals, resp.Metadata, resp.Supply, resp.Owner, resp.Warp, nil + return true, resp.Symbol, resp.Decimals, resp.Metadata, resp.Supply, resp.Owner, nil } func (cli *JSONRPCClient) Balance(ctx context.Context, addr string, asset ids.ID) (uint64, error) { @@ -180,7 +180,7 @@ func (cli *JSONRPCClient) WaitForBalance( asset ids.ID, min uint64, ) error { - exists, symbol, decimals, _, _, _, _, err := cli.Asset(ctx, asset, true) + exists, symbol, decimals, _, _, _, err := cli.Asset(ctx, asset, true) if err != nil { return err } diff --git a/examples/tokenvm/rpc/jsonrpc_server.go b/examples/tokenvm/rpc/jsonrpc_server.go index fd94f5a94c..da64956056 100644 --- a/examples/tokenvm/rpc/jsonrpc_server.go +++ b/examples/tokenvm/rpc/jsonrpc_server.go @@ -71,14 +71,13 @@ type AssetReply struct { Metadata []byte `json:"metadata"` Supply uint64 `json:"supply"` Owner string `json:"owner"` - Warp bool `json:"warp"` } func (j *JSONRPCServer) Asset(req *http.Request, args *AssetArgs, reply *AssetReply) error { ctx, span := j.c.Tracer().Start(req.Context(), "Server.Asset") defer span.End() - exists, symbol, decimals, metadata, supply, owner, warp, err := j.c.GetAssetFromState(ctx, args.Asset) + exists, symbol, decimals, metadata, supply, owner, err := j.c.GetAssetFromState(ctx, args.Asset) if err != nil { return err } @@ -90,7 +89,6 @@ func (j *JSONRPCServer) Asset(req *http.Request, args *AssetArgs, reply *AssetRe reply.Metadata = metadata reply.Supply = supply reply.Owner = codec.MustAddressBech32(consts.HRP, owner) - reply.Warp = warp return err } diff --git a/examples/tokenvm/storage/storage.go b/examples/tokenvm/storage/storage.go index 2dcf9cb364..e8382a8e16 100644 --- a/examples/tokenvm/storage/storage.go +++ b/examples/tokenvm/storage/storage.go @@ -30,7 +30,7 @@ type ReadState func(context.Context, [][]byte) ([][]byte, []error) // 0x0/ (balance) // -> [owner|asset] => balance // 0x1/ (assets) -// -> [asset] => metadataLen|metadata|supply|owner|warp +// -> [asset] => metadataLen|metadata|supply|owner // 0x2/ (orders) // -> [txID] => in|out|rate|remaining|owner // 0x3/ (loans) @@ -38,23 +38,19 @@ type ReadState func(context.Context, [][]byte) ([][]byte, []error) // 0x4/ (hypersdk-height) // 0x5/ (hypersdk-timestamp) // 0x6/ (hypersdk-fee) -// 0x7/ (hypersdk-incoming warp) -// 0x8/ (hypersdk-outgoing warp) const ( // metaDB txPrefix = 0x0 // stateDB - balancePrefix = 0x0 - assetPrefix = 0x1 - orderPrefix = 0x2 - loanPrefix = 0x3 - heightPrefix = 0x4 - timestampPrefix = 0x5 - feePrefix = 0x6 - incomingWarpPrefix = 0x7 - outgoingWarpPrefix = 0x8 + balancePrefix = 0x0 + assetPrefix = 0x1 + orderPrefix = 0x2 + loanPrefix = 0x3 + heightPrefix = 0x4 + timestampPrefix = 0x5 + feePrefix = 0x6 ) const ( @@ -298,7 +294,7 @@ func GetAssetFromState( ctx context.Context, f ReadState, asset ids.ID, -) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, error) { values, errs := f(ctx, [][]byte{AssetKey(asset)}) return innerGetAsset(values[0], errs[0]) } @@ -307,7 +303,7 @@ func GetAsset( ctx context.Context, im state.Immutable, asset ids.ID, -) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, error) { k := AssetKey(asset) return innerGetAsset(im.GetValue(ctx, k)) } @@ -315,12 +311,12 @@ func GetAsset( func innerGetAsset( v []byte, err error, -) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, error) { if errors.Is(err, database.ErrNotFound) { - return false, nil, 0, nil, 0, codec.EmptyAddress, false, nil + return false, nil, 0, nil, 0, codec.EmptyAddress, nil } if err != nil { - return false, nil, 0, nil, 0, codec.EmptyAddress, false, err + return false, nil, 0, nil, 0, codec.EmptyAddress, err } symbolLen := binary.BigEndian.Uint16(v) symbol := v[consts.Uint16Len : consts.Uint16Len+symbolLen] @@ -330,8 +326,7 @@ func innerGetAsset( supply := binary.BigEndian.Uint64(v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen:]) var addr codec.Address copy(addr[:], v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len:]) - warp := v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+codec.AddressLen] == 0x1 - return true, symbol, decimals, metadata, supply, addr, warp, nil + return true, symbol, decimals, metadata, supply, addr, nil } func SetAsset( @@ -343,12 +338,11 @@ func SetAsset( metadata []byte, supply uint64, owner codec.Address, - warp bool, ) error { k := AssetKey(asset) symbolLen := len(symbol) metadataLen := len(metadata) - v := make([]byte, consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+codec.AddressLen+1) + v := make([]byte, consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+codec.AddressLen) binary.BigEndian.PutUint16(v, uint16(symbolLen)) copy(v[consts.Uint16Len:], symbol) v[consts.Uint16Len+symbolLen] = decimals @@ -356,11 +350,6 @@ func SetAsset( copy(v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len:], metadata) binary.BigEndian.PutUint64(v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen:], supply) copy(v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len:], owner[:]) - b := byte(0x0) - if warp { - b = 0x1 - } - v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+codec.AddressLen] = b return mu.Insert(ctx, k, v) } @@ -588,18 +577,3 @@ func TimestampKey() (k []byte) { func FeeKey() (k []byte) { return feeKey } - -func IncomingWarpKeyPrefix(sourceChainID ids.ID, msgID ids.ID) (k []byte) { - k = make([]byte, 1+consts.IDLen*2) - k[0] = incomingWarpPrefix - copy(k[1:], sourceChainID[:]) - copy(k[1+consts.IDLen:], msgID[:]) - return k -} - -func OutgoingWarpKeyPrefix(txID ids.ID) (k []byte) { - k = make([]byte, 1+consts.IDLen) - k[0] = outgoingWarpPrefix - copy(k[1:], txID[:]) - return k -} diff --git a/examples/tokenvm/tests/e2e/e2e_test.go b/examples/tokenvm/tests/e2e/e2e_test.go index ac1c43e6bc..60532dea6d 100644 --- a/examples/tokenvm/tests/e2e/e2e_test.go +++ b/examples/tokenvm/tests/e2e/e2e_test.go @@ -16,7 +16,6 @@ import ( "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" @@ -527,7 +526,6 @@ var _ = ginkgo.Describe("[Test]", func() { submit, tx, maxFee, err := instancesA[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: aother, Value: sendAmount, @@ -583,743 +581,6 @@ var _ = ginkgo.Describe("[Test]", func() { }) }) - ginkgo.It("performs a warp transfer of the native asset", func() { - other, err := ed25519.GeneratePrivateKey() - gomega.Ω(err).Should(gomega.BeNil()) - aother := codec.MustAddressBech32(consts.HRP, auth.NewED25519Address(other.PublicKey())) - source, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - destination, err := ids.FromString(blockchainIDB) - gomega.Ω(err).Should(gomega.BeNil()) - otherFactory := auth.NewED25519Factory(other) - - var txID ids.ID - ginkgo.By("submitting an export action on source", func() { - otherBalance, err := instancesA[0].tcli.Balance(context.Background(), aother, ids.Empty) - gomega.Ω(err).Should(gomega.BeNil()) - senderBalance, err := instancesA[0].tcli.Balance(context.Background(), sender, ids.Empty) - gomega.Ω(err).Should(gomega.BeNil()) - - parser, err := instancesA[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesA[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.ExportAsset{ - To: auth.NewED25519Address(other.PublicKey()), - Asset: ids.Empty, - Value: sendAmount, - Return: false, - Destination: destination, - }, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - - // Broadcast and wait for transaction - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, fee, err := instancesA[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp export transaction{{/}}\n") - - // Check loans and balances - amount, err := instancesA[0].tcli.Loan(context.Background(), ids.Empty, destination) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(amount).Should(gomega.Equal(sendAmount)) - aotherBalance, err := instancesA[0].tcli.Balance(context.Background(), aother, ids.Empty) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(otherBalance).Should(gomega.Equal(aotherBalance)) - asenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(asenderBalance).Should(gomega.Equal(senderBalance - sendAmount - fee)) - }) - - ginkgo.By("fund other account with native", func() { - parser, err := instancesB[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesB[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.Transfer{ - To: auth.NewED25519Address(other.PublicKey()), - Asset: ids.Empty, - Value: 500_000_000, - }, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID := tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - - // Broadcast transaction (wait for after all broadcast) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - - // Confirm transaction is accepted - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, _, err := instancesB[0].tcli.WaitForTransaction(ctx, txID) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found transaction %s on B{{/}}\n", txID) - }) - - ginkgo.By("submitting an import action on destination", func() { - bIDA, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - newAsset := actions.ImportedAssetID(ids.Empty, bIDA) - nativeOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newOtherBalance).Should(gomega.Equal(uint64(0))) - nativeSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newSenderBalance).Should(gomega.Equal(uint64(0))) - - var ( - msg *warp.Message - subnetWeight, sigWeight uint64 - ) - for { - msg, subnetWeight, sigWeight, err = instancesA[0].cli.GenerateAggregateWarpSignature( - context.Background(), - txID, - ) - if sigWeight == subnetWeight && err == nil { - break - } - if err == nil { - hutils.Outf( - "{{yellow}}waiting for signature weight:{{/}} %d {{yellow}}observed:{{/}} %d\n", - subnetWeight, - sigWeight, - ) - } else { - hutils.Outf("{{red}}found error:{{/}} %v\n", err) - } - time.Sleep(1 * time.Second) - } - hutils.Outf( - "{{green}}fetched signature weight:{{/}} %d {{green}}total weight:{{/}} %d\n", - sigWeight, - subnetWeight, - ) - gomega.Ω(subnetWeight).Should(gomega.Equal(sigWeight)) - - parser, err := instancesB[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesB[0].cli.GenerateTransaction( - context.Background(), - parser, - msg, - &actions.ImportAsset{}, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, fee, err := instancesB[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp import transaction{{/}}\n") - - // Check asset info and balance - aNativeOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(nativeOtherBalance).Should(gomega.Equal(aNativeOtherBalance)) - aNewOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewOtherBalance).Should(gomega.Equal(sendAmount)) - aNativeSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNativeSenderBalance).Should(gomega.Equal(nativeSenderBalance - fee)) - aNewSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewSenderBalance).Should(gomega.Equal(uint64(0))) - exists, symbol, decimals, metadata, supply, owner, warp, err := instancesB[0].tcli.Asset(context.Background(), newAsset, false) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(exists).Should(gomega.BeTrue()) - gomega.Ω(string(symbol)).Should(gomega.Equal(consts.Symbol)) - gomega.Ω(decimals).Should(gomega.Equal(uint8(consts.Decimals))) - gomega.Ω(metadata).Should(gomega.Equal(actions.ImportedAssetMetadata(ids.Empty, bIDA))) - gomega.Ω(supply).Should(gomega.Equal(sendAmount)) - gomega.Ω(owner).Should(gomega.Equal(codec.MustAddressBech32(consts.HRP, codec.EmptyAddress))) - gomega.Ω(warp).Should(gomega.BeTrue()) - }) - - ginkgo.By("submitting an invalid export action to new destination", func() { - bIDA, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - newAsset := actions.ImportedAssetID(ids.Empty, bIDA) - parser, err := instancesB[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesB[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.ExportAsset{ - To: rsender, - Asset: newAsset, - Value: 100, - Return: false, - Destination: ids.GenerateTestID(), - }, - otherFactory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - - // Broadcast and wait for transaction - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, _, err := instancesB[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeFalse()) - - // Confirm balances are unchanged - newOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newOtherBalance).Should(gomega.Equal(sendAmount)) - }) - - ginkgo.By("submitting first (2000) return export action on destination", func() { - bIDA, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - newAsset := actions.ImportedAssetID(ids.Empty, bIDA) - parser, err := instancesB[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesB[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.ExportAsset{ - To: rsender, - Asset: newAsset, - Value: 2000, - Return: true, - Destination: source, - Reward: 100, - }, - otherFactory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - - // Broadcast and wait for transaction - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, _, err := instancesB[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp export transaction{{/}}\n") - - // Check balances and asset info - amount, err := instancesB[0].tcli.Loan(context.Background(), newAsset, source) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(amount).Should(gomega.Equal(uint64(0))) - otherBalance, err := instancesB[0].tcli.Balance(context.Background(), aother, newAsset) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(otherBalance).Should(gomega.Equal(uint64(2900))) - exists, symbol, decimals, metadata, supply, owner, warp, err := instancesB[0].tcli.Asset(context.Background(), newAsset, false) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(exists).Should(gomega.BeTrue()) - gomega.Ω(string(symbol)).Should(gomega.Equal(consts.Symbol)) - gomega.Ω(decimals).Should(gomega.Equal(uint8(consts.Decimals))) - gomega.Ω(metadata).Should(gomega.Equal(actions.ImportedAssetMetadata(ids.Empty, bIDA))) - gomega.Ω(supply).Should(gomega.Equal(uint64(2900))) - gomega.Ω(owner).Should(gomega.Equal(codec.MustAddressBech32(consts.HRP, codec.EmptyAddress))) - gomega.Ω(warp).Should(gomega.BeTrue()) - }) - - ginkgo.By("submitting first import action on source", func() { - bIDA, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - newAsset := actions.ImportedAssetID(ids.Empty, bIDA) - nativeOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newOtherBalance).Should(gomega.Equal(uint64(0))) - nativeSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newSenderBalance).Should(gomega.Equal(uint64(0))) - - var ( - msg *warp.Message - subnetWeight, sigWeight uint64 - ) - for { - msg, subnetWeight, sigWeight, err = instancesB[0].cli.GenerateAggregateWarpSignature( - context.Background(), - txID, - ) - if sigWeight == subnetWeight && err == nil { - break - } - if err == nil { - hutils.Outf( - "{{yellow}}waiting for signature weight:{{/}} %d {{yellow}}observed:{{/}} %d\n", - subnetWeight, - sigWeight, - ) - } else { - hutils.Outf("{{red}}found error:{{/}} %v\n", err) - } - time.Sleep(1 * time.Second) - } - hutils.Outf( - "{{green}}fetched signature weight:{{/}} %d {{green}}total weight:{{/}} %d\n", - sigWeight, - subnetWeight, - ) - gomega.Ω(subnetWeight).Should(gomega.Equal(sigWeight)) - - parser, err := instancesA[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesA[0].cli.GenerateTransaction( - context.Background(), - parser, - msg, - &actions.ImportAsset{}, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, fee, err := instancesA[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp import transaction{{/}}\n") - - // Check balances and loan - aNativeOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(nativeOtherBalance).Should(gomega.Equal(aNativeOtherBalance)) - aNewOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewOtherBalance).Should(gomega.Equal(uint64(0))) - aNativeSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNativeSenderBalance). - Should(gomega.Equal(nativeSenderBalance - fee + 2000 + 100)) - aNewSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewSenderBalance).Should(gomega.Equal(uint64(0))) - amount, err := instancesA[0].tcli.Loan(context.Background(), ids.Empty, destination) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(amount).Should(gomega.Equal(uint64(2900))) - }) - - ginkgo.By("submitting second (2900) return export action on destination", func() { - bIDA, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - newAsset := actions.ImportedAssetID(ids.Empty, bIDA) - parser, err := instancesB[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesB[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.ExportAsset{ - To: auth.NewED25519Address(other.PublicKey()), - Asset: newAsset, - Value: 2900, - Return: true, - Destination: source, - }, - otherFactory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - - // Broadcast and wait for transaction - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, _, err := instancesB[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp export transaction{{/}}\n") - - // Check balances and asset info - amount, err := instancesB[0].tcli.Loan(context.Background(), newAsset, source) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(amount).Should(gomega.Equal(uint64(0))) - otherBalance, err := instancesB[0].tcli.Balance(context.Background(), aother, newAsset) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(otherBalance).Should(gomega.Equal(uint64(0))) - exists, _, _, _, _, _, _, err := instancesB[0].tcli.Asset(context.Background(), newAsset, false) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(exists).Should(gomega.BeFalse()) - }) - - ginkgo.By("submitting second import action on source", func() { - bIDA, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - newAsset := actions.ImportedAssetID(ids.Empty, bIDA) - nativeOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newOtherBalance).Should(gomega.Equal(uint64(0))) - nativeSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newSenderBalance).Should(gomega.Equal(uint64(0))) - - var ( - msg *warp.Message - subnetWeight, sigWeight uint64 - ) - for { - msg, subnetWeight, sigWeight, err = instancesB[0].cli.GenerateAggregateWarpSignature( - context.Background(), - txID, - ) - if sigWeight == subnetWeight && err == nil { - break - } - if err == nil { - hutils.Outf( - "{{yellow}}waiting for signature weight:{{/}} %d {{yellow}}observed:{{/}} %d\n", - subnetWeight, - sigWeight, - ) - } else { - hutils.Outf("{{red}}found error:{{/}} %v\n", err) - } - time.Sleep(1 * time.Second) - } - hutils.Outf( - "{{green}}fetched signature weight:{{/}} %d {{green}}total weight:{{/}} %d\n", - sigWeight, - subnetWeight, - ) - gomega.Ω(subnetWeight).Should(gomega.Equal(sigWeight)) - - parser, err := instancesA[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesA[0].cli.GenerateTransaction( - context.Background(), - parser, - msg, - &actions.ImportAsset{}, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, fee, err := instancesA[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp import transaction{{/}}\n") - - // Check balances and loan - aNativeOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNativeOtherBalance).Should(gomega.Equal(nativeOtherBalance + 2900)) - aNewOtherBalance, err := instancesA[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewOtherBalance).Should(gomega.Equal(uint64(0))) - aNativeSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNativeSenderBalance).Should(gomega.Equal(nativeSenderBalance - fee)) - aNewSenderBalance, err := instancesA[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewSenderBalance).Should(gomega.Equal(uint64(0))) - amount, err := instancesA[0].tcli.Loan(context.Background(), ids.Empty, destination) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(amount).Should(gomega.Equal(uint64(0))) - }) - - ginkgo.By("swaping into destination", func() { - bIDA, err := ids.FromString(blockchainIDA) - gomega.Ω(err).Should(gomega.BeNil()) - newAsset := actions.ImportedAssetID(ids.Empty, bIDA) - parser, err := instancesA[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instancesA[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.ExportAsset{ - To: auth.NewED25519Address(other.PublicKey()), - Asset: ids.Empty, // becomes newAsset - Value: 2000, - Return: false, - SwapIn: 100, - AssetOut: ids.Empty, - SwapOut: 200, - SwapExpiry: time.Now().UnixMilli() + 100_000, - Destination: destination, - }, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - - // Broadcast and wait for transaction - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) - success, _, err := instancesA[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp export transaction{{/}}\n") - - // Record balances on destination - nativeOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newOtherBalance).Should(gomega.Equal(uint64(0))) - nativeSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - newSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(newSenderBalance).Should(gomega.Equal(uint64(0))) - - var ( - msg *warp.Message - subnetWeight, sigWeight uint64 - ) - for { - msg, subnetWeight, sigWeight, err = instancesA[0].cli.GenerateAggregateWarpSignature( - context.Background(), - txID, - ) - if sigWeight == subnetWeight && err == nil { - break - } - if err == nil { - hutils.Outf( - "{{yellow}}waiting for signature weight:{{/}} %d {{yellow}}observed:{{/}} %d\n", - subnetWeight, - sigWeight, - ) - } else { - hutils.Outf("{{red}}found error:{{/}} %v\n", err) - } - time.Sleep(1 * time.Second) - } - hutils.Outf( - "{{green}}fetched signature weight:{{/}} %d {{green}}total weight:{{/}} %d\n", - sigWeight, - subnetWeight, - ) - gomega.Ω(subnetWeight).Should(gomega.Equal(sigWeight)) - - parser, err = instancesB[0].tcli.Parser(context.TODO()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err = instancesB[0].cli.GenerateTransaction( - context.Background(), - parser, - msg, - &actions.ImportAsset{ - Fill: true, - }, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - txID = tx.ID() - hutils.Outf("{{yellow}}generated transaction:{{/}} %s\n", txID) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") - ctx, cancel = context.WithTimeout(context.Background(), requestTimeout) - success, fee, err := instancesB[0].tcli.WaitForTransaction(ctx, tx.ID()) - cancel() - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found warp import transaction{{/}}\n") - - // Check balances following swap - aNativeOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNativeOtherBalance).Should(gomega.Equal(nativeOtherBalance + 200)) - aNewOtherBalance, err := instancesB[0].tcli.Balance( - context.Background(), - aother, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewOtherBalance).Should(gomega.Equal(uint64(1900))) - aNativeSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - ids.Empty, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNativeSenderBalance). - Should(gomega.Equal(nativeSenderBalance - fee - 200)) - aNewSenderBalance, err := instancesB[0].tcli.Balance( - context.Background(), - sender, - newAsset, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(aNewSenderBalance).Should(gomega.Equal(uint64(100))) - }) - }) - // TODO: add custom asset test // TODO: test with only part of sig weight // TODO: attempt to mint a warp asset @@ -1530,7 +791,6 @@ func generateBlocks( submit, _, _, err := instances[cumulativeTxs%len(instances)].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: auth.NewED25519Address(other.PublicKey()), Value: 1, @@ -1598,7 +858,6 @@ func acceptTransaction(cli *rpc.JSONRPCClient, tcli *trpc.JSONRPCClient) { submit, tx, maxFee, err := cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: auth.NewED25519Address(other.PublicKey()), Value: sendAmount, diff --git a/examples/tokenvm/tests/integration/integration_test.go b/examples/tokenvm/tests/integration/integration_test.go index 1a2dfebb49..34e3a10be3 100644 --- a/examples/tokenvm/tests/integration/integration_test.go +++ b/examples/tokenvm/tests/integration/integration_test.go @@ -22,12 +22,10 @@ import ( "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/fatih/color" ginkgo "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -210,7 +208,6 @@ var _ = ginkgo.BeforeSuite(func() { ChainDataDir: dname, Metrics: metrics.NewOptionalGatherer(), PublicKey: bls.PublicFromSecretKey(sk), - WarpSigner: warp.NewSigner(sk, networkID, chainID), ValidatorState: &validators.TestState{}, } @@ -270,7 +267,7 @@ var _ = ginkgo.BeforeSuite(func() { gomega.Ω(balance).Should(gomega.Equal(alloc.Balance)) csupply += alloc.Balance } - exists, symbol, decimals, metadata, supply, owner, warp, err := cli.Asset(context.Background(), ids.Empty, false) + exists, symbol, decimals, metadata, supply, owner, err := cli.Asset(context.Background(), ids.Empty, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeTrue()) gomega.Ω(string(symbol)).Should(gomega.Equal(tconsts.Symbol)) @@ -278,7 +275,6 @@ var _ = ginkgo.BeforeSuite(func() { gomega.Ω(string(metadata)).Should(gomega.Equal(tconsts.Name)) gomega.Ω(supply).Should(gomega.Equal(csupply)) gomega.Ω(owner).Should(gomega.Equal(codec.MustAddressBech32(tconsts.HRP, codec.EmptyAddress))) - gomega.Ω(warp).Should(gomega.BeFalse()) } blocks = []snowman.Block{} @@ -337,7 +333,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, transferTx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: rsender2, Value: 100_000, // must be more than StateLockup @@ -370,7 +365,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: 0, MaxFee: 1000, }, - nil, &actions.Transfer{ To: rsender2, Value: 110, @@ -437,19 +431,19 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // read: 2 keys reads, 1 had 0 chunks // allocate: 1 key created // write: 1 key modified, 1 key new - transferTxConsumed := fees.Dimensions{227, 7, 12, 25, 26} + transferTxConsumed := fees.Dimensions{223, 7, 12, 25, 26} gomega.Ω(results[0].Consumed).Should(gomega.Equal(transferTxConsumed)) // Fee explanation // // Multiply all unit consumption by 1 and sum - gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(297))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(293))) }) ginkgo.By("ensure balance is updated", func() { balance, err := instances[1].tcli.Balance(context.Background(), sender, ids.Empty) gomega.Ω(err).To(gomega.BeNil()) - gomega.Ω(balance).To(gomega.Equal(uint64(9899703))) + gomega.Ω(balance).To(gomega.Equal(uint64(9899707))) balance2, err := instances[1].tcli.Balance(context.Background(), sender2, ids.Empty) gomega.Ω(err).To(gomega.BeNil()) gomega.Ω(balance2).To(gomega.Equal(uint64(100000))) @@ -463,7 +457,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: rsender2, Value: 101, @@ -493,7 +486,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: rsender2, Value: 200, @@ -508,7 +500,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err = instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: rsender2, Value: 201, @@ -538,7 +529,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[1].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: rsender2, Value: 203, @@ -640,7 +630,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, transfer, factory, ) @@ -689,7 +678,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { _, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, transfer, factory, ) @@ -733,7 +721,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.Transfer{ To: auth.NewED25519Address(other.PublicKey()), Value: 10, @@ -759,7 +746,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), MaxFee: 1001, }, - nil, &actions.Transfer{ To: auth.NewED25519Address(other.PublicKey()), Value: 10, @@ -792,7 +778,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.MintAsset{ To: auth.NewED25519Address(other.PublicKey()), Asset: assetID, @@ -810,7 +795,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(string(result.Output)). Should(gomega.ContainSubstring("asset missing")) - exists, _, _, _, _, _, _, err := instances[0].tcli.Asset(context.TODO(), assetID, false) + exists, _, _, _, _, _, err := instances[0].tcli.Asset(context.TODO(), assetID, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeFalse()) }) @@ -822,7 +807,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), MaxFee: 1001, }, - nil, &actions.CreateAsset{ Symbol: []byte("s0"), Decimals: 0, @@ -853,7 +837,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), MaxFee: 1001, }, - nil, &actions.CreateAsset{ Symbol: nil, Decimals: 0, @@ -884,7 +867,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), MaxFee: 1000, }, - nil, &actions.CreateAsset{ Symbol: []byte("s0"), Decimals: 0, @@ -914,7 +896,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateAsset{ Symbol: asset1Symbol, Decimals: asset1Decimals, @@ -934,7 +915,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(uint64(0))) - exists, symbol, decimals, metadata, supply, owner, warp, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) + exists, symbol, decimals, metadata, supply, owner, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeTrue()) gomega.Ω(symbol).Should(gomega.Equal(asset1Symbol)) @@ -942,7 +923,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(metadata).Should(gomega.Equal(asset1)) gomega.Ω(supply).Should(gomega.Equal(uint64(0))) gomega.Ω(owner).Should(gomega.Equal(sender)) - gomega.Ω(warp).Should(gomega.BeFalse()) }) ginkgo.It("mint a new asset", func() { @@ -951,7 +931,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.MintAsset{ To: rsender2, Asset: asset1ID, @@ -973,7 +952,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(uint64(0))) - exists, symbol, decimals, metadata, supply, owner, warp, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) + exists, symbol, decimals, metadata, supply, owner, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeTrue()) gomega.Ω(symbol).Should(gomega.Equal(asset1Symbol)) @@ -981,7 +960,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(metadata).Should(gomega.Equal(asset1)) gomega.Ω(supply).Should(gomega.Equal(uint64(15))) gomega.Ω(owner).Should(gomega.Equal(sender)) - gomega.Ω(warp).Should(gomega.BeFalse()) }) ginkgo.It("mint asset from wrong owner", func() { @@ -992,7 +970,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.MintAsset{ To: auth.NewED25519Address(other.PublicKey()), Asset: asset1ID, @@ -1010,7 +987,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(string(result.Output)). Should(gomega.ContainSubstring("wrong owner")) - exists, symbol, decimals, metadata, supply, owner, warp, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) + exists, symbol, decimals, metadata, supply, owner, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeTrue()) gomega.Ω(symbol).Should(gomega.Equal(asset1Symbol)) @@ -1018,7 +995,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(metadata).Should(gomega.Equal(asset1)) gomega.Ω(supply).Should(gomega.Equal(uint64(15))) gomega.Ω(owner).Should(gomega.Equal(sender)) - gomega.Ω(warp).Should(gomega.BeFalse()) }) ginkgo.It("burn new asset", func() { @@ -1027,7 +1003,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.BurnAsset{ Asset: asset1ID, Value: 5, @@ -1048,7 +1023,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(uint64(0))) - exists, symbol, decimals, metadata, supply, owner, warp, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) + exists, symbol, decimals, metadata, supply, owner, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeTrue()) gomega.Ω(symbol).Should(gomega.Equal(asset1Symbol)) @@ -1056,7 +1031,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(metadata).Should(gomega.Equal(asset1)) gomega.Ω(supply).Should(gomega.Equal(uint64(10))) gomega.Ω(owner).Should(gomega.Equal(sender)) - gomega.Ω(warp).Should(gomega.BeFalse()) }) ginkgo.It("burn missing asset", func() { @@ -1065,7 +1039,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.BurnAsset{ Asset: asset1ID, Value: 10, @@ -1082,7 +1055,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(string(result.Output)). Should(gomega.ContainSubstring("invalid balance")) - exists, symbol, decimals, metadata, supply, owner, warp, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) + exists, symbol, decimals, metadata, supply, owner, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeTrue()) gomega.Ω(symbol).Should(gomega.Equal(asset1Symbol)) @@ -1090,7 +1063,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(metadata).Should(gomega.Equal(asset1)) gomega.Ω(supply).Should(gomega.Equal(uint64(10))) gomega.Ω(owner).Should(gomega.Equal(sender)) - gomega.Ω(warp).Should(gomega.BeFalse()) }) ginkgo.It("rejects empty mint", func() { @@ -1102,7 +1074,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), MaxFee: 1000, }, - nil, &actions.MintAsset{ To: auth.NewED25519Address(other.PublicKey()), Asset: asset1ID, @@ -1131,7 +1102,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.MintAsset{ To: rsender2, Asset: asset1ID, @@ -1156,7 +1126,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(uint64(0))) - exists, symbol, decimals, metadata, supply, owner, warp, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) + exists, symbol, decimals, metadata, supply, owner, err := instances[0].tcli.Asset(context.TODO(), asset1ID, false) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(exists).Should(gomega.BeTrue()) gomega.Ω(symbol).Should(gomega.Equal(asset1Symbol)) @@ -1164,7 +1134,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(metadata).Should(gomega.Equal(asset1)) gomega.Ω(supply).Should(gomega.Equal(uint64(10))) gomega.Ω(owner).Should(gomega.Equal(sender)) - gomega.Ω(warp).Should(gomega.BeFalse()) }) ginkgo.It("rejects mint of native token", func() { @@ -1176,7 +1145,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), MaxFee: 1000, }, - nil, &actions.MintAsset{ To: auth.NewED25519Address(other.PublicKey()), Value: 10, @@ -1205,7 +1173,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateAsset{ Symbol: asset2Symbol, Decimals: asset2Decimals, @@ -1224,7 +1191,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err = instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.MintAsset{ To: rsender, Asset: asset2ID, @@ -1250,7 +1216,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateAsset{ Symbol: asset3Symbol, Decimals: asset3Decimals, @@ -1269,7 +1234,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err = instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.MintAsset{ To: rsender2, Asset: asset3ID, @@ -1295,7 +1259,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateOrder{ In: asset3ID, InTick: 1, @@ -1333,7 +1296,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateOrder{ In: asset2ID, InTick: 4, @@ -1360,7 +1322,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateOrder{ In: asset2ID, InTick: 4, @@ -1398,7 +1359,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateOrder{ In: asset2ID, InTick: 5, @@ -1431,7 +1391,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.FillOrder{ Order: order.ID, Owner: owner, @@ -1464,7 +1423,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.FillOrder{ Order: order.ID, Owner: owner, @@ -1497,7 +1455,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.FillOrder{ Order: order.ID, Owner: owner, @@ -1544,7 +1501,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CloseOrder{ Order: order.ID, Out: asset3ID, @@ -1572,7 +1528,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CloseOrder{ Order: order.ID, Out: asset3ID, @@ -1611,7 +1566,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, tx, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.CreateOrder{ In: asset2ID, InTick: 2, @@ -1655,7 +1609,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { submit, _, _, err := instances[0].cli.GenerateTransaction( context.Background(), parser, - nil, &actions.FillOrder{ Order: order.ID, Owner: owner, @@ -1695,260 +1648,6 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(orders).Should(gomega.HaveLen(0)) }) - - ginkgo.It("import warp message with nil when expected", func() { - tx := chain.NewTx( - &chain.Base{ - ChainID: instances[0].chainID, - Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), - MaxFee: 1000, - }, - nil, - &actions.ImportAsset{}, - ) - // Must do manual construction to avoid `tx.Sign` error (would fail with - // empty warp) - msg, err := tx.Digest() - gomega.Ω(err).To(gomega.BeNil()) - auth, err := factory.Sign(msg) - gomega.Ω(err).To(gomega.BeNil()) - tx.Auth = auth - p := codec.NewWriter(0, consts.MaxInt) // test codec growth - gomega.Ω(tx.Marshal(p)).To(gomega.BeNil()) - gomega.Ω(p.Err()).To(gomega.BeNil()) - _, err = instances[0].cli.SubmitTx( - context.Background(), - p.Bytes(), - ) - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("expected warp message")) - }) - - ginkgo.It("import warp message empty", func() { - wm, err := warp.NewMessage(&warp.UnsignedMessage{}, &warp.BitSetSignature{}) - gomega.Ω(err).Should(gomega.BeNil()) - tx := chain.NewTx( - &chain.Base{ - ChainID: instances[0].chainID, - Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), - MaxFee: 1000, - }, - wm, - &actions.ImportAsset{}, - ) - // Must do manual construction to avoid `tx.Sign` error (would fail with - // empty warp) - msg, err := tx.Digest() - gomega.Ω(err).To(gomega.BeNil()) - auth, err := factory.Sign(msg) - gomega.Ω(err).To(gomega.BeNil()) - tx.Auth = auth - p := codec.NewWriter(0, consts.MaxInt) // test codec growth - gomega.Ω(tx.Marshal(p)).To(gomega.BeNil()) - gomega.Ω(p.Err()).To(gomega.BeNil()) - _, err = instances[0].cli.SubmitTx( - context.Background(), - p.Bytes(), - ) - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("empty warp payload")) - }) - - ginkgo.It("import with wrong payload", func() { - uwm, err := warp.NewUnsignedMessage(networkID, ids.Empty, []byte("hello")) - gomega.Ω(err).Should(gomega.BeNil()) - wm, err := warp.NewMessage(uwm, &warp.BitSetSignature{}) - gomega.Ω(err).Should(gomega.BeNil()) - tx := chain.NewTx( - &chain.Base{ - ChainID: instances[0].chainID, - Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), - MaxFee: 1000, - }, - wm, - &actions.ImportAsset{}, - ) - // Must do manual construction to avoid `tx.Sign` error (would fail with - // invalid object) - msg, err := tx.Digest() - gomega.Ω(err).To(gomega.BeNil()) - auth, err := factory.Sign(msg) - gomega.Ω(err).To(gomega.BeNil()) - tx.Auth = auth - p := codec.NewWriter(0, consts.MaxInt) // test codec growth - gomega.Ω(tx.Marshal(p)).To(gomega.BeNil()) - gomega.Ω(p.Err()).To(gomega.BeNil()) - _, err = instances[0].cli.SubmitTx( - context.Background(), - p.Bytes(), - ) - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("insufficient length for input")) - }) - - ginkgo.It("import with invalid payload", func() { - wt := &actions.WarpTransfer{} - wtb, err := wt.Marshal() - gomega.Ω(err).Should(gomega.BeNil()) - uwm, err := warp.NewUnsignedMessage(networkID, ids.Empty, wtb) - gomega.Ω(err).Should(gomega.BeNil()) - wm, err := warp.NewMessage(uwm, &warp.BitSetSignature{}) - gomega.Ω(err).Should(gomega.BeNil()) - tx := chain.NewTx( - &chain.Base{ - ChainID: instances[0].chainID, - Timestamp: hutils.UnixRMilli(-1, 5*consts.MillisecondsPerSecond), - MaxFee: 1000, - }, - wm, - &actions.ImportAsset{}, - ) - // Must do manual construction to avoid `tx.Sign` error (would fail with - // invalid object) - msg, err := tx.Digest() - gomega.Ω(err).To(gomega.BeNil()) - auth, err := factory.Sign(msg) - gomega.Ω(err).To(gomega.BeNil()) - tx.Auth = auth - p := codec.NewWriter(0, consts.MaxInt) // test codec growth - gomega.Ω(tx.Marshal(p)).To(gomega.BeNil()) - gomega.Ω(p.Err()).To(gomega.BeNil()) - _, err = instances[0].cli.SubmitTx( - context.Background(), - p.Bytes(), - ) - gomega.Ω(err.Error()).Should(gomega.ContainSubstring("field is not populated")) - }) - - ginkgo.It("import with wrong destination", func() { - wt := &actions.WarpTransfer{ - To: rsender, - Symbol: []byte("s"), - Decimals: 2, - Asset: ids.GenerateTestID(), - Value: 100, - Return: false, - Reward: 100, - TxID: ids.GenerateTestID(), - DestinationChainID: ids.GenerateTestID(), - } - wtb, err := wt.Marshal() - gomega.Ω(err).Should(gomega.BeNil()) - uwm, err := warp.NewUnsignedMessage(networkID, ids.Empty, wtb) - gomega.Ω(err).Should(gomega.BeNil()) - wm, err := warp.NewMessage(uwm, &warp.BitSetSignature{}) - gomega.Ω(err).Should(gomega.BeNil()) - parser, err := instances[0].tcli.Parser(context.Background()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, _, _, err := instances[0].cli.GenerateTransaction( - context.Background(), - parser, - wm, - &actions.ImportAsset{}, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - - // Build block with no context (should fail) - gomega.Ω(instances[0].vm.Builder().Force(context.TODO())).To(gomega.BeNil()) - <-instances[0].toEngine - blk, err := instances[0].vm.BuildBlock(context.TODO()) - gomega.Ω(err).To(gomega.Not(gomega.BeNil())) - gomega.Ω(blk).To(gomega.BeNil()) - - // Wait for mempool to be size 1 (txs are restored async) - for { - if instances[0].vm.Mempool().Len(context.Background()) > 0 { - break - } - log.Info("waiting for txs to be restored") - time.Sleep(100 * time.Millisecond) - } - - // Build block with context - accept := expectBlkWithContext(instances[0]) - results := accept(false) - gomega.Ω(results).Should(gomega.HaveLen(1)) - result := results[0] - gomega.Ω(result.Success).Should(gomega.BeFalse()) - gomega.Ω(string(result.Output)).Should(gomega.ContainSubstring("warp verification failed")) - }) - - ginkgo.It("export native asset", func() { - dest := ids.GenerateTestID() - loan, err := instances[0].tcli.Loan(context.TODO(), ids.Empty, dest) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(loan).Should(gomega.Equal(uint64(0))) - - parser, err := instances[0].tcli.Parser(context.Background()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, tx, _, err := instances[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.ExportAsset{ - To: rsender, - Asset: ids.Empty, - Value: 100, - Return: false, - Reward: 10, - Destination: dest, - }, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - accept := expectBlk(instances[0]) - results := accept(false) - gomega.Ω(results).Should(gomega.HaveLen(1)) - result := results[0] - gomega.Ω(result.Success).Should(gomega.BeTrue()) - wt := &actions.WarpTransfer{ - To: rsender, - Symbol: []byte(tconsts.Symbol), - Decimals: tconsts.Decimals, - Asset: ids.Empty, - Value: 100, - Return: false, - Reward: 10, - TxID: tx.ID(), - DestinationChainID: dest, - } - wtb, err := wt.Marshal() - gomega.Ω(err).Should(gomega.BeNil()) - wm, err := warp.NewUnsignedMessage(networkID, instances[0].chainID, wtb) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(result.WarpMessage).Should(gomega.Equal(wm)) - - loan, err = instances[0].tcli.Loan(context.TODO(), ids.Empty, dest) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(loan).Should(gomega.Equal(uint64(110))) - }) - - ginkgo.It("export native asset (invalid return)", func() { - parser, err := instances[0].tcli.Parser(context.Background()) - gomega.Ω(err).Should(gomega.BeNil()) - submit, _, _, err := instances[0].cli.GenerateTransaction( - context.Background(), - parser, - nil, - &actions.ExportAsset{ - To: rsender, - Asset: ids.Empty, - Value: 100, - Return: true, - Reward: 10, - Destination: ids.GenerateTestID(), - }, - factory, - ) - gomega.Ω(err).Should(gomega.BeNil()) - gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - accept := expectBlk(instances[0]) - results := accept(false) - gomega.Ω(results).Should(gomega.HaveLen(1)) - result := results[0] - gomega.Ω(result.Success).Should(gomega.BeFalse()) - gomega.Ω(string(result.Output)).Should(gomega.ContainSubstring("not warp asset")) - }) }) func expectBlk(i instance) func(bool) []*chain.Result { @@ -1984,42 +1683,6 @@ func expectBlk(i instance) func(bool) []*chain.Result { } } -// TODO: unify with expectBlk -func expectBlkWithContext(i instance) func(bool) []*chain.Result { - ctx := context.TODO() - - // manually signal ready - gomega.Ω(i.vm.Builder().Force(ctx)).To(gomega.BeNil()) - // manually ack ready sig as in engine - <-i.toEngine - - bctx := &block.Context{PChainHeight: 1} - blk, err := i.vm.BuildBlockWithContext(ctx, bctx) - gomega.Ω(err).To(gomega.BeNil()) - gomega.Ω(blk).To(gomega.Not(gomega.BeNil())) - cblk := blk.(block.WithVerifyContext) - - gomega.Ω(cblk.VerifyWithContext(ctx, bctx)).To(gomega.BeNil()) - gomega.Ω(blk.Status()).To(gomega.Equal(choices.Processing)) - - err = i.vm.SetPreference(ctx, blk.ID()) - gomega.Ω(err).To(gomega.BeNil()) - - return func(add bool) []*chain.Result { - gomega.Ω(blk.Accept(ctx)).To(gomega.BeNil()) - gomega.Ω(blk.Status()).To(gomega.Equal(choices.Accepted)) - - if add { - blocks = append(blocks, blk) - } - - lastAccepted, err := i.vm.LastAccepted(ctx) - gomega.Ω(err).To(gomega.BeNil()) - gomega.Ω(lastAccepted).To(gomega.Equal(blk.ID())) - return blk.(*chain.StatelessBlock).Results() - } -} - var _ common.AppSender = &appSender{} type appSender struct { diff --git a/examples/tokenvm/tests/load/load_test.go b/examples/tokenvm/tests/load/load_test.go index 23aefec630..c4278ef280 100644 --- a/examples/tokenvm/tests/load/load_test.go +++ b/examples/tokenvm/tests/load/load_test.go @@ -533,7 +533,6 @@ func issueSimpleTx( ChainID: i.chainID, MaxFee: maxFee, }, - nil, &actions.Transfer{ To: to, Value: amount, diff --git a/go.mod b/go.mod index 545118f941..e7b5855d7b 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,6 @@ require ( go.uber.org/mock v0.4.0 go.uber.org/zap v1.26.0 golang.org/x/crypto v0.17.0 - golang.org/x/exp v0.0.0-20231127185646-65229373498e golang.org/x/sync v0.5.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -76,6 +75,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.11.2 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect diff --git a/rpc/dependencies.go b/rpc/dependencies.go index d9cde2e5bf..028aaacc06 100644 --- a/rpc/dependencies.go +++ b/rpc/dependencies.go @@ -10,7 +10,6 @@ import ( "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/fees" @@ -30,11 +29,8 @@ type VM interface { ) (errs []error) LastAcceptedBlock() *chain.StatelessBlock UnitPrices(context.Context) (fees.Dimensions, error) - GetOutgoingWarpMessage(ids.ID) (*warp.UnsignedMessage, error) - GetWarpSignatures(ids.ID) ([]*chain.WarpSignature, error) CurrentValidators( context.Context, ) (map[ids.NodeID]*validators.GetValidatorOutput, map[string]struct{}) - GatherSignatures(context.Context, ids.ID, []byte) GetVerifyAuth() bool } diff --git a/rpc/jsonrpc_client.go b/rpc/jsonrpc_client.go index fa14d6f91d..fedccd7d7d 100644 --- a/rpc/jsonrpc_client.go +++ b/rpc/jsonrpc_client.go @@ -10,19 +10,11 @@ import ( "time" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/validators" - "github.com/ava-labs/avalanchego/utils/crypto/bls" - "github.com/ava-labs/avalanchego/utils/math" - "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "golang.org/x/exp/maps" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/fees" "github.com/ava-labs/hypersdk/requester" "github.com/ava-labs/hypersdk/utils" - - avautils "github.com/ava-labs/avalanchego/utils" ) const ( @@ -123,41 +115,6 @@ func (cli *JSONRPCClient) SubmitTx(ctx context.Context, d []byte) (ids.ID, error return resp.TxID, err } -func (cli *JSONRPCClient) GetWarpSignatures( - ctx context.Context, - txID ids.ID, -) (*warp.UnsignedMessage, map[ids.NodeID]*validators.GetValidatorOutput, []*chain.WarpSignature, error) { - resp := new(GetWarpSignaturesReply) - if err := cli.requester.SendRequest( - ctx, - "getWarpSignatures", - &GetWarpSignaturesArgs{TxID: txID}, - resp, - ); err != nil { - return nil, nil, nil, err - } - // Ensure message is initialized - if err := resp.Message.Initialize(); err != nil { - return nil, nil, nil, err - } - m := map[ids.NodeID]*validators.GetValidatorOutput{} - for _, vdr := range resp.Validators { - vout := &validators.GetValidatorOutput{ - NodeID: vdr.NodeID, - Weight: vdr.Weight, - } - if len(vdr.PublicKey) > 0 { - pk, err := bls.PublicKeyFromBytes(vdr.PublicKey) - if err != nil { - return nil, nil, nil, err - } - vout.PublicKey = pk - } - m[vdr.NodeID] = vout - } - return resp.Message, m, resp.Signatures, nil -} - type Modifier interface { Base(*chain.Base) } @@ -165,7 +122,6 @@ type Modifier interface { func (cli *JSONRPCClient) GenerateTransaction( ctx context.Context, parser chain.Parser, - wm *warp.Message, action chain.Action, authFactory chain.AuthFactory, modifiers ...Modifier, @@ -176,7 +132,7 @@ func (cli *JSONRPCClient) GenerateTransaction( return nil, nil, 0, err } - maxUnits, err := chain.EstimateMaxUnits(parser.Rules(time.Now().UnixMilli()), action, authFactory, wm) + maxUnits, err := chain.EstimateMaxUnits(parser.Rules(time.Now().UnixMilli()), action, authFactory) if err != nil { return nil, nil, 0, err } @@ -184,7 +140,7 @@ func (cli *JSONRPCClient) GenerateTransaction( if err != nil { return nil, nil, 0, err } - f, tx, err := cli.GenerateTransactionManual(parser, wm, action, authFactory, maxFee, modifiers...) + f, tx, err := cli.GenerateTransactionManual(parser, action, authFactory, maxFee, modifiers...) if err != nil { return nil, nil, 0, err } @@ -193,7 +149,6 @@ func (cli *JSONRPCClient) GenerateTransaction( func (cli *JSONRPCClient) GenerateTransactionManual( parser chain.Parser, - wm *warp.Message, action chain.Action, authFactory chain.AuthFactory, maxFee uint64, @@ -213,16 +168,9 @@ func (cli *JSONRPCClient) GenerateTransactionManual( m.Base(base) } - // Ensure warp message is intialized before we marshal it - if wm != nil { - if err := wm.Initialize(); err != nil { - return nil, nil, err - } - } - // Build transaction actionRegistry, authRegistry := parser.Registry() - tx := chain.NewTx(base, wm, action) + tx := chain.NewTx(base, action) tx, err := tx.Sign(authFactory, actionRegistry, authRegistry) if err != nil { return nil, nil, fmt.Errorf("%w: failed to sign transaction", err) @@ -248,101 +196,3 @@ func Wait(ctx context.Context, check func(ctx context.Context) (bool, error)) er } return ctx.Err() } - -// getCanonicalValidatorSet returns the validator set of [subnetID] in a canonical ordering. -// Also returns the total weight on [subnetID]. -func getCanonicalValidatorSet( - _ context.Context, - vdrSet map[ids.NodeID]*validators.GetValidatorOutput, -) ([]*warp.Validator, uint64, error) { - var ( - vdrs = make(map[string]*warp.Validator, len(vdrSet)) - totalWeight uint64 - err error - ) - for _, vdr := range vdrSet { - totalWeight, err = math.Add64(totalWeight, vdr.Weight) - if err != nil { - return nil, 0, fmt.Errorf("%w: %v", warp.ErrWeightOverflow, err) //nolint:errorlint - } - - if vdr.PublicKey == nil { - fmt.Println("skipping validator because of empty public key", vdr.NodeID) - continue - } - - pkBytes := bls.PublicKeyToBytes(vdr.PublicKey) - uniqueVdr, ok := vdrs[string(pkBytes)] - if !ok { - uniqueVdr = &warp.Validator{ - PublicKey: vdr.PublicKey, - PublicKeyBytes: pkBytes, - } - vdrs[string(pkBytes)] = uniqueVdr - } - - uniqueVdr.Weight += vdr.Weight // Impossible to overflow here - uniqueVdr.NodeIDs = append(uniqueVdr.NodeIDs, vdr.NodeID) - } - - // Sort validators by public key - vdrList := maps.Values(vdrs) - avautils.Sort(vdrList) - return vdrList, totalWeight, nil -} - -func (cli *JSONRPCClient) GenerateAggregateWarpSignature( - ctx context.Context, - txID ids.ID, -) (*warp.Message, uint64, uint64, error) { - unsignedMessage, validators, signatures, err := cli.GetWarpSignatures(ctx, txID) - if err != nil { - return nil, 0, 0, fmt.Errorf("%w: failed to fetch warp signatures", err) - } - - // Get canonical validator ordering to generate signature bit set - canonicalValidators, weight, err := getCanonicalValidatorSet(ctx, validators) - if err != nil { - return nil, 0, 0, fmt.Errorf("%w: failed to get canonical validator set", err) - } - - // Generate map of bls.PublicKey => Signature - signatureMap := map[ids.ID][]byte{} - for _, signature := range signatures { - // Convert to hash for easy comparison (could just as easily store the raw - // public key but that would involve a number of memory copies) - signatureMap[utils.ToID(signature.PublicKey)] = signature.Signature - } - - // Generate signature - signers := set.NewBits() - var signatureWeight uint64 - orderedSignatures := []*bls.Signature{} - for i, vdr := range canonicalValidators { - sig, ok := signatureMap[utils.ToID(vdr.PublicKeyBytes)] - if !ok { - continue - } - blsSig, err := bls.SignatureFromBytes(sig) - if err != nil { - return nil, 0, 0, err - } - signers.Add(i) - signatureWeight += vdr.Weight - orderedSignatures = append(orderedSignatures, blsSig) - } - aggSignature, err := bls.AggregateSignatures(orderedSignatures) - if err != nil { - return nil, 0, 0, fmt.Errorf("%w: failed to aggregate signatures", err) - } - aggSignatureBytes := bls.SignatureToBytes(aggSignature) - signature := &warp.BitSetSignature{ - Signers: signers.Bytes(), - } - copy(signature.Signature[:], aggSignatureBytes) - message, err := warp.NewMessage(unsignedMessage, signature) - if err != nil { - return nil, 0, 0, fmt.Errorf("%w: failed to generate warp message", err) - } - return message, weight, signatureWeight, nil -} diff --git a/rpc/jsonrpc_server.go b/rpc/jsonrpc_server.go index f18211abbc..509a087e14 100644 --- a/rpc/jsonrpc_server.go +++ b/rpc/jsonrpc_server.go @@ -4,15 +4,11 @@ package rpc import ( - "context" "errors" "fmt" "net/http" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/crypto/bls" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "go.uber.org/zap" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" @@ -122,82 +118,3 @@ func (j *JSONRPCServer) UnitPrices( reply.UnitPrices = unitPrices return nil } - -type GetWarpSignaturesArgs struct { - TxID ids.ID `json:"txID"` -} - -type WarpValidator struct { - NodeID ids.NodeID `json:"nodeID"` - PublicKey []byte `json:"publicKey"` - Weight uint64 `json:"weight"` -} - -type GetWarpSignaturesReply struct { - Validators []*WarpValidator `json:"validators"` - Message *warp.UnsignedMessage `json:"message"` - Signatures []*chain.WarpSignature `json:"signatures"` -} - -func (j *JSONRPCServer) GetWarpSignatures( - req *http.Request, - args *GetWarpSignaturesArgs, - reply *GetWarpSignaturesReply, -) error { - _, span := j.vm.Tracer().Start(req.Context(), "JSONRPCServer.GetWarpSignatures") - defer span.End() - - message, err := j.vm.GetOutgoingWarpMessage(args.TxID) - if err != nil { - return err - } - if message == nil { - return ErrMessageMissing - } - - signatures, err := j.vm.GetWarpSignatures(args.TxID) - if err != nil { - return err - } - - // Ensure we only return valid signatures - validSignatures := []*chain.WarpSignature{} - warpValidators := []*WarpValidator{} - validators, publicKeys := j.vm.CurrentValidators(req.Context()) - for _, sig := range signatures { - if _, ok := publicKeys[string(sig.PublicKey)]; !ok { - continue - } - validSignatures = append(validSignatures, sig) - } - for _, vdr := range validators { - wv := &WarpValidator{ - NodeID: vdr.NodeID, - Weight: vdr.Weight, - } - if vdr.PublicKey != nil { - wv.PublicKey = bls.PublicKeyToBytes(vdr.PublicKey) - } - warpValidators = append(warpValidators, wv) - } - - // Optimistically request that we gather signatures if we don't have all of them - if len(validSignatures) < len(publicKeys) { - j.vm.Logger().Info( - "fetching missing signatures", - zap.Stringer("txID", args.TxID), - zap.Int( - "previously collected", - len(signatures), - ), - zap.Int("valid", len(validSignatures)), - zap.Int("current public key count", len(publicKeys)), - ) - j.vm.GatherSignatures(context.TODO(), args.TxID, message.Bytes()) - } - - reply.Message = message - reply.Validators = warpValidators - reply.Signatures = validSignatures - return nil -} diff --git a/vm/network_warp.go b/vm/network_warp.go deleted file mode 100644 index 45b5169507..0000000000 --- a/vm/network_warp.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package vm - -import ( - "context" - "time" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/version" -) - -type WarpHandler struct { - vm *VM -} - -func NewWarpHandler(vm *VM) *WarpHandler { - return &WarpHandler{vm} -} - -func (*WarpHandler) Connected(context.Context, ids.NodeID, *version.Application) error { - return nil -} - -func (*WarpHandler) Disconnected(context.Context, ids.NodeID) error { - return nil -} - -func (*WarpHandler) AppGossip(context.Context, ids.NodeID, []byte) error { - return nil -} - -func (w *WarpHandler) AppRequest( - ctx context.Context, - nodeID ids.NodeID, - requestID uint32, - _ time.Time, - request []byte, -) error { - return w.vm.warpManager.AppRequest(ctx, nodeID, requestID, request) -} - -func (w *WarpHandler) AppRequestFailed( - _ context.Context, - _ ids.NodeID, - requestID uint32, -) error { - return w.vm.warpManager.HandleRequestFailed(requestID) -} - -func (w *WarpHandler) AppResponse( - _ context.Context, - _ ids.NodeID, - requestID uint32, - response []byte, -) error { - return w.vm.warpManager.HandleResponse(requestID, response) -} - -func (*WarpHandler) CrossChainAppRequest(context.Context, ids.ID, uint32, time.Time, []byte) error { - return nil -} - -func (*WarpHandler) CrossChainAppRequestFailed(context.Context, ids.ID, uint32) error { - return nil -} - -func (*WarpHandler) CrossChainAppResponse(context.Context, ids.ID, uint32, []byte) error { - return nil -} diff --git a/vm/resolutions.go b/vm/resolutions.go index ccd243964c..f12d892224 100644 --- a/vm/resolutions.go +++ b/vm/resolutions.go @@ -26,12 +26,11 @@ import ( ) var ( - _ chain.VM = (*VM)(nil) - _ gossiper.VM = (*VM)(nil) - _ builder.VM = (*VM)(nil) - _ block.ChainVM = (*VM)(nil) - _ block.StateSyncableVM = (*VM)(nil) - _ block.BuildBlockWithContextChainVM = (*VM)(nil) + _ chain.VM = (*VM)(nil) + _ gossiper.VM = (*VM)(nil) + _ builder.VM = (*VM)(nil) + _ block.ChainVM = (*VM)(nil) + _ block.StateSyncableVM = (*VM)(nil) ) func (vm *VM) ChainID() ids.ID { @@ -175,36 +174,10 @@ func (vm *VM) processAcceptedBlock(b *chain.StatelessBlock) { vm.Fatal("accepted processing failed", zap.Error(err)) } - // Sign and store any warp messages (regardless if validator now, may become one) - results := b.Results() - for i, tx := range b.Txs { + // TODO: consider removing this (unused and requires an extra iteration) + for _, tx := range b.Txs { // Only cache auth for accepted blocks to prevent cache manipulation from RPC submissions vm.cacheAuth(tx.Auth) - - result := results[i] - if result.WarpMessage == nil { - continue - } - start := time.Now() - signature, err := vm.snowCtx.WarpSigner.Sign(result.WarpMessage) - if err != nil { - vm.Fatal("unable to sign warp message", zap.Error(err)) - } - if err := vm.StoreWarpSignature(tx.ID(), vm.snowCtx.PublicKey, signature); err != nil { - vm.Fatal("unable to store warp signature", zap.Error(err)) - } - vm.snowCtx.Log.Info( - "signed and stored warp message signature", - zap.Stringer("txID", tx.ID()), - zap.Duration("t", time.Since(start)), - ) - - // Kickoff job to fetch signatures from other validators in the - // background - // - // We pass bytes here so that signatures returned from validators can be - // verified before they are persisted. - vm.warpManager.GatherSignatures(context.TODO(), tx.ID(), result.WarpMessage.Bytes()) } // Update server @@ -333,10 +306,6 @@ func (vm *VM) CurrentValidators( return vm.proposerMonitor.Validators(ctx) } -func (vm *VM) GatherSignatures(ctx context.Context, txID ids.ID, msg []byte) { - vm.warpManager.GatherSignatures(ctx, txID, msg) -} - func (vm *VM) NodeID() ids.NodeID { return vm.snowCtx.NodeID } diff --git a/vm/storage.go b/vm/storage.go index f89cd1385a..9c4d36df0d 100644 --- a/vm/storage.go +++ b/vm/storage.go @@ -4,7 +4,6 @@ package vm import ( - "bytes" "context" "encoding/binary" "errors" @@ -12,17 +11,13 @@ import ( "math/rand" "time" - "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/choices" - "github.com/ava-labs/avalanchego/utils/crypto/bls" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "go.uber.org/zap" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/keys" ) // compactionOffset is used to randomize the height that we compact @@ -38,15 +33,11 @@ const ( blockPrefix = 0x0 // TODO: move to flat files (https://github.com/ava-labs/hypersdk/issues/553) blockIDHeightPrefix = 0x1 // ID -> Height blockHeightIDPrefix = 0x2 // Height -> ID (don't always need full block from disk) - warpSignaturePrefix = 0x3 - warpFetchPrefix = 0x4 ) var ( isSyncing = []byte("is_syncing") lastAccepted = []byte("last_accepted") - - signatureLRU = &cache.LRU[string, *chain.WarpSignature]{Size: 1024} ) func PrefixBlockKey(height uint64) []byte { @@ -220,96 +211,3 @@ func (vm *VM) PutDiskIsSyncing(v bool) error { } return vm.vmDB.Put(isSyncing, []byte{0x0}) } - -func (vm *VM) GetOutgoingWarpMessage(txID ids.ID) (*warp.UnsignedMessage, error) { - p := vm.c.StateManager().OutgoingWarpKeyPrefix(txID) - k := keys.EncodeChunks(p, chain.MaxOutgoingWarpChunks) - vs, errs := vm.ReadState(context.TODO(), [][]byte{k}) - v, err := vs[0], errs[0] - if errors.Is(err, database.ErrNotFound) { - return nil, nil - } - if err != nil { - return nil, err - } - return warp.ParseUnsignedMessage(v) -} - -func PrefixWarpSignatureKey(txID ids.ID, signer *bls.PublicKey) []byte { - k := make([]byte, 1+consts.IDLen+bls.PublicKeyLen) - k[0] = warpSignaturePrefix - copy(k[1:], txID[:]) - copy(k[1+consts.IDLen:], bls.PublicKeyToBytes(signer)) - return k -} - -func (vm *VM) StoreWarpSignature(txID ids.ID, signer *bls.PublicKey, signature []byte) error { - k := PrefixWarpSignatureKey(txID, signer) - // Cache any signature we produce for later queries from peers - if bytes.Equal(vm.pkBytes, bls.PublicKeyToBytes(signer)) { - signatureLRU.Put(string(k), chain.NewWarpSignature(vm.pkBytes, signature)) - } - return vm.vmDB.Put(k, signature) -} - -func (vm *VM) GetWarpSignature(txID ids.ID, signer *bls.PublicKey) (*chain.WarpSignature, error) { - k := PrefixWarpSignatureKey(txID, signer) - if ws, ok := signatureLRU.Get(string(k)); ok { - return ws, nil - } - v, err := vm.vmDB.Get(k) - if errors.Is(err, database.ErrNotFound) { - return nil, nil - } - if err != nil { - return nil, err - } - ws := &chain.WarpSignature{ - PublicKey: bls.PublicKeyToBytes(signer), - Signature: v, - } - return ws, nil -} - -func (vm *VM) GetWarpSignatures(txID ids.ID) ([]*chain.WarpSignature, error) { - prefix := make([]byte, 1+consts.IDLen) - prefix[0] = warpSignaturePrefix - copy(prefix[1:], txID[:]) - iter := vm.vmDB.NewIteratorWithPrefix(prefix) - defer iter.Release() - - // Collect all signatures we have for a txID - signatures := []*chain.WarpSignature{} - for iter.Next() { - k := iter.Key() - signatures = append(signatures, &chain.WarpSignature{ - PublicKey: k[len(k)-bls.PublicKeyLen:], - Signature: iter.Value(), - }) - } - return signatures, iter.Error() -} - -func PrefixWarpFetchKey(txID ids.ID) []byte { - k := make([]byte, 1+consts.IDLen) - k[0] = warpFetchPrefix - copy(k[1:], txID[:]) - return k -} - -func (vm *VM) StoreWarpFetch(txID ids.ID) error { - k := PrefixWarpFetchKey(txID) - return vm.vmDB.Put(k, binary.BigEndian.AppendUint64(nil, uint64(time.Now().UnixMilli()))) -} - -func (vm *VM) GetWarpFetch(txID ids.ID) (int64, error) { - k := PrefixWarpFetchKey(txID) - v, err := vm.vmDB.Get(k) - if errors.Is(err, database.ErrNotFound) { - return -1, nil - } - if err != nil { - return -1, err - } - return int64(binary.BigEndian.Uint64(v)), nil -} diff --git a/vm/vm.go b/vm/vm.go index c47fec0f19..ab91d2970f 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -17,7 +17,6 @@ import ( "github.com/ava-labs/avalanchego/snow/choices" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/profiler" "github.com/ava-labs/avalanchego/utils/set" @@ -112,10 +111,6 @@ type VM struct { stateSyncNetworkClient avasync.NetworkClient stateSyncNetworkServer *avasync.NetworkServer - // Warp manager fetches signatures from other validators for a given accepted - // txID - warpManager *WarpManager - // Network manager routes p2p messages to pre-registered handlers networkManager *network.Manager @@ -167,10 +162,6 @@ func (vm *VM) Initialize( vm.proposerMonitor = NewProposerMonitor(vm) vm.networkManager = network.NewManager(vm.snowCtx.Log, vm.snowCtx.NodeID, appSender) - warpHandler, warpSender := vm.networkManager.Register() - vm.warpManager = NewWarpManager(vm) - vm.networkManager.SetHandler(warpHandler, NewWarpHandler(vm)) - go vm.warpManager.Run(warpSender) vm.baseDB = baseDB // Always initialize implementation first @@ -561,7 +552,6 @@ func (vm *VM) Shutdown(ctx context.Context) error { <-vm.acceptorDone // Shutdown other async VM mechanisms - vm.warpManager.Done() vm.builder.Done() vm.gossiper.Done() vm.authVerifiers.Stop() @@ -708,7 +698,16 @@ func (vm *VM) ParseBlock(ctx context.Context, source []byte) (snowman.Block, err return newBlk, nil } -func (vm *VM) buildBlock(ctx context.Context, blockContext *block.Context) (snowman.Block, error) { +// implements "block.ChainVM" +func (vm *VM) BuildBlock(ctx context.Context) (snowman.Block, error) { + start := time.Now() + defer func() { + vm.metrics.blockBuild.Observe(float64(time.Since(start))) + }() + + ctx, span := vm.tracer.Start(ctx, "VM.BuildBlock") + defer span.End() + // If the node isn't ready, we should exit. // // We call [QueueNotify] when the VM becomes ready, so exiting @@ -738,7 +737,7 @@ func (vm *VM) buildBlock(ctx context.Context, blockContext *block.Context) (snow vm.snowCtx.Log.Warn("unable to get preferred block", zap.Error(err)) return nil, err } - blk, err := chain.BuildBlock(ctx, vm, preferredBlk, blockContext) + blk, err := chain.BuildBlock(ctx, vm, preferredBlk) if err != nil { // This is a DEBUG log because BuildBlock may fail before // the min build gap (especially when there are no transactions). @@ -749,32 +748,6 @@ func (vm *VM) buildBlock(ctx context.Context, blockContext *block.Context) (snow return blk, nil } -// implements "block.ChainVM" -func (vm *VM) BuildBlock(ctx context.Context) (snowman.Block, error) { - start := time.Now() - defer func() { - vm.metrics.blockBuild.Observe(float64(time.Since(start))) - }() - - ctx, span := vm.tracer.Start(ctx, "VM.BuildBlock") - defer span.End() - - return vm.buildBlock(ctx, nil) -} - -// implements "block.BuildBlockWithContextChainVM" -func (vm *VM) BuildBlockWithContext(ctx context.Context, blockContext *block.Context) (snowman.Block, error) { - start := time.Now() - defer func() { - vm.metrics.blockBuild.Observe(float64(time.Since(start))) - }() - - ctx, span := vm.tracer.Start(ctx, "VM.BuildBlockWithContext") - defer span.End() - - return vm.buildBlock(ctx, blockContext) -} - func (vm *VM) Submit( ctx context.Context, verifyAuth bool, diff --git a/vm/warp_manager.go b/vm/warp_manager.go deleted file mode 100644 index 13c851e277..0000000000 --- a/vm/warp_manager.go +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package vm - -import ( - "bytes" - "context" - "encoding/hex" - "sync" - "time" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/snow/engine/common" - "github.com/ava-labs/avalanchego/utils/crypto/bls" - "github.com/ava-labs/avalanchego/utils/set" - "go.uber.org/zap" - - "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/codec" - "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/heap" - "github.com/ava-labs/hypersdk/utils" -) - -const ( - maxWarpResponse = bls.PublicKeyLen + bls.SignatureLen - minGatherInterval = 30 * 60 // 30 minutes - initialBackoff = 2 // give time for others to sign - backoffIncrease = 5 - maxRetries = 10 - maxOutstanding = 8 // TODO: make a config -) - -// WarpManager takes requests to get signatures from other nodes and then -// stores the result in our DB for future usage. -type WarpManager struct { - vm *VM - appSender common.AppSender - - l sync.Mutex - requestID uint32 - - pendingJobs *heap.Heap[*signatureJob, int64] - jobs map[uint32]*signatureJob - - done chan struct{} -} - -type signatureJob struct { - id ids.ID - nodeID ids.NodeID - publicKey []byte - txID ids.ID - retry int - msg []byte -} - -func NewWarpManager(vm *VM) *WarpManager { - return &WarpManager{ - vm: vm, - pendingJobs: heap.New[*signatureJob, int64](64, true), - jobs: map[uint32]*signatureJob{}, - done: make(chan struct{}), - } -} - -func (w *WarpManager) Run(appSender common.AppSender) { - w.appSender = appSender - - w.vm.Logger().Info("starting warp manager") - defer close(w.done) - - t := time.NewTicker(1 * time.Second) - defer t.Stop() - for { - select { - case <-t.C: - w.l.Lock() - now := time.Now().Unix() - for w.pendingJobs.Len() > 0 && len(w.jobs) < maxOutstanding { - first := w.pendingJobs.First() - if first.Val > now { - break - } - w.pendingJobs.Pop() - - // Send request - job := first.Item - if err := w.request(context.Background(), job); err != nil { - w.vm.snowCtx.Log.Error( - "unable to request signature", - zap.Stringer("nodeID", job.nodeID), - zap.Error(err), - ) - } - } - l := w.pendingJobs.Len() - w.l.Unlock() - w.vm.snowCtx.Log.Debug("checked for ready jobs", zap.Int("pending", l)) - case <-w.vm.stop: - w.vm.Logger().Info("stopping warp manager") - return - } - } -} - -// GatherSignatures makes a best effort to acquire signatures from other -// validators and store them inside the vmDB. -// -// GatherSignatures may be called when a block is accepted (optimistically) or -// may be triggered by RPC (if missing signatures are detected). To prevent RPC -// abuse, we limit how frequently we attempt to gather signatures for a given -// TxID. -func (w *WarpManager) GatherSignatures(ctx context.Context, txID ids.ID, msg []byte) { - lastFetch, err := w.vm.GetWarpFetch(txID) - if err != nil { - w.vm.snowCtx.Log.Error("unable to get last fetch", zap.Error(err)) - return - } - if time.Now().Unix()-lastFetch < minGatherInterval { - w.vm.snowCtx.Log.Error("skipping fetch too recent", zap.Stringer("txID", txID)) - return - } - if err := w.vm.StoreWarpFetch(txID); err != nil { - w.vm.snowCtx.Log.Error("unable to get last fetch", zap.Error(err)) - return - } - height, err := w.vm.snowCtx.ValidatorState.GetCurrentHeight(ctx) - if err != nil { - w.vm.snowCtx.Log.Error("unable to get current p-chain height", zap.Error(err)) - return - } - validators, err := w.vm.snowCtx.ValidatorState.GetValidatorSet( - ctx, - height, - w.vm.snowCtx.SubnetID, - ) - if err != nil { - w.vm.snowCtx.Log.Error("unable to get validator set", zap.Error(err)) - return - } - for nodeID, validator := range validators { - // Only request from validators that have registered BLS public keys and - // that we have not already gotten a signature from. - if validator.PublicKey == nil { - w.vm.snowCtx.Log.Info( - "skipping fetch for validator with no registered public key", - zap.Stringer("nodeID", nodeID), - zap.Uint64("pchain height", height), - ) - continue - } - previousSignature, err := w.vm.GetWarpSignature(txID, validator.PublicKey) - if err != nil { - w.vm.snowCtx.Log.Error("unable to fetch previous signature", zap.Error(err)) - return - } - if previousSignature != nil { - continue - } - - idb := make([]byte, consts.IDLen+consts.NodeIDLen) - copy(idb, txID[:]) - copy(idb[consts.IDLen:], nodeID.Bytes()) - id := utils.ToID(idb) - w.l.Lock() - if w.pendingJobs.Has(id) { - // We may already have enqueued a job when the block was accepted. - w.l.Unlock() - continue - } - w.pendingJobs.Push(&heap.Entry[*signatureJob, int64]{ - ID: id, - Item: &signatureJob{ - id, - nodeID, - bls.PublicKeyToBytes(validator.PublicKey), - txID, - 0, - msg, - }, - Val: time.Now().Unix() + initialBackoff, - Index: w.pendingJobs.Len(), - }) - w.l.Unlock() - w.vm.snowCtx.Log.Debug( - "enqueued fetch job", - zap.Stringer("nodeID", nodeID), - zap.Stringer("txID", txID), - ) - } -} - -// you must hold [w.l] when calling this function -func (w *WarpManager) request( - ctx context.Context, - j *signatureJob, -) error { - requestID := w.requestID - w.requestID++ - w.jobs[requestID] = j - - return w.appSender.SendAppRequest( - ctx, - set.Of(j.nodeID), - requestID, - j.txID[:], - ) -} - -func (w *WarpManager) AppRequest( - ctx context.Context, - nodeID ids.NodeID, - requestID uint32, - request []byte, -) error { - rp := codec.NewReader(request, consts.IDLen) - var txID ids.ID - rp.UnpackID(true, &txID) - if err := rp.Err(); err != nil { - w.vm.snowCtx.Log.Warn("unable to unpack request", zap.Error(err)) - return nil - } - sig, err := w.vm.GetWarpSignature(txID, w.vm.snowCtx.PublicKey) - if err != nil { - w.vm.snowCtx.Log.Warn("could not fetch warp signature", zap.Error(err)) - return nil - } - if sig == nil { - // Generate and save signature if it does not exist but is in state (may - // have been offline when message was accepted) - msg, err := w.vm.GetOutgoingWarpMessage(txID) - if msg == nil || err != nil { - w.vm.snowCtx.Log.Warn("could not get outgoing warp message", zap.Error(err)) - return nil - } - rSig, err := w.vm.snowCtx.WarpSigner.Sign(msg) - if err != nil { - w.vm.snowCtx.Log.Warn("could not sign outgoing warp message", zap.Error(err)) - return nil - } - if err := w.vm.StoreWarpSignature(txID, w.vm.snowCtx.PublicKey, rSig); err != nil { - w.vm.snowCtx.Log.Warn("could not store warp signature", zap.Error(err)) - return nil - } - sig = &chain.WarpSignature{ - PublicKey: w.vm.pkBytes, - Signature: rSig, - } - } - size := len(sig.PublicKey) + len(sig.Signature) - wp := codec.NewWriter(size, maxWarpResponse) - wp.PackFixedBytes(sig.PublicKey) - wp.PackFixedBytes(sig.Signature) - if err := wp.Err(); err != nil { - w.vm.snowCtx.Log.Warn("could not encode warp signature", zap.Error(err)) - return nil - } - return w.appSender.SendAppResponse(ctx, nodeID, requestID, wp.Bytes()) -} - -func (w *WarpManager) HandleResponse(requestID uint32, msg []byte) error { - w.l.Lock() - job, ok := w.jobs[requestID] - delete(w.jobs, requestID) - w.l.Unlock() - if !ok { - return nil - } - - // Parse message - r := codec.NewReader(msg, maxWarpResponse) - publicKey := make([]byte, bls.PublicKeyLen) - r.UnpackFixedBytes(bls.PublicKeyLen, &publicKey) - signature := make([]byte, bls.SignatureLen) - r.UnpackFixedBytes(bls.SignatureLen, &signature) - if err := r.Err(); err != nil { - w.vm.snowCtx.Log.Warn("could not decode warp signature", zap.Error(err)) - return nil - } - - // Check public key is expected - if !bytes.Equal(publicKey, job.publicKey) { - w.vm.snowCtx.Log.Warn( - "public key mismatch", - zap.String("found", hex.EncodeToString(publicKey)), - zap.String("expected", hex.EncodeToString(job.publicKey)), - ) - return nil - } - - // Check signature validity - pk, err := bls.PublicKeyFromBytes(publicKey) - if err != nil { - w.vm.snowCtx.Log.Warn("could not decode public key", zap.Error(err)) - return nil - } - sig, err := bls.SignatureFromBytes(signature) - if err != nil { - w.vm.snowCtx.Log.Warn("could not decode signature", zap.Error(err)) - return nil - } - if !bls.Verify(pk, sig, job.msg) { - w.vm.snowCtx.Log.Warn("could not verify signature") - return nil - } - - // Store in DB - if err := w.vm.StoreWarpSignature(job.txID, pk, signature); err != nil { - w.vm.snowCtx.Log.Warn("could not store warp signature", zap.Error(err)) - return nil - } - - w.vm.snowCtx.Log.Info( - "fetched and stored signature", - zap.Stringer("txID", job.txID), - zap.Stringer( - "nodeID", - job.nodeID, - ), - zap.String("publicKey", hex.EncodeToString(job.publicKey)), - ) - return nil -} - -func (w *WarpManager) HandleRequestFailed(requestID uint32) error { - w.l.Lock() - job, ok := w.jobs[requestID] - delete(w.jobs, requestID) - w.l.Unlock() - if !ok { - return nil - } - - // Drop if we've already retried too many times - if job.retry >= maxRetries { - w.vm.snowCtx.Log.Info( - "fetch job failed", - zap.Stringer("nodeID", job.nodeID), - zap.Stringer("txID", job.txID), - ) - return nil - } - job.retry++ - - w.l.Lock() - w.pendingJobs.Push(&heap.Entry[*signatureJob, int64]{ - ID: job.id, - Item: job, - Val: time.Now().Unix() + int64(backoffIncrease*job.retry), - Index: w.pendingJobs.Len(), - }) - w.l.Unlock() - return nil -} - -func (w *WarpManager) Done() { - <-w.done -} diff --git a/x/programs/cmd/simulator/cmd/program.go b/x/programs/cmd/simulator/cmd/program.go index 48409d2495..d0c9a98307 100644 --- a/x/programs/cmd/simulator/cmd/program.go +++ b/x/programs/cmd/simulator/cmd/program.go @@ -114,7 +114,7 @@ func programCreateFunc(ctx context.Context, db *state.SimpleMutable, path string } // execute the action - success, _, output, _, err := programCreateAction.Execute(ctx, nil, db, 0, codec.EmptyAddress, programID, false) + success, _, output, err := programCreateAction.Execute(ctx, nil, db, 0, codec.EmptyAddress, programID) if output != nil { fmt.Println(string(output)) } @@ -157,7 +157,7 @@ func programExecuteFunc( } // execute the action - success, _, resp, _, err := programExecuteAction.Execute(ctx, nil, db, 0, codec.EmptyAddress, programTxID, false) + success, _, resp, err := programExecuteAction.Execute(ctx, nil, db, 0, codec.EmptyAddress, programTxID) if !success { return ids.Empty, nil, 0, fmt.Errorf("program execution failed: %s", string(resp)) diff --git a/x/programs/cmd/simulator/vm/actions/program_create.go b/x/programs/cmd/simulator/vm/actions/program_create.go index 512454108b..f010f1cb31 100644 --- a/x/programs/cmd/simulator/vm/actions/program_create.go +++ b/x/programs/cmd/simulator/vm/actions/program_create.go @@ -7,7 +7,6 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -36,10 +35,6 @@ func (*ProgramCreate) StateKeysMaxChunks() []uint16 { return []uint16{storage.ProgramChunks} } -func (*ProgramCreate) OutputsWarpMessage() bool { - return false -} - func (t *ProgramCreate) Execute( ctx context.Context, _ chain.Rules, @@ -47,17 +42,16 @@ func (t *ProgramCreate) Execute( _ int64, _ codec.Address, id ids.ID, - _ bool, -) (bool, uint64, []byte, *warp.UnsignedMessage, error) { +) (bool, uint64, []byte, error) { if len(t.Program) == 0 { - return false, 1, OutputValueZero, nil, nil + return false, 1, OutputValueZero, nil } if err := storage.SetProgram(ctx, mu, id, t.Program); err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } - return true, 1, nil, nil, nil + return true, 1, nil, nil } func (*ProgramCreate) MaxComputeUnits(chain.Rules) uint64 { @@ -72,7 +66,7 @@ func (t *ProgramCreate) Marshal(p *codec.Packer) { p.PackBytes(t.Program) } -func UnmarshalProgramCreate(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalProgramCreate(p *codec.Packer) (chain.Action, error) { var pc ProgramCreate p.UnpackBytes(-1, true, &pc.Program) return &pc, p.Err() diff --git a/x/programs/cmd/simulator/vm/actions/program_execute.go b/x/programs/cmd/simulator/vm/actions/program_execute.go index a6ea74c397..cb7c51b988 100644 --- a/x/programs/cmd/simulator/vm/actions/program_execute.go +++ b/x/programs/cmd/simulator/vm/actions/program_execute.go @@ -16,7 +16,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -53,10 +52,6 @@ func (*ProgramExecute) StateKeysMaxChunks() []uint16 { return []uint16{storage.ProgramChunks} } -func (*ProgramExecute) OutputsWarpMessage() bool { - return false -} - func (t *ProgramExecute) Execute( ctx context.Context, _ chain.Rules, @@ -64,41 +59,40 @@ func (t *ProgramExecute) Execute( _ int64, actor codec.Address, _ ids.ID, - _ bool, -) (success bool, computeUnits uint64, output []byte, warpMessage *warp.UnsignedMessage, err error) { +) (success bool, computeUnits uint64, output []byte, err error) { if len(t.Function) == 0 { - return false, 1, OutputValueZero, nil, nil + return false, 1, OutputValueZero, nil } if len(t.Params) == 0 { - return false, 1, OutputValueZero, nil, nil + return false, 1, OutputValueZero, nil } programIDStr, ok := t.Params[0].Value.(string) if !ok { - return false, 1, utils.ErrBytes(fmt.Errorf("invalid call param: must be ID")), nil, nil + return false, 1, utils.ErrBytes(fmt.Errorf("invalid call param: must be ID")), nil } // TODO: take fee out of balance? programID, err := ids.FromString(programIDStr) if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } programBytes, err := storage.GetProgram(ctx, mu, programID) if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } // TODO: get cfg from genesis cfg := runtime.NewConfig() if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } ecfg, err := engine.NewConfigBuilder(). WithDefaultCache(true). Build() if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } eng := engine.New(ecfg) @@ -121,22 +115,22 @@ func (t *ProgramExecute) Execute( t.rt = runtime.New(logging.NoLog{}, eng, imports, cfg) err = t.rt.Initialize(ctx, callContext, programBytes, t.MaxUnits) if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } defer t.rt.Stop() mem, err := t.rt.Memory() if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } params, err := WriteParams(mem, t.Params) if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } resp, err := t.rt.Call(ctx, t.Function, callContext, params[1:]...) if err != nil { - return false, 1, utils.ErrBytes(err), nil, nil + return false, 1, utils.ErrBytes(err), nil } // TODO: remove this is to support readonly response for now. @@ -145,7 +139,7 @@ func (t *ProgramExecute) Execute( p.PackInt64(r) } - return true, 1, p.Bytes(), nil, nil + return true, 1, p.Bytes(), nil } func (*ProgramExecute) MaxComputeUnits(chain.Rules) uint64 { @@ -164,7 +158,7 @@ func (t *ProgramExecute) GetBalance() (uint64, error) { return t.rt.Meter().GetBalance() } -func UnmarshalProgramExecute(p *codec.Packer, _ *warp.Message) (chain.Action, error) { +func UnmarshalProgramExecute(p *codec.Packer) (chain.Action, error) { // TODO return nil, nil } diff --git a/x/programs/cmd/simulator/vm/consts/consts.go b/x/programs/cmd/simulator/vm/consts/consts.go index bbf8a04a46..67a3270180 100644 --- a/x/programs/cmd/simulator/vm/consts/consts.go +++ b/x/programs/cmd/simulator/vm/consts/consts.go @@ -5,7 +5,6 @@ package consts import ( "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" @@ -33,6 +32,6 @@ func init() { // Instantiate registry here so it can be imported by any package. We set these // values in [controller/registry]. var ( - ActionRegistry *codec.TypeParser[chain.Action, *warp.Message, bool] - AuthRegistry *codec.TypeParser[chain.Auth, *warp.Message, bool] + ActionRegistry *codec.TypeParser[chain.Action, bool] + AuthRegistry *codec.TypeParser[chain.Auth, bool] ) diff --git a/x/programs/cmd/simulator/vm/genesis/genesis.go b/x/programs/cmd/simulator/vm/genesis/genesis.go index fcce6f63f2..958ca59817 100644 --- a/x/programs/cmd/simulator/vm/genesis/genesis.go +++ b/x/programs/cmd/simulator/vm/genesis/genesis.go @@ -48,9 +48,6 @@ type Genesis struct { // Tx Fee Parameters BaseComputeUnits uint64 `json:"baseUnits"` - BaseWarpComputeUnits uint64 `json:"baseWarpUnits"` - WarpComputeUnitsPerSigner uint64 `json:"warpUnitsPerSigner"` - OutgoingWarpComputeUnits uint64 `json:"outgoingWarpComputeUnits"` StorageKeyReadUnits uint64 `json:"storageKeyReadUnits"` StorageValueReadUnits uint64 `json:"storageValueReadUnits"` StorageKeyAllocateUnits uint64 `json:"storageKeyAllocateUnits"` @@ -84,10 +81,7 @@ func Default() *Genesis { MaxBlockUnits: fees.Dimensions{1_800_000, 2_000, 2_000, 2_000, 2_000}, // Tx Fee Compute Parameters - BaseComputeUnits: 1, - BaseWarpComputeUnits: 1_024, - WarpComputeUnitsPerSigner: 128, - OutgoingWarpComputeUnits: 1_024, + BaseComputeUnits: 1, // Tx Fee Storage Parameters // diff --git a/x/programs/cmd/simulator/vm/genesis/rules.go b/x/programs/cmd/simulator/vm/genesis/rules.go index b3248485a5..f9b1c10d87 100644 --- a/x/programs/cmd/simulator/vm/genesis/rules.go +++ b/x/programs/cmd/simulator/vm/genesis/rules.go @@ -59,18 +59,6 @@ func (r *Rules) GetBaseComputeUnits() uint64 { return r.g.BaseComputeUnits } -func (r *Rules) GetBaseWarpComputeUnits() uint64 { - return r.g.BaseWarpComputeUnits -} - -func (r *Rules) GetWarpComputeUnitsPerSigner() uint64 { - return r.g.WarpComputeUnitsPerSigner -} - -func (r *Rules) GetOutgoingWarpComputeUnits() uint64 { - return r.g.OutgoingWarpComputeUnits -} - func (r *Rules) GetStorageKeyReadUnits() uint64 { return r.g.StorageKeyReadUnits } @@ -95,10 +83,6 @@ func (r *Rules) GetStorageValueWriteUnits() uint64 { return r.g.StorageValueWriteUnits } -func (*Rules) GetWarpConfig(ids.ID) (bool, uint64, uint64) { - return false, 0, 0 -} - func (r *Rules) NetworkID() uint32 { return r.networkID } diff --git a/x/programs/cmd/simulator/vm/registry/registry.go b/x/programs/cmd/simulator/vm/registry/registry.go index 9cfda75a8f..00df69e6a1 100644 --- a/x/programs/cmd/simulator/vm/registry/registry.go +++ b/x/programs/cmd/simulator/vm/registry/registry.go @@ -5,7 +5,6 @@ package registry import ( "github.com/ava-labs/avalanchego/utils/wrappers" - "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" @@ -15,8 +14,8 @@ import ( // Setup types func init() { - consts.ActionRegistry = codec.NewTypeParser[chain.Action, *warp.Message]() - consts.AuthRegistry = codec.NewTypeParser[chain.Auth, *warp.Message]() + consts.ActionRegistry = codec.NewTypeParser[chain.Action]() + consts.AuthRegistry = codec.NewTypeParser[chain.Auth]() errs := &wrappers.Errs{} errs.Add( diff --git a/x/programs/cmd/simulator/vm/storage/state_manager.go b/x/programs/cmd/simulator/vm/storage/state_manager.go index 05e6e38b06..ea4d62ea29 100644 --- a/x/programs/cmd/simulator/vm/storage/state_manager.go +++ b/x/programs/cmd/simulator/vm/storage/state_manager.go @@ -6,7 +6,6 @@ package storage import ( "context" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/state" ) @@ -44,11 +43,3 @@ func (*StateManager) TimestampKey() []byte { func (*StateManager) FeeKey() []byte { return FeeKey() } - -func (*StateManager) IncomingWarpKeyPrefix(sourceChainID ids.ID, msgID ids.ID) []byte { - return IncomingWarpKeyPrefix(sourceChainID, msgID) -} - -func (*StateManager) OutgoingWarpKeyPrefix(txID ids.ID) []byte { - return OutgoingWarpKeyPrefix(txID) -} diff --git a/x/programs/cmd/simulator/vm/storage/storage.go b/x/programs/cmd/simulator/vm/storage/storage.go index 0c22d4a878..c3a6b70728 100644 --- a/x/programs/cmd/simulator/vm/storage/storage.go +++ b/x/programs/cmd/simulator/vm/storage/storage.go @@ -24,12 +24,10 @@ const ( // stateDB keyPrefix = 0x0 - programPrefix = 0x1 - heightPrefix = 0x2 - timestampPrefix = 0x3 - feePrefix = 0x4 - incomingWarpPrefix = 0x5 - outgoingWarpPrefix = 0x6 + programPrefix = 0x1 + heightPrefix = 0x2 + timestampPrefix = 0x3 + feePrefix = 0x4 ) var ( @@ -174,18 +172,3 @@ func TimestampKey() (k []byte) { func FeeKey() (k []byte) { return feeKey } - -func IncomingWarpKeyPrefix(sourceChainID ids.ID, msgID ids.ID) (k []byte) { - k = make([]byte, 1+consts.IDLen*2) - k[0] = incomingWarpPrefix - copy(k[1:], sourceChainID[:]) - copy(k[1+consts.IDLen:], msgID[:]) - return k -} - -func OutgoingWarpKeyPrefix(txID ids.ID) (k []byte) { - k = make([]byte, 1+consts.IDLen) - k[0] = outgoingWarpPrefix - copy(k[1:], txID[:]) - return k -} From b7f339150d419f582971d5960489cce82441104c Mon Sep 17 00:00:00 2001 From: Franfran <51274081+iFrostizz@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:55:21 +0200 Subject: [PATCH 2/2] Keep track of pointers and their length with a custom store (#857) * implement new alloc fun with global store * make sure that only known bounds are reached * use a map for the store * remove use of HostPtr * return pointer from go alloc * apply review suggestions * Fix the counter tests * fix token test * remove usage of SmartPtr in test * use is_null to check for pointer nulleness * silent deprecated tag * fix clippy in test * fix panic *expected* messages * remove useless test --------- Co-authored-by: Richard Pringle --- .../simulator/vm/actions/program_execute.go | 18 +-- x/programs/examples/counter_test.go | 39 ++++-- .../examples/imports/program/program.go | 10 +- x/programs/examples/token.go | 62 ++++++++- x/programs/examples/token_test.go | 10 +- x/programs/examples/utils.go | 4 +- x/programs/program/function.go | 10 +- x/programs/program/memory.go | 11 +- x/programs/runtime/dependencies.go | 2 +- x/programs/runtime/runtime.go | 2 +- x/programs/runtime/runtime_test.go | 4 +- x/programs/rust/wasmlanche-sdk/src/memory.rs | 119 +++++++++++------- x/programs/rust/wasmlanche-sdk/src/state.rs | 5 +- .../wasmlanche-sdk/tests/public_function.rs | 42 ++++--- x/programs/tests/fixture/counter.wasm | Bin 40570 -> 45406 bytes x/programs/tests/fixture/token.wasm | Bin 36631 -> 41683 bytes 16 files changed, 221 insertions(+), 117 deletions(-) diff --git a/x/programs/cmd/simulator/vm/actions/program_execute.go b/x/programs/cmd/simulator/vm/actions/program_execute.go index cb7c51b988..2a1f351935 100644 --- a/x/programs/cmd/simulator/vm/actions/program_execute.go +++ b/x/programs/cmd/simulator/vm/actions/program_execute.go @@ -175,32 +175,32 @@ type CallParam struct { // WriteParams is a helper function that writes the given params to memory if non integer. // Supported types include int, uint64 and string. -func WriteParams(m *program.Memory, p []CallParam) ([]program.SmartPtr, error) { - var params []program.SmartPtr +func WriteParams(m *program.Memory, p []CallParam) ([]uint32, error) { + var params []uint32 for _, param := range p { switch v := param.Value.(type) { case []byte: - smartPtr, err := program.BytesToSmartPtr(v, m) + smartPtr, err := program.AllocateBytes(v, m) if err != nil { return nil, err } params = append(params, smartPtr) case ids.ID: - smartPtr, err := program.BytesToSmartPtr(v[:], m) + smartPtr, err := program.AllocateBytes(v[:], m) if err != nil { return nil, err } params = append(params, smartPtr) case string: - smartPtr, err := program.BytesToSmartPtr([]byte(v), m) + smartPtr, err := program.AllocateBytes([]byte(v), m) if err != nil { return nil, err } params = append(params, smartPtr) - case program.SmartPtr: + case uint32: params = append(params, v) default: - ptr, err := argumentToSmartPtr(v, m) + ptr, err := writeToMem(v, m) if err != nil { return nil, err } @@ -218,11 +218,11 @@ func serializeParameter(obj interface{}) ([]byte, error) { } // Serialize the parameter and create a smart ptr -func argumentToSmartPtr(obj interface{}, memory *program.Memory) (program.SmartPtr, error) { +func writeToMem(obj interface{}, memory *program.Memory) (uint32, error) { bytes, err := serializeParameter(obj) if err != nil { return 0, err } - return program.BytesToSmartPtr(bytes, memory) + return program.AllocateBytes(bytes, memory) } diff --git a/x/programs/examples/counter_test.go b/x/programs/examples/counter_test.go index 88a7430260..42601db8a9 100644 --- a/x/programs/examples/counter_test.go +++ b/x/programs/examples/counter_test.go @@ -78,7 +78,7 @@ func TestCounterProgram(t *testing.T) { require.NoError(err) // write alice's key to stack and get pointer - alicePtr, err := argumentToSmartPtr(alicePublicKey, mem) + alicePtr, err := writeToMem(alicePublicKey, mem) require.NoError(err) // create counter for alice on program 1 @@ -86,6 +86,9 @@ func TestCounterProgram(t *testing.T) { require.NoError(err) require.Equal(int64(1), result[0]) + alicePtr, err = writeToMem(alicePublicKey, mem) + require.NoError(err) + // validate counter at 0 result, err = rt.Call(ctx, "get_value", callContext, alicePtr) require.NoError(err) @@ -115,7 +118,7 @@ func TestCounterProgram(t *testing.T) { require.NoError(err) // write alice's key to stack and get pointer - alicePtr2, err := argumentToSmartPtr(alicePublicKey, mem2) + alicePtr2, err := writeToMem(alicePublicKey, mem2) require.NoError(err) callContext1 := program.Context{ProgramID: programID} @@ -128,12 +131,19 @@ func TestCounterProgram(t *testing.T) { // increment alice's counter on program 2 by 10 incAmount := int64(10) - incAmountPtr, err := argumentToSmartPtr(incAmount, mem2) + incAmountPtr, err := writeToMem(incAmount, mem2) + require.NoError(err) + + alicePtr2, err = writeToMem(alicePublicKey, mem2) + require.NoError(err) result, err = rt2.Call(ctx, "inc", callContext2, alicePtr2, incAmountPtr) require.NoError(err) require.Equal(int64(1), result[0]) + alicePtr2, err = writeToMem(alicePublicKey, mem2) + require.NoError(err) + result, err = rt2.Call(ctx, "get_value", callContext2, alicePtr2) require.NoError(err) require.Equal(incAmount, result[0]) @@ -146,12 +156,19 @@ func TestCounterProgram(t *testing.T) { require.NoError(err) // increment alice's counter on program 1 - onePtr, err := argumentToSmartPtr(int64(1), mem) + onePtr, err := writeToMem(int64(1), mem) + require.NoError(err) + + alicePtr, err = writeToMem(alicePublicKey, mem) require.NoError(err) + result, err = rt.Call(ctx, "inc", callContext1, alicePtr, onePtr) require.NoError(err) require.Equal(int64(1), result[0]) + alicePtr, err = writeToMem(alicePublicKey, mem) + require.NoError(err) + result, err = rt.Call(ctx, "get_value", callContext1, alicePtr) require.NoError(err) @@ -160,20 +177,28 @@ func TestCounterProgram(t *testing.T) { ) // write program id 2 to stack of program 1 - target, err := argumentToSmartPtr(programID2, mem) + target, err := writeToMem(programID2, mem) require.NoError(err) maxUnitsProgramToProgram := int64(10000) - maxUnitsProgramToProgramPtr, err := argumentToSmartPtr(maxUnitsProgramToProgram, mem) + maxUnitsProgramToProgramPtr, err := writeToMem(maxUnitsProgramToProgram, mem) require.NoError(err) // increment alice's counter on program 2 - fivePtr, err := argumentToSmartPtr(int64(5), mem) + fivePtr, err := writeToMem(int64(5), mem) + require.NoError(err) + alicePtr, err = writeToMem(alicePublicKey, mem) require.NoError(err) result, err = rt.Call(ctx, "inc_external", callContext1, target, maxUnitsProgramToProgramPtr, alicePtr, fivePtr) require.NoError(err) require.Equal(int64(1), result[0]) + target, err = writeToMem(programID2, mem) + require.NoError(err) + alicePtr, err = writeToMem(alicePublicKey, mem) + require.NoError(err) + maxUnitsProgramToProgramPtr, err = writeToMem(maxUnitsProgramToProgram, mem) + require.NoError(err) // expect alice's counter on program 2 to be 15 result, err = rt.Call(ctx, "get_value_external", callContext1, target, maxUnitsProgramToProgramPtr, alicePtr) require.NoError(err) diff --git a/x/programs/examples/imports/program/program.go b/x/programs/examples/imports/program/program.go index 1d8d6d76c5..37fca43eeb 100644 --- a/x/programs/examples/imports/program/program.go +++ b/x/programs/examples/imports/program/program.go @@ -185,8 +185,8 @@ func (i *Import) callProgramFn(callContext program.Context) func(*wasmtime.Calle } // getCallArgs returns the arguments to be passed to the program being invoked from [buffer]. -func getCallArgs(_ context.Context, memory *program.Memory, buffer []byte) ([]program.SmartPtr, error) { - var args []program.SmartPtr +func getCallArgs(_ context.Context, memory *program.Memory, buffer []byte) ([]uint32, error) { + var args []uint32 for i := 0; i < len(buffer); { // unpacks uint32 @@ -201,11 +201,7 @@ func getCallArgs(_ context.Context, memory *program.Memory, buffer []byte) ([]pr if err != nil { return nil, err } - argPtr, err := program.NewSmartPtr(ptr, int(length)) - if err != nil { - return nil, err - } - args = append(args, argPtr) + args = append(args, ptr) } return args, nil diff --git a/x/programs/examples/token.go b/x/programs/examples/token.go index ec38ce26c0..7bc67c3bb7 100644 --- a/x/programs/examples/token.go +++ b/x/programs/examples/token.go @@ -134,7 +134,7 @@ func (t *Token) Run(ctx context.Context) error { } // write alice's key to stack and get pointer - alicePtr, err := argumentToSmartPtr(alicePublicKey, mem) + alicePtr, err := writeToMem(alicePublicKey, mem) if err != nil { return err } @@ -146,7 +146,7 @@ func (t *Token) Run(ctx context.Context) error { } // write bob's key to stack and get pointer - bobPtr, err := argumentToSmartPtr(bobPublicKey, mem) + bobPtr, err := writeToMem(bobPublicKey, mem) if err != nil { return err } @@ -162,7 +162,7 @@ func (t *Token) Run(ctx context.Context) error { // mint 100 tokens to alice mintAlice := int64(1000) - mintAlicePtr, err := argumentToSmartPtr(mintAlice, mem) + mintAlicePtr, err := writeToMem(mintAlice, mem) if err != nil { return err } @@ -175,6 +175,11 @@ func (t *Token) Run(ctx context.Context) error { zap.Int64("alice", mintAlice), ) + alicePtr, err = writeToMem(alicePublicKey, mem) + if err != nil { + return err + } + // check balance of alice result, err = rt.Call(ctx, "get_balance", programContext, alicePtr) if err != nil { @@ -184,6 +189,11 @@ func (t *Token) Run(ctx context.Context) error { zap.Int64("alice", result[0]), ) + bobPtr, err = writeToMem(bobPublicKey, mem) + if err != nil { + return err + } + // check balance of bob result, err = rt.Call(ctx, "get_balance", programContext, bobPtr) if err != nil { @@ -195,10 +205,20 @@ func (t *Token) Run(ctx context.Context) error { // transfer 50 from alice to bob transferToBob := int64(50) - transferToBobPtr, err := argumentToSmartPtr(transferToBob, mem) + transferToBobPtr, err := writeToMem(transferToBob, mem) + if err != nil { + return err + } + bobPtr, err = writeToMem(bobPublicKey, mem) + if err != nil { + return err + } + + alicePtr, err = writeToMem(alicePublicKey, mem) if err != nil { return err } + _, err = rt.Call(ctx, "transfer", programContext, alicePtr, bobPtr, transferToBobPtr) if err != nil { return err @@ -208,7 +228,17 @@ func (t *Token) Run(ctx context.Context) error { zap.Int64("to bob", transferToBob), ) - onePtr, err := argumentToSmartPtr(int64(1), mem) + onePtr, err := writeToMem(int64(1), mem) + if err != nil { + return err + } + + bobPtr, err = writeToMem(bobPublicKey, mem) + if err != nil { + return err + } + + alicePtr, err = writeToMem(alicePublicKey, mem) if err != nil { return err } @@ -222,6 +252,11 @@ func (t *Token) Run(ctx context.Context) error { zap.Int64("to bob", 1), ) + alicePtr, err = writeToMem(alicePublicKey, mem) + if err != nil { + return err + } + // get balance alice result, err = rt.Call(ctx, "get_balance", programContext, alicePtr) if err != nil { @@ -231,6 +266,11 @@ func (t *Token) Run(ctx context.Context) error { zap.Int64("alice", result[0]), ) + bobPtr, err = writeToMem(bobPublicKey, mem) + if err != nil { + return err + } + // get balance bob result, err = rt.Call(ctx, "get_balance", programContext, bobPtr) if err != nil { @@ -259,7 +299,7 @@ func (t *Token) Run(ctx context.Context) error { }, } - mintersPtr, err := argumentToSmartPtr(minters, mem) + mintersPtr, err := writeToMem(minters, mem) if err != nil { return err } @@ -274,6 +314,11 @@ func (t *Token) Run(ctx context.Context) error { zap.Int32("to bob", minters[1].Amount), ) + alicePtr, err = writeToMem(alicePublicKey, mem) + if err != nil { + return err + } + // get balance alice result, err = rt.Call(ctx, "get_balance", programContext, alicePtr) if err != nil { @@ -283,6 +328,11 @@ func (t *Token) Run(ctx context.Context) error { zap.Int64("alice", result[0]), ) + bobPtr, err = writeToMem(bobPublicKey, mem) + if err != nil { + return err + } + // get balance bob result, err = rt.Call(ctx, "get_balance", programContext, bobPtr) if err != nil { diff --git a/x/programs/examples/token_test.go b/x/programs/examples/token_test.go index e556567eff..58a37358c5 100644 --- a/x/programs/examples/token_test.go +++ b/x/programs/examples/token_test.go @@ -54,17 +54,20 @@ func TestTokenProgram(t *testing.T) { require.NoError(err) // write alice's key to stack and get pointer - alicePtr, err := argumentToSmartPtr(alicePublicKey, mem) + alicePtr, err := writeToMem(alicePublicKey, mem) require.NoError(err) // mint 100 tokens to alice mintAlice := int64(1000) - mintAlicePtr, err := argumentToSmartPtr(mintAlice, mem) + mintAlicePtr, err := writeToMem(mintAlice, mem) require.NoError(err) _, err = rt.Call(ctx, "mint_to", callContext, alicePtr, mintAlicePtr) require.NoError(err) + alicePtr, err = writeToMem(alicePublicKey, mem) + require.NoError(err) + // check balance of alice result, err := rt.Call(ctx, "get_balance", callContext, alicePtr) require.NoError(err) @@ -75,6 +78,9 @@ func TestTokenProgram(t *testing.T) { require.NoError(err) require.Equal(int64(1000), aliceBalance) + alicePtr, err = writeToMem(alicePublicKey, mem) + require.NoError(err) + // burn alice tokens _, err = rt.Call(ctx, "burn_from", callContext, alicePtr) require.NoError(err) diff --git a/x/programs/examples/utils.go b/x/programs/examples/utils.go index 033784727f..e33df72d4f 100644 --- a/x/programs/examples/utils.go +++ b/x/programs/examples/utils.go @@ -25,13 +25,13 @@ func newKey() (ed25519.PublicKey, error) { } // Serialize the parameter and create a smart ptr -func argumentToSmartPtr(obj interface{}, memory *program.Memory) (program.SmartPtr, error) { +func writeToMem(obj interface{}, memory *program.Memory) (uint32, error) { bytes, err := borsh.Serialize(obj) if err != nil { return 0, err } - return program.BytesToSmartPtr(bytes, memory) + return program.AllocateBytes(bytes, memory) } var ( diff --git a/x/programs/program/function.go b/x/programs/program/function.go index 5bcbb4f1e5..3c6ae3946d 100644 --- a/x/programs/program/function.go +++ b/x/programs/program/function.go @@ -24,7 +24,7 @@ func NewFunc(inner *wasmtime.Func, inst Instance) *Func { } } -func (f *Func) Call(context Context, params ...SmartPtr) ([]int64, error) { +func (f *Func) Call(context Context, params ...uint32) ([]int64, error) { fnParams := f.Type().Params()[1:] // strip program_id if len(params) != len(fnParams) { return nil, fmt.Errorf("%w for function: %d expected: %d", ErrInvalidParamCount, len(params), len(fnParams)) @@ -40,7 +40,7 @@ func (f *Func) Call(context Context, params ...SmartPtr) ([]int64, error) { if err != nil { return nil, err } - contextPtr, err := argumentToSmartPtr(context, mem) + contextPtr, err := writeToMem(context, mem) if err != nil { return nil, err } @@ -64,13 +64,13 @@ func (f *Func) Call(context Context, params ...SmartPtr) ([]int64, error) { } } -func argumentToSmartPtr(obj interface{}, memory *Memory) (SmartPtr, error) { +func writeToMem(obj interface{}, memory *Memory) (uint32, error) { bytes, err := borsh.Serialize(obj) if err != nil { return 0, err } - return BytesToSmartPtr(bytes, memory) + return AllocateBytes(bytes, memory) } func (f *Func) Type() *wasmtime.FuncType { @@ -78,7 +78,7 @@ func (f *Func) Type() *wasmtime.FuncType { } // mapFunctionParams maps call input to the expected wasm function params. -func mapFunctionParams(input []SmartPtr, values []*wasmtime.ValType) ([]interface{}, error) { +func mapFunctionParams(input []uint32, values []*wasmtime.ValType) ([]interface{}, error) { params := make([]interface{}, len(values)) for i, v := range values { switch v.Kind() { diff --git a/x/programs/program/memory.go b/x/programs/program/memory.go index 4ea4e7feb4..b926fa75bd 100644 --- a/x/programs/program/memory.go +++ b/x/programs/program/memory.go @@ -188,14 +188,14 @@ func (s SmartPtr) Bytes(memory *Memory) ([]byte, error) { return bytes, nil } -// BytesToSmartPtr writes [bytes] to memory and returns the resulting SmartPtr. -func BytesToSmartPtr(bytes []byte, memory *Memory) (SmartPtr, error) { +// AllocateBytes writes [bytes] to memory and returns the resulting SmartPtr. +func AllocateBytes(bytes []byte, memory *Memory) (uint32, error) { ptr, err := WriteBytes(memory, bytes) if err != nil { return 0, err } - return NewSmartPtr(ptr, len(bytes)) + return ptr, nil } // NewSmartPtr returns a SmartPtr from [ptr] and [byteLen]. @@ -205,8 +205,5 @@ func NewSmartPtr(ptr uint32, byteLen int) (SmartPtr, error) { return 0, errors.New("length of bytes is greater than int32") } - lenUpperBits := int64(byteLen) << 32 - ptrLowerBits := int64(ptr) - - return SmartPtr(lenUpperBits | ptrLowerBits), nil + return SmartPtr(int64(ptr)), nil } diff --git a/x/programs/runtime/dependencies.go b/x/programs/runtime/dependencies.go index 6f2544b5fe..df75d03eda 100644 --- a/x/programs/runtime/dependencies.go +++ b/x/programs/runtime/dependencies.go @@ -18,7 +18,7 @@ type Runtime interface { // Call invokes an exported guest function with the given parameters. // Returns the results of the call or an error if the call failed. // If the function called does not return a result this value is set to nil. - Call(context.Context, string, program.Context, ...program.SmartPtr) ([]int64, error) + Call(context.Context, string, program.Context, ...uint32) ([]int64, error) // Memory returns the program memory. Memory() (*program.Memory, error) // Meter returns the engine meter. diff --git a/x/programs/runtime/runtime.go b/x/programs/runtime/runtime.go index b0afce835a..98227f8878 100644 --- a/x/programs/runtime/runtime.go +++ b/x/programs/runtime/runtime.go @@ -99,7 +99,7 @@ func (r *WasmRuntime) Initialize(ctx context.Context, callContext program.Contex return nil } -func (r *WasmRuntime) Call(_ context.Context, name string, context program.Context, params ...program.SmartPtr) ([]int64, error) { +func (r *WasmRuntime) Call(_ context.Context, name string, context program.Context, params ...uint32) ([]int64, error) { fn, err := r.inst.GetFunc(name) if err != nil { return nil, err diff --git a/x/programs/runtime/runtime_test.go b/x/programs/runtime/runtime_test.go index 0cfc194e0b..01f4415211 100644 --- a/x/programs/runtime/runtime_test.go +++ b/x/programs/runtime/runtime_test.go @@ -99,10 +99,10 @@ func TestCallParams(t *testing.T) { err = runtime.Initialize(ctx, programContext, wasm, maxUnits) require.NoError(err) - arg := 10 + arg := uint32(10) // all arguments are smart-pointers so this is a bit of a hack - resp, err := runtime.Call(ctx, "add", programContext, program.SmartPtr(arg), program.SmartPtr(arg)) + resp, err := runtime.Call(ctx, "add", programContext, arg, arg) require.NoError(err) require.Equal(int64(arg+arg), resp[0]) diff --git a/x/programs/rust/wasmlanche-sdk/src/memory.rs b/x/programs/rust/wasmlanche-sdk/src/memory.rs index 955fe7a189..5f491291ec 100644 --- a/x/programs/rust/wasmlanche-sdk/src/memory.rs +++ b/x/programs/rust/wasmlanche-sdk/src/memory.rs @@ -6,6 +6,7 @@ use crate::state::Error as StateError; use borsh::{from_slice, BorshDeserialize}; +use std::{alloc::Layout, cell::RefCell, collections::HashMap}; /// Represents a pointer to a block of memory allocated by the global allocator. #[derive(Clone, Copy)] @@ -32,8 +33,14 @@ impl From for *mut u8 { /// `HostPtr` is an i64 where the first 4 bytes represent the length of the bytes /// and the following 4 bytes represent a pointer to WASM memeory where the bytes are stored. +// #[deprecated] TODO fix in a followup pr pub type HostPtr = i64; +thread_local! { + /// Map of pointer to the length of its content on the heap + static GLOBAL_STORE: RefCell> = RefCell::new(HashMap::new()); +} + /// Converts a pointer to a i64 with the first 4 bytes of the pointer /// representing the length of the memory block. /// # Errors @@ -53,21 +60,6 @@ pub fn to_host_ptr(arg: &[u8]) -> Result { Ok(host_ptr) } -/// Converts a i64 to a pointer with the first 4 bytes of the pointer -/// representing the length of the memory block. -/// # Panics -/// Panics if arg is negative. -#[must_use] -#[allow(clippy::cast_sign_loss)] -pub fn split_host_ptr(arg: HostPtr) -> (i64, usize) { - assert!(arg >= 0); - - let len = arg >> 32; - let mask: u32 = !0; - let ptr = arg & i64::from(mask); - (ptr, len as usize) -} - /// Converts a raw pointer to a deserialized value. /// Expects the first 4 bytes of the pointer to represent the `length` of the serialized value, /// with the subsequent `length` bytes comprising the serialized data. @@ -77,56 +69,89 @@ pub fn split_host_ptr(arg: HostPtr) -> (i64, usize) { /// This function is unsafe because it dereferences raw pointers. /// # Errors /// Returns an [`StateError`] if the bytes cannot be deserialized. -pub unsafe fn from_host_ptr(ptr: HostPtr) -> Result +pub fn from_host_ptr(ptr: i64) -> Result where V: BorshDeserialize, { - let bytes = into_bytes(ptr); - from_slice::(&bytes).map_err(|_| StateError::Deserialization) + match into_bytes(ptr) { + Some(bytes) => from_slice::(&bytes).map_err(|_| StateError::Deserialization), + None => Err(StateError::InvalidPointer), + } } -/// Returns a tuple of the bytes and length of the argument. +/// Reconstructs the vec from the pointer with the length given by the store /// `host_ptr` is encoded using Big Endian as an i64. #[must_use] -pub fn into_bytes(host_ptr: HostPtr) -> Vec { - // grab length from ptrArg - let (ptr, len) = split_host_ptr(host_ptr); - let value = unsafe { std::slice::from_raw_parts(ptr as *const u8, len) }; - value.to_vec() +fn into_bytes(ptr: HostPtr) -> Option> { + GLOBAL_STORE + .with_borrow_mut(|s| s.remove(&(ptr as *const u8))) + .map(|len| unsafe { std::vec::Vec::from_raw_parts(ptr as *mut u8, len, len) }) } /* memory functions ------------------------------------------- */ /// Allocate memory into the instance of Program and return the offset to the /// start of the block. /// # Panics -/// Panics if the pointer exceeds the maximum size of an i64. +/// Panics if the pointer exceeds the maximum size of an isize or that the allocated memory is null. #[no_mangle] pub extern "C" fn alloc(len: usize) -> *mut u8 { - // create a new mutable buffer with capacity `len` - let mut buf = Vec::with_capacity(len); - // take a mutable pointer to the buffer - let ptr = buf.as_mut_ptr(); - // ensure memory pointer is fits in an i64 - // to avoid potential issues when passing - // across wasm boundary - assert!(i64::try_from(ptr as u64).is_ok()); - // take ownership of the memory block and - // ensure that its destructor is not - // called when the object goes out of scope - // at the end of the function - std::mem::forget(buf); + assert!(len > 0, "cannot allocate 0 sized data"); + // can only fail if `len > isize::MAX` for u8 + let layout = Layout::array::(len).expect("capacity overflow"); + // take a mutable pointer to the layout + let ptr = unsafe { std::alloc::alloc(layout) }; + if ptr.is_null() { + std::alloc::handle_alloc_error(layout); + } + // keep track of the pointer and the length of the allocated data + GLOBAL_STORE.with_borrow_mut(|s| s.insert(ptr, len)); // return the pointer so the runtime // can write data at this offset ptr } -/// # Safety -/// `ptr` must be a pointer to a block of memory. -/// -/// deallocates the memory block at `ptr` with a given `capacity`. -#[no_mangle] -pub unsafe extern "C" fn dealloc(ptr: *mut u8, capacity: usize) { - // always deallocate the full capacity, initialize vs uninitialized memory is irrelevant here - let data = Vec::from_raw_parts(ptr, capacity, capacity); - std::mem::drop(data); +#[cfg(test)] +mod tests { + use super::{alloc, into_bytes}; + use crate::memory::GLOBAL_STORE; + + #[test] + fn data_allocation() { + let len = 1024; + let ptr = alloc(len); + let vec = vec![1; len]; + unsafe { std::ptr::copy(vec.as_ptr(), ptr, vec.len()) } + let val = into_bytes(ptr as i64).unwrap(); + assert_eq!(val, vec); + assert!(GLOBAL_STORE.with_borrow(|s| s.get(&(ptr.cast_const())).is_none())); + } + + #[test] + #[should_panic = "cannot allocate 0 sized data"] + fn zero_allocation_panics() { + alloc(0); + } + + #[test] + #[should_panic = "capacity overflow"] + fn big_allocation_fails() { + // see https://doc.rust-lang.org/1.77.2/std/alloc/struct.Layout.html#method.array + alloc(isize::MAX as usize + 1); + } + + // TODO these two tests make the code abort and not panic, it's hard to write an assertion here + // #[test] + // #[should_panic] + // fn two_big_allocation_fails() { + // // see https://doc.rust-lang.org/1.77.2/std/alloc/struct.Layout.html#method.array + // alloc((isize::MAX / 2) as usize + 1); + // alloc((isize::MAX / 2) as usize + 1); + // } + + // #[test] + // #[should_panic] + // fn null_pointer_allocation() { + // // see https://doc.rust-lang.org/1.77.2/std/alloc/struct.Layout.html#method.array + // alloc(isize::MAX as usize); + // } } diff --git a/x/programs/rust/wasmlanche-sdk/src/state.rs b/x/programs/rust/wasmlanche-sdk/src/state.rs index 9662541d98..eab090ec9e 100644 --- a/x/programs/rust/wasmlanche-sdk/src/state.rs +++ b/x/programs/rust/wasmlanche-sdk/src/state.rs @@ -13,6 +13,9 @@ pub enum Error { #[error("invalid byte length: {0}")] InvalidByteLength(usize), + #[error("invalid pointer offset")] + InvalidPointer, + #[error("invalid tag: {0}")] InvalidTag(u8), @@ -79,7 +82,7 @@ impl State { } // Wrap in OK for now, change from_raw_ptr to return Result - unsafe { from_host_ptr(val_ptr) } + from_host_ptr(val_ptr) } /// Delete a value from the hosts's storage. diff --git a/x/programs/rust/wasmlanche-sdk/tests/public_function.rs b/x/programs/rust/wasmlanche-sdk/tests/public_function.rs index 0881b9b40d..7e68339d86 100644 --- a/x/programs/rust/wasmlanche-sdk/tests/public_function.rs +++ b/x/programs/rust/wasmlanche-sdk/tests/public_function.rs @@ -2,7 +2,7 @@ use std::{ path::{Path, PathBuf}, process::Command, }; -use wasmlanche_sdk::{Context, HostPtr, Program}; +use wasmlanche_sdk::{Context, Program}; use wasmtime::{Instance, Module, Store, TypedFunc}; const WASM_TARGET: &str = "wasm32-unknown-unknown"; @@ -46,19 +46,10 @@ fn public_functions() { let mut test_crate = TestCrate::new(wasm_path); - let context_ptr = { - let program_id: [u8; Program::LEN] = std::array::from_fn(|_| 1); - // this is a hack to create a program since the constructor is private - let program: Program = - borsh::from_slice(&program_id).expect("the program should deserialize"); - let context = Context { program }; - let serialized_context = borsh::to_vec(&context).expect("failed to serialize context"); - - test_crate.allocate(serialized_context) - }; - + let context_ptr = test_crate.write_context(); assert!(test_crate.always_true(context_ptr)); + let context_ptr = test_crate.write_context(); let combined_binary_digits = test_crate.combine_last_bit_of_each_id_byte(context_ptr); assert_eq!(combined_binary_digits, u32::MAX); } @@ -70,8 +61,8 @@ struct TestCrate { store: Store<()>, instance: Instance, allocate_func: TypedFunc, - always_true_func: TypedFunc, - combine_last_bit_of_each_id_byte_func: TypedFunc, + always_true_func: TypedFunc, + combine_last_bit_of_each_id_byte_func: TypedFunc, } impl TestCrate { @@ -100,7 +91,18 @@ impl TestCrate { } } - fn allocate(&mut self, data: Vec) -> HostPtr { + fn write_context(&mut self) -> i32 { + let program_id: [u8; Program::LEN] = std::array::from_fn(|_| 1); + // this is a hack to create a program since the constructor is private + let program: Program = + borsh::from_slice(&program_id).expect("the program should deserialize"); + let context = Context { program }; + let serialized_context = borsh::to_vec(&context).expect("failed to serialize context"); + + self.allocate(serialized_context) + } + + fn allocate(&mut self, data: Vec) -> i32 { let offset = self .allocate_func .call(&mut self.store, data.len() as i32) @@ -114,19 +116,19 @@ impl TestCrate { .write(&mut self.store, offset as usize, &data) .expect("failed to write data to memory"); - ((data.len() as HostPtr) << 32) | offset as HostPtr + offset } - fn always_true(&mut self, ptr: HostPtr) -> bool { + fn always_true(&mut self, ptr: i32) -> bool { self.always_true_func - .call(&mut self.store, ptr) + .call(&mut self.store, ptr as i64) .expect("failed to call `always_true` function") == true as i64 } - fn combine_last_bit_of_each_id_byte(&mut self, ptr: HostPtr) -> u32 { + fn combine_last_bit_of_each_id_byte(&mut self, ptr: i32) -> u32 { self.combine_last_bit_of_each_id_byte_func - .call(&mut self.store, ptr) + .call(&mut self.store, ptr as i64) .expect("failed to call `combine_last_bit_of_each_id_byte` function") } } diff --git a/x/programs/tests/fixture/counter.wasm b/x/programs/tests/fixture/counter.wasm index 794aa6585a84ddd2d6cc4d01c19adb310bf48710..232da1db6929aa8a5a6c4ad3e581b0de44f13f95 100755 GIT binary patch delta 15139 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 U7NO7vzj^5mBaMS~Q>N>`0fW~zO#lD@ delta 10292 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 delta 9353 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 q8&